#include "../../unity/unity.h" #include #include #include #include /* Unity fixtures */ void setUp(void) { /* No global setup required */ } void tearDown(void) { /* No global teardown required */ } /* Helper: create a struct line with optional fields array and optional buffer. Note: freeline() frees only line->fields and line->buf.buffer, then delseq frees the struct line itself. We therefore avoid allocating per-field beg, to prevent leaks not handled by freeline. */ static struct line* make_line_with(size_t nfields, size_t bufsize_bytes) { struct line *ln = (struct line*) malloc(sizeof *ln); if (!ln) return NULL; memset(ln, 0, sizeof *ln); if (nfields > 0) { ln->fields = (struct field*) malloc(nfields * sizeof *ln->fields); if (!ln->fields) { free(ln); return NULL; } /* We do not allocate per-field beg pointers; leave them NULL with len 0. */ for (size_t i = 0; i < nfields; i++) { ln->fields[i].beg = NULL; ln->fields[i].len = 0; } ln->nfields = (idx_t)nfields; ln->nfields_allocated = (idx_t)nfields; } else { ln->fields = NULL; ln->nfields = 0; ln->nfields_allocated = 0; } if (bufsize_bytes > 0) { ln->buf.buffer = (char*) malloc(bufsize_bytes); if (ln->buf.buffer) { memset(ln->buf.buffer, 'X', bufsize_bytes); ln->buf.length = (idx_t)bufsize_bytes; } else { ln->buf.buffer = NULL; ln->buf.length = 0; } } else { ln->buf.buffer = NULL; ln->buf.length = 0; } return ln; } /* Test: delseq on an empty sequence (alloc=0, lines=NULL) should be a no-op */ static void test_delseq_empty_seq(void) { struct seq s; s.count = 0; s.alloc = 0; s.lines = NULL; delseq(&s); TEST_ASSERT_EQUAL_UINT(0, (unsigned)s.alloc); TEST_ASSERT_NULL(s.lines); } /* Test: delseq on a lines array populated entirely with NULL entries */ static void test_delseq_null_entries_only(void) { struct seq s; s.count = 0; s.alloc = 5; s.lines = (struct line**) calloc(s.alloc, sizeof *s.lines); TEST_ASSERT_NOT_NULL(s.lines); void *oldptr = s.lines; delseq(&s); /* delseq does not modify s.alloc nor s.lines pointer value */ TEST_ASSERT_EQUAL_UINT(5, (unsigned)s.alloc); TEST_ASSERT_EQUAL_PTR(oldptr, s.lines); } /* Test: delseq on a sequence with mixed entries (NULL and allocated lines). Verifies it safely frees buffers and fields of non-NULL lines. */ static void test_delseq_mixed_entries(void) { struct seq s; s.count = 0; s.alloc = 4; s.lines = (struct line**) malloc(s.alloc * sizeof *s.lines); TEST_ASSERT_NOT_NULL(s.lines); s.lines[0] = make_line_with(0, 0); /* No fields, no buffer */ s.lines[1] = NULL; /* NULL entry */ s.lines[2] = make_line_with(3, 64); /* Fields array and a buffer */ s.lines[3] = make_line_with(0, 128); /* Only buffer */ TEST_ASSERT_NOT_NULL(s.lines[0]); TEST_ASSERT_NULL(s.lines[1]); TEST_ASSERT_NOT_NULL(s.lines[2]); TEST_ASSERT_NOT_NULL(s.lines[3]); void *oldptr = s.lines; delseq(&s); TEST_ASSERT_EQUAL_UINT(4, (unsigned)s.alloc); TEST_ASSERT_EQUAL_PTR(oldptr, s.lines); } /* Test: delseq on a larger array to exercise loop bounds and performance */ static void test_delseq_large_array(void) { const size_t N = 1000; struct seq s; s.count = 0; s.alloc = (idx_t)N; s.lines = (struct line**) malloc(N * sizeof *s.lines); TEST_ASSERT_NOT_NULL(s.lines); /* Populate every 10th element; leave others NULL */ for (size_t i = 0; i < N; i++) { if (i % 10 == 0) { s.lines[i] = make_line_with(2, 32); TEST_ASSERT_NOT_NULL_MESSAGE(s.lines[i], "Allocation failed in setup"); } else { s.lines[i] = NULL; } } void *oldptr = s.lines; delseq(&s); TEST_ASSERT_EQUAL_UINT(N, (unsigned)s.alloc); TEST_ASSERT_EQUAL_PTR(oldptr, s.lines); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_delseq_empty_seq); RUN_TEST(test_delseq_null_entries_only); RUN_TEST(test_delseq_mixed_entries); RUN_TEST(test_delseq_large_array); return UNITY_END(); }