#include "../../unity/unity.h" #include #include #include #include #include /* Unity fixtures */ void setUp(void) { /* Ensure default state before each test */ /* empty_filler is a static global from the program; reset here */ extern const char *empty_filler; empty_filler = NULL; } void tearDown(void) { /* Reset empty_filler after each test to avoid cross-test bleed */ extern const char *empty_filler; empty_filler = NULL; } /* Helper: initialize a struct line with given fields. datas: array of pointers to byte buffers (can contain NUL) lens: array of lengths for each field (if NULL, strlen(datas[i]) is used) n: number of fields */ static void helper_init_line(struct line *line, const char *const *datas, const size_t *lens, size_t n) { memset(line, 0, sizeof(*line)); line->nfields = (idx_t)n; line->nfields_allocated = (idx_t)n; if (n) { line->fields = (struct field *)calloc(n, sizeof(struct field)); for (size_t i = 0; i < n; i++) { size_t len = lens ? lens[i] : strlen(datas[i]); char *buf = (char *)malloc(len + 1); if (len) memcpy(buf, datas[i], len); buf[len] = '\0'; /* for safety; not used by prfield */ line->fields[i].beg = buf; line->fields[i].len = (idx_t)len; } } else { line->fields = NULL; } } /* Helper: free any allocations within a struct line created by helper_init_line */ static void helper_free_line(struct line *line) { if (!line) return; for (idx_t i = 0; i < line->nfields; i++) { free(line->fields[i].beg); line->fields[i].beg = NULL; } free(line->fields); line->fields = NULL; line->nfields = 0; line->nfields_allocated = 0; } /* Capture stdout while invoking prfield(n, line). Returns malloc'd buffer containing captured bytes (NUL-terminated for convenience). out_len, if non-NULL, is set to the number of bytes captured. Returns NULL on failure. */ static char *capture_prfield_output(idx_t n, const struct line *line, size_t *out_len) { fflush(stdout); int saved_fd = dup(STDOUT_FILENO); if (saved_fd < 0) { return NULL; } FILE *tmp = tmpfile(); if (!tmp) { close(saved_fd); return NULL; } int tmp_fd = fileno(tmp); if (dup2(tmp_fd, STDOUT_FILENO) < 0) { fclose(tmp); close(saved_fd); return NULL; } /* Call the target function while stdout is redirected. Do not use Unity assertions until stdout is restored. */ prfield(n, line); fflush(stdout); /* Determine size and read back */ long sz = 0; if (fseek(tmp, 0, SEEK_END) == 0) { sz = ftell(tmp); if (sz < 0) sz = 0; fseek(tmp, 0, SEEK_SET); } size_t size = (size_t)sz; char *buf = (char *)malloc(size + 1); if (!buf) { /* Restore stdout before returning on failure */ dup2(saved_fd, STDOUT_FILENO); close(saved_fd); fclose(tmp); return NULL; } if (size) { size_t r = fread(buf, 1, size, tmp); (void)r; /* best effort */ } buf[size] = '\0'; /* Restore stdout */ fflush(stdout); dup2(saved_fd, STDOUT_FILENO); close(saved_fd); fclose(tmp); if (out_len) *out_len = size; return buf; } /* Tests */ static void test_prfield_prints_existing_nonempty_field(void) { struct line line; const char *datas[] = { "key", "value", "foo" }; helper_init_line(&line, datas, NULL, 3); size_t out_len = 0; char *out = capture_prfield_output((idx_t)1, &line, &out_len); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_size_t(strlen("value"), out_len); TEST_ASSERT_EQUAL_STRING_LEN("value", out, out_len); free(out); helper_free_line(&line); } static void test_prfield_uses_empty_filler_for_empty_field(void) { extern const char *empty_filler; empty_filler = "(nil)"; struct line line; const char *datas[] = { "" }; size_t lens[] = { 0 }; /* explicitly empty field */ helper_init_line(&line, datas, lens, 1); size_t out_len = 0; char *out = capture_prfield_output((idx_t)0, &line, &out_len); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_size_t(strlen("(nil)"), out_len); TEST_ASSERT_EQUAL_STRING_LEN("(nil)", out, out_len); free(out); helper_free_line(&line); } static void test_prfield_prints_nothing_for_empty_field_without_filler(void) { extern const char *empty_filler; empty_filler = NULL; struct line line; const char *datas[] = { "" }; size_t lens[] = { 0 }; helper_init_line(&line, datas, lens, 1); size_t out_len = 0; char *out = capture_prfield_output((idx_t)0, &line, &out_len); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_size_t(0, out_len); TEST_ASSERT_EQUAL_STRING("", out); free(out); helper_free_line(&line); } static void test_prfield_uses_empty_filler_for_missing_field(void) { extern const char *empty_filler; empty_filler = "[MISSING]"; struct line line; const char *datas[] = { "only-one" }; helper_init_line(&line, datas, NULL, 1); size_t out_len = 0; char *out = capture_prfield_output((idx_t)5, &line, &out_len); /* beyond nfields */ TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_size_t(strlen("[MISSING]"), out_len); TEST_ASSERT_EQUAL_STRING_LEN("[MISSING]", out, out_len); free(out); helper_free_line(&line); } static void test_prfield_prints_nothing_for_missing_field_without_filler(void) { extern const char *empty_filler; empty_filler = NULL; struct line line; const char *datas[] = { "field0" }; helper_init_line(&line, datas, NULL, 1); size_t out_len = 0; char *out = capture_prfield_output((idx_t)1000, &line, &out_len); /* far beyond nfields */ TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_size_t(0, out_len); TEST_ASSERT_EQUAL_STRING("", out); free(out); helper_free_line(&line); } static void test_prfield_handles_binary_field_data(void) { /* Field contains embedded NUL: "AB\0C" length 4 */ char bin[4]; bin[0] = 'A'; bin[1] = 'B'; bin[2] = '\0'; bin[3] = 'C'; const char *datas[] = { bin }; size_t lens[] = { 4 }; struct line line; helper_init_line(&line, datas, lens, 1); size_t out_len = 0; char *out = capture_prfield_output((idx_t)0, &line, &out_len); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_size_t(4, out_len); TEST_ASSERT_EQUAL_CHAR('A', out[0]); TEST_ASSERT_EQUAL_CHAR('B', out[1]); TEST_ASSERT_EQUAL_CHAR('\0', out[2]); TEST_ASSERT_EQUAL_CHAR('C', out[3]); free(out); helper_free_line(&line); } /* Unity runner */ int main(void) { UNITY_BEGIN(); RUN_TEST(test_prfield_prints_existing_nonempty_field); RUN_TEST(test_prfield_uses_empty_filler_for_empty_field); RUN_TEST(test_prfield_prints_nothing_for_empty_field_without_filler); RUN_TEST(test_prfield_uses_empty_filler_for_missing_field); RUN_TEST(test_prfield_prints_nothing_for_missing_field_without_filler); RUN_TEST(test_prfield_handles_binary_field_data); return UNITY_END(); }