File size: 5,273 Bytes
78d2150 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
#include "../../unity/unity.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
/* 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();
} |