#include "../../unity/unity.h" #include #include /* We rely on the definitions from the included program: - static struct buffer_record *head; - static void save_buffer(struct buffer_record *buf); - struct buffer_record and struct line definitions. Since this file is included into the same translation unit, we can access them directly. */ /* Helper to allocate and zero a line node */ static struct line* helper_alloc_line(void) { struct line* l = (struct line*)malloc(sizeof(struct line)); if (l) { memset(l, 0, sizeof(*l)); } return l; } /* Helper to allocate and zero a buffer_record, with a provided line_start. curr_line is initialized to a different allocated line to verify that save_buffer() updates it to line_start. */ static struct buffer_record* helper_alloc_buf(struct line* line_start, struct line** out_orig_curr_line) { struct buffer_record* b = (struct buffer_record*)malloc(sizeof(struct buffer_record)); if (b) { memset(b, 0, sizeof(*b)); b->line_start = line_start; struct line* bogus_curr = helper_alloc_line(); b->curr_line = bogus_curr; b->next = (struct buffer_record*)0x1; /* set non-NULL intentionally; will be reset */ if (out_orig_curr_line) { *out_orig_curr_line = bogus_curr; } } return b; } /* Helper to free a chain of buffers created in tests. We only free line_start and the buffer itself since save_buffer() sets curr_line = line_start. */ static void helper_free_chain(struct buffer_record* start) { while (start) { struct buffer_record* n = start->next; if (start->line_start) { free(start->line_start); } free(start); start = n; } } /* setUp/tearDown required by Unity */ void setUp(void) { /* Reset the global list head to ensure isolation between tests. */ head = NULL; } void tearDown(void) { /* Free any buffers that may still be linked from head. */ if (head) { helper_free_chain(head); head = NULL; } } /* Test: When head is NULL, save_buffer should set head to buf, set buf->curr_line to buf->line_start, and clear buf->next to NULL. */ void test_save_buffer_initial_insert_sets_head_and_fields(void) { struct line* l1 = helper_alloc_line(); TEST_ASSERT_NOT_NULL(l1); struct line* orig_curr = NULL; struct buffer_record* buf = helper_alloc_buf(l1, &orig_curr); TEST_ASSERT_NOT_NULL(buf); TEST_ASSERT_NOT_NULL(orig_curr); /* Also create a garbage next to ensure it is cleared (we'll free it ourselves). */ struct buffer_record* garbage_next = (struct buffer_record*)malloc(sizeof(struct buffer_record)); TEST_ASSERT_NOT_NULL(garbage_next); memset(garbage_next, 0, sizeof(*garbage_next)); buf->next = garbage_next; TEST_ASSERT_NULL(head); save_buffer(buf); TEST_ASSERT_EQUAL_PTR(buf, head); TEST_ASSERT_EQUAL_PTR(buf->line_start, buf->curr_line); TEST_ASSERT_NULL(buf->next); /* Cleanup: free the original (now orphan) curr_line and garbage_next, then the chain via head */ free(orig_curr); free(garbage_next); helper_free_chain(head); head = NULL; } /* Test: Appending to a single-element list. */ void test_save_buffer_appends_to_single_element_list(void) { /* Prepare existing head */ struct line* l_head = helper_alloc_line(); struct line* orig_curr_head = NULL; struct buffer_record* buf_head = helper_alloc_buf(l_head, &orig_curr_head); TEST_ASSERT_NOT_NULL(buf_head); /* First insert: set head */ save_buffer(buf_head); TEST_ASSERT_EQUAL_PTR(buf_head, head); TEST_ASSERT_EQUAL_PTR(buf_head->line_start, buf_head->curr_line); TEST_ASSERT_NULL(buf_head->next); /* Prepare new buffer to append */ struct line* l_new = helper_alloc_line(); struct line* orig_curr_new = NULL; struct buffer_record* buf_new = helper_alloc_buf(l_new, &orig_curr_new); TEST_ASSERT_NOT_NULL(buf_new); /* Append */ save_buffer(buf_new); TEST_ASSERT_EQUAL_PTR(buf_head, head); TEST_ASSERT_EQUAL_PTR(buf_new, buf_head->next); TEST_ASSERT_NULL(buf_new->next); TEST_ASSERT_EQUAL_PTR(buf_new->line_start, buf_new->curr_line); /* Ensure existing node's curr_line remained at its line_start (already set by first save_buffer) */ TEST_ASSERT_EQUAL_PTR(buf_head->line_start, buf_head->curr_line); /* Cleanup orphan curr_line allocations and chain */ free(orig_curr_head); free(orig_curr_new); helper_free_chain(head); head = NULL; } /* Test: Appending to a multi-element list (length >= 2). */ void test_save_buffer_appends_to_multi_element_list(void) { /* Build initial chain of two buffers: A -> B */ struct line* la = helper_alloc_line(); struct line* lb = helper_alloc_line(); struct line* orig_curr_a = NULL; struct line* orig_curr_b = NULL; struct buffer_record* A = helper_alloc_buf(la, &orig_curr_a); struct buffer_record* B = helper_alloc_buf(lb, &orig_curr_b); TEST_ASSERT_NOT_NULL(A); TEST_ASSERT_NOT_NULL(B); /* Insert A then B via save_buffer to form the chain */ save_buffer(A); save_buffer(B); TEST_ASSERT_EQUAL_PTR(A, head); TEST_ASSERT_EQUAL_PTR(B, A->next); TEST_ASSERT_NULL(B->next); /* Prepare C and append it */ struct line* lc = helper_alloc_line(); struct line* orig_curr_c = NULL; struct buffer_record* C = helper_alloc_buf(lc, &orig_curr_c); TEST_ASSERT_NOT_NULL(C); /* Record B's curr_line before appending to ensure it doesn't change */ struct line* B_curr_before = B->curr_line; save_buffer(C); /* Verify the chain: A -> B -> C -> NULL */ TEST_ASSERT_EQUAL_PTR(A, head); TEST_ASSERT_EQUAL_PTR(B, A->next); TEST_ASSERT_EQUAL_PTR(C, B->next); TEST_ASSERT_NULL(C->next); /* Verify new node fields updated */ TEST_ASSERT_EQUAL_PTR(C->line_start, C->curr_line); /* Verify previous nodes unchanged except B->next now points to C */ TEST_ASSERT_EQUAL_PTR(B_curr_before, B->curr_line); /* Cleanup orphan curr_line allocations and chain */ free(orig_curr_a); free(orig_curr_b); free(orig_curr_c); helper_free_chain(head); head = NULL; } /* Test: Multiple appends in sequence to ensure traversal loop works repeatedly. */ void test_save_buffer_multiple_appends_sequence(void) { struct line* l1 = helper_alloc_line(); struct line* l2 = helper_alloc_line(); struct line* l3 = helper_alloc_line(); struct line* oc1 = NULL; struct line* oc2 = NULL; struct line* oc3 = NULL; struct buffer_record* b1 = helper_alloc_buf(l1, &oc1); struct buffer_record* b2 = helper_alloc_buf(l2, &oc2); struct buffer_record* b3 = helper_alloc_buf(l3, &oc3); save_buffer(b1); save_buffer(b2); save_buffer(b3); TEST_ASSERT_EQUAL_PTR(b1, head); TEST_ASSERT_EQUAL_PTR(b2, b1->next); TEST_ASSERT_EQUAL_PTR(b3, b2->next); TEST_ASSERT_NULL(b3->next); TEST_ASSERT_EQUAL_PTR(b1->line_start, b1->curr_line); TEST_ASSERT_EQUAL_PTR(b2->line_start, b2->curr_line); TEST_ASSERT_EQUAL_PTR(b3->line_start, b3->curr_line); free(oc1); free(oc2); free(oc3); helper_free_chain(head); head = NULL; } int main(void) { UNITY_BEGIN(); RUN_TEST(test_save_buffer_initial_insert_sets_head_and_fields); RUN_TEST(test_save_buffer_appends_to_single_element_list); RUN_TEST(test_save_buffer_appends_to_multi_element_list); RUN_TEST(test_save_buffer_multiple_appends_sequence); return UNITY_END(); }