feat(sdk): meow_config_* App配置读写API(agent-158)

- 6 API: meow_config_dir/set/get/int/bool/remove; 落 TF MeowData/<pkg>/data/<key>.cfg(env MEOW_DATA_DIR, 回退/tmp/meowcfg_<pid>)
- 与系统设置 meow_settings_*(板载settings.conf) 区分; key清洗[A-Za-z0-9._-]→_ 防路径穿越; 值上限4096; 降级不崩(pthread_once/mutex)
- MEOW_CFG_* 错误码(避开 meow_ipc.h 枚举命名空间, 防宏展开冲突); 数值与 MEOWOS_API/meow_settings.h 同值
- meow_ui.a=92e61fb8(新增config成员) hello=5e31d919不变(0回归, 静态链未拉入新成员); build_sdk 0warning; 宿主test_meow_ui_config ALL PASS
- 未重链launcher(launcher未用config); 遗留真机验证MEOW_DATA_DIR落盘/无卡降级
This commit is contained in:
MeowOS committed 2026-08-30 23:01:31 +08:00
1 parent 9c733fcf73
commit dcba5975a2
4 files changed
+388 -1

No files matched your search

+126
View File
@@ -0,0 +1,126 @@
/*
* 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=<mkdtemp>,验证持久目录读写
* ./test_meow_ui_config tmp → unsetenv MEOW_DATA_DIR,验证 /tmp/meowcfg_<pid> 回退
*
* 任一断言失败 → 打印 FAIL 并以非零退出码结束。
*/
#include "meow_ui.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
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_<pid>");
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;
}