- sdk/meow_time32: 抽出可独立链接的 o32 time32 取时/睡眠设施(不依赖 meow_ui.a, watchdog 等独立静态组件可直接链;可选 -DMEOW_TIME32_LIBC_OVERRIDES 接管 libc 入口) - watchdog: now_ms/usleep 改走 time32(修复 held 变天文数字→每次单击都弹菜单); 阈值读 power.longpress_menu_ms/forceoff_ms(缺省 3000/8000),删除 PWR_TOGGLE_MS==PWR_MENU_MS;新增 wd_power.h 纯判定 + 宿主行为矩阵测试 - launcher: App 前台电源菜单补齐 §10.5(notif 全抑制 + 帧恢复 + SIGCONT 排空 + 标记翻回前台);show_power_menu 恢复进入前背景帧(关闭后不再停留菜单画面); 重绘挪到首轮按键采样之后;activity 标记排空窗 + 会话入口清理 + manual_off 去闩锁 - lock_screen: 电源改按下边沿 + 进场 300ms 才熄屏;电源熄屏退出也恢复底层帧; launcher 在 SIGCONT 前写 /tmp/meow_app_repaint,SDK present 消费并 dirty_full - power_menu: 菜单内强关接 power.forceoff_ms(按 longpress 抵扣),日志不再谎称 8s - 重编 meow_ui.a 并重链 meow_launcherd/ncm/files/hello/reader(picbrowser 无源码) - fw_content.conf: 同步 launcherd/watchdog 新 md5;顺带修正 M35 遗留的 nyai 失效钉值 Host: test_wd_fw 66/66、test_overlay_gate 40/40、test_meow_time64/time/repaint PASS。 真机未验证(设备关机);未刷机、未写设备。
371 lines
17 KiB
C
371 lines
17 KiB
C
/* test_wd_fw.c — 宿主单测:meow_watchdog 的纯逻辑(M2.86 框架 App 判定 + M20 亮屏亮度 +
|
||
* M40 time32 取时/睡眠 + 电源阈值行为矩阵)。
|
||
*
|
||
* 编译:#include meow_watchdog.c(去 main 拿 wd_app_is_framework/json_str/from_pid、
|
||
* wd_bri_decide、wd_settings_brightness、wd_panel_off/on;wd_power.h 的纯判定)。
|
||
* gcc -Wall -Wextra -Os -DWd_NO_MAIN -Wno-unused-function -Wno-unused-parameter \
|
||
* test_wd_fw.c ../sdk/meow_time32.c -o test_wd_fw
|
||
* (meow_watchdog.c 含仅 main 使用的静态函数,宿主去 main 后须 -Wno-unused-function 静音;
|
||
* mips 目标构建不定义 WD_NO_MAIN,不受影响。)
|
||
* 运行:./test_wd_fw;全 PASS -> exit 0。
|
||
* M20 用例用 env MEOW_BACKLIGHT / MEOW_SETTINGS_FILE 把背光与 settings 指到临时文件,
|
||
* 真的走 wd_panel_off/wd_panel_on 的读写路径(真机无这两个 env,走 /sys 与 /usr/data 默认)。
|
||
*/
|
||
#define WD_NO_MAIN
|
||
#include "meow_watchdog.c"
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <unistd.h>
|
||
#include <sys/stat.h>
|
||
|
||
static int g_pass = 0, g_fail = 0;
|
||
|
||
static void check(const char *name, int got, int want)
|
||
{
|
||
if (got == want) { g_pass++; printf("PASS %s\n", name); }
|
||
else { g_fail++; printf("FAIL %s: got %d want %d\n", name, got, want); }
|
||
}
|
||
|
||
/* 临时目录根:优先 $TMPDIR(Termux/Android 上 /tmp 属于 shell 用户、本进程不可写),
|
||
* 否则 /tmp(Linux 构建容器)。让 `build_watchdog.sh host` 在两种环境都能直接跑。 */
|
||
static const char *tmp_root(void)
|
||
{
|
||
const char *t = getenv("TMPDIR");
|
||
return (t && t[0]) ? t : "/tmp";
|
||
}
|
||
|
||
/* ---- M2.87:SIGUSR1 手势让位(handle_touch 左缘滑动分支)----
|
||
* 判定(meow_watchdog.c):`!g_fw_app && immersive` 才发 SIGUSR1(只给裸沉浸 App);
|
||
* SDK 框架 App(g_fw_app=1)不发(返回由 SDK on_swipe 处理),防 SIGUSR1 杀死 App。 */
|
||
static volatile int g_sigusr1_hits = 0;
|
||
static void on_sigusr1(int s) { (void)s; g_sigusr1_hits++; }
|
||
|
||
/* 经 handle_touch 模拟一次「左缘起点+向右滑」完整手势(press→release),返回 SIGUSR1 命中数。 */
|
||
static int sim_swipe_sigusr1(int fw, int immersive)
|
||
{
|
||
g_fw_app = fw;
|
||
g_sigusr1_hits = 0;
|
||
last_x = 5; last_y = 300; /* 左缘原点(x<EDGE_W=24) */
|
||
unsigned long long now = now_ms();
|
||
handle_touch(immersive, getpid(), now - 3000, 1); /* press(过冷却期 + 坐标就绪) */
|
||
last_x = 100; /* 向右滑 95px(>SWIPE_DX=50) */
|
||
handle_touch(immersive, getpid(), now - 3000, 0); /* release */
|
||
return g_sigusr1_hits;
|
||
}
|
||
|
||
/* 在 <base>/<name> 建目录并写 app.json;写失败返回 -1。 */
|
||
static int fixture(const char *base, const char *name, const char *appjson)
|
||
{
|
||
char dir[512], path[560];
|
||
snprintf(dir, sizeof dir, "%s/%s", base, name);
|
||
if (mkdir(dir, 0755) != 0 && access(dir, F_OK) != 0) return -1;
|
||
snprintf(path, sizeof path, "%s/app.json", dir);
|
||
FILE *f = fopen(path, "w");
|
||
if (!f) return -1;
|
||
fputs(appjson, f);
|
||
fclose(f);
|
||
return 0;
|
||
}
|
||
|
||
static int expect_fw(const char *base, const char *name, const char *appjson, int want)
|
||
{
|
||
char dir[512];
|
||
snprintf(dir, sizeof dir, "%s/%s", base, name);
|
||
if (fixture(base, name, appjson) != 0) { g_fail++; printf("FAIL %s: cannot write fixture\n", name); return 0; }
|
||
char label[256];
|
||
snprintf(label, sizeof label, "is_fw(%s)", name);
|
||
check(label, wd_app_is_framework(dir), want);
|
||
return 1;
|
||
}
|
||
|
||
/* ---- M20:亮屏亮度决策(wd_bri_decide 纯函数)----
|
||
* 真机 bug(0.8.46,2026-11-05):settings display.brightness=1,亮屏时 watchdog 写入固定 60
|
||
* 覆盖它。新语义:settings 优先 → 真实快照 → 面板已亮则不写 → 面板黑才写安全低亮。 */
|
||
static void test_bri_decide(void)
|
||
{
|
||
const char *src = NULL;
|
||
int v;
|
||
|
||
v = wd_bri_decide(1, 60, 60, &src); /* 用户值 1 对上被污染的 60/当前 60:必须写 1 */
|
||
check("bri: settings wins over saved/cur", v == 1 && !strcmp(src, "settings"), 1);
|
||
v = wd_bri_decide(7, 0, 7, &src);
|
||
check("bri: settings 7", v == 7 && !strcmp(src, "settings"), 1);
|
||
v = wd_bri_decide(-1, 42, 3, &src); /* settings 不可读 → 用真实快照 */
|
||
check("bri: snapshot fallback", v == 42 && !strcmp(src, "snapshot"), 1);
|
||
v = wd_bri_decide(-1, 0, 3, &src); /* 都未知但面板已亮 → 不写(核心修复) */
|
||
check("bri: untouched keeps cur", v == -1 && !strcmp(src, "untouched"), 1);
|
||
v = wd_bri_decide(-1, 0, 0, &src); /* 都未知且面板黑 → 安全低亮,绝不 60 */
|
||
check("bri: safe-low not 60", v == WD_BRI_SAFE_LOW && v != 60 && v > 0 &&
|
||
!strcmp(src, "safe-low"), 1);
|
||
v = wd_bri_decide(-1, 0, -1, &src); /* 背光读失败按「黑」处理,仍要点亮 */
|
||
check("bri: read-fail -> safe-low", v == WD_BRI_SAFE_LOW && !strcmp(src, "safe-low"), 1);
|
||
v = wd_bri_decide(0, -1, 5, &src); /* 非正值一律按「未知」,不当作亮度 */
|
||
check("bri: non-positive treated unknown", v == -1 && !strcmp(src, "untouched"), 1);
|
||
}
|
||
|
||
/* ---- M20:settings 解析 + 重试 ----
|
||
* 用真机 /usr/data/meow/settings.conf 的关键行断言「路径/键名/解析/clamp 都是对的」,
|
||
* 证明真机写 60 不是本函数返回 -1(原因见报文:出货二进制根本没编进这段代码)。 */
|
||
static void test_settings_brightness(void)
|
||
{
|
||
char path[600];
|
||
snprintf(path, sizeof path, "%s/wdsetXXXXXX", tmp_root());
|
||
int fd = mkstemp(path);
|
||
if (fd < 0) { g_fail++; printf("FAIL settings: mkstemp\n"); return; }
|
||
FILE *f = fdopen(fd, "w");
|
||
fputs("# meow-settings 1\n"
|
||
"display.brightness = 1\n"
|
||
"display.brightness_min = 1\n"
|
||
"audio.volume = 10\n", f);
|
||
fclose(f);
|
||
setenv("MEOW_SETTINGS_FILE", path, 1);
|
||
check("settings: device file -> 1", wd_settings_brightness(), 1);
|
||
|
||
f = fopen(path, "w"); /* clamp 抬到 brightness_min */
|
||
fputs("display.brightness = 0\ndisplay.brightness_min = 3\n", f);
|
||
fclose(f);
|
||
check("settings: clamp up to min", wd_settings_brightness(), 3);
|
||
|
||
f = fopen(path, "w"); /* clamp 压到 100 */
|
||
fputs("display.brightness = 200\ndisplay.brightness_min = 1\n", f);
|
||
fclose(f);
|
||
check("settings: clamp down to 100", wd_settings_brightness(), 100);
|
||
|
||
f = fopen(path, "w"); /* 键缺失:与 launcher 设置表同默认 101 -> clamp 100 */
|
||
fputs("audio.volume = 10\n", f);
|
||
fclose(f);
|
||
check("settings: missing key -> 100", wd_settings_brightness(), 100);
|
||
|
||
/* 不可读(原子替换/未挂载窗口):open 重试 3 次仍失败才 -1,总耗时 ≈ 20ms */
|
||
unlink(path);
|
||
unsigned long long t0 = now_ms();
|
||
int r = wd_settings_brightness();
|
||
unsigned long long dt = now_ms() - t0;
|
||
check("settings: missing -> -1", r, -1);
|
||
check("settings: open retried (>=15ms)", dt >= 15, 1);
|
||
unsetenv("MEOW_SETTINGS_FILE");
|
||
}
|
||
|
||
static void put_file(const char *path, const char *body)
|
||
{
|
||
FILE *f = fopen(path, "w");
|
||
if (!f) return;
|
||
fputs(body, f);
|
||
fclose(f);
|
||
}
|
||
|
||
static int get_file_int(const char *path)
|
||
{
|
||
FILE *f = fopen(path, "r");
|
||
if (!f) return -1;
|
||
int v = -1;
|
||
if (fscanf(f, "%d", &v) != 1) v = -1;
|
||
fclose(f);
|
||
return v;
|
||
}
|
||
|
||
/* ---- M20:panel OFF/ON 端到端(真的读写一个「背光文件」,不 mock 函数)----
|
||
* 复现真机场景:settings display.brightness=1 → 熄屏 → 亮屏。旧代码亮屏写 60,
|
||
* 新代码必须写 1;且 settings 不可读、快照未知时保持当前值而不是回退 60。 */
|
||
static void test_panel_brightness_e2e(void)
|
||
{
|
||
char bl[600], st[600];
|
||
snprintf(bl, sizeof bl, "%s/wdblXXXXXX", tmp_root());
|
||
snprintf(st, sizeof st, "%s/wdstXXXXXX", tmp_root());
|
||
int fd = mkstemp(bl);
|
||
if (fd < 0) { g_fail++; printf("FAIL e2e: mkstemp bl\n"); return; }
|
||
close(fd);
|
||
fd = mkstemp(st);
|
||
if (fd < 0) { g_fail++; printf("FAIL e2e: mkstemp st\n"); unlink(bl); return; }
|
||
close(fd);
|
||
setenv("MEOW_BACKLIGHT", bl, 1);
|
||
setenv("MEOW_SETTINGS_FILE", st, 1);
|
||
|
||
/* 1) 真机 bug 场景:settings=1、屏亮 1 → OFF 快照 1;ON 必须写 1,绝不 60 */
|
||
put_file(bl, "1\n");
|
||
put_file(st, "display.brightness = 1\ndisplay.brightness_min = 1\n");
|
||
wd_saved_bri = WD_BRI_UNKNOWN;
|
||
wd_panel_off();
|
||
check("e2e: panel OFF writes 0", get_file_int(bl), 0);
|
||
check("e2e: panel OFF snapshot = 1", wd_saved_bri, 1);
|
||
wd_panel_on();
|
||
check("e2e: panel ON restores settings 1 (not 60)", get_file_int(bl), 1);
|
||
|
||
/* 2) 用户改了亮度:settings 仍是唯一权威(旧快照 1 不得覆盖 5) */
|
||
put_file(st, "display.brightness = 5\ndisplay.brightness_min = 1\n");
|
||
put_file(bl, "0\n");
|
||
wd_panel_on();
|
||
check("e2e: panel ON follows new settings 5", get_file_int(bl), 5);
|
||
|
||
/* 3) settings 不可读 + 快照未知 + 屏亮 3 → 不写(保持 3,不再回退 60) */
|
||
unlink(st);
|
||
put_file(bl, "3\n");
|
||
wd_saved_bri = WD_BRI_UNKNOWN;
|
||
wd_panel_on();
|
||
check("e2e: panel ON leaves 3 untouched", get_file_int(bl), 3);
|
||
|
||
/* 4) settings 不可读 + 快照未知 + 屏黑 0 → 写安全低亮(>0 且 != 60) */
|
||
put_file(bl, "0\n");
|
||
wd_panel_on();
|
||
check("e2e: panel ON dark -> safe-low", get_file_int(bl), WD_BRI_SAFE_LOW);
|
||
|
||
/* 5) settings 不可读但本次运行期真观测到 7 → 快照优先于「不动」 */
|
||
put_file(bl, "7\n");
|
||
wd_saved_bri = WD_BRI_UNKNOWN;
|
||
wd_panel_off(); /* 观测到 7,写 0 */
|
||
check("e2e: panel OFF snapshot = 7", wd_saved_bri, 7);
|
||
wd_panel_on();
|
||
check("e2e: panel ON uses real snapshot 7", get_file_int(bl), 7);
|
||
|
||
unlink(bl);
|
||
unsetenv("MEOW_BACKLIGHT");
|
||
unsetenv("MEOW_SETTINGS_FILE");
|
||
}
|
||
|
||
/* ---- M40:time32 取时/睡眠(与 libc 正常取时一致;o32 syscall 路径由交叉二进制反汇编验证)---- */
|
||
static void test_time32(void)
|
||
{
|
||
struct meow_time32 t;
|
||
check("time32 gettime ok", meow_time32_clock_gettime(CLOCK_MONOTONIC, &t) == 0, 1);
|
||
struct timespec ts;
|
||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||
check("time32 sec == libc sec", t.tv_sec == (int32_t)ts.tv_sec, 1);
|
||
long dn = (long)t.tv_nsec - (long)ts.tv_nsec;
|
||
if (dn < 0) dn = -dn;
|
||
check("time32 nsec == libc nsec (<1ms)", dn < 1000000, 1);
|
||
|
||
uint64_t a = meow_time32_monotonic_ms();
|
||
uint64_t b = meow_time32_monotonic_ms();
|
||
check("monotonic nonzero", a > 0, 1);
|
||
check("monotonic nondecreasing", b >= a, 1);
|
||
check("ms from parts 1135.008s", meow_time_ms_from_parts(1135, 8000000) == 1135008ULL, 1);
|
||
|
||
unsigned long long s0 = now_ms();
|
||
meow_time32_sleep_us(20000);
|
||
unsigned long long el = now_ms() - s0;
|
||
check("sleep 20ms actually sleeps (>=15ms)", el >= 15, 1);
|
||
}
|
||
|
||
/* ---- M40:电源阈值/边沿行为矩阵(settings 取不同值)---- */
|
||
static void test_power_matrix(void)
|
||
{
|
||
unsigned long long lp = 0, fo = 0;
|
||
|
||
wd_pwr_resolve_thresholds(0, 0, &lp, &fo);
|
||
check("pwr default longpress=3000", lp == 3000, 1);
|
||
check("pwr default forceoff=8000", fo == 8000, 1);
|
||
|
||
wd_pwr_resolve_thresholds(1500, 5000, &lp, &fo);
|
||
check("pwr settings longpress=1500", lp == 1500, 1);
|
||
check("pwr settings forceoff=5000", fo == 5000, 1);
|
||
|
||
wd_pwr_resolve_thresholds(-1, 0, &lp, &fo);
|
||
check("pwr invalid -> default longpress", lp == 3000, 1);
|
||
check("pwr invalid -> default forceoff", fo == 8000, 1);
|
||
|
||
wd_pwr_resolve_thresholds(3000, 2000, &lp, &fo);
|
||
check("pwr forceoff<=longpress lifted to lp+1000", fo == 4000, 1);
|
||
|
||
/* 松开边沿:单击 / 长按但未到阈值 / 到阈值 / 唤醒 / 菜单已请求 */
|
||
check("release 100ms screen-on -> toggle off",
|
||
wd_pwr_release_action(0, 0, 100, 3000, 50) == WD_PWR_REL_TOGGLE_OFF, 1);
|
||
check("release 100ms screen-off -> toggle on",
|
||
wd_pwr_release_action(0, 0, 100, 3000, 0) == WD_PWR_REL_TOGGLE_ON, 1);
|
||
check("release 2500ms (<3000) still click -> toggle off",
|
||
wd_pwr_release_action(0, 0, 2500, 3000, 50) == WD_PWR_REL_TOGGLE_OFF, 1);
|
||
check("release 3000ms (>=longpress) -> no toggle",
|
||
wd_pwr_release_action(0, 0, 3000, 3000, 50) == WD_PWR_REL_NONE, 1);
|
||
check("release after wake -> no toggle",
|
||
wd_pwr_release_action(1, 0, 100, 3000, 0) == WD_PWR_REL_NONE, 1);
|
||
check("release after menu req -> no toggle",
|
||
wd_pwr_release_action(0, 1, 3500, 3000, 50) == WD_PWR_REL_NONE, 1);
|
||
|
||
/* 按住 tick:单击窗 / 长按出菜单 / 强关兜底 / 菜单已接管 / 已强关 */
|
||
check("tick 2999ms -> none",
|
||
wd_pwr_tick_action(0, 1, 0, 0, 2999, 3000, 8000) == WD_PWR_TICK_NONE, 1);
|
||
check("tick 3000ms -> menu",
|
||
wd_pwr_tick_action(0, 1, 0, 0, 3000, 3000, 8000) == WD_PWR_TICK_MENU, 1);
|
||
check("tick 8000ms menu requested, no takeover -> forceoff",
|
||
wd_pwr_tick_action(0, 1, 1, 0, 8000, 3000, 8000) == WD_PWR_TICK_FORCEOFF, 1);
|
||
check("tick 8000ms launcher menu active -> none",
|
||
wd_pwr_tick_action(1, 1, 1, 0, 8000, 3000, 8000) == WD_PWR_TICK_NONE, 1);
|
||
check("tick forceoff already done -> none",
|
||
wd_pwr_tick_action(0, 1, 1, 1, 9000, 3000, 8000) == WD_PWR_TICK_NONE, 1);
|
||
check("tick not down -> none",
|
||
wd_pwr_tick_action(0, 0, 0, 0, 9000, 3000, 8000) == WD_PWR_TICK_NONE, 1);
|
||
/* settings 取短值:1000/2000 */
|
||
check("tick custom 1000ms -> menu",
|
||
wd_pwr_tick_action(0, 1, 0, 0, 1000, 1000, 2000) == WD_PWR_TICK_MENU, 1);
|
||
check("tick custom 2000ms no takeover -> forceoff",
|
||
wd_pwr_tick_action(0, 1, 1, 0, 2000, 1000, 2000) == WD_PWR_TICK_FORCEOFF, 1);
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
signal(SIGUSR1, on_sigusr1); /* 捕获 handle_touch 可能发出的 SIGUSR1(测命中) */
|
||
char dir[600];
|
||
snprintf(dir, sizeof dir, "%s/wdfwXXXXXX", tmp_root());
|
||
if (!mkdtemp(dir)) { perror("mkdtemp"); return 2; }
|
||
|
||
/* 框架 App:各值形态 */
|
||
expect_fw(dir, "v_num", "{\"name\":\"x\",\"framework\":1,\"exec\":\"/a/b\"}", 1); /* JSON 数值 1 */
|
||
expect_fw(dir, "v_true", "{\"name\":\"x\",\"framework\":true}", 1); /* true */
|
||
expect_fw(dir, "v_yes", "framework=yes", 1); /* 扁平 yes */
|
||
expect_fw(dir, "v_str", "{\"framework\":\"true\"}", 1); /* 字符串 "true" */
|
||
expect_fw(dir, "v_space","{\"name\":\"x\", \"framework\" : 1 }", 1); /* 空格容错 */
|
||
|
||
/* 非框架 App */
|
||
expect_fw(dir, "v_zero", "{\"name\":\"x\",\"framework\":0}", 0); /* 数值 0 */
|
||
expect_fw(dir, "v_false","{\"framework\":false}", 0); /* false */
|
||
expect_fw(dir, "v_none", "{\"name\":\"x\",\"exec\":\"/a/b\"}", 0); /* 无 framework 键 */
|
||
expect_fw(dir, "v_sub", "{\"x_framework\":1}", 0); /* 键前缀歧义:不误判 */
|
||
|
||
/* 多行 JSON(framework 单独一行,值独立) */
|
||
expect_fw(dir, "multiline",
|
||
"{\n \"name\": \"x\",\n \"framework\": 1,\n \"exec\": \"/a/b\"\n}\n", 1);
|
||
|
||
/* wd_json_str 直接断言(防 "myframework" / 前缀干扰) */
|
||
{
|
||
char out[16] = "";
|
||
check("json_str exact", wd_json_str("{\"framework\":1}", "framework", out, sizeof out) > 0 &&
|
||
!strcmp(out, "1"), 1);
|
||
check("json_str prefix-reject",
|
||
wd_json_str("{\"x_framework\":1}", "framework", out, sizeof out), 0);
|
||
check("json_str no-hit", wd_json_str("{\"name\":\"x\"}", "framework", out, sizeof out), 0);
|
||
}
|
||
|
||
/* wd_framework_from_pid:自进程 /proc/self/exe 目录无 app.json -> 0,不崩 */
|
||
check("from_pid(no app.json)", wd_framework_from_pid(getpid()), 0);
|
||
|
||
/* ---- M2.87 SIGUSR1 让位判定(仅裸沉浸 App 发 SIGUSR1)---- */
|
||
check("sigusr1 fw-app (framework=1) -> 0", sim_swipe_sigusr1(1, 1), 0); /* SDK 框架 App:不发 */
|
||
check("sigusr1 bare-immersive -> 1", sim_swipe_sigusr1(0, 1), 1); /* 裸沉浸:发(命中) */
|
||
check("sigusr1 bare-nonimmersive -> 0", sim_swipe_sigusr1(0, 0), 0); /* 裸非沉浸:不发 */
|
||
|
||
/* ---- M20:亮屏亮度(决策 + settings 解析/重试 + panel OFF/ON 端到端)---- */
|
||
test_bri_decide();
|
||
test_settings_brightness();
|
||
test_panel_brightness_e2e();
|
||
|
||
/* ---- M40:time32 取时/睡眠 + 电源阈值行为矩阵 ---- */
|
||
test_time32();
|
||
test_power_matrix();
|
||
|
||
/* 清理 */
|
||
{
|
||
char path[560];
|
||
const char *names[] = { "v_num","v_true","v_yes","v_str","v_space","v_zero",
|
||
"v_false","v_none","v_sub","multiline" };
|
||
for (unsigned i = 0; i < sizeof names / sizeof names[0]; i++) {
|
||
snprintf(path, sizeof path, "%s/%s/app.json", dir, names[i]); unlink(path);
|
||
snprintf(path, sizeof path, "%s/%s", dir, names[i]); rmdir(path);
|
||
}
|
||
rmdir(dir);
|
||
}
|
||
|
||
printf("\n%d pass, %d fail\n", g_pass, g_fail);
|
||
return g_fail ? 1 : 0;
|
||
}
|