/* test_key_router_vol.c — 宿主单测:launcher-UI 页物理键统一路由(KEY_ROUTER_UNIFY P1)。 * * 测「key_router 自身的音量编排」——它只产「音量边沿 + 原始 held + ramp 状态」, * 由调用页决定 settings_volume_adj(±step) / 光标导航 / ramp 推进。本测用内存桩: * - kp_poll/kp_open/kp_is_down → 注入 raw 键位矩阵到 key_router 的静态 kp_keys.cur[]; * - kd_tick → 用 keys.c 的真实现(40ms 防抖,V4 依赖);keys.c 内 kp_open/kp_poll/ * kp_is_down/meow_volpage_armed 用宏重命名避开真实 ioctl 与桩冲突; * - settings_volume_adj/settings_volume_step/vol_ramp_dir/vol_ramp_tick → 内存桩; * - now_ms → 假时钟(手动推进,40ms 防抖可瞬时测); * - meow_ui_activity_* → 内存桩(V5 嵌套:真语义=纯静态数组,桩复刻)。 * * 断言: * V1 音量减边沿 → settings_volume_adj(-step) 恰一次 + last_input 复位; * V2 同会话重复(同 raw 仍按住)→ 不再触发 settings_volume_adj; * V3 长按 held → vol_ramp_tick 被驱动(vol_ramp_dir!=0,held=正确方向 raw);松开 → dir=0; * V4 40ms 防抖:raw 在 40ms 内抖动 → 不产边沿;稳定按下 ≥40ms → 才产边沿; * V5 嵌套返回:page_enter(0x53)→page_exit(0x53)→page_enter(0x55),基线重播后 held 键不产假边沿; * V6 meow_volpage_armed() → 音量边沿被抑制(不下发,不调 settings_volume_adj)。 * * 编译(在 patch_v2/meow_launcherd 下): * gcc -Wall -Wextra -I../sdk -I. -ffunction-sections -fdata-sections \ * -Wl,--gc-sections test_key_router_vol.c -o test_key_router_vol * ./test_key_router_vol */ #include #include #include #include /* ---- 假时钟 ---- */ static unsigned long long g_now = 100000; unsigned long long now_ms(void) { return g_now; } static void advance_ms(unsigned long long d) { g_now += d; } /* ---- 音量机外部符号桩(key_router 依赖)---- */ int settings_volume_step = 5; static int g_adj_count = 0, g_adj_delta = 0; int vol_ramp_dir = 0; /* 桩全局:模拟 audio.c 的 ramp 状态 */ static int g_tick_count = 0, g_tick_held = -1; void settings_volume_adj(int delta) { g_adj_count++; g_adj_delta = delta; if (delta > 0) vol_ramp_dir = 1; else if (delta < 0) vol_ramp_dir = -1; } int vol_ramp_tick(int key_held) { g_tick_count++; g_tick_held = key_held; if (!key_held) vol_ramp_dir = 0; return vol_ramp_dir != 0; } static int g_volpage = 0; /* 桩:meow_volpage_armed */ /* ---- keys.c 内 kp_open/kp_poll/kp_is_down/meow_volpage_armed 用宏重命名,避开真实 ioctl ---- * (宏在 #include keys.c 前生效,仅重命名 keys.c 体内的调用/定义) ---- */ #define kp_open kp_open_rt_ #define kp_poll kp_poll_rt_ #define kp_is_down kp_is_down_rt_ #define meow_volpage_armed meow_volpage_armed_rt_ #include "keys.c" /* 真 kd_tick(40ms 防抖素材) */ #undef kp_open #undef kp_poll #undef kp_is_down #undef meow_volpage_armed /* host 桩声明(key_router.c 需要;恢复到正常名,与 keys.c 被更名后的定义不冲突) */ struct keypoll; int kp_open(struct keypoll *kp, const char *dev); int kp_poll(struct keypoll *kp, int (*cb)(int code, int down, void *ctx), void *ctx); int kp_is_down(const struct keypoll *k, int code); int meow_volpage_armed(void); #include "key_router.c" /* 同 TU #include:拿到静态 kp_keys/kp_power/kd_vold/kd_volu + 全部函数 */ #include "key_router.h" /* ---- meow_ui_activity_* 桩(复刻 sdk/meow_ui_activity.c:纯静态数组,真语义)---- */ static int g_act[32]; static int g_act_n = 0; void meow_ui_activity_push(int id) { if (g_act_n < 32) g_act[g_act_n++] = id; } void meow_ui_activity_pop(void) { if (g_act_n > 0) g_act_n--; } int meow_ui_activity_active(void) { return g_act_n > 0 ? g_act[g_act_n - 1] : 0; } /* ---- 注入 raw 到 key_router 静态 kp_keys ---- */ static void set_vol(int vold, int volu) { for (int w = 0; w < KEYBUF_WORDS; w++) kp_keys.cur[w] = 0; if (vold) kp_keys.cur[KEY_VOLUMEDOWN / 32] |= (1UL << (KEY_VOLUMEDOWN % 32)); if (volu) kp_keys.cur[KEY_VOLUMEUP / 32] |= (1UL << (KEY_VOLUMEUP % 32)); } /* ---- host 桩:kp_*(prev=cur 模拟真实 poll 的键位更新)+ meow_volpage_armed ---- */ int kp_open(struct keypoll *kp, const char *dev) { (void)dev; memset(kp, 0, sizeof *kp); kp->ok = 1; return 0; } int kp_poll(struct keypoll *kp, int (*cb)(int, int, void *), void *ctx) { (void)cb; (void)ctx; for (int w = 0; w < KEYBUF_WORDS; w++) kp->prev[w] = kp->cur[w]; return 0; } int kp_is_down(const struct keypoll *k, int code) { return (k->cur[code / 32] >> (code % 32)) & 1; } int meow_volpage_armed(void) { return g_volpage; } /* 其它全局桩(keys.c 内被更名函数引用;本测 GC 后不保留,仅满足声明一致性) */ uint32_t menu_bg = 0; int screen_off_timeout = 0, screen_off_mode = 0, power_longpress_menu_ms = 0; int power_forceoff_ms = 0, refresh_rate_cfg = 60, refresh_always_max = 0; void logmsg(const char *fmt, ...) { (void)fmt; } static int g_fails = 0; #define CHECK(cond, name) do { \ if (cond) printf(" PASS %s\n", name); \ else { printf(" FAIL %s (%s:%d)\n", name, __FILE__, __LINE__); g_fails++; } } while (0) static void reset_router(void) { set_vol(0, 0); key_router_rebase(); g_adj_count = 0; g_adj_delta = 0; g_tick_count = 0; g_tick_held = -1; vol_ramp_dir = 0; g_volpage = 0; g_act_n = 0; } /* 按下并稳定:register(pump) → hold ≥40ms → pump(stable=1, 边沿)。 */ static key_evt_t press_settle(int vold, int volu) { set_vol(vold, volu); key_router_pump(); /* 登记 raw 跳变(防抖窗口起点) */ advance_ms(50); /* ≥KEY_DEBOUNCE_MS(40) */ return key_router_pump(); /* stable→1,边沿 */ } /* 抬起并稳定:register(pump) → → pump(stable=0)。 */ static key_evt_t release_settle(void) { set_vol(0, 0); key_router_pump(); /* 登记 release */ advance_ms(50); return key_router_pump(); /* stable→0 */ } int main(void) { unsigned long long last_input = 0; /* ---- V1 音量减边沿 → settings_volume_adj(-step) 恰一次 + last_input 复位 ---- */ reset_router(); set_vol(0, 0); advance_ms(10); key_router_pump(); /* 全抬起基线 */ last_input = 0; key_evt_t ev = press_settle(1, 0); CHECK(ev.vol_dn_edge == 1, "V1 vol-down edge fires"); key_router_default_volume(&ev, &last_input); CHECK(g_adj_count == 1 && g_adj_delta == -settings_volume_step, "V1 vol-down -> settings_volume_adj(-step) exactly once"); CHECK(last_input == g_now, "V1 last_input reset on edge"); /* ---- V2 同会话重复(同 raw 仍按住)→ 不再触发 ---- */ reset_router(); set_vol(0, 0); advance_ms(10); key_router_pump(); ev = press_settle(1, 0); CHECK(ev.vol_dn_edge == 1, "V2a press edge fires"); ev = key_router_pump(); /* 同 raw 仍按住,无新边沿 */ CHECK(ev.vol_dn_edge == 0, "V2 held: no re-trigger edge"); key_router_default_volume(&ev, &last_input); CHECK(g_adj_count == 0, "V2 held: no double settings_volume_adj"); /* ---- V3 长按 held → vol_ramp_tick 被驱动(held=正确方向 raw);松开 → dir=0 ---- */ reset_router(); set_vol(0, 0); advance_ms(10); key_router_pump(); last_input = 0; ev = press_settle(1, 0); /* 按下(vol down)并转稳定 */ key_router_default_volume(&ev, &last_input); CHECK(g_tick_count == 1 && g_tick_held == ev.vol_held_dn && vol_ramp_dir != 0, "V3 hold vol-down -> ramp driven with held=raw down"); advance_ms(50); ev = key_router_pump(); /* 继续长按(raw=1 仍按住) */ key_router_default_volume(&ev, &last_input); CHECK(g_tick_count == 2 && g_tick_held == 1, "V3 long-press continues ramp"); ev = release_settle(); /* 松开:raw held 立即为 0 */ key_router_default_volume(&ev, &last_input); CHECK(vol_ramp_dir == 0, "V3 release -> ramp dir 0 (stops)"); /* ---- V4 40ms 防抖:raw 在 40ms 内抖动不产边沿;稳定 ≥40ms 才产 ---- */ reset_router(); set_vol(0, 0); advance_ms(10); key_router_pump(); set_vol(1, 0); advance_ms(20); key_router_pump(); /* 按下未满 40ms */ set_vol(0, 0); advance_ms(20); key_router_pump(); /* 40ms 内又抬起 */ ev = key_router_pump(); CHECK(ev.vol_dn_edge == 0, "V4 <40ms jitter -> no edge"); ev = press_settle(1, 0); /* 稳定按下 ≥40ms */ CHECK(ev.vol_dn_edge == 1, "V4 stable >=40ms press -> edge"); ev = release_settle(); /* 抬起稳定 */ CHECK(ev.vol_dn_edge == 0 && ev.vol_up_edge == 0, "V4 released -> no residual edge"); ev = press_settle(0, 1); /* 稳定按音量加 */ CHECK(ev.vol_up_edge == 1, "V4 vol-up stable press -> up edge"); /* ---- V5 嵌套返回:page_enter/exit 重播基线,held 键不产假边沿 ---- */ reset_router(); meow_ui_activity_push(0x55); key_router_page_enter(0x55); /* 主菜单 */ set_vol(0, 0); advance_ms(10); key_router_pump(); ev = press_settle(1, 0); CHECK(ev.vol_dn_edge == 1, "V5a press edge in menu"); /* 嵌套进设置页:压栈 + 登记 */ meow_ui_activity_push(0x53); key_router_page_enter(0x53); CHECK(meow_ui_activity_active() == 0x53, "V5b settings active sentinel"); ev = release_settle(); /* 设置页内把键抬起 */ CHECK(ev.vol_dn_edge == 0, "V5c settings-up: no fresh edge (consumer)"); /* 设置页返回(模拟 run_settings_page 结束):弹栈 + 重播基线 → m_prev=当前 raw(0) */ key_router_page_exit(0x53); meow_ui_activity_pop(); key_router_page_enter(0x55); CHECK(meow_ui_activity_active() == 0x55, "V5d menu active again"); /* 主菜单基线已重播;再次按下应为新边沿,而非残留/双发 */ ev = press_settle(1, 0); CHECK(ev.vol_dn_edge == 1, "V5e fresh press after nest return -> single edge"); /* ---- V6 meow_volpage_armed() → 音量边沿被抑制(不下发)---- */ reset_router(); set_vol(0, 0); advance_ms(10); key_router_pump(); g_volpage = 1; ev = press_settle(1, 0); CHECK(ev.vol_dn_edge == 0, "V6 volpage armed -> edge suppressed"); key_router_default_volume(&ev, &last_input); CHECK(g_adj_count == 0, "V6 volpage armed -> settings_volume_adj NOT called"); g_volpage = 0; printf("\nRESULT: %s (%d fails)\n", g_fails ? "FAIL" : "ALL PASS", g_fails); return g_fails ? 1 : 0; }