#include "c_ArrayList.h" #include #include #include /* ------------------------------------------------------------------------------------------------------------------ */ /* */ typedef struct { size_t active_allocations; size_t total_alloc_bytes; size_t total_free_bytes; size_t realloc_calls; size_t realloc_in_place_count; // 记录有多少次 Realloc 实现了就地复用(模拟伙伴系统) } TestMemoryTracker_t; static TestMemoryTracker_t g_tracker = {0}; static size_t mock_buddy_power_of_two(size_t size) { if (size == 0) return 0; size_t p = 1; while (p < size) p <<= 1; return p; } static void* test_alloc(size_t size, void* ctx) { TestMemoryTracker_t* tracker = (TestMemoryTracker_t*)ctx; if (tracker) { tracker->active_allocations++; tracker->total_alloc_bytes += size; } return malloc(size); } static void test_free(void* ptr, void* ctx) { TestMemoryTracker_t* tracker = (TestMemoryTracker_t*)ctx; if (ptr && tracker) { tracker->active_allocations--; } free(ptr); } static void* test_realloc(void* ptr, size_t old_size, size_t new_size, void* ctx) { TestMemoryTracker_t* tracker = (TestMemoryTracker_t*)ctx; if (tracker) tracker->realloc_calls++; if (new_size == 0) { test_free(ptr, ctx); if (tracker) tracker->total_free_bytes += old_size; return NULL; } if (!ptr) { return test_alloc(new_size, ctx); } // 【模拟伙伴系统核心逻辑】:如果新旧大小落在同一个 2 的幂阶数区间,则直接原地返回,零搬迁! size_t old_buddy = mock_buddy_power_of_two(old_size); size_t new_buddy = mock_buddy_power_of_two(new_size); if (old_buddy == new_buddy && old_buddy != 0) { if (tracker) tracker->realloc_in_place_count++; return ptr; } // 阶数改变,模拟搬迁 void* new_ptr = malloc(new_size); if (!new_ptr) return NULL; size_t copy_size = (old_size < new_size) ? old_size : new_size; memcpy(new_ptr, ptr, copy_size); free(ptr); if (tracker) { tracker->total_alloc_bytes += new_size; tracker->total_free_bytes += old_size; } return new_ptr; } /* ------------------------------------------------------------------------------------------------------------------ */ /* */ // 供 RUN_TEST_FIXTURE 使用的 Setup 和 Teardown 钩子 static void custom_allocator_setup(void) { memset(&g_tracker, 0, sizeof(TestMemoryTracker_t)); } static void custom_allocator_teardown(void) { // 每次测试结束,严格确保没有发生内存泄漏 // 注意:不能在此处直接调用带有返回的断言,因为破坏了测试函数的封装,仅在内部测试中做二次校验或由测试用例本身断言。 } /* ------------------------------------------------------------------------------------------------------------------ */ /* */ typedef struct { int node_id; float threshold; char name[16]; } SensorNode_t; /* ------------------------------------------------------------------------------------------------------------------ */ /* */ // 测试一:基础生命周期与边界防御测试 TEST_CASE(test_lifecycle_and_defense) { c_ArrayList_t list; // 防御测试:无效参数传入 ASSERT_INT_EQ_MSG(C_ERR_PARAM, c_ArrayList_Init(NULL, sizeof(int), 4, NULL), "Should return C_ERR_PARAM when self is NULL"); ASSERT_INT_EQ_MSG(C_ERR_PARAM, c_ArrayList_Init(&list, 0, 4, NULL), "Should return C_ERR_PARAM when item_size is 0"); ASSERT_INT_EQ(0, c_ArrayList_Size(NULL)); // 正常原地初始化 c_err_t err = c_ArrayList_Init(&list, sizeof(int), 5, NULL); ASSERT_INT_EQ(C_SUCCESS, err); ASSERT_INT_EQ(0, c_ArrayList_Size(&list)); ASSERT_INT_EQ(5, list.capacity); ASSERT_PTR_NOT_NULL(list.array); // 销毁幂等性与清除检查 c_ArrayList_Destroy(&list); ASSERT_TRUE(list.array == NULL); ASSERT_INT_EQ(0, list.capacity); ASSERT_INT_EQ(0, list.size); c_ArrayList_Destroy(&list); // 重复销毁不应崩溃 } // 测试二:泛型值复制存储、安全 Read/Get 访问测试 TEST_CASE(test_value_copy_and_access) { c_ArrayList_t list; c_ArrayList_Init(&list, sizeof(SensorNode_t), 2, NULL); SensorNode_t node1 = { .node_id = 101, .threshold = 45.2f, .name = "Temp01" }; SensorNode_t node2 = { .node_id = 102, .threshold = 12.8f, .name = "Humid02" }; // 写入测试 ASSERT_INT_EQ(C_SUCCESS, c_ArrayList_Add(&list, &node1)); ASSERT_INT_EQ(C_SUCCESS, c_ArrayList_Add(&list, &node2)); ASSERT_INT_EQ(2, c_ArrayList_Size(&list)); // 强隔离隔离性检查:修改外部临时变量,内部数据不应被污染 node1.node_id = 999; // 1. 测试 c_ArrayList_Read 拷出副本能力 SensorNode_t read_buffer; ASSERT_INT_EQ(C_SUCCESS, c_ArrayList_Read(&list, 0, &read_buffer)); ASSERT_INT_EQ(101, read_buffer.node_id); // 应该依旧是原值 101 ASSERT_DOUBLE_EQ_MSG(45.2f, read_buffer.threshold, "Float precision checking"); ASSERT_TRUE(strcmp(read_buffer.name, "Temp01") == 0); // 2. 测试 c_ArrayList_Get 直接指针读取能力 SensorNode_t* direct_ptr = (SensorNode_t*)c_ArrayList_Get(&list, 1); ASSERT_PTR_NOT_NULL(direct_ptr); ASSERT_INT_EQ(102, direct_ptr->node_id); // 3. 越界保护检查 ASSERT_INT_EQ(C_ERR_PARAM, c_ArrayList_Read(&list, 2, &read_buffer)); ASSERT_TRUE(c_ArrayList_Get(&list, 5) == NULL); c_ArrayList_Destroy(&list); } // 测试三:高危内存移动(memmove)及 Remove 位移正确性测试 TEST_CASE(test_element_removal_and_shifting) { c_ArrayList_t list; c_ArrayList_Init(&list, sizeof(int), 5, NULL); int values[] = {10, 20, 30, 40, 50}; for(int i = 0; i < 5; i++) { c_ArrayList_Add(&list, &values[i]); } // 移除中间的数字 30 (索引 2) 并精准接住拷出值 int removed_val = 0; ASSERT_INT_EQ(C_ERR_OK, c_ArrayList_Remove(&list, 2, &removed_val)); ASSERT_INT_EQ(30, removed_val); ASSERT_INT_EQ(4, c_ArrayList_Size(&list)); // 极其严苛地检测后面所有元素的向前位移是否对齐正确 ASSERT_INT_EQ(10, *(int*)c_ArrayList_Get(&list, 0)); ASSERT_INT_EQ(20, *(int*)c_ArrayList_Get(&list, 1)); ASSERT_INT_EQ(40, *(int*)c_ArrayList_Get(&list, 2)); // 40 顶替了 30 的位置 ASSERT_INT_EQ(50, *(int*)c_ArrayList_Get(&list, 3)); // 50 顶替了 40 的位置 // 验证静默删除(out_item 为 NULL) ASSERT_INT_EQ(C_SUCCESS, c_ArrayList_Remove(&list, 0, NULL)); ASSERT_INT_EQ(20, *(int*)c_ArrayList_Get(&list, 0)); // 20 变成了头元素 c_ArrayList_Destroy(&list); } // 测试四:挂载自定义分配器,并验证伙伴系统(Buddy System)的 O(1) 就地复用优化 TEST_CASE(test_buddy_system_allocator_integration) { c_Allocator_t buddy_allocator = { .alloc = test_alloc, .realloc = test_realloc, .free = test_free, .ud = &g_tracker }; c_ArrayList_t list; // 单个元素 8 字节,初始容量 2。整个缓冲区 = 2 * 8 = 16 字节 c_ArrayList_Init(&list, 8, 2, &buddy_allocator); // 验证 active_allocations 计数 (1控制头由调用者在栈分配,因此分配器内只有 1 个内部数据缓冲区 array) ASSERT_INT_EQ(1, g_tracker.active_allocations); // 1. 显式调整容量从 2 -> 3 // 旧大小 16 字节 (2^4),新大小 24 字节 (向上对齐到 2^5 = 32),阶数改变,模拟真实搬迁 ASSERT_INT_EQ(C_SUCCESS, c_ArrayList_Resize(&list, 3)); ASSERT_INT_EQ(0, g_tracker.realloc_in_place_count); // 跨越了幂次墙,没有就地复用 // 2. 深度契合点:再次调整容量从 3 -> 4 // 旧大小 24 字节 (2^5 范围内),新大小 32 字节 (刚好跨入 2^5 满额边界) // 【期望结果】:落在同一阶数内,c_Buddy_Realloc 应当直接 O(1) 返回原指针! ASSERT_INT_EQ(C_SUCCESS, c_ArrayList_Resize(&list, 4)); ASSERT_INT_EQ(1, g_tracker.realloc_in_place_count); // 完美!命中伙伴系统就地复用优化次数 1 次 // 3. 极限截断缩小到 0 ASSERT_INT_EQ(C_SUCCESS, c_ArrayList_Resize(&list, 0)); ASSERT_INT_EQ(0, c_ArrayList_Size(&list)); ASSERT_INT_EQ(0, list.capacity); ASSERT_TRUE(list.array == NULL); c_ArrayList_Destroy(&list); // 验证测试环境有没有发生任何内存泄漏 ASSERT_INT_EQ_MSG(0, g_tracker.active_allocations, "Memory leak detected inside allocator!"); } /* ------------------------------------------------------------------------------------------------------------------ */ /* */ int main(int argc, char** argv){ TEST_START(C_ArrayList_Module_Tests); // 运行常规测试 RUN_TEST(test_lifecycle_and_defense); RUN_TEST(test_value_copy_and_access); RUN_TEST(test_element_removal_and_shifting); // 使用 Fixture 模式运行涉及自定义状态追踪的伙伴系统测试 RUN_TEST_FIXTURE(test_buddy_system_allocator_integration, custom_allocator_setup, custom_allocator_teardown); TEST_REPORT(); RETURN_TEST_STATUS; }