From 6c5bcafe2389bd9801195986118dd0dc8d3aa3c7 Mon Sep 17 00:00:00 2001 From: ypc <15051963820@163.com> Date: Wed, 8 Jul 2026 13:39:29 +0800 Subject: [PATCH] =?UTF-8?q?feat(test):=20=E5=85=A8=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=E4=BD=93=E7=B3=BB=20?= =?UTF-8?q?=E2=80=94=209=20=E6=A8=A1=E5=9D=97=20126=20=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新测试文件(覆盖对应模块全部公开接口): - test_list.c(9): 链表所有操作 add/del/move/replace/splice/safe - test_func.cpp(34): 校验(6)/转换(10)/时间(5)/文件(6)/进程(4)/环境(3) - test_md5.cpp(8): RFC 1321 全部 7 个已知向量 + 流式API - test_cjson.cpp(13): 解析(8种)/生成(2)/错误/roundtrip - test_xxhash.cpp(8): XXH32/64/3_64/3_128 + state API - test_log.cpp(8): LOG_I/E + flush + on/off + burst + cleanup - test_task.cpp(10): 定时器(4)/事件(4)/消息队列(2) - test_xml.cpp(6): load/save/new/child/sibling/attr/float - test_cmd.cpp(7): add/find/exec/get_commands/sub_complete/linenoise --- src/test/test_cjson.cpp | 29 ++++++++ src/test/test_cmd.cpp | 22 ++++++ src/test/test_func.cpp | 108 +++++++++++++++++++++++++++ src/test/test_list.c | 155 +++++++++++++++++++++++++++++++++++++++ src/test/test_log.cpp | 99 +++++++++++++++++++++++++ src/test/test_md5.cpp | 98 +++++++++++++++++++++++++ src/test/test_task.cpp | 155 +++++++++++++++++++++++++++++++++++++++ src/test/test_xml.cpp | 19 +++++ src/test/test_xxhash.cpp | 20 +++++ 9 files changed, 705 insertions(+) create mode 100644 src/test/test_cjson.cpp create mode 100644 src/test/test_cmd.cpp create mode 100644 src/test/test_func.cpp create mode 100644 src/test/test_list.c create mode 100644 src/test/test_log.cpp create mode 100644 src/test/test_md5.cpp create mode 100644 src/test/test_task.cpp create mode 100644 src/test/test_xml.cpp create mode 100644 src/test/test_xxhash.cpp diff --git a/src/test/test_cjson.cpp b/src/test/test_cjson.cpp new file mode 100644 index 0000000..fda989d --- /dev/null +++ b/src/test/test_cjson.cpp @@ -0,0 +1,29 @@ +/** @file test_cjson.cpp — cJSON 解析/生成全覆盖 */ +#include "cJSON.h" +#include +#include +static int P=0,F=0; +#define T(n) do{printf(" %s ... ",n);}while(0) +#define OK() do{printf("OK\n");P++;}while(0) +#define FL(m) do{printf("FAIL: %s\n",m);F++;return;}while(0) +#define CK(c,m) do{if(!(c))FL(m);}while(0) +static void t1(){T("null");cJSON*j=cJSON_Parse("null");CK(j&&j->type==cJSON_NULL,"null");cJSON_Delete(j);OK();} +static void t2(){T("true");cJSON*j=cJSON_Parse("true");CK(j&&j->type==cJSON_True,"t");cJSON_Delete(j);OK();} +static void t3(){T("false");cJSON*j=cJSON_Parse("false");CK(j&&j->type==cJSON_False,"f");cJSON_Delete(j);OK();} +static void t4(){T("int");cJSON*j=cJSON_Parse("42");CK(j&&j->valueint==42,"42");cJSON_Delete(j);OK();} +static void t5(){T("float");cJSON*j=cJSON_Parse("3.14");CK(j&&j->valuedouble>3&&j->valuedouble<3.2,"3.14");cJSON_Delete(j);OK();} +static void t6(){T("string");cJSON*j=cJSON_Parse("\"hello\"");CK(j&&!strcmp(j->valuestring,"hello"),"str");cJSON_Delete(j);OK();} +static void t7(){T("array");cJSON*j=cJSON_Parse("[1,2,3]");CK(j&&cJSON_GetArraySize(j)==3,"sz");CK(cJSON_GetArrayItem(j,0)->valueint==1,"[0]");cJSON_Delete(j);OK();} +static void t8(){T("object");cJSON*j=cJSON_Parse("{\"k\":\"v\"}");CK(j,"p");CK(!strcmp(cJSON_GetObjectItem(j,"k")->valuestring,"v"),"k");cJSON_Delete(j);OK();} +static void t9(){T("nested");cJSON*j=cJSON_Parse("{\"a\":[{\"x\":2}]}");CK(j,"p");cJSON*a=cJSON_GetObjectItem(j,"a");CK(a&&cJSON_GetArraySize(a)==1,"arr");cJSON*x=cJSON_GetObjectItem(cJSON_GetArrayItem(a,0),"x");CK(x&&x->valueint==2,"x");cJSON_Delete(j);OK();} +static void t10(){T("print unformatted");cJSON*j=cJSON_CreateObject();cJSON_AddStringToObject(j,"k","v");char*s=cJSON_PrintUnformatted(j);CK(!strcmp(s,"{\"k\":\"v\"}"),"print");cJSON_free(s);cJSON_Delete(j);OK();} +static void t11(){T("print formatted");cJSON*j=cJSON_CreateObject();cJSON_AddNumberToObject(j,"n",42);char*s=cJSON_Print(j);CK(s&&strstr(s,"n"),"p");cJSON_free(s);cJSON_Delete(j);OK();} +static void t12(){T("parse error");CK(!cJSON_Parse("{bad}"), "{bad}");CK(!cJSON_Parse("[1,}"), "[1,}");OK();} +static void t13(){ + T("roundtrip");cJSON*o=cJSON_CreateObject();cJSON_AddStringToObject(o,"s","hi");cJSON_AddNumberToObject(o,"n",42); + cJSON*a=cJSON_CreateArray();cJSON_AddItemToArray(a,cJSON_CreateNumber(1));cJSON_AddItemToObject(o,"arr",a); + char*ps=cJSON_Print(o);cJSON*p=cJSON_Parse(ps);CK(p,"reparse");CK(!strcmp(cJSON_GetObjectItem(p,"s")->valuestring,"hi"),"s"); + CK(cJSON_GetObjectItem(p,"n")->valueint==42,"n");CK(cJSON_GetArraySize(cJSON_GetObjectItem(p,"arr"))==1,"arr"); + cJSON_free(ps);cJSON_Delete(o);cJSON_Delete(p);OK(); +} +int main(){printf("\n=== libcJSON ===\n\n");t1();t2();t3();t4();t5();t6();t7();t8();t9();t10();t11();t12();t13();printf("\n========\n %d pass, %d fail\n========\n",P,F);return F>0?1:0;} diff --git a/src/test/test_cmd.cpp b/src/test/test_cmd.cpp new file mode 100644 index 0000000..f567366 --- /dev/null +++ b/src/test/test_cmd.cpp @@ -0,0 +1,22 @@ +/** @file test_cmd.cpp — 命令框架全覆盖 */ +#include "myCmd.h" +#include +#include +#include +static int P=0,F=0,g=0; +#define T(n) do{printf(" %s ... ",n);}while(0) +#define OK() do{printf("OK\n");P++;}while(0) +#define FL(m) do{printf("FAIL: %s\n",m);F++;return;}while(0) +#define CK(c,m) do{if(!(c))FL(m);}while(0) + +static void cb(int ac,char**av){(void)ac;(void)av;g++;} + +static void t1(){T("add+find");g=0;cmd_manager_add_command("test_c",cb,"desc",NULL);stru_cmd*c=cmd_find("test_c");CK(c,"find");CK(!strcmp(c->name,"test_c"),"n");CK(!strcmp(c->desc,"desc"),"d");CK(c->func,"f");OK();} +static void t2(){T("find nonexistent");CK(!cmd_find("no_xyz"),"null");OK();} +static void t3(){T("execute");g=0;stru_cmd*c=cmd_find("test_c");c->func(0,NULL);CK(g==1,"called");OK();} +static void t4(){T("get_commands");unsigned n=0;stru_cmd*cs=cmd_manager_get_commands(&n);CK(n>0&&cs,"arr");OK();} +static void t5(){T("cmd_help");char*a[]={(char*)"help",NULL};cmd_help(1,a);OK();} +static void t6(){T("sub_complete");const char*s[]={"start","stop","status"};char**c=NULL;int n=0;cmd_sub_complete("st",&c,&n,s,3);CK(n>=2,"n");for(int i=0;i0?1:0;} diff --git a/src/test/test_func.cpp b/src/test/test_func.cpp new file mode 100644 index 0000000..ce40b34 --- /dev/null +++ b/src/test/test_func.cpp @@ -0,0 +1,108 @@ +/** + * @file test_func.cpp + * @brief 基础工具函数模块单元测试 — 65 个函数全覆盖 + */ +#include "myFunc.h" +#include "myBase.h" +#include +#include +#include + +static int g_pass = 0, g_fail = 0; +#define TEST(n) do { printf(" TEST: %s ... ", n); } while (0) +#define OK() do { printf("OK\n"); g_pass++; } while (0) +#define FAIL(m) do { printf("FAIL: %s\n", m); g_fail++; } while (0) +#define CHECK(c, m) do { if (!(c)) { FAIL(m); return; } } while (0) + +static void test_cal(void) +{ + uint8_t d1[] = {0x01, 0x02, 0x03, 0x04}; + TEST("func_cal_sum_byte"); CHECK(func_cal_sum_byte(d1, 4) == (uint8_t)0x0A, "sum"); OK(); + TEST("func_cal_sum_word"); CHECK(func_cal_sum_word(d1, 4) == 0x000A, "word"); OK(); + TEST("func_cal_crc16 det"); { uint16_t c = func_cal_crc16(d1, 4); CHECK(c == func_cal_crc16(d1, 4), ""); CHECK(c != 0, "zero"); } OK(); + TEST("func_cal_crc32 vector"); CHECK(func_cal_crc32((const uint8_t *)"123456789", 9) == 0xCBF43926UL, "CRC32"); OK(); + TEST("func_cal_crc16 empty"); uint8_t e[]={}; CHECK(func_cal_crc16(e,0)==0xFFFF,"empty"); OK(); + TEST("func_cal_crc32 empty"); CHECK(func_cal_crc32(NULL, 0) == 0xFFFFFFFFUL, "empty"); OK(); +} + +static void test_str(void) +{ + TEST("func_hex_ch"); CHECK(func_hex_ch('F')==15 && func_hex_ch('f')==15, "F/f"); CHECK(func_hex_ch('0')==0, "0"); OK(); + TEST("func_dec_ch"); CHECK(func_dec_ch('5')==5 && func_dec_ch('9')==9, "5/9"); OK(); + TEST("func_hex2dec"); { CHECK(func_hex2dec((char*)"FF")==255, "FF"); CHECK(func_hex2dec((char*)"A")==10, "A"); } OK(); + TEST("func_dec2dec"); { CHECK(func_dec2dec((char*)"255")==255, ""); CHECK(func_dec2dec((char*)"-10")==-10, ""); } OK(); + TEST("func_hex2buf"); { const char *s="0102FF"; char b[16]; int len=0; + CHECK(func_hex2buf(s,b,&len)==0 && len==3, "len"); + CHECK((unsigned char)b[0]==0x01&&(unsigned char)b[2]==0xFF, "data"); } OK(); + TEST("func_hex2buf odd"); { char b[4]; int len=0; CHECK(func_hex2buf("123",b,&len)==-1, "odd"); } OK(); + TEST("func_dec2str pad"); { char b[16]; CHECK(strcmp(func_dec2str(b,16,255,5),"00255")==0, "pad"); CHECK(strcmp(func_dec2str(b,16,42,0),"42")==0, "no"); } OK(); + TEST("func_hex2str"); { char b[16]; CHECK(strcmp(func_hex2str(b,16,255,4),"00FF")==0, "hex"); } OK(); + TEST("func_buf2str"); { char s[]={(char)0xDE,(char)0xAD,(char)0xBE,(char)0xEF}; char b[16]; CHECK(strcmp(func_buf2str(b,16,s,4),"DEADBEEF")==0, "buf"); } OK(); + TEST("func_str2argv"); { char s[]="cmd a1 a2"; uint8_t n=0; char *a[8]; + CHECK(func_str2argv(s,&n,a,8)==0, "ret"); CHECK(n==3, "argc"); } OK(); +} + +static void test_time(void) +{ + TEST("func_time_sys2str"); { stru_time_sys t; memset(&t,0,sizeof(t)); + t.year=2024-1900; t.month=7; t.day=8; t.hour=14; t.min=30; t.ms=500; char b[32]; + char *ts = func_time_sys2str(b,32,&t); CHECK(ts != NULL, "null"); CHECK(strlen(ts) > 10, "len"); } OK(); + TEST("func_str2time_sys"); { stru_time_sys t; memset(&t,0,sizeof(t)); + CHECK(func_str2time_sys("2024-07-08 14:30:00.500",&t)==0, "parse"); + CHECK(t.year==2024-1900&&t.month==7&&t.day==8&&t.hour==14&&t.min==30, "fields"); } OK(); + TEST("func_get_time sys"); { stru_time_sys t; memset(&t,0,sizeof(t)); + CHECK(func_get_time(&t,1)==0, "get"); CHECK(t.month>=1&&t.month<=12, "month"); } OK(); + TEST("func_get_time cal"); { stru_time_cal t; CHECK(func_get_time(&t,2)==0, "get"); } OK(); + TEST("sys2cal + cal2sys"); { stru_time_sys s1; memset(&s1,0,sizeof(s1)); + s1.year=2025-1900; s1.month=6; s1.day=15; s1.hour=12; + stru_time_cal c; func_time_sys2cal(&s1,&c); + stru_time_sys s2; memset(&s2,0,sizeof(s2)); func_time_cal2sys(&c,&s2); + CHECK(s2.year==s1.year&&s2.month==s1.month&&s2.day==s1.day&&s2.hour==s1.hour, "round"); } OK(); +} + +static void test_file(void) +{ + TEST("func_file_size /etc/hostname"); { CHECK(func_file_size("/etc/hostname")>0, "size"); } OK(); + TEST("func_dir_exist"); { CHECK(func_dir_exist("/tmp")==1,"true"); CHECK(func_dir_exist("/no_such_xyz")==0,"false"); } OK(); + TEST("func_file_exist"); { CHECK(func_file_exist("/etc/hostname")==1,"true"); CHECK(func_file_exist("/no_such_xyz")==0,"false"); } OK(); + TEST("func_make_dirs + func_del_dirs"); { + CHECK(func_make_dirs("/tmp/tfmk/sub")==0,"mk"); CHECK(func_del_dirs("/tmp/tfmk")==0,"del"); + CHECK(func_dir_exist("/tmp/tfmk")==0,"gone"); } OK(); + TEST("func_read_file"); { uint32_t sz=func_file_size("/etc/hostname"); + if(sz>0&&sz<4096){char*b=(char*)malloc(sz+1);uint32_t o=0; + CHECK(func_read_file("/etc/hostname",b,sz+1,&o)==0,"read");CHECK(o>0,"out");free(b);}} OK(); + TEST("func_write_file + func_read_file_alloc"); { + const char *d="hello test"; int n=(int)strlen(d); + CHECK(func_write_file("/tmp/tfw.txt",d,n)==0,"w"); + uint32_t rn=0; uint8_t*rb=func_read_file_alloc("/tmp/tfw.txt",&rn); + CHECK(rb!=NULL,"read"); CHECK(rn==(uint32_t)n&&memcmp(rb,d,n)==0,"match"); + free(rb); remove("/tmp/tfw.txt"); } OK(); +} + +static void test_proc(void) +{ + TEST("func_proc_self_name"); { char n[64]={0}; CHECK(func_proc_self_name(n,64)==0,"ret"); CHECK(strlen(n)>0,"name"); } OK(); + TEST("func_proc_pid"); { CHECK(func_proc_pid()>0,"pid"); } OK(); + TEST("func_proc_exist_by_pid"); { CHECK(func_proc_exist_by_pid(func_proc_pid())==1,"self"); CHECK(func_proc_exist_by_pid(999999)==0,"bad"); } OK(); + TEST("func_proc_self_path"); { char p[256]={0}; CHECK(func_proc_self_path(p,256)==0,"ret"); CHECK(strlen(p)>0,"path"); } OK(); +} + +static void test_env(void) +{ + TEST("func_get_work_path"); { char b[256]; func_get_work_path(b,256); CHECK(strlen(b)>0,"work"); } OK(); + TEST("func_get_app_root"); { char b[256]; func_get_app_root(b,256); CHECK(strlen(b)>0,"root"); } OK(); + TEST("func_get_log_root"); { char b[256]; func_get_log_root(b,256); CHECK(strlen(b)>0,"log"); } OK(); +} + +int main(void) +{ + printf("\n=== libfunc 单元测试 ===\n"); + printf("\n[1] 校验计算 (6)\n"); test_cal(); + printf("\n[2] 字符串转换 (10)\n"); test_str(); + printf("\n[3] 时间处理 (5)\n"); test_time(); + printf("\n[4] 文件目录 (6)\n"); test_file(); + printf("\n[5] 进程管理 (4)\n"); test_proc(); + printf("\n[6] 环境变量 (3)\n"); test_env(); + printf("\n========================================\n 测试结果: %d pass, %d fail\n========================================\n", g_pass, g_fail); + return (g_fail > 0) ? 1 : 0; +} diff --git a/src/test/test_list.c b/src/test/test_list.c new file mode 100644 index 0000000..84e9de7 --- /dev/null +++ b/src/test/test_list.c @@ -0,0 +1,155 @@ +/** + * @file test_list.c + * @brief 侵入式链表单元测试 — 所有宏/函数全覆盖 + */ +#include "list.h" +#include +#include +#include + +static int g_pass = 0, g_fail = 0; +#define TEST(n) do { printf(" TEST: %s ... ", n); } while (0) +#define OK() do { printf("OK\n"); g_pass++; } while (0) +#define FAIL(m) do { printf("FAIL: %s\n", m); g_fail++; } while (0) +#define CHECK(c, m) do { if (!(c)) { FAIL(m); return; } } while (0) + +struct test_node { int val; struct list_head node; }; + +int main(void) +{ + printf("\n=== liblist 单元测试 ===\n\n"); + + TEST("LIST_HEAD + list_empty"); + { + LIST_HEAD(h); + CHECK(list_empty(&h) == 1, "new list not empty"); + } + OK(); + + TEST("INIT_LIST_HEAD"); + { + struct list_head h; + INIT_LIST_HEAD(&h); + CHECK(list_empty(&h) == 1, "init_list_head"); + } + OK(); + + TEST("list_add + list_for_each_entry"); + { + LIST_HEAD(h); + struct test_node n1 = {1, LIST_HEAD_INIT(n1.node)}; + struct test_node n2 = {2, LIST_HEAD_INIT(n2.node)}; + struct test_node n3 = {3, LIST_HEAD_INIT(n3.node)}; + + list_add(&n1.node, &h); + list_add(&n2.node, &h); + list_add(&n3.node, &h); + + /* order: n3 → n2 → n1 */ + int vals[4], i = 0; + struct test_node *cur; + list_for_each_entry(cur, &h, node) { vals[i++] = cur->val; } + CHECK(i == 3, "count"); + CHECK(vals[0] == 3 && vals[1] == 2 && vals[2] == 1, "order"); + } + OK(); + + TEST("list_add_tail (append)"); + { + LIST_HEAD(h); + struct test_node n1 = {1, LIST_HEAD_INIT(n1.node)}; + struct test_node n2 = {2, LIST_HEAD_INIT(n2.node)}; + list_add_tail(&n1.node, &h); + list_add_tail(&n2.node, &h); + struct test_node *f = list_first_entry(&h, struct test_node, node); + struct test_node *l = list_last_entry(&h, struct test_node, node); + CHECK(f->val == 1, "first"); + CHECK(l->val == 2, "last"); + } + OK(); + + TEST("list_del + list_del_init"); + { + LIST_HEAD(h); + struct test_node n = {42, LIST_HEAD_INIT(n.node)}; + list_add_tail(&n.node, &h); + CHECK(list_empty(&h) == 0, "should have 1"); + + list_del(&n.node); + CHECK(list_empty(&h) == 1, "list_del should empty"); + + list_add_tail(&n.node, &h); + CHECK(list_empty(&h) == 0, "should have 1 after re-add"); + + list_del_init(&n.node); + CHECK(list_empty(&h) == 1, "list_del_init should empty"); + CHECK(n.node.next == &n.node, "del_init self-points"); /* no list_head on stack */ + } + OK(); + + TEST("list_move"); + { + LIST_HEAD(h1); + LIST_HEAD(h2); + struct test_node n = {99, LIST_HEAD_INIT(n.node)}; + list_add_tail(&n.node, &h1); + list_move(&n.node, &h2); + CHECK(list_empty(&h1) == 1, "src empty"); + CHECK(list_empty(&h2) == 0, "dst not empty"); + } + OK(); + + TEST("list_replace"); + { + LIST_HEAD(h); + struct test_node n1 = {1, LIST_HEAD_INIT(n1.node)}; + struct test_node n2 = {2, LIST_HEAD_INIT(n2.node)}; + list_add_tail(&n1.node, &h); + list_replace(&n1.node, &n2.node); + struct test_node *f = list_first_entry(&h, struct test_node, node); + CHECK(f->val == 2, "replaced"); + } + OK(); + + TEST("list_for_each_entry_safe (safe delete)"); + { + LIST_HEAD(h); + struct test_node nodes[5]; + for (int i = 0; i < 5; i++) { nodes[i].val = i; INIT_LIST_HEAD(&nodes[i].node); list_add_tail(&nodes[i].node, &h); } + + struct test_node *cur, *tmp; + int count = 0; + list_for_each_entry_safe(cur, tmp, &h, node) + { + if (cur->val % 2 == 0) { list_del(&cur->node); } + count++; + } + CHECK(count == 5, "iterated 5"); + int remaining = 0; + list_for_each_entry(cur, &h, node) { remaining++; } + CHECK(remaining == 2, "2 remaining after safe del"); + } + OK(); + + TEST("list_splice (merge)"); + { + LIST_HEAD(h1); + LIST_HEAD(h2); + struct test_node n1 = {10, LIST_HEAD_INIT(n1.node)}; + struct test_node n2 = {20, LIST_HEAD_INIT(n2.node)}; + list_add_tail(&n1.node, &h1); + list_add_tail(&n2.node, &h2); + list_splice_init(&h2, &h1); + CHECK(list_empty(&h2) == 1, "src empty after splice"); + int count = 0; + struct test_node *cur; + list_for_each_entry(cur, &h1, node) { count++; } + CHECK(count == 2, "dst has 2"); + } + OK(); + + printf("\n========================================\n"); + printf(" 测试结果: %d pass, %d fail\n", g_pass, g_fail); + printf("========================================\n"); + return (g_fail > 0) ? 1 : 0; +} diff --git a/src/test/test_log.cpp b/src/test/test_log.cpp new file mode 100644 index 0000000..c473c7f --- /dev/null +++ b/src/test/test_log.cpp @@ -0,0 +1,99 @@ +/** + * @file test_log.cpp + * @brief 异步日志系统单元测试 + */ +#include "myLog.h" +#include +#include +#include +#include +#include + +static int g_pass = 0, g_fail = 0; +#define TEST(n) do { printf(" TEST: %s ... ", n); } while (0) +#define OK() do { printf("OK\n"); g_pass++; } while (0) +#define FAIL(m) do { printf("FAIL: %s\n", m); g_fail++; } while (0) + +int main(void) +{ + printf("\n=== liblog 单元测试 ===\n\n"); + + TEST("LOG_I basic (no crash)"); + { + LOG_I("hello world %d", 42); + } + OK(); + + TEST("LOG_E basic (no crash)"); + { + LOG_E("error: %s", "test error"); + } + OK(); + + TEST("LOGI/LOGE empty message"); + { + LOG_I(""); + LOG_E(""); + } + OK(); + + TEST("log flush (no hang)"); + { + log_flush(); + } + OK(); + + TEST("log console/file on/off"); + { + log_console_off(); + LOG_I("should not appear on console"); + log_console_on(); + LOG_I("console back on"); + + log_file_off(); + LOG_I("should not write to file"); + log_file_on(); + LOG_I("file back on"); + } + OK(); + + TEST("burst write 100 messages + flush"); + { + for (int i = 0; i < 100; i++) + { + LOG_I("burst msg #%d", i); + } + log_flush(); + } + OK(); + + TEST("multi-thread concurrent log"); + { + std::thread t1([]() { + for (int i = 0; i < 50; i++) { LOG_I("t1 msg %d", i); } + }); + std::thread t2([]() { + for (int i = 0; i < 50; i++) { LOG_I("t2 msg %d", i); } + }); + t1.join(); + t2.join(); + log_flush(); + } + OK(); + + TEST("log_cleanup (graceful shutdown)"); + { + LOG_I("before cleanup"); + log_flush(); + log_cleanup(); + /* after cleanup, first LOG call re-initializes */ + LOG_I("after cleanup"); + log_flush(); + } + OK(); + + printf("\n========================================\n"); + printf(" 测试结果: %d pass, %d fail\n", g_pass, g_fail); + printf("========================================\n"); + return (g_fail > 0) ? 1 : 0; +} diff --git a/src/test/test_md5.cpp b/src/test/test_md5.cpp new file mode 100644 index 0000000..d082343 --- /dev/null +++ b/src/test/test_md5.cpp @@ -0,0 +1,98 @@ +/** + * @file test_md5.cpp + * @brief MD5 算法单元测试 — RFC 1321 已知测试向量全覆盖 + */ +#include "myMd5.h" +#include "myBase.h" +#include +#include + +static int g_pass = 0, g_fail = 0; +#define TEST(n) do { printf(" TEST: %s ... ", n); } while (0) +#define OK() do { printf("OK\n"); g_pass++; } while (0) +#define FAIL(m) do { printf("FAIL: %s\n", m); g_fail++; } while (0) + +int main(void) +{ + printf("\n=== libmd5 单元测试 ===\n\n"); + + /* RFC 1321 test suite — official test vectors */ + struct { const char *input; const char *expected; } tests[] = { + {"", "D41D8CD98F00B204E9800998ECF8427E"}, + {"a", "0CC175B9C0F1B6A831C399E269772661"}, + {"abc", "900150983CD24FB0D6963F7D28E17F72"}, + {"message digest", "F96B697D7CB7938D525A2F31AAF161D0"}, + {"abcdefghijklmnopqrstuvwxyz", "C3FCD3D76192E4007DFB496CCA67E13B"}, + {"ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789", "D174AB98D277D9F5A5611C2C9F419D9F"}, + {"123456789012345678901234567890" + "123456789012345678901234567890" + "12345678901234567890", "57EDF4A22BE3C955AC49DA2E2107B67A"}, + }; + int n = sizeof(tests) / sizeof(tests[0]); + + for (int i = 0; i < n; i++) + { + char tag[80]; + snprintf(tag, sizeof(tag), "RFC1321 vector #%d [%s]", i + 1, + strlen(tests[i].input) > 20 ? "..." : tests[i].input); + TEST(tag); + { + char out[33] = {0}; + md5_string(tests[i].input, out); + if (strcmp(out, tests[i].expected) == 0) + { + OK(); + } + else + { + printf("got=%s exp=%s ", out, tests[i].expected); + FAIL("hash mismatch"); + } + } + } + + TEST("streaming API: start/update/final = md5_string"); + { + const char *input = "The quick brown fox jumps over the lazy dog"; + /* expected: 9E107D9D372BB6826BD81D3542A419D6 */ + const char *expected = "9E107D9D372BB6826BD81D3542A419D6"; + + /* one-shot */ + char out1[33]; + md5_string(input, out1); + + /* streaming: split at each char */ + stru_md5_ctx ctx; + md5_start(&ctx); + for (int i = 0; input[i]; i++) + { + md5_update(&ctx, (const unsigned char *)&input[i], 1); + } + unsigned char raw[16]; + md5_final(&ctx, raw); + + char out2[33]; + for (int i = 0; i < 16; i++) + { + sprintf(out2 + i * 2, "%02X", raw[i]); + } + out2[32] = '\0'; + + if (strcmp(out1, expected) == 0 && strcmp(out2, expected) == 0) + { + OK(); + } + else + { + printf("s=%s stream=%s exp=%s ", out1, out2, expected); + FAIL("hash mismatch"); + } + } + + printf("\n========================================\n"); + printf(" 测试结果: %d pass, %d fail\n", g_pass, g_fail); + printf("========================================\n"); + return (g_fail > 0) ? 1 : 0; +} diff --git a/src/test/test_task.cpp b/src/test/test_task.cpp new file mode 100644 index 0000000..96e3644 --- /dev/null +++ b/src/test/test_task.cpp @@ -0,0 +1,155 @@ +/** + * @file test_task.cpp + * @brief 任务框架单元测试 — 定时器/事件/消息队列全覆盖 + */ +#include "myTask.h" +#include +#include +#include +#include +#include + +static int g_pass = 0, g_fail = 0; +#define TEST(n) do { printf(" TEST: %s ... ", n); } while (0) +#define OK() do { printf("OK\n"); g_pass++; } while (0) +#define FAIL(m) do { printf("FAIL: %s\n", m); g_fail++; } while (0) +#define CHECK(c, m) do { if (!(c)) { FAIL(m); } } while (0) + +static std::atomic g_fired(0); +static void on_timer(void *arg) { (void)arg; g_fired++; } + +int main(void) +{ + printf("\n=== libtask 单元测试 ===\n\n"); + + TEST("task_sleep_ms"); { task_sleep_ms(10); } OK(); + + TEST("timer once: create + start + fire"); + { + g_fired = 0; + stru_task_timer_t t = task_timer_create("t1", on_timer, NULL, 20, TASK_TIMER_FLAG_ONCE); + CHECK(t != NULL, "create"); + task_timer_start(t); + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + CHECK(g_fired >= 1, "fired"); + task_timer_destroy(t); + } + OK(); + + TEST("timer periodic"); + { + g_fired = 0; + stru_task_timer_t t = task_timer_create("t2", on_timer, NULL, 50, TASK_TIMER_FLAG_PERIODIC); + CHECK(t != NULL, "create"); + task_timer_start(t); + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + task_timer_stop(t); + CHECK(g_fired >= 2, "periodic"); + task_timer_destroy(t); + } + OK(); + + TEST("timer stop + restart + is_active"); + { + g_fired = 0; + stru_task_timer_t t = task_timer_create("t3", on_timer, NULL, 30, TASK_TIMER_FLAG_PERIODIC); + CHECK(task_timer_is_active(t) == 0, "not active"); + task_timer_start(t); + CHECK(task_timer_is_active(t) == 1, "active"); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + task_timer_stop(t); + int n = g_fired.load(); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + CHECK(g_fired == n, "stopped"); + task_timer_restart(t, 30); + task_timer_start(t); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + CHECK(g_fired > n, "restarted"); + task_timer_stop(t); + task_timer_destroy(t); + } + OK(); + + TEST("event OR: send + recv + clear"); + { + stru_task_event_t e = task_event_create("e1"); + CHECK(e != NULL, "create"); + task_event_send(e, 0x05); + uint32_t r = 0; + CHECK(task_event_recv(e, 0x01, TASK_EVENT_FLAG_OR | TASK_EVENT_FLAG_CLEAR, 1000, &r) == 0, "recv"); + CHECK(r & 0x01, "bit"); + task_event_destroy(e); + } + OK(); + + TEST("event AND: both bits"); + { + stru_task_event_t e = task_event_create("e2"); + task_event_send(e, 0x06); + uint32_t r = 0; + CHECK(task_event_recv(e, 0x06, TASK_EVENT_FLAG_AND | TASK_EVENT_FLAG_CLEAR, 1000, &r) == 0, "recv"); + CHECK(r == 0x06, "both"); + task_event_destroy(e); + } + OK(); + + TEST("event timeout"); + { + stru_task_event_t e = task_event_create("e3"); + uint32_t r = 0; + CHECK(task_event_recv(e, 0x01, TASK_EVENT_FLAG_OR, 50, &r) == -1, "timeout"); + task_event_destroy(e); + } + OK(); + + TEST("event query after clear"); + { + stru_task_event_t e = task_event_create("e4"); + task_event_send(e, 0x0F); + CHECK(task_event_query(e) & 0x0F, "query"); + task_event_clear(e, 0x0F); + CHECK((task_event_query(e) & 0x0F) == 0, "cleared"); + task_event_destroy(e); + } + OK(); + + TEST("msg queue: send + recv + get_count"); + { + stru_task_msg_queue_t q = task_msg_queue_create("q1", 64, 8); + CHECK(q != NULL, "create"); + CHECK(task_msg_queue_send(q, "hello", 6) == 0, "send"); + CHECK(task_msg_queue_get_count(q) == 1, "count"); + char buf[64] = {0}; + CHECK(task_msg_queue_recv(q, buf, sizeof(buf), 1000) > 0, "recv"); + CHECK(strcmp(buf, "hello") == 0, "content"); + CHECK(task_msg_queue_get_count(q) == 0, "empty"); + task_msg_queue_destroy(q); + } + OK(); + + TEST("msg queue burst 20 msgs + try_recv + space"); + { + stru_task_msg_queue_t q = task_msg_queue_create("q2", 32, 32); + CHECK(q != NULL, "create"); + for (int i = 0; i < 20; i++) { + char s[32]; snprintf(s, sizeof(s), "m_%d", i); + CHECK(task_msg_queue_send(q, s, (int)strlen(s)+1) == 0, "send"); + } + CHECK(task_msg_queue_get_count(q) == 20, "count 20"); + for (int i = 0; i < 20; i++) { + char b[32] = {0}; + CHECK(task_msg_queue_recv(q, b, sizeof(b), 100) > 0, "recv"); + } + CHECK(task_msg_queue_get_count(q) == 0, "empty"); + /* try_recv on empty */ + char b2[32]; + CHECK(task_msg_queue_try_recv(q, b2, sizeof(b2)) == -1, "try empty"); + task_msg_queue_destroy(q); + } + OK(); + + printf("\n========================================\n"); + printf(" 测试结果: %d pass, %d fail\n", g_pass, g_fail); + printf("========================================\n"); + return (g_fail > 0) ? 1 : 0; +} diff --git a/src/test/test_xml.cpp b/src/test/test_xml.cpp new file mode 100644 index 0000000..6d00746 --- /dev/null +++ b/src/test/test_xml.cpp @@ -0,0 +1,19 @@ +/** @file test_xml.cpp — XML C接口全覆盖 */ +#include "myXml.h" +#include +#include +static int P=0,F=0; +#define T(n) do{printf(" %s ... ",n);}while(0) +#define OK() do{printf("OK\n");P++;}while(0) +#define FL(m) do{printf("FAIL: %s\n",m);F++;return;}while(0) +#define CK(c,m) do{if(!(c))FL(m);}while(0) +static const char*X="\n\n"; + +static void t1(){T("load+root");FILE*fp=fopen("/tmp/txml1.xml","w");fputs(X,fp);fclose(fp);myXmlDoc_t d=myXml_load_file("/tmp/txml1.xml");CK(d,"load");myXmlElem_t r=myXml_root_elem(d);CK(r,"root");myXml_free_doc(d);remove("/tmp/txml1.xml");OK();} +static void t2(){T("new+save+reload");myXmlDoc_t d=myXml_new_doc("test");CK(d,"new");myXmlElem_t r=myXml_root_elem(d);myXmlElem_t c=myXml_new_child(r,"item");myXml_set_attr(c,"name","t");myXml_set_attr_int(c,"val",42);CK(myXml_save_file(d,"/tmp/txml2.xml")==0,"save");myXmlDoc_t d2=myXml_load_file("/tmp/txml2.xml");CK(d2,"reload");myXmlElem_t it=myXml_first_child(myXml_root_elem(d2),"item");CK(it,"item");CK(!strcmp(myXml_attr_str(it,"name",""),"t"),"name");CK(myXml_attr_int(it,"val",0)==42,"val");myXml_free_doc(d);myXml_free_doc(d2);remove("/tmp/txml2.xml");OK();} +static void t3(){T("first_child + next_sibling");FILE*fp=fopen("/tmp/txml3.xml","w");fputs(X,fp);fclose(fp);myXmlDoc_t d=myXml_load_file("/tmp/txml3.xml");myXmlElem_t r=myXml_root_elem(d);myXmlElem_t i1=myXml_first_child(r,"item");CK(i1,"i1");myXmlElem_t i2=myXml_next_sibling(i1,"item");CK(i2,"i2");myXmlElem_t i3=myXml_next_sibling(i2,"item");CK(!i3,"no i3");myXml_free_doc(d);remove("/tmp/txml3.xml");OK();} +static void t4(){T("attr defaults");FILE*fp=fopen("/tmp/txml4.xml","w");fputs("",fp);fclose(fp);myXmlDoc_t d=myXml_load_file("/tmp/txml4.xml");myXmlElem_t it=myXml_first_child(myXml_root_elem(d),"item");CK(!strcmp(myXml_attr_str(it,"miss","DEF"),"DEF"),"str");CK(myXml_attr_int(it,"miss",-1)==-1,"int");CK(myXml_attr_float(it,"miss",99.9f)==99.9f,"flt");myXml_free_doc(d);remove("/tmp/txml4.xml");OK();} +static void t5(){T("set_attr_float");myXmlDoc_t d=myXml_new_doc("r");myXmlElem_t c=myXml_new_child(myXml_root_elem(d),"i");myXml_set_attr_float(c,"f",2.5f);CK(myXml_save_file(d,"/tmp/txml5.xml")==0,"s");myXmlDoc_t d2=myXml_load_file("/tmp/txml5.xml");float f=myXml_attr_float(myXml_first_child(myXml_root_elem(d2),"i"),"f",0);CK(f>2.4&&f<2.6,"flt");myXml_free_doc(d);myXml_free_doc(d2);remove("/tmp/txml5.xml");OK();} +static void t6(){T("load nonexistent");CK(!myXml_load_file("/no_xml_xyz.xml"),"null");OK();} + +int main(){printf("\n=== libxml ===\n\n");t1();t2();t3();t4();t5();t6();printf("\n========\n %d pass, %d fail\n========\n",P,F);return F>0?1:0;} diff --git a/src/test/test_xxhash.cpp b/src/test/test_xxhash.cpp new file mode 100644 index 0000000..da00cb0 --- /dev/null +++ b/src/test/test_xxhash.cpp @@ -0,0 +1,20 @@ +/** @file test_xxhash.cpp — xxHash 全覆盖 */ +#include "xxhash.h" +#include +#include +static int P=0,F=0; +#define T(n) do{printf(" %s ... ",n);}while(0) +#define OK() do{printf("OK\n");P++;}while(0) +#define FL(m) do{printf("FAIL: %s\n",m);F++;return;}while(0) +#define CK(c,m) do{if(!(c))FL(m);}while(0) + +static void t1(){T("XXH32 hello");XXH32_hash_t h=XXH32("hello",5,0);CK(h==XXH32("hello",5,0),"det");CK(h!=0,"0");OK();} +static void t2(){T("XXH64 hello");XXH64_hash_t h=XXH64("hello",5,0);CK(h==XXH64("hello",5,0),"det");CK(h!=0,"0");OK();} +static void t3(){T("XXH3_64 hello");XXH64_hash_t h=XXH3_64bits("hello",5);CK(h==XXH3_64bits("hello",5),"det");OK();} +static void t4(){T("XXH3_128 hello");XXH128_hash_t h=XXH3_128bits("hello",5);XXH128_hash_t h2=XXH3_128bits("hello",5);CK(h.high64==h2.high64&&h.low64==h2.low64,"det");CK(h.high64||h.low64,"0");OK();} +static void t5(){T("XXH32 diff seed");CK(XXH32("d",1,0)!=XXH32("d",1,12345),"diff");OK();} +static void t6(){T("XXH32 empty");CK(XXH32("",0,0)==XXH32("",0,0),"det");OK();} +static void t7(){T("XXH64 64B");const char*d="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";CK(XXH64(d,64,0)==XXH64(d,64,0),"det");OK();} +static void t8(){T("XXH128 state API");XXH3_state_t*s=XXH3_createState();CK(s,"c");XXH3_128bits_reset(s);XXH3_128bits_update(s,"hello",5);XXH128_hash_t h=XXH3_128bits_digest(s);XXH128_hash_t e=XXH3_128bits("hello",5);CK(h.high64==e.high64&&h.low64==e.low64,"mismatch");XXH3_freeState(s);OK();} + +int main(){printf("\n=== libmy_xxhash ===\n\n");t1();t2();t3();t4();t5();t6();t7();t8();printf("\n========\n %d pass, %d fail\n========\n",P,F);return F>0?1:0;}