Search / Sort
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
#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;
|
||||
}
|
||||
Reference in New Issue
Block a user