#include "../../unity/unity.h" #include #include #include /* setUp and tearDown are required by Unity */ void setUp(void) { /* Disable order checking to avoid dereferencing any stale prevline pointers left from previous tests, since we free lines after each test. */ check_input_order = CHECK_ORDER_DISABLED; /* Reset the unpairable flag just in case any logic consults it. */ seen_unpairable = false; } void tearDown(void) { /* Ensure any spare lines are freed to avoid leaks across tests. */ free_spareline(); } /* Helper: create a temporary file preloaded with 'content' and rewound. */ static FILE* make_tempfile_with(const char* content) { FILE *fp = tmpfile(); TEST_ASSERT_NOT_NULL_MESSAGE(fp, "tmpfile() failed"); if (content && *content) { size_t len = strlen(content); TEST_ASSERT_EQUAL_MESSAGE(len, fwrite(content, 1, len, fp), "Failed to write content to tmp file"); } fflush(fp); rewind(fp); return fp; } /* Validate a field equals the given string (length and bytes). */ static void assert_field_equals(const struct field *f, const char *s) { size_t slen = strlen(s); TEST_ASSERT_EQUAL_size_t(slen, (size_t)f->len); if (slen > 0) { TEST_ASSERT_NOT_NULL(f->beg); TEST_ASSERT_EQUAL_INT(0, memcmp(f->beg, s, slen)); } } /* Test: getseq reads a single line and parses three fields separated by blanks. */ void test_getseq_reads_single_line_and_fields(void) { FILE *fp = make_tempfile_with("a b c\n"); struct seq s; initseq(&s); bool ok = getseq(fp, &s, 1); TEST_ASSERT_TRUE(ok); TEST_ASSERT_TRUE(s.count == 1); TEST_ASSERT_NOT_NULL(s.lines); TEST_ASSERT_TRUE(s.alloc >= 1); TEST_ASSERT_NOT_NULL(s.lines[0]); struct line *l = s.lines[0]; TEST_ASSERT_NOT_NULL(l); TEST_ASSERT_TRUE(l->nfields == 3); TEST_ASSERT_NOT_NULL(l->fields); assert_field_equals(&l->fields[0], "a"); assert_field_equals(&l->fields[1], "b"); assert_field_equals(&l->fields[2], "c"); delseq(&s); fclose(fp); } /* Test: getseq returns false on EOF and does not increment count. */ void test_getseq_returns_false_on_empty_file(void) { FILE *fp = make_tempfile_with(""); struct seq s; initseq(&s); bool ok = getseq(fp, &s, 1); TEST_ASSERT_FALSE(ok); TEST_ASSERT_TRUE(s.count == 0); /* Storage should have been allocated, and the first slot initialized to NULL. */ TEST_ASSERT_NOT_NULL(s.lines); TEST_ASSERT_TRUE(s.alloc >= 1); TEST_ASSERT_NULL(s.lines[0]); delseq(&s); fclose(fp); } /* Test: getseq reads multiple lines and increments count appropriately. */ void test_getseq_reads_multiple_lines(void) { FILE *fp = make_tempfile_with("x\ny\nz\n"); struct seq s; initseq(&s); bool ok1 = getseq(fp, &s, 1); TEST_ASSERT_TRUE(ok1); TEST_ASSERT_TRUE(s.count == 1); TEST_ASSERT_NOT_NULL(s.lines[0]); TEST_ASSERT_TRUE(s.lines[0]->nfields == 1); assert_field_equals(&s.lines[0]->fields[0], "x"); bool ok2 = getseq(fp, &s, 1); TEST_ASSERT_TRUE(ok2); TEST_ASSERT_TRUE(s.count == 2); TEST_ASSERT_NOT_NULL(s.lines[1]); TEST_ASSERT_TRUE(s.lines[1]->nfields == 1); assert_field_equals(&s.lines[1]->fields[0], "y"); bool ok3 = getseq(fp, &s, 1); TEST_ASSERT_TRUE(ok3); TEST_ASSERT_TRUE(s.count == 3); TEST_ASSERT_NOT_NULL(s.lines[2]); TEST_ASSERT_TRUE(s.lines[2]->nfields == 1); assert_field_equals(&s.lines[2]->fields[0], "z"); /* Next call should hit EOF and return false; count should remain unchanged. */ bool ok4 = getseq(fp, &s, 1); TEST_ASSERT_FALSE(ok4); TEST_ASSERT_TRUE(s.count == 3); delseq(&s); fclose(fp); } /* Test: getseq works for both whichfile values independently. */ void test_getseq_works_for_both_files(void) { FILE *fp1 = make_tempfile_with("a\nb\n"); FILE *fp2 = make_tempfile_with("c\nd\n"); struct seq s1, s2; initseq(&s1); initseq(&s2); /* Read first line from file 1 */ TEST_ASSERT_TRUE(getseq(fp1, &s1, 1)); TEST_ASSERT_TRUE(s1.count == 1); TEST_ASSERT_TRUE(s1.lines[0]->nfields == 1); assert_field_equals(&s1.lines[0]->fields[0], "a"); /* Read first line from file 2 */ TEST_ASSERT_TRUE(getseq(fp2, &s2, 2)); TEST_ASSERT_TRUE(s2.count == 1); TEST_ASSERT_TRUE(s2.lines[0]->nfields == 1); assert_field_equals(&s2.lines[0]->fields[0], "c"); /* Read second line from each */ TEST_ASSERT_TRUE(getseq(fp1, &s1, 1)); TEST_ASSERT_TRUE(s1.count == 2); assert_field_equals(&s1.lines[1]->fields[0], "b"); TEST_ASSERT_TRUE(getseq(fp2, &s2, 2)); TEST_ASSERT_TRUE(s2.count == 2); assert_field_equals(&s2.lines[1]->fields[0], "d"); /* EOF on both */ TEST_ASSERT_FALSE(getseq(fp1, &s1, 1)); TEST_ASSERT_FALSE(getseq(fp2, &s2, 2)); delseq(&s1); delseq(&s2); fclose(fp1); fclose(fp2); } /* Test: handle a line without a trailing newline */ void test_getseq_handles_line_without_trailing_newline(void) { FILE *fp = make_tempfile_with("no-trailing-newline"); struct seq s; initseq(&s); TEST_ASSERT_TRUE(getseq(fp, &s, 1)); TEST_ASSERT_TRUE(s.count == 1); TEST_ASSERT_NOT_NULL(s.lines[0]); TEST_ASSERT_TRUE(s.lines[0]->nfields == 1); assert_field_equals(&s.lines[0]->fields[0], "no-trailing-newline"); /* Next should be EOF */ TEST_ASSERT_FALSE(getseq(fp, &s, 1)); delseq(&s); fclose(fp); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_getseq_reads_single_line_and_fields); RUN_TEST(test_getseq_returns_false_on_empty_file); RUN_TEST(test_getseq_reads_multiple_lines); RUN_TEST(test_getseq_works_for_both_files); RUN_TEST(test_getseq_handles_line_without_trailing_newline); return UNITY_END(); }