Files
cKit/Foundation/c_ArrayList.t.c
T
2026-08-30 01:48:03 +08:00

253 lines
9.0 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "c_ArrayList.h"
#include <stdlib.h>
#include <stdio.h>
#include <c_Test.h>
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
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;
}