P2-1(必修):`multi_font: class %s font %s missing, fallback global` 与上一轮修掉的 enabled 行同在 meow_font_reload_multifont() 里、被 main_menu.c:1385 每 2s 调一次; 命中「某个 font.class_* 非空但文件缺失/不可读」时最多 6 行/2s(3 行/s),比 enabled 行凶 6 倍,会让「空转 60s 日志不增长」在 class 缺失形态下失效。 改法:循环内只记「本轮缺失类别位图」,循环外经新纯函数 mf_missing_warn_mask() 做跃迁 判定(缺失集合不变 → 静默;新类别缺失 → 1 条;恢复后再缺失 → 再 1 条)。回退行为不变 (缺失类别照旧回退全局),日志文本/参数不变。另加 _Static_assert 钉住类别数 ≤32(位图宽度)。 nit:wd_log() 的 snprintf 返回值显式 clamp(原「buf≤511+前缀≤23<544」是隐式不变量); test_wd_fw 的 "scan finds self" 原断言带 `|| np > 0` 恒真、等于没测 → 改为真查表断言 getpid() 在 wd_rg_pid[] 中且 comm 非空、并与 /proc/self/stat 的 pid/ppid 交叉核对。 验证:test_wd_fw 128 pass/0 fail(原 124,+4 来自强化后的自扫断言); test_mf_logsig 21 pass/0 fail(原 13,+8 为 P2-1 验收:单类缺失 60s@2s 只 1 条、 缺失集合变化再 1 条、恢复后再缺失再 1 条);launcher 宿主测试 ALL PASS; 三处交叉编译 0 warning(watchdog 100,116 B/25f76950、launcherd 902,044 B/75629078、 audiod 68,732 B/ebb9a7ac 未变=bind hunk 未动)。
137 lines
6.9 KiB
C
137 lines
6.9 KiB
C
/* test_mf_logsig.c — 宿主单测:多字体「日志跃迁签名」与门控(M52 刷屏整改)。
|
||
*
|
||
* 背景:meow_font_reload_multifont() 被 launcher 主循环每 2s 调一次,旧实现每次无条件
|
||
* 打 `multi_font: enabled, ...`(真机 5297/6603 行 = 80%)。整改后只在配置签名变化时打。
|
||
* 本测试守两件事:
|
||
* (1) 签名对**所有**能改变行为的输入敏感(on / global / 各类别 / distinct / truncated);
|
||
* (2) 输入不变时签名恒定 —— 即 60s 空转(30 次调用)只产生 1 条日志。
|
||
*
|
||
* 编译运行:gcc -I. -Wall -Wextra test_mf_logsig.c -o test_mf_logsig && ./test_mf_logsig
|
||
* (不依赖 launcher 其它模块 / 不依赖设备;纯头文件逻辑)
|
||
*/
|
||
#include "mf_logsig.h"
|
||
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
|
||
#define NCLS 6
|
||
|
||
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); }
|
||
}
|
||
|
||
/* 真机 settings.conf 的多字体形态(2026-11-11):
|
||
* display.multi_font = 1
|
||
* font.file = MeowOS/fonts/ztest/regular/font.png → global = ztest
|
||
* font.class_num / font.class_latin = MeowOS/fonts/google_sans/regular/font.png
|
||
* → distinct = 2(ztest + google_sans) */
|
||
#define G_ZTEST "/usr/data/mnt/sd_0/MeowOS/fonts/ztest/regular/font.png"
|
||
#define C_GOOGLE "/usr/data/mnt/sd_0/MeowOS/fonts/google_sans/regular/font.png"
|
||
#define C_MISANS "/usr/data/mnt/sd_0/MeowOS/fonts/misans/regular/font.png"
|
||
|
||
int main(void)
|
||
{
|
||
/* ---- 1. 真机形态:同输入 → 同签名(1000 次不变,这就是「空转不打日志」的根基)---- */
|
||
const char *base[NCLS] = { NULL, NULL, C_GOOGLE, C_GOOGLE, NULL, NULL };
|
||
uint64_t s0 = mf_logsig(1, G_ZTEST, base, NCLS, 2, 0);
|
||
int stable = 1;
|
||
for (int i = 0; i < 1000; i++)
|
||
if (mf_logsig(1, G_ZTEST, base, NCLS, 2, 0) != s0) stable = 0;
|
||
check("same config -> same sig (1000x)", stable, 1);
|
||
|
||
/* ---- 2. 每一条输入变化都必须换签名 ---- */
|
||
check("on 1->0 changes sig", mf_logsig(0, G_ZTEST, base, NCLS, 2, 0) != s0, 1);
|
||
check("global font changes sig",
|
||
mf_logsig(1, "/usr/data/mnt/sd_0/MeowOS/fonts/default/regular/font.png",
|
||
base, NCLS, 2, 0) != s0, 1);
|
||
{
|
||
const char *c[NCLS] = { NULL, NULL, C_MISANS, C_GOOGLE, NULL, NULL };
|
||
check("class_num changes sig", mf_logsig(1, G_ZTEST, c, NCLS, 2, 0) != s0, 1);
|
||
}
|
||
check("distinct 2->1 changes sig", mf_logsig(1, G_ZTEST, base, NCLS, 1, 0) != s0, 1);
|
||
check("truncated 0->1 changes sig", mf_logsig(1, G_ZTEST, base, NCLS, 2, 1) != s0, 1);
|
||
check("config-fail (-1) changes sig", mf_logsig(1, G_ZTEST, base, NCLS, -1, 0) != s0, 1);
|
||
|
||
/* ---- 3. 空配置项形态:NULL 与 "" 等价(settings 空值 = 该类回退全局)---- */
|
||
{
|
||
const char *a[NCLS] = { NULL, NULL, NULL, NULL, NULL, NULL };
|
||
const char *b[NCLS] = { "", "", "", "", "", "" };
|
||
check("NULL == \"\"", mf_logsig(1, G_ZTEST, a, NCLS, 1, 0) ==
|
||
mf_logsig(1, G_ZTEST, b, NCLS, 1, 0), 1);
|
||
check("null cpng array tolerated", mf_logsig(1, G_ZTEST, NULL, NCLS, 1, 0) > 0, 1);
|
||
}
|
||
|
||
/* ---- 4. 字段边界不串味:("ab","c") 与 ("a","bc") 不能撞 ---- */
|
||
{
|
||
const char *x[NCLS] = { "ab", "c", NULL, NULL, NULL, NULL };
|
||
const char *y[NCLS] = { "a", "bc", NULL, NULL, NULL, NULL };
|
||
check("field boundary no collision",
|
||
mf_logsig(1, G_ZTEST, x, NCLS, 2, 0) != mf_logsig(1, G_ZTEST, y, NCLS, 2, 0), 1);
|
||
}
|
||
|
||
/* ---- 5. 门控:60s 空转(2s 一次 × 30)只打 1 条;改配置再打 1 条;改回来再打 1 条 ---- */
|
||
{
|
||
uint64_t last = 0;
|
||
int valid = 0, emits = 0;
|
||
for (int i = 0; i < 30; i++) { /* 首轮 + 29 轮空转 */
|
||
uint64_t sig = mf_logsig(1, G_ZTEST, base, NCLS, 2, 0);
|
||
if (mf_logsig_changed(&last, &valid, sig)) emits++;
|
||
}
|
||
check("60s idle @2s -> exactly 1 log line", emits, 1);
|
||
{
|
||
const char *c[NCLS] = { NULL, NULL, C_MISANS, C_GOOGLE, NULL, NULL };
|
||
uint64_t sig = mf_logsig(1, G_ZTEST, c, NCLS, 2, 0);
|
||
if (mf_logsig_changed(&last, &valid, sig)) emits++;
|
||
}
|
||
check("class change -> 2nd log line", emits, 2);
|
||
for (int i = 0; i < 30; i++) { /* 再空转 60s:不应再增长 */
|
||
uint64_t sig = mf_logsig(1, G_ZTEST, base, NCLS, 2, 0);
|
||
if (mf_logsig_changed(&last, &valid, sig)) emits++;
|
||
}
|
||
check("back to original -> 3rd line, then silent", emits, 3);
|
||
/* 真实行数:旧实现 60s = 30 行(每 2s 一条),新实现 60s = 0 行(稳定后) */
|
||
printf(" (旧实现 60s 空转 = 30 行;新实现稳态 = 0 行,本用例全程 %d 行)\n", emits);
|
||
}
|
||
|
||
/* ---- 6. 「class 字体缺失」WARN 的跃迁门控(r1 评审 P2-1 的验收断言)----
|
||
* 场景:某个 font.class_* 配置了但字体文件缺失 → 该类别回退全局(行为不变),
|
||
* 旧实现每 2s 每类各打一条(最多 6 行/2s = 3 行/s)。要求:
|
||
* 缺失集合不变 → 60s(30 次) 只出现 1 次;
|
||
* 缺失的类别变了(另一个 class 也缺失)→ 再出现 1 次(不是一次性闩锁);
|
||
* 某类恢复后再缺失 → 也算新跃迁,再 1 次。 */
|
||
{
|
||
unsigned logged = 0;
|
||
int warns = 0;
|
||
/* (a) 只有 class_num(2) 缺失:连续 30 次(60s @2s) */
|
||
for (int i = 0; i < 30; i++)
|
||
if (mf_missing_warn_mask(&logged, 1u << 2)) warns++;
|
||
check("class missing 60s @2s -> exactly 1 WARN", warns, 1);
|
||
/* (b) 缺失集合不变(再 30 次):不增长 */
|
||
for (int i = 0; i < 30; i++)
|
||
if (mf_missing_warn_mask(&logged, 1u << 2)) warns++;
|
||
check("class missing unchanged -> no new WARN", warns, 1);
|
||
/* (c) class_latin(3) 也变缺失:只多 1 次(且只针对新缺失的那一类) */
|
||
{
|
||
unsigned fresh = mf_missing_warn_mask(&logged, (1u << 2) | (1u << 3));
|
||
warns += fresh ? 1 : 0;
|
||
check("new missing class -> exactly 1 more WARN", fresh, 1u << 3);
|
||
}
|
||
check("total WARN after set change", warns, 2);
|
||
/* (d) class_num(2) 恢复 → 位图缩小但不再打;再次缺失 → 重新打一次 */
|
||
check("recovered class -> no WARN", mf_missing_warn_mask(&logged, 1u << 3), 0);
|
||
check("re-missing after recovery -> WARN again",
|
||
mf_missing_warn_mask(&logged, (1u << 2) | (1u << 3)), 1u << 2);
|
||
/* (e) 全部恢复 → 位图清零、静默 */
|
||
check("none missing -> no WARN", mf_missing_warn_mask(&logged, 0), 0);
|
||
check("logged mask reset", logged, 0);
|
||
printf(" (旧实现 60s @2s 单类缺失 = 30 行;新实现 = 1 行)\n");
|
||
}
|
||
|
||
printf("\n%d pass, %d fail\n", g_pass, g_fail);
|
||
return g_fail ? 1 : 0;
|
||
}
|