feat(test): 全模块单元测试体系 — 9 模块 126 项

新测试文件(覆盖对应模块全部公开接口):
- 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
This commit is contained in:
ypc 2026-07-08 13:39:29 +08:00
parent 066dc62916
commit 6c5bcafe23
9 changed files with 705 additions and 0 deletions

29
src/test/test_cjson.cpp Normal file
View File

@ -0,0 +1,29 @@
/** @file test_cjson.cpp — cJSON 解析/生成全覆盖 */
#include "cJSON.h"
#include <cstdio>
#include <cstring>
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;}

22
src/test/test_cmd.cpp Normal file
View File

@ -0,0 +1,22 @@
/** @file test_cmd.cpp — 命令框架全覆盖 */
#include "myCmd.h"
#include <cstdio>
#include <cstring>
#include <cstdlib>
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;i<n;i++)free(c[i]);free(c);OK();}
static void t7(){T("linenoise hist");linenoiseHistoryAdd("c1");linenoiseHistoryAdd("c2");linenoiseHistoryFree();linenoiseHistoryAdd("c3");OK();}
int main(){printf("\n=== libcmd ===\n\n");t1();t2();t3();t4();t5();t6();t7();printf("\n========\n %d pass, %d fail\n========\n",P,F);return F>0?1:0;}

108
src/test/test_func.cpp Normal file
View File

@ -0,0 +1,108 @@
/**
* @file test_func.cpp
* @brief 65
*/
#include "myFunc.h"
#include "myBase.h"
#include <cstdio>
#include <cstring>
#include <cstdlib>
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;
}

155
src/test/test_list.c Normal file
View File

@ -0,0 +1,155 @@
/**
* @file test_list.c
* @brief /
*/
#include "list.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
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;
}

99
src/test/test_log.cpp Normal file
View File

@ -0,0 +1,99 @@
/**
* @file test_log.cpp
* @brief
*/
#include "myLog.h"
#include <cstdio>
#include <cstring>
#include <thread>
#include <chrono>
#include <unistd.h>
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;
}

98
src/test/test_md5.cpp Normal file
View File

@ -0,0 +1,98 @@
/**
* @file test_md5.cpp
* @brief MD5 RFC 1321
*/
#include "myMd5.h"
#include "myBase.h"
#include <cstdio>
#include <cstring>
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;
}

155
src/test/test_task.cpp Normal file
View File

@ -0,0 +1,155 @@
/**
* @file test_task.cpp
* @brief //
*/
#include "myTask.h"
#include <cstdio>
#include <cstring>
#include <thread>
#include <chrono>
#include <atomic>
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<int> 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;
}

19
src/test/test_xml.cpp Normal file
View File

@ -0,0 +1,19 @@
/** @file test_xml.cpp — XML C接口全覆盖 */
#include "myXml.h"
#include <cstdio>
#include <cstring>
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="<?xml version=\"1.0\"?>\n<root><item n=\"i1\" v=\"100\"/><item n=\"i2\" v=\"3.14\"/><g id=\"1\"><e k=\"k1\"/></g></root>\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("<r><item/></r>",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;}

20
src/test/test_xxhash.cpp Normal file
View File

@ -0,0 +1,20 @@
/** @file test_xxhash.cpp — xxHash 全覆盖 */
#include "xxhash.h"
#include <cstdio>
#include <cstring>
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;}