#include "../../unity/unity.h" #include #include #include #include #include #include /* Helper: free all dynamically allocated outlist nodes and reset head/tail. outlist_head and outlist_end are defined in the program under test. */ static void reset_outlist(void) { /* Free all nodes starting from the first real node. */ struct outlist *p = outlist_head.next; while (p) { struct outlist *n = p->next; free(p); p = n; } /* Reset list to initial empty state. */ outlist_head.next = NULL; outlist_end = &outlist_head; } /* Subprocess runner to check that invalid inputs are rejected. Returns: 1 if child terminated abnormally (signal) or with non-zero exit. 0 if child exited normally with status 0 (unexpected). On fork/wait errors, this test will assert-fail. */ static int run_child_expect_failure(int file, idx_t field) { fflush(stdout); fflush(stderr); pid_t pid = fork(); if (pid < 0) { TEST_FAIL_MESSAGE("fork failed"); return 0; } if (pid == 0) { /* Child: attempt invalid call. If it returns, exit 0 to signal failure. */ /* Ensure child's list state is independent due to copy-on-write; no need to cleanup. */ add_field(file, field); _exit(0); } int status = 0; pid_t w = waitpid(pid, &status, 0); if (w < 0) { TEST_FAIL_MESSAGE("waitpid failed"); return 0; } if (WIFEXITED(status)) { int code = WEXITSTATUS(status); return code != 0; /* success if non-zero exit */ } else if (WIFSIGNALED(status)) { /* Aborted by a signal (e.g., SIGABRT). Treat as success. */ return 1; } /* Other cases are unexpected; treat as failure. */ return 0; } void setUp(void) { reset_outlist(); } void tearDown(void) { reset_outlist(); } /* Test: adding a single (file=0, field=0) node into empty list. */ static void test_add_field_single_zero_file_zero_field(void) { TEST_ASSERT_NULL(outlist_head.next); TEST_ASSERT_EQUAL_PTR(&outlist_head, outlist_end); add_field(0, 0); TEST_ASSERT_NOT_NULL(outlist_head.next); struct outlist *n1 = outlist_head.next; TEST_ASSERT_EQUAL_INT(0, n1->file); TEST_ASSERT_EQUAL(0, n1->field); TEST_ASSERT_NULL(n1->next); TEST_ASSERT_EQUAL_PTR(n1, outlist_end); } /* Test: appending multiple nodes maintains correct order and content. */ static void test_add_field_multiple_append_order_and_values(void) { add_field(1, 3); add_field(2, 4); add_field(1, 0); struct outlist *n1 = outlist_head.next; TEST_ASSERT_NOT_NULL(n1); TEST_ASSERT_EQUAL_INT(1, n1->file); TEST_ASSERT_EQUAL(3, n1->field); struct outlist *n2 = n1->next; TEST_ASSERT_NOT_NULL(n2); TEST_ASSERT_EQUAL_INT(2, n2->file); TEST_ASSERT_EQUAL(4, n2->field); struct outlist *n3 = n2->next; TEST_ASSERT_NOT_NULL(n3); TEST_ASSERT_EQUAL_INT(1, n3->file); TEST_ASSERT_EQUAL(0, n3->field); TEST_ASSERT_NULL(n3->next); TEST_ASSERT_EQUAL_PTR(n3, outlist_end); } /* Test: outlist_end updates correctly after each append. */ static void test_add_field_tail_updates_correctly(void) { TEST_ASSERT_EQUAL_PTR(&outlist_head, outlist_end); add_field(1, 1); struct outlist *n1 = outlist_head.next; TEST_ASSERT_EQUAL_PTR(n1, outlist_end); TEST_ASSERT_NULL(n1->next); add_field(2, 2); struct outlist *n2 = n1->next; TEST_ASSERT_NOT_NULL(n2); TEST_ASSERT_EQUAL_PTR(n2, outlist_end); TEST_ASSERT_NULL(n2->next); TEST_ASSERT_EQUAL_PTR(n2, outlist_head.next->next); add_field(1, 99); struct outlist *n3 = n2->next; TEST_ASSERT_NOT_NULL(n3); TEST_ASSERT_EQUAL_PTR(n3, outlist_end); TEST_ASSERT_NULL(n3->next); TEST_ASSERT_EQUAL_INT(1, n3->file); TEST_ASSERT_EQUAL(99, n3->field); } /* Test: large field value is stored as-is. */ static void test_add_field_large_field_value(void) { idx_t big = (idx_t)123456789; add_field(2, big); struct outlist *n1 = outlist_head.next; TEST_ASSERT_NOT_NULL(n1); TEST_ASSERT_EQUAL_INT(2, n1->file); TEST_ASSERT_EQUAL(big, n1->field); TEST_ASSERT_EQUAL_PTR(n1, outlist_end); } /* Test: invalid file number should fail (affirm). */ static void test_add_field_invalid_file_number_fails(void) { /* Values not equal to 0,1,2 should trigger failure. */ int ok_neg = run_child_expect_failure(-1, 0); int ok_3 = run_child_expect_failure(3, 0); TEST_ASSERT_TRUE(ok_neg); TEST_ASSERT_TRUE(ok_3); } /* Test: file=0 with non-zero field should fail (affirm). */ static void test_add_field_file_zero_with_nonzero_field_fails(void) { int ok = run_child_expect_failure(0, 1); TEST_ASSERT_TRUE(ok); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_add_field_single_zero_file_zero_field); RUN_TEST(test_add_field_multiple_append_order_and_values); RUN_TEST(test_add_field_tail_updates_correctly); RUN_TEST(test_add_field_large_field_value); RUN_TEST(test_add_field_invalid_file_number_fails); RUN_TEST(test_add_field_file_zero_with_nonzero_field_fails); return UNITY_END(); }