Files
cKit/Search/c_HashST.t.c
T
2026-08-30 22:24:45 +08:00

217 lines
8.4 KiB
C

#include "c_HashST.h"
#include "c_Test.h"
#include <stdlib.h>
#include <stdio.h>
// ==========================================
// 1. 测试框架配套:虚操作函数模拟实现
// ==========================================
// --- Key 虚操作实现:以动态独立分配内存、长度未知的“深层堆字符串指针 (char**)”作为键 ---
static uint32_t test_key_string_hash(const void* data, void* arg) {
(void)arg;
const char* str = *(const char* const*)data;
uint32_t hash = 0;
while (*str) {
hash = 31 * hash + (unsigned char)(*str);
str++;
}
return hash;
}
static void* test_key_string_cp(const void* data, void* arg) {
(void)arg;
const char* original = *(const char* const*)data;
char* clone = (char*)malloc(strlen(original) + 1);
if (clone) strcpy(clone, original);
char** storage = (char**)malloc(sizeof(char*));
if (storage) *storage = clone;
return (void*)storage;
}
static void test_key_string_free(void* data, void* arg) {
(void)arg;
if (data) {
char** storage = (char**)data;
free(*storage);
free(storage);
}
}
static bool test_key_string_eq(const void* data1, const void* data2, void* arg) {
(void)arg;
const char* s1 = *(const char* const*)data1;
const char* s2 = *(const char* const*)data2;
return strcmp(s1, s2) == 0;
}
// --- Value 虚操作实现:以动态独立分配内存的“结构体对象指针 (Task_t**)”作为值 ---
typedef struct {
int id;
int payload;
} TestTask_t;
static void* test_val_task_cp(const void* data, void* arg) {
(void)arg;
const TestTask_t* original = *(const TestTask_t* const*)data;
TestTask_t* clone = (TestTask_t*)malloc(sizeof(TestTask_t));
if (clone) {
clone->id = original->id;
clone->payload = original->payload;
}
TestTask_t** storage = (TestTask_t**)malloc(sizeof(TestTask_t*));
if (storage) *storage = clone;
return (void*)storage;
}
static void test_val_task_free(void* data, void* arg) {
(void)arg;
if (data) {
TestTask_t** storage = (TestTask_t**)data;
free(*storage);
free(storage);
}
}
static bool test_val_task_eq(const void* data1, const void* data2, void* arg) {
(void)arg;
const TestTask_t* t1 = *(const TestTask_t* const*)data1;
const TestTask_t* t2 = *(const TestTask_t* const*)data2;
return (t1->id == t2->id && t1->payload == t2->payload);
}
// ==========================================
// 2. 自动化单元测试集
// ==========================================
TEST_CASE(test_c_HashST_Base_CRUD_Flow) {
c_HashST_t map;
// 1. 初始化多态虚操作集(Key 采用堆字符串,Value 采用堆任务结构体)
c_HashKeyOps_t key_ops = { test_key_string_hash, test_key_string_cp, test_key_string_free, test_key_string_eq, NULL };
c_HashValOps_t val_ops = { test_val_task_cp, test_val_task_free, test_val_task_eq, NULL };
// 2. 初始化初始容量为 4 的哈希符号表
c_err_t err = c_HashST_Init(&map, 4, key_ops, val_ops, &c_DefaultAllocator);
ASSERT_INT_EQ(C_ERR_OK, err);
// 声明待插入的临时浅数据线
const char* k1 = "Task_Alpha"; TestTask_t t1_raw = { 101, 555 }; const TestTask_t* v1 = &t1_raw;
const char* k2 = "Task_Beta"; TestTask_t t2_raw = { 102, 666 }; const TestTask_t* v2 = &t2_raw;
const char* k3 = "Task_Gamma"; TestTask_t t3_raw = { 103, 777 }; const TestTask_t* v3 = &t3_raw;
// 3. Put 添加行为断言
ASSERT_INT_EQ(C_ERR_OK, c_HashST_Put(&map, &k1, &v1));
ASSERT_INT_EQ(C_ERR_OK, c_HashST_Put(&map, &k2, &v2));
ASSERT_INT_EQ(C_ERR_OK, c_HashST_Put(&map, &k3, &v3));
ASSERT_INT_EQ(3, (int)map.size);
// 4. Contains 包含性状态断言(使用物理地址不同的副本 Key 进行全等检索)
const char* search_k1 = "Task_Alpha";
const char* search_fake = "Task_Unknown";
ASSERT_TRUE(c_HashST_Contains(&map, &search_k1));
ASSERT_TRUE(!c_HashST_Contains(&map, &search_fake));
// 5. Get 数据读取与物理结构体字段穿透比对
void* get_res_ptr = c_HashST_Get(&map, &search_k1);
ASSERT_TRUE(get_res_ptr != NULL);
// 穿透二级指针,精确检验深拷贝出来的值的成员变量
TestTask_t* matched_task = *(TestTask_t**)get_res_ptr;
ASSERT_INT_EQ(101, matched_task->id);
ASSERT_INT_EQ(555, matched_task->payload);
// 6. Put 相同键下的覆写更新(Overwrite)语义核验
TestTask_t t1_update_raw = { 101, 9999 }; const TestTask_t* v1_update = &t1_update_raw;
ASSERT_INT_EQ(C_ERR_OK, c_HashST_Put(&map, &k1, &v1_update));
ASSERT_INT_EQ(3, (int)map.size); // 覆写后,全局哈希大小必须守恒
get_res_ptr = c_HashST_Get(&map, &search_k1);
ASSERT_TRUE(get_res_ptr != NULL);
ASSERT_INT_EQ(9999, (*(TestTask_t**)get_res_ptr)->payload); // 配额必须已被更新覆盖
// 7. Remove 单路摘除断言
ASSERT_INT_EQ(C_ERR_OK, c_HashST_Remove(&map, &search_k1));
ASSERT_INT_EQ(2, (int)map.size); // 递减正确
ASSERT_TRUE(c_HashST_Get(&map, &search_k1) == NULL); // 再拿应该彻底变空
// 8. 全清理注销销毁线,多态自由解构,阻断任何残留
c_HashST_Destroy(&map);
}
TEST_CASE(test_c_HashST_Resize_And_Collision) {
c_HashST_t map;
c_HashKeyOps_t key_ops = { test_key_string_hash, test_key_string_cp, test_key_string_free, test_key_string_eq, NULL };
c_HashValOps_t val_ops = { test_val_task_cp, test_val_task_free, test_val_task_eq, NULL };
// 1. 初始化一个极小容量的哈希表(故意设为 1,强迫后面插入的所有节点落入同一个桶中,构成 100% 碰撞拉链链条)
c_err_t err = c_HashST_Init(&map, 1, key_ops, val_ops, &c_DefaultAllocator);
ASSERT_INT_EQ(C_ERR_OK, err);
const char* k1 = "Key_Coll_1"; TestTask_t t1 = { 1, 10 }; const TestTask_t* v1 = &t1;
const char* k2 = "Key_Coll_2"; TestTask_t t2 = { 2, 20 }; const TestTask_t* v2 = &t2;
const char* k3 = "Key_Coll_3"; TestTask_t t3 = { 3, 30 }; const TestTask_t* v3 = &t3;
ASSERT_INT_EQ(C_ERR_OK, c_HashST_Put(&map, &k1, &v1));
ASSERT_INT_EQ(C_ERR_OK, c_HashST_Put(&map, &k2, &v2));
ASSERT_INT_EQ(C_ERR_OK, c_HashST_Put(&map, &k3, &v3));
// 2. NumCol 碰撞指标验证:在容量为 1 且塞入 3 个数据时,产生的物理碰撞线数必然为 2 次
ASSERT_INT_EQ(2, (int)c_HashST_NumCol(&map));
// 3. 核心压测:触发大跨度扩容变轨(容量从 1 直接倍增重塑至 16)
// 扩容后,原先挤在 0 号桶拉链中的 3 个冲突节点必须被重新洗牌、散列分流到各个新桶中
ASSERT_INT_EQ(C_ERR_OK, c_HashST_Resize(&map, 16));
ASSERT_INT_EQ(16, (int)map.capacity);
ASSERT_INT_EQ(3, (int)map.size); // 全局大小守恒不变
// 4. 扩容分流后的碰撞指标必须自适应下降(通常随机散列到 16 个桶中后,碰撞数会锐减为 0)
ASSERT_TRUE(c_HashST_NumCol(&map) < 2);
// 5. 扩容变轨后,再次使用 Get 穿透读取,验证多态路由未丢失
const char* search_k3 = "Key_Coll_3";
void* get_res_ptr = c_HashST_Get(&map, &search_k3);
ASSERT_TRUE(get_res_ptr != NULL);
ASSERT_INT_EQ(30, (*(TestTask_t**)get_res_ptr)->payload);
c_HashST_Destroy(&map);
}
TEST_CASE(test_c_HashST_Empty_And_Toxicity_Defenses) {
c_HashST_t map;
c_HashKeyOps_t key_ops = { test_key_string_hash, test_key_string_cp, test_key_string_free, test_key_string_eq, NULL };
c_HashValOps_t val_ops = { test_val_task_cp, test_val_task_free, test_val_task_eq, NULL };
c_HashST_Init(&map, 4, key_ops, val_ops, NULL); // 降级测试
const char* k = "Toxic_Key";
// 1. 空仓返回与毒入参前置拦截防御
ASSERT_TRUE(c_HashST_Get(&map, &k) == NULL);
ASSERT_TRUE(!c_HashST_Contains(&map, &k));
ASSERT_INT_EQ(C_ERR_NOTFOUND, c_HashST_Remove(&map, &k));
ASSERT_INT_EQ(0, (int)c_HashST_NumCol(&map));
ASSERT_INT_EQ(C_ERR_PARAM, c_HashST_Init(NULL, 4, key_ops, val_ops, NULL));
c_HashST_Destroy(&map);
}
// ==========================================
// 3. 独立测试运行入口
// ==========================================
int main(void) {
TEST_START(C_HashST_PolymorphicObject_TestSuite);
// 顺序调度高级多态哈希表的各项极限业务场景
RUN_TEST(test_c_HashST_Base_CRUD_Flow);
RUN_TEST(test_c_HashST_Resize_And_Collision);
RUN_TEST(test_c_HashST_Empty_And_Toxicity_Defenses);
TEST_REPORT();
RETURN_TEST_STATUS;
}