Search / Sort

This commit is contained in:
2026-08-31 11:50:04 +08:00
parent 0cb98557da
commit 109e8af3d5
10 changed files with 818 additions and 219 deletions
+89 -212
View File
@@ -7,246 +7,123 @@
/* */
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;
uint32_t uid;
int data_block;
} UserPayload_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) {
TEST_CASE(test_c_ArrayList_CRUD) {
c_ArrayList_t list;
// 初始化物理插槽总上限被死死限制为 2 的轻量级列表
c_err_t err = c_ArrayList_Init(&list, sizeof(UserPayload_t), 2, &c_DefaultAllocator);
ASSERT_INT_EQ(C_ERR_OK, err);
ASSERT_INT_EQ(0, (int)c_ArrayList_Size(&list)); // 验证内联函数 c_ArrayList_Size 是否正确返回当前 0
ASSERT_INT_EQ(0, (int)list.size);
// 防御测试:无效参数传入
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));
UserPayload_t p1 = { 8001, 10 };
UserPayload_t p2 = { 8002, 20 };
UserPayload_t p3 = { 8003, 30 };
// 正常原地初始化
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);
// 1. 在容量空间未达到天花板前,轻量 Add 应当平滑写入
ASSERT_INT_EQ(C_ERR_OK, c_ArrayList_Add(&list, &p1));
ASSERT_INT_EQ(C_ERR_OK, c_ArrayList_Add(&list, &p2));
ASSERT_INT_EQ(2, (int)list.size);
// 销毁幂等性与清除检查
c_ArrayList_Destroy(&list);
ASSERT_TRUE(list.array == NULL);
ASSERT_INT_EQ(0, list.capacity);
ASSERT_INT_EQ(0, list.size);
// 🌟【确定性轻量策略绝杀断言 1】:当塞入第 3 个数据导致容量溢出时,
// 轻量版 Add 必须无条件前置拦截、拒绝隐式重分配,严肃返回状态码 C_ERR_OUTOFBOUND
ASSERT_INT_EQ(C_ERR_FULL, c_ArrayList_Add(&list, &p3));
ASSERT_INT_EQ(2, (int)list.size); // 大小纹丝不动
c_ArrayList_Destroy(&list); // 重复销毁不应崩溃
}
// 2. 验证 Read 与 Get 的读取差异
UserPayload_t read_buf;
ASSERT_INT_EQ(C_ERR_OK, c_ArrayList_Read(&list, 1, &read_buf));
ASSERT_INT_EQ(8002, (int)read_buf.uid); // 副本安全导出
// 测试二:泛型值复制存储、安全 Read/Get 访问测试
TEST_CASE(test_value_copy_and_access) {
c_ArrayList_t list;
c_ArrayList_Init(&list, sizeof(SensorNode_t), 2, NULL);
// 通过 Get 拿到内部指针直接进行原地篡改
UserPayload_t* p_raw_ptr = (UserPayload_t*)c_ArrayList_Get(&list, 1);
ASSERT_TRUE(p_raw_ptr != NULL);
p_raw_ptr->data_block = 9999; // 原地篡改内部物理缓冲区
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_ERR_OK, c_ArrayList_Read(&list, 1, &read_buf));
ASSERT_INT_EQ(9999, read_buf.data_block); // 指针篡改成功核回
// 写入测试
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));
// 🌟【确定性轻量策略绝杀断言 2】:使用轻量版 PopBack 弹出
UserPayload_t pop_buf;
ASSERT_INT_EQ(C_ERR_OK, c_ArrayList_PopBack(&list, &pop_buf));
ASSERT_INT_EQ(8002, (int)pop_buf.uid);
ASSERT_INT_EQ(1, (int)list.size);
// 强隔离隔离性检查:修改外部临时变量,内部数据不应被污染
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);
// 尽管存量已经萎缩到总容量的 1/4 以下,轻量版 PopBack 也决不能引发缩容,容量必须卡死留守在 2!
ASSERT_INT_EQ(2, (int)list.capacity);
c_ArrayList_Destroy(&list);
}
// 测试三:高危内存移动(memmove)及 Remove 位移正确性测试
TEST_CASE(test_element_removal_and_shifting) {
TEST_CASE(test_c_ArrayList_Resize) {
c_ArrayList_t list;
c_ArrayList_Init(&list, sizeof(int), 5, NULL);
c_ArrayList_Init(&list, sizeof(UserPayload_t), 2, NULL); // 降级测试
int values[] = {10, 20, 30, 40, 50};
for(int i = 0; i < 5; i++) {
c_ArrayList_Add(&list, &values[i]);
}
UserPayload_t p1 = { 9001, 100 };
UserPayload_t p2 = { 9002, 200 };
UserPayload_t p3 = { 9003, 300 }; // 会强迫弹性版触发自动倍增
// 移除中间的数字 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));
// 1. 验证自适应变轨 ResizeAdd
ASSERT_INT_EQ(C_ERR_OK, c_ArrayList_ResizeAdd(&list, &p1));
ASSERT_INT_EQ(C_ERR_OK, c_ArrayList_ResizeAdd(&list, &p2));
// 极其严苛地检测后面所有元素的向前位移是否对齐正确
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 的位置
// 绝杀:第 3 个数据会自适应变轨,将容量从 2 直接倍增拓宽至 4
ASSERT_INT_EQ(C_ERR_OK, c_ArrayList_ResizeAdd(&list, &p3));
ASSERT_INT_EQ(3, (int)list.size);
ASSERT_INT_EQ(4, (int)list.capacity);
// 验证静默删除(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 变成了头元素
// 2. 验证中段中段摘除平移覆盖
UserPayload_t remove_verify;
ASSERT_INT_EQ(C_ERR_OK, c_ArrayList_RemoveAt(&list, 1, &remove_verify)); // 拿掉 1 号位的 P2
ASSERT_INT_EQ(9002, (int)remove_verify.uid);
ASSERT_INT_EQ(2, (int)list.size);
// 搬运平移断言:原先在 2 号位的 P3(9003) 应该已经被向前挪移顶替到了 1 号位
UserPayload_t read_verify;
ASSERT_INT_EQ(C_ERR_OK, c_ArrayList_Read(&list, 1, &read_verify));
ASSERT_INT_EQ(9003, (int)read_verify.uid);
// 3. 验证弹性版 PopBackResize 与常数缩容锁
UserPayload_t pop_resize_verify;
ASSERT_INT_EQ(C_ERR_OK, c_ArrayList_PopBackResize(&list, &pop_resize_verify));
ASSERT_INT_EQ(9003, (int)pop_resize_verify.uid);
ASSERT_INT_EQ(1, (int)list.size);
// 🌟【核心弹性缩容断言】:此时 N <= Capacity/4 (1 <= 4/4) 且大于配额上限
// 自适应版 PopBackResize 必须自动通过常数缩容锁将物理存储空间腰斩,重新高效卡回容量 2!
ASSERT_INT_EQ(2, (int)list.capacity);
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
};
TEST_CASE(test_c_ArrayList_Toxicity_Defenses) {
c_ArrayList_t local_list;
c_ArrayList_Init(&local_list, sizeof(int), 4, NULL);
c_ArrayList_t list;
// 单个元素 8 字节,初始容量 2。整个缓冲区 = 2 * 8 = 16 字节
c_ArrayList_Init(&list, 8, 2, &buddy_allocator);
int dummy = 0;
// 验证各类入参毒参数及空仓边界的状态码返回线
ASSERT_INT_EQ(C_ERR_PARAM, c_ArrayList_Init(NULL, sizeof(int), 4, NULL));
ASSERT_INT_EQ(C_ERR_EMPTY, c_ArrayList_PopBack(&local_list, &dummy)); // 拦截 C_ERR_EMPTY
ASSERT_INT_EQ(C_ERR_OUTOFBOUND, c_ArrayList_Set(&local_list, 999, &dummy)); // 越界拦截
ASSERT_TRUE(c_ArrayList_Get(&local_list, 0) == NULL);
// 验证 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!");
c_ArrayList_Destroy(&local_list);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
// ==========================================
// 5. 主集成入口
// ==========================================
int main(void) {
TEST_START(C_ArrayList_DoubleStrategy_TestSuite);
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);
RUN_TEST(test_c_ArrayList_CRUD);
RUN_TEST(test_c_ArrayList_Resize);
RUN_TEST(test_c_ArrayList_Toxicity_Defenses);
TEST_REPORT();
RETURN_TEST_STATUS;
}
return (g_test_registry.failed_count > 0 ? 1 : 0);
}