/* * test_meow_ui_config.c — meow_config_* 宿主单测(2026-08-30)。 * * 编译(宿主,config 为纯 libc,无需 SDK 其它对象): * gcc -Wall -Wextra test_meow_ui_config.c meow_ui_config.c -o test_meow_ui_config -lpthread * * 两种模式(meow_config_dir 经 pthread_once 缓存,目录只在首次调用确定,故分两次跑): * ./test_meow_ui_config env → 设 MEOW_DATA_DIR=,验证持久目录读写 * ./test_meow_ui_config tmp → unsetenv MEOW_DATA_DIR,验证 /tmp/meowcfg_ 回退 * * 任一断言失败 → 打印 FAIL 并以非零退出码结束。 */ #include "meow_ui.h" #include #include #include #include static int g_fail = 0; #define CHECK(cond, msg) do { \ if (cond) { printf(" PASS: %s\n", msg); } \ else { printf(" FAIL: %s\n", msg); g_fail = 1; } \ } while (0) int main(int argc, char **argv) { const char *mode = (argc > 1) ? argv[1] : "env"; char *tmpdir = NULL; if (strcmp(mode, "tmp") == 0) { unsetenv("MEOW_DATA_DIR"); } else { tmpdir = strdup("/tmp/meowcfgtestXXXXXX"); if (!mkdtemp(tmpdir)) { printf("FATAL: mkdtemp failed\n"); return 2; } setenv("MEOW_DATA_DIR", tmpdir, 1); } printf("== meow_config test [%s] ==\n", mode); const char *dir = meow_config_dir(); CHECK(dir != NULL, "meow_config_dir() 非 NULL"); printf(" dir = %s\n", dir ? dir : "(null)"); if (strcmp(mode, "tmp") == 0) CHECK(dir && strstr(dir, "/tmp/meowcfg_") != NULL, "tmp 回退目录为 /tmp/meowcfg_"); else CHECK(dir && strcmp(dir, tmpdir) == 0, "env 目录 = MEOW_DATA_DIR"); /* --- set/get 往返 --- */ CHECK(meow_config_set("k1", "hello") == MEOW_CFG_OK, "set k1=hello -> MEOW_CFG_OK"); char buf[128]; CHECK(meow_config_get("k1", buf, sizeof buf) == MEOW_CFG_OK, "get k1 -> MEOW_CFG_OK"); CHECK(strcmp(buf, "hello") == 0, "get k1 值 = hello"); /* --- 全数字 int / bool 解析 --- */ meow_config_set("num", "42"); CHECK(meow_config_int("num", -1) == 42, "int(num)=42"); meow_config_set("truth", "true"); CHECK(meow_config_bool("truth", 0) == 1, "bool(true)=1"); meow_config_set("lie", "off"); CHECK(meow_config_bool("lie", 1) == 0, "bool(off)=0"); CHECK(meow_config_int("nonnum", 7) == 7, "int 非数字 -> def"); CHECK(meow_config_bool("nonbool", 3) == 3, "bool 非法 -> def"); /* --- 未设键 --- */ CHECK(meow_config_get("absent", buf, sizeof buf) == MEOW_CFG_ERR_NOENT, "get 未设键 -> MEOW_CFG_ERR_NOENT"); CHECK(meow_config_int("absent", 9) == 9, "int 未设键 -> def"); CHECK(meow_config_bool("absent", 1) == 1, "bool 未设键 -> def"); /* --- int 便捷写(经 set)再读 --- */ char nbuf[16]; meow_config_get("num", nbuf, sizeof nbuf); CHECK(strcmp(nbuf, "42") == 0, "int 存储为十进制 '42'"); /* --- 空值(NULL=清空为 0 字节文件,与「未设」区分)--- */ meow_config_set("empty", ""); CHECK(meow_config_get("empty", buf, sizeof buf) == MEOW_CFG_OK, "get 空值键 -> MEOW_CFG_OK"); CHECK(buf[0] == '\0', "空值键读回空串"); CHECK(meow_config_get("empty", buf, sizeof buf) != MEOW_CFG_ERR_NOENT, "空值键 ≠ 未设"); /* --- remove --- */ meow_config_remove("k1"); CHECK(meow_config_get("k1", buf, sizeof buf) == MEOW_CFG_ERR_NOENT, "remove 后 get -> NOENT"); /* --- key 清洗:'/' 等非法字符替换为 '_',杜绝路径穿越 --- */ meow_config_set("a/b", "slash"); CHECK(meow_config_get("a_b", buf, sizeof buf) == MEOW_CFG_OK && strcmp(buf, "slash") == 0, "key 'a/b' 清洗为 'a_b'"); /* 路径穿越尝试:key 含 '/' 的非法串,不得逃出数据目录('/'→'_','.' 保留为字面量)。 */ meow_config_set("../../etc/passwd", "evil"); CHECK(meow_config_get(".._.._etc_passwd", buf, sizeof buf) == MEOW_CFG_OK && strcmp(buf, "evil") == 0, "key '../../etc/passwd' 清洗为 '.._.._etc_passwd'('/'→'_',无穿越)"); /* --- 参数校验 --- */ CHECK(meow_config_get(NULL, buf, sizeof buf) == MEOW_CFG_ERR_INVAL, "get(NULL) -> INVAL"); CHECK(meow_config_get("x", NULL, 10) == MEOW_CFG_ERR_INVAL, "get(out=NULL) -> INVAL"); CHECK(meow_config_get("x", buf, 0) == MEOW_CFG_ERR_INVAL, "get(outsz=0) -> INVAL"); CHECK(meow_config_set("", "v") == MEOW_CFG_ERR_INVAL, "set(空 key) -> INVAL"); /* --- 多键独立 --- */ meow_config_set("ind.a", "1"); meow_config_set("ind.b", "2"); meow_config_get("ind.a", buf, sizeof buf); CHECK(strcmp(buf, "1") == 0, "多键独立 a"); meow_config_get("ind.b", buf, sizeof buf); CHECK(strcmp(buf, "2") == 0, "多键独立 b"); meow_config_remove("ind.a"); meow_config_get("ind.b", buf, sizeof buf); CHECK(strcmp(buf, "2") == 0, "删除 a 不影响 b"); /* --- 值截断(>MEOW_CFG_VAL_MAX 4096 截断;读合法)--- */ { char big[4096 * 2]; char rbuf[4100]; memset(big, 'A', sizeof big - 1); big[sizeof big - 1] = '\0'; meow_config_set("big", big); meow_config_get("big", rbuf, sizeof rbuf); CHECK(strlen(rbuf) == 4096, "值 >4096 截断为 4096"); } if (tmpdir) free(tmpdir); if (g_fail) { printf("\nRESULT: FAIL (%d)\n", g_fail); return 1; } printf("\nRESULT: ALL PASS\n"); return 0; }