#include "../../unity/unity.h" #include #include #include #include #include #include #include /* 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(); }