coreutils / tests /cut /tests_for_cut_fields.c
AryaWu's picture
Upload folder using huggingface_hub
78d2150 verified
#include "../../unity/unity.h"
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
/* Globals from cut.c are visible since this file is included into cut.c */
extern struct field_range_pair *frp;
/* Helper: build selection ranges and set frp.
'pairs' is an array of {lo, hi} with 'count' entries.
Returns the allocated array to be freed by the caller. */
static struct field_range_pair* set_frp_from_pairs(const uintmax_t (*pairs)[2], size_t count) {
struct field_range_pair *arr = (struct field_range_pair*) malloc(sizeof(*arr) * (count + 1));
if (!arr) return NULL;
for (size_t i = 0; i < count; i++) {
arr[i].lo = pairs[i][0];
arr[i].hi = pairs[i][1];
}
/* Sentinel to ensure no stepping beyond valid ranges. */
arr[count].lo = UINTMAX_MAX;
arr[count].hi = UINTMAX_MAX;
frp = arr;
return arr;
}
/* Helper: run cut_fields on provided input string with given configuration,
capturing stdout and returning the captured output in a malloc'ed string.
The caller must free the returned string.
out_delim_str==NULL => use default (input delimiter). */
static char* run_cut_fields_capture(
const char *input,
const uintmax_t (*pairs)[2], size_t npairs,
unsigned char in_delim,
const char *out_delim_str,
bool only_delimited)
{
/* Configure globals used by cut_fields. */
suppress_non_delimited = only_delimited;
/* Set input delimiter. */
delim = in_delim;
/* Configure default and actual output delimiter. */
output_delimiter_default[0] = delim;
if (out_delim_str == NULL) {
output_delimiter_string = output_delimiter_default;
output_delimiter_length = 1;
} else {
output_delimiter_string = (char*)out_delim_str;
output_delimiter_length = strlen(out_delim_str);
}
/* Ensure line delimiter is standard newline for these tests. */
line_delim = '\n';
/* Set selection ranges. */
struct field_range_pair *local_frp = set_frp_from_pairs(pairs, npairs);
if (!local_frp) {
/* Allocation failure – return a copy of empty string to avoid NULL. */
char *empty = (char*)malloc(1);
if (empty) empty[0] = '\0';
return empty;
}
/* Prepare input stream. */
FILE *in = tmpfile();
if (!in) {
free(local_frp);
char *empty = (char*)malloc(1);
if (empty) empty[0] = '\0';
return empty;
}
if (input && *input) {
fwrite(input, 1, strlen(input), in);
}
fflush(in);
fseek(in, 0, SEEK_SET);
/* Capture stdout to a temporary file. */
fflush(stdout);
int saved_stdout_fd = dup(fileno(stdout));
FILE *cap = tmpfile();
if (cap == NULL) {
fclose(in);
free(local_frp);
char *empty = (char*)malloc(1);
if (empty) empty[0] = '\0';
return empty;
}
dup2(fileno(cap), fileno(stdout));
clearerr(stdout);
/* Call target function. */
cut_fields(in);
/* Stop capturing and restore stdout. */
fflush(stdout);
dup2(saved_stdout_fd, fileno(stdout));
close(saved_stdout_fd);
/* Read captured output into a buffer. */
fflush(cap);
fseek(cap, 0, SEEK_END);
long sz = ftell(cap);
if (sz < 0) sz = 0;
fseek(cap, 0, SEEK_SET);
char *out = (char*)malloc((size_t)sz + 1);
if (!out) {
out = (char*)malloc(1);
if (out) out[0] = '\0';
} else {
size_t nread = fread(out, 1, (size_t)sz, cap);
out[nread] = '\0';
}
/* Cleanup. */
fclose(cap);
fclose(in);
free(local_frp);
return out;
}
void setUp(void) {
/* Ensure a clean starting state for globals influenced by previous calls. */
suppress_non_delimited = false;
/* Default delimiters */
delim = '\t';
line_delim = '\n';
output_delimiter_default[0] = delim;
output_delimiter_string = output_delimiter_default;
output_delimiter_length = 1;
}
void tearDown(void) {
/* Release buffer possibly allocated by cut_fields. */
if (field_1_buffer) {
free(field_1_buffer);
field_1_buffer = NULL;
field_1_bufsize = 0;
}
}
/* Test: Basic selection with default delimiter (TAB): select fields 2-3. */
void test_cut_fields_basic_selection_default_delim(void) {
const char *input = "a\tb\tc\n1\t2\t3\n";
const uintmax_t pairs[][2] = { {2,3} };
char *out = run_cut_fields_capture(input, pairs, 1, '\t', NULL, false);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("b\tc\n2\t3\n", out);
free(out);
}
/* Test: Non-delimited line without -s should be printed unchanged. */
void test_cut_fields_nondelimited_without_s(void) {
const char *input = "abc\n";
const uintmax_t pairs[][2] = { {2,2} }; /* select field 2 */
char *out = run_cut_fields_capture(input, pairs, 1, ',', NULL, false);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("abc\n", out);
free(out);
}
/* Test: Non-delimited line with -s should be suppressed entirely. */
void test_cut_fields_nondelimited_with_s(void) {
const char *input = "abc\n";
const uintmax_t pairs[][2] = { {1,1} }; /* select field 1 */
char *out = run_cut_fields_capture(input, pairs, 1, ',', NULL, true);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("", out);
free(out);
}
/* Test: Custom output delimiter between selected fields (select 1 and 3). */
void test_cut_fields_custom_output_delimiter(void) {
const char *input = "a,b,c\nx,y,z\n";
const uintmax_t pairs[][2] = { {1,1}, {3,3} };
char *out = run_cut_fields_capture(input, pairs, 2, ',', ":", false);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("a:c\nx:z\n", out);
free(out);
}
/* Test: Line has delimiters but not enough fields; output should be empty line(s). */
void test_cut_fields_missing_field_produces_empty_line(void) {
const char *input = "a,b\nx,y\n";
const uintmax_t pairs[][2] = { {3,3} }; /* third field does not exist */
char *out = run_cut_fields_capture(input, pairs, 1, ',', NULL, false);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("\n\n", out);
free(out);
}
/* Test: Input without trailing newline – ensure output is newline-terminated. */
void test_cut_fields_no_trailing_newline(void) {
const char *input = "a,b,c";
const uintmax_t pairs[][2] = { {2,2} }; /* select field 2 */
char *out = run_cut_fields_capture(input, pairs, 1, ',', NULL, false);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("b\n", out);
free(out);
}
/* Test: Empty selected field (adjacent delimiters) should yield empty output for that line. */
void test_cut_fields_empty_selected_field(void) {
const char *input = "a,,c\n";
const uintmax_t pairs[][2] = { {2,2} }; /* select empty field 2 */
char *out = run_cut_fields_capture(input, pairs, 1, ',', NULL, false);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("\n", out);
free(out);
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_cut_fields_basic_selection_default_delim);
RUN_TEST(test_cut_fields_nondelimited_without_s);
RUN_TEST(test_cut_fields_nondelimited_with_s);
RUN_TEST(test_cut_fields_custom_output_delimiter);
RUN_TEST(test_cut_fields_missing_field_produces_empty_line);
RUN_TEST(test_cut_fields_no_trailing_newline);
RUN_TEST(test_cut_fields_empty_selected_field);
return UNITY_END();
}