/* test_overlay_gate.c — §310 三连 bug 的**纯决策**宿主单测(overlay_gate.h + lock_screen.h 谓词)。 * * 覆盖三个症状对应的关闭/恢复决策,不依赖信号/设备/文件系统: * S1 锁屏期触摸不穿透 → overlay_drain_required():App 被 SIGSTOP 过(非保活)必须在 * unlink /tmp/meow_notif_active 前先 SIGCONT + 排窗,让 SDK 输入线程 * 排空冻结期缓冲的 evdev 事件副本;OVERLAY_DRAIN_WINDOW_MS > SDK 200ms 节流。 * S2 解锁状态栏恢复 → overlay_force_sb_resync():解锁后必须强制 g_sb_mode_last=-1 重叠加, * 不等 app 循环 1s 慢刷(消除「解锁回 App 无状态栏」残留)。 * S3 App 死亡触发主屏重绘 → lock_overlay_app_died_exit():锁屏叠加期 App 崩溃(僵尸)必须走 * 统一退出路径(交回 waitpid 循环回收 + run_menu 重绘主屏含状态栏)。 * S1b 未冻结 App 输入被排 → overlay_keepalive_needs_grab():媒体保活 App(保持运行、SDK 输入 * 线程实时读 evdev)在锁屏期必须靠 launcher EVIOCGRAB 独占触控设备 + * 标记让 SDK 排空——而非冻结 App 的 SIGCONT 排空(drain_required=0)。 * * 编译运行(在 patch_v2/meow_launcherd 下,无其它依赖): * gcc -Wall -Wextra -o test_overlay_gate test_overlay_gate.c && ./test_overlay_gate * 全 PASS -> exit 0。 */ #include #include "overlay_gate.h" #include "lock_screen.h" static int g_pass = 0, g_fail = 0; #define CHECK(cond, msg) do { \ if (cond) { g_pass++; printf("PASS %s\n", msg); } \ else { g_fail++; printf("FAIL %s\n", msg); } \ } while (0) int main(void) { /* ---- S1 输入穿透:SIGSTOP 过的 App 关闭时必须先排空再 unlink ---- */ CHECK(overlay_drain_required(0) == 1, "S1 drain required (app was SIGSTOP/frozen)"); CHECK(overlay_drain_required(1) == 0, "S1 no drain needed (media keep-alive, never stopped)"); /* 排空窗必须大于 SDK overlay_input_suppressed 的 200ms 标记节流窗,保证恢复后确实重新 stat 到 * 标记再整批丢弃缓冲事件;此处断言契约不被踩线。 */ CHECK(OVERLAY_DRAIN_WINDOW_MS > 200, "S1 drain window > SDK 200ms throttle"); /* ---- S1b 未冻结(媒体保活)App 输入被排:靠 EVIOCGRAB 独占隔离,非 SIGCONT 排空 ---- * 保活 App 从不 SIGSTOP(保 PCM),SDK 输入线程实时读 evdev;launcher 只能靠 EVIOCGRAB 独占 * 触控设备(真机 goodix 已证 EVIOCGRAB 返回 0)使任何前台进程锁屏期收不到触摸。 */ CHECK(overlay_keepalive_needs_grab(1) == 1, "S1b keep-alive app needs EVIOCGRAB isolation"); CHECK(overlay_keepalive_needs_grab(0) == 0, "S1b frozen app -> SIGCONT drain, not grab"); /* 保活 App 隔离与冻结 App 排空互斥:一条路径而非两条。 */ CHECK(overlay_drain_required(1) == 0 && overlay_keepalive_needs_grab(1) == 1, "S1b keep-alive: grab path (drain==0)"); CHECK(overlay_drain_required(0) == 1 && overlay_keepalive_needs_grab(0) == 0, "S1b frozen: drain path (grab==0)"); /* ---- S2 解锁状态栏恢复:必须强制重同步 ---- */ CHECK(overlay_force_sb_resync() == 1, "S2 force status bar re-sync after unlock"); /* ---- S3 App 死亡触发主屏重绘:崩溃必须走统一退出路径 ---- */ CHECK(lock_overlay_app_died_exit(1) == 1, "S3 app died -> unified exit path (redraw home)"); CHECK(lock_overlay_app_died_exit(0) == 0, "S3 app alive -> normal restore"); /* ---- §310 锁屏叠加期中止谓词(lock_screen.h 纯判定)---- */ CHECK(lck_overlay_should_abort(0, 0) == 0, "S3 abort=0 (menu lock, no overlay pid)"); CHECK(lck_overlay_should_abort(0, 1) == 0, "S3 abort=0 (no overlay pid, zombie ignored)"); CHECK(lck_overlay_should_abort(4321, 0) == 0, "S3 abort=0 (overlay pid, app alive)"); CHECK(lck_overlay_should_abort(4321, 1) == 1, "S3 abort=1 (overlay pid + zombie -> abort lock)"); printf("\n%s (%d passed, %d failed)\n", g_fail ? "SOME TESTS FAILED" : "ALL PASS", g_pass, g_fail); return g_fail ? 1 : 0; }