Files
MeowOS-For-R3P2/patch_v2/bootmenu/test_bm_fw.c
T
AskaEth bc7bafa5dc fix(bootmenu): M23 亮度单一权威源(只交源码,二进制由 M43 统一重编)
真机 0.8.46 两个症状:
1) 开机菜单亮度 = 第一屏的 60(用户设置 1)——根因是 M6.25 的选单亮度写回只改了源码、
   二进制没重编(strings 无 display.brightness_min),本次只交源码,重编交给 M43 批次。
2) HibyOS 亮/灭屏瞬间爆亮——supervisor 把启动瞬间面板读数(播放器写的 60)当保底,
   且 >60 一律钉 60;扫描窗保底只要读到亮度<=0 就在 400ms 内写回该值,用户主动熄屏
   也会被强行点亮。

改动(全部失败也沿用现状行为):
- saved_brightness 权威源改为 settings.conf 的 display.brightness(与 watchdog/launcher
  同源同 clamp);面板读数只在 settings 不可读时兜底;两者都不可知才写 BM_BRI_SAFE_LOW。
- 旧的 `>60 → 60` 硬钉改为 clamp 进 [display.brightness_min, max_brightness](bm_bri_clamp),
  不再用常量顶替用户值;为什么不会重新引入 M2.9 开机闪屏写在 bm_bri_pick_saved 注释里。
- 扫描窗保底加「播放器忙(proc 态 D)+ 面板未下电(bl_power<=0)」两门,窗口结束彻底放手。
- 热键熄屏恢复:面板真实正值优先(HibyOS 内改过亮度),否则用 settings 值,夹进 [min,max]。
- 决策抽纯函数 bm_bri_pick_saved / bm_bri_clamp / bm_bri_floor_decide / bm_bri_hotkey_saved。
- 亮度判定/写回补 bm_log 落卡(stderr 原行全部保留),只在状态跃迁时打,不在高频路径刷日志。
- test_bm_fw.c:宿主单测(settings=1/saved=60/now=0/blank 矩阵 + clamp 矩阵 + 真文件 e2e)。
- -Wall -Wextra 清理:3 处 unsigned/int 比较、未用参数、未用变量、sys/poll.h→poll.h。

不含二进制、不改 fw_content.conf 钉值(保持 4441822d)。
2026-11-11 18:42:41 +08:00

256 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* test_bm_fw.c — 宿主单测:meow_bootmenu 的亮度决策(M23 亮度单一权威源)。
*
* 覆盖真机 0.8.46 两个症状的判定矩阵:
* ① 开机菜单/选单亮度 = 面板继承值 60(用户设置 1)—— 选单路径 bootmenu_settings_brightness()
* ② HibyOS 亮/灭屏瞬间爆亮 —— supervisor 起始 saved、扫描窗保底、热键熄屏恢复三处决策
*
* 编译(build_bootmenu.sh host;与 meow_watchdog 的 test_wd_fw.c 同款 include 打法):
* gcc -Wall -Wextra -Os -I../meow_launcherd -DBM_NO_MAIN \
* -Wno-unused-function -Wno-unused-parameter -Wno-unused-variable \
* test_bm_fw.c ../meow_launcherd/meow_touch.c -o test_bm_fw
* 运行:./test_bm_fw;全 PASS -> exit 0。
* env MEOW_BACKLIGHT / MEOW_SETTINGS_FILE 把背光与 settings 指到临时文件(真机无这两个 env,
* 走 /sys 与 /usr/data 默认路径),因此这里的读写断言就是真机同一条代码路径。
*/
#define BM_NO_MAIN
#include "meow_bootmenu.c"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.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 构建容器)——与 test_wd_fw.c 一致。 */
static const char *tmp_root(void)
{
const char *t = getenv("TMPDIR");
return (t && t[0]) ? t : "/tmp";
}
static void put_file(const char *path, const char *body)
{
FILE *f = fopen(path, "w");
if (!f) { g_fail++; printf("FAIL cannot write %s\n", path); return; }
fputs(body, f);
fclose(f);
}
static int get_file_int(const char *path)
{
FILE *f = fopen(path, "r");
int v = -1;
if (!f) return -2;
if (fscanf(f, "%d", &v) != 1) v = -1;
fclose(f);
return v;
}
/* ---- A) supervisor 起始 saved_brightness:settings 是唯一权威 ----
* 真机 bug:settings display.brightness=1,面板被播放器写成 60 → 旧代码 saved=60(>60→60 的
* 硬钉同宗),随后扫描窗保底/熄屏恢复把 60 写回 → 亮/灭屏爆亮。新语义必须取 1。 */
static void test_pick_saved(void)
{
const char *src = NULL;
int v;
v = bm_bri_pick_saved(1, 60, 1, 101, &src); /* ← 真机症状本体 */
check("saved: settings 1 wins over panel 60", v == 1 && !strcmp(src, "settings"), 1);
v = bm_bri_pick_saved(1, 0, 1, 101, &src); /* 播放器已把面板写成 0 */
check("saved: settings 1 when panel dark", v == 1 && !strcmp(src, "settings"), 1);
v = bm_bri_pick_saved(7, 7, 1, 101, &src);
check("saved: settings 7", v == 7 && !strcmp(src, "settings"), 1);
/* settings 不可读(降级路径):沿用面板读数,夹进 [brightness_min, max_brightness](M23 新语义) */
v = bm_bri_pick_saved(-1, 60, 1, 101, &src);
check("saved: fallback keeps panel 60", v == 60 && !strcmp(src, "panel"), 1);
v = bm_bri_pick_saved(-1, 80, 1, 101, &src); /* 用户真设 80:不再被旧 `>60→60` 顶成 60 */
check("saved: fallback keeps 80 (old code pinned 60)", v == 80 && !strcmp(src, "panel"), 1);
v = bm_bri_pick_saved(-1, 200, 1, 101, &src); /* 越界读数夹到 max_brightness */
check("saved: fallback clamps 200 -> max 101", v == 101 && !strcmp(src, "panel"), 1);
v = bm_bri_pick_saved(-1, 37, 1, 32, &src); /* 面板上限 32 时按 32 */
check("saved: fallback clamps to max_brightness 32", v == 32 && !strcmp(src, "panel"), 1);
v = bm_bri_pick_saved(-1, 2, 6, 101, &src); /* 低于 brightness_min 时抬到 min(防黑屏) */
check("saved: fallback clamps up to brightness_min 6", v == 6 && !strcmp(src, "panel"), 1);
v = bm_bri_pick_saved(0, 3, 1, 101, &src); /* 非正值按住「未知」处理 */
check("saved: user<=0 treated unknown", v == 3 && !strcmp(src, "panel"), 1);
/* settings 与面板都不可知且面板黑:安全低亮,绝不再写 60 */
v = bm_bri_pick_saved(-1, 0, 1, 101, &src);
check("saved: dark + no settings -> safe-low", v == BM_BRI_SAFE_LOW && v != 60 &&
v > 0 && !strcmp(src, "safe-low"), 1);
v = bm_bri_pick_saved(-1, -1, 1, 0, &src); /* 读失败 + max 读不到 */
check("saved: read-fail -> safe-low", v == BM_BRI_SAFE_LOW && !strcmp(src, "safe-low"), 1);
/* max 读不到(0)时只夹下限,不臆造上限 */
v = bm_bri_pick_saved(-1, 90, 1, 0, &src);
check("saved: unknown max keeps 90", v == 90 && !strcmp(src, "panel"), 1);
}
/* ---- A2) clamp 语义单测:取代旧的 `>60 → 60` 硬钉 ---- */
static void test_clamp(void)
{
check("clamp: in-range untouched", bm_bri_clamp(37, 1, 101), 37);
check("clamp: above max -> max", bm_bri_clamp(200, 1, 101), 101);
check("clamp: 80 stays 80 (was pinned 60)", bm_bri_clamp(80, 1, 101), 80);
check("clamp: below min -> min", bm_bri_clamp(0, 3, 101), 3);
check("clamp: mn<1 -> 1", bm_bri_clamp(0, 0, 101), 1);
check("clamp: unknown max only floors", bm_bri_clamp(90, 1, 0), 90);
}
/* ---- B) 扫描窗保底:只在「播放器忙 + 面板暗 + 面板未下电」时补,补的是用户值 ---- */
static void test_floor_decide(void)
{
const char *why = NULL;
int v;
v = bm_bri_floor_decide(1, 0, 1, 0, 5000, &why); /* settings=1 + 扫描忙把面板写 0 */
check("floor: writes user value 1 (not 60)", v == 1 && !strcmp(why, "scan-floor"), 1);
v = bm_bri_floor_decide(80, 0, 1, 0, 5000, &why); /* 降级路径 saved=80 照写 80(旧码钉 60) */
check("floor: degraded saved 80 floors at 80", v == 80 && !strcmp(why, "scan-floor"), 1);
v = bm_bri_floor_decide(0, 0, 1, 0, 5000, &why); /* saved 未知 -> 安全低亮 */
check("floor: unknown saved -> safe-low", v == BM_BRI_SAFE_LOW && !strcmp(why, "scan-floor"), 1);
v = bm_bri_floor_decide(1, 5, 1, 0, 5000, &why); /* 面板已亮:不动 */
check("floor: visible -> no write", v == 0 && !strcmp(why, "visible"), 1);
v = bm_bri_floor_decide(1, 0, 0, 0, 5000, &why); /* ← 用户主动熄屏(播放器不忙):不动 */
check("floor: user screen-off (idle) -> no write", v == 0 && !strcmp(why, "idle-blank"), 1);
v = bm_bri_floor_decide(1, 0, 1, 1, 5000, &why); /* 面板已下电(bl_power>0):不动 */
check("floor: panel powered off -> no write", v == 0 && !strcmp(why, "panel-off"), 1);
v = bm_bri_floor_decide(1, 0, 1, 0, 0, &why); /* 90s 窗口结束:放手 */
check("floor: window over -> no write", v == 0 && !strcmp(why, "window-over"), 1);
v = bm_bri_floor_decide(1, 0, 1, 0, -1, &why);
check("floor: negative window -> no write", v == 0 && !strcmp(why, "window-over"), 1);
}
/* ---- C) 热键冻结前的熄屏恢复值:面板真实值 > settings saved > 安全低亮 ---- */
static void test_hotkey_saved(void)
{
const char *src = NULL;
int v;
v = bm_bri_hotkey_saved(1, 0, 1, 101, &src); /* 电源已熄屏:恢复用户值 1,不再回 60 */
check("hotkey: blank -> saved (settings 1)", v == 1 && !strcmp(src, "saved"), 1);
v = bm_bri_hotkey_saved(60, 0, 1, 101, &src); /* 降级 saved=60(settings 不可读)时才用 60 */
check("hotkey: blank -> degraded saved 60", v == 60 && !strcmp(src, "saved"), 1);
v = bm_bri_hotkey_saved(1, 77, 1, 101, &src); /* 面板亮着:跟随面板(HibyOS 里改过亮度) */
check("hotkey: panel-now wins", v == 77 && !strcmp(src, "panel-now"), 1);
v = bm_bri_hotkey_saved(1, 200, 1, 101, &src); /* 越界值 clamp 到 max_brightness */
check("hotkey: panel-now clamped to max", v == 101 && !strcmp(src, "panel-now"), 1);
v = bm_bri_hotkey_saved(1, 1, 6, 101, &src); /* 低于 brightness_min 抬到 min */
check("hotkey: panel-now raised to min 6", v == 6 && !strcmp(src, "panel-now"), 1);
v = bm_bri_hotkey_saved(0, 0, 1, 101, &src);
check("hotkey: nothing known -> safe-low", v == BM_BRI_SAFE_LOW && !strcmp(src, "safe-low"), 1);
}
/* ---- D) settings.conf 解析(与 watchdog 同源同 clamp)+ 打开重试 ---- */
static void test_settings_parse(void)
{
char path[600];
snprintf(path, sizeof path, "%s/bmsetXXXXXX", tmp_root());
int fd = mkstemp(path);
if (fd < 0) { g_fail++; printf("FAIL settings: mkstemp\n"); return; }
close(fd);
setenv("MEOW_SETTINGS_FILE", path, 1);
put_file(path, "# meow-settings 1\ndisplay.brightness = 1\ndisplay.brightness_min = 1\n"
"audio.volume = 10\n");
check("settings: device file -> 1", bootmenu_settings_brightness(NULL), 1);
put_file(path, "display.brightness = 0\ndisplay.brightness_min = 3\n");
int mn = 0;
check("settings: clamp up to min", bootmenu_settings_brightness(&mn), 3);
check("settings: brightness_min parsed", mn, 3);
put_file(path, "display.brightness = 200\ndisplay.brightness_min = 1\n");
check("settings: clamp down to 100", bootmenu_settings_brightness(NULL), 100);
put_file(path, "audio.volume = 10\n");
check("settings: missing key -> 100", bootmenu_settings_brightness(NULL), 100);
unlink(path);
unsigned long long t0 = now_ms();
int r = bootmenu_settings_brightness(NULL);
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");
}
/* ---- E) 端到端:真的读写一个「背光文件」,复现两个真机场景 ----
* 1) settings=1、播放器把面板写成 60 → supervisor 起始 saved 必须是 1,扫描窗保底写 1(不是 60)
* 2) 用户按电源熄屏(播放器不忙)→ 保底决策返回 0,面板保持 0(不被抢亮 = 熄屏不爆亮) */
static void test_e2e_backlight(void)
{
char bl[600], st[600];
snprintf(bl, sizeof bl, "%s/bmblXXXXXX", tmp_root());
snprintf(st, sizeof st, "%s/bmstXXXXXX", 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);
put_file(st, "display.brightness = 1\ndisplay.brightness_min = 1\n");
put_file(bl, "60\n"); /* 播放器启动画面写的 60 */
int mn_e2e = 1;
int user = bootmenu_settings_brightness(&mn_e2e);
int panel = read_brightness();
const char *src = "?";
int saved = bm_bri_pick_saved(user, panel, mn_e2e, 101, &src);
check("e2e: panel file read 60", panel, 60);
check("e2e: saved from settings = 1", saved, 1);
/* 扫描窗:播放器忙把背光写 0 → 保底写用户值 1 */
put_file(bl, "0\n");
const char *why = NULL;
int floor = bm_bri_floor_decide(saved, read_brightness(), 1, 0, 5000, &why);
if (floor > 0) write_brightness(floor);
check("e2e: scan floor writes 1 (not 60)", get_file_int(bl), 1);
/* 用户主动熄屏(不忙)→ 保底不写,面板保持 0 */
put_file(bl, "0\n");
floor = bm_bri_floor_decide(saved, read_brightness(), 0, 0, 5000, &why);
if (floor > 0) write_brightness(floor);
check("e2e: user screen-off stays dark", get_file_int(bl), 0);
/* 热键确认框:面板黑着 → 按 saved(用户值 1)点亮,不是 60 */
int hk = bm_bri_hotkey_saved(saved, read_brightness(), mn_e2e, 101, &src);
write_brightness(hk);
check("e2e: confirm box lights at 1", get_file_int(bl), 1);
/* settings 被改:saved 不再跟旧值(每次开机重读 settings) */
put_file(st, "display.brightness = 5\ndisplay.brightness_min = 1\n");
src = "?";
saved = bm_bri_pick_saved(bootmenu_settings_brightness(NULL), read_brightness(), mn_e2e, 101, &src);
check("e2e: saved follows new settings 5", saved, 5);
unlink(bl); unlink(st);
unsetenv("MEOW_BACKLIGHT");
unsetenv("MEOW_SETTINGS_FILE");
}
int main(void)
{
test_pick_saved();
test_clamp();
test_floor_decide();
test_hotkey_saved();
test_settings_parse();
test_e2e_backlight();
printf("\n%d passed, %d failed\n", g_pass, g_fail);
return g_fail ? 1 : 0;
}