/* bench/toggle_sim.c — MSG_PLAYCTL cmd=6(toggle) 离线回归(0 真实请求)。 * * 背景(真机 bug):物理播放/暂停键(164)在 SDK 侧曾按「最近上报 state」本地判 play/pause, * 但 ncm 走自有 ipc_media 线程、不经 SDK meow_ui_media_meta 上报 → SDK 侧 state 恒 0 → * 恒发 cmd=1(播放),播放中按键无效。修法:SDK 恒发 cmd=6 toggle,源侧(本文件 apply_playctl) * 按自身当前态翻转——源是状态权威。 * * 同 TU 平移 app/play/ipc_media.c(含 static apply_playctl),player_manager_* 用记录桩 * (-ffunction-sections + --gc-sections 丢弃未触达的 send_meta/ipc_thread 及其依赖)。 * * 用例: * T1 播放中 toggle → pause(1); * T2 暂停中 toggle → pause(0); * T3 停止/缓冲中 toggle → pause(1)(player_manager_pause 内部按态过滤,no-op 安全); * T4 旧 cmd 回归:1=play→pause(0) 2=pause→pause(1) 3=prev→next(-1) 4=next→next(1) 5=seek。 * * 编译(宿主,ncm_app 目录下): * gcc -Wall -Wextra -O2 -ffunction-sections -DJSMN_HEADER -I. -I../sdk -I../meow_launcherd \ * -Icore -Ivendor/jsmn -c bench/toggle_sim.c -o /tmp/tg.o * gcc -O2 -Wl,--gc-sections -o toggle_sim /tmp/tg.o -lpthread -lm * 运行:./toggle_sim (PASS/FAIL 退出码) */ #include "../app/ncm_app_internal.h" /* ---- player_manager 记录桩(覆盖真实实现,仅记录调用)---- */ static int g_fake_state = NCM_PC_STOPPED; static int g_pause_calls = 0, g_pause_last = -1; static int g_next_calls = 0, g_next_last = 0; static int g_seek_calls = 0; static int64_t g_seek_last = -1; int player_manager_state(void) { return g_fake_state; } void player_manager_pause(int on) { g_pause_calls++; g_pause_last = on; } void player_manager_next(int dir) { g_next_calls++; g_next_last = dir; } void player_manager_seek(int64_t ms) { g_seek_calls++; g_seek_last = ms; } /* ---- 被平移 TU 引用但本测试不触达的符号(gc-sections 会丢,仅为防漏列桩)---- */ #include "../app/play/ipc_media.c" static int failures = 0; static void check(const char *name, int cond) { printf("%s %s\n", cond ? "PASS" : "FAIL", name); if (!cond) failures++; } static m_playctl_t mk(uint32_t cmd, int64_t seek) { m_playctl_t c; memset(&c, 0, sizeof c); c.cmd = cmd; c.seek_ms = seek; return c; } int main(void) { m_playctl_t c; /* T1 播放中 toggle → 暂停 */ g_fake_state = NCM_PC_PLAYING; c = mk(NCM_CTL_TOGGLE, 0); apply_playctl(&c); check("T1 PLAYING+toggle -> pause(1)", g_pause_calls == 1 && g_pause_last == 1); /* T2 暂停中 toggle → 播放 */ g_fake_state = NCM_PC_PAUSED; apply_playctl(&c); check("T2 PAUSED+toggle -> pause(0)", g_pause_calls == 2 && g_pause_last == 0); /* T3 停止/缓冲 toggle → pause(1)(内部按态过滤,不崩) */ g_fake_state = NCM_PC_STOPPED; apply_playctl(&c); check("T3 STOPPED+toggle -> pause(1) 内部过滤", g_pause_calls == 3 && g_pause_last == 1); g_fake_state = NCM_PC_BUFFER; apply_playctl(&c); check("T3 BUFFER+toggle -> pause(1) 内部过滤", g_pause_calls == 4 && g_pause_last == 1); /* T4 旧 cmd 回归 */ c = mk(NCM_CTL_PLAY, 0); apply_playctl(&c); check("T4 PLAY -> pause(0)", g_pause_calls == 5 && g_pause_last == 0); c = mk(NCM_CTL_PAUSE, 0); apply_playctl(&c); check("T4 PAUSE -> pause(1)", g_pause_calls == 6 && g_pause_last == 1); c = mk(NCM_CTL_PREV, 0); apply_playctl(&c); check("T4 PREV -> next(-1)", g_next_calls == 1 && g_next_last == -1); c = mk(NCM_CTL_NEXT, 0); apply_playctl(&c); check("T4 NEXT -> next(1)", g_next_calls == 2 && g_next_last == 1); c = mk(NCM_CTL_SEEK, 12345); apply_playctl(&c); check("T4 SEEK -> seek(12345)", g_seek_calls == 1 && g_seek_last == 12345); c = mk(99, 0); apply_playctl(&c); /* 未知 cmd:无动作不崩 */ check("T4 unknown cmd=99 -> no-op", g_pause_calls == 6 && g_next_calls == 2 && g_seek_calls == 1); printf("\n%s (%d failure%s)\n", failures ? "RESULT: FAIL" : "RESULT: PASS", failures, failures == 1 ? "" : "s"); return failures ? 1 : 0; }