/* test_ams_start.c — 宿主单测:MSG_AMS_START 拉起决策(meow_ams_start.c)。 * * 覆盖(AMSD_DESIGN §4.3 缺口回归): * 1) 合法 MSG_AMS_START(裸启动 act 空)→ 置 pending → consume → ops.launch(idx, NULL); * 2) 合法 MSG_AMS_START + activity → resolve → consume → ops.launch(idx, chain); * 3) 参数非法(pkg 空)→ meow_ams_pending_start_set 返回 MEOW_ERR_INVAL,不置位、不拉起、不崩; * 4) 有前台 App(busy)→ set 返回 MEOW_ERR_BUSY(负错误码,根因修复),不拉起; * 5) 列表未就绪(state!=0)→ consume 返回 2(pending 保留),不拉起;后就绪再 consume → 拉起; * 6) pkg 不在应用表 → consume 返回 1,toast「NO APP」,不拉起; * 7) activity 无路由 → consume 返回 1,toast「NO ROUTE」,不拉起; * 8) 启动失败(ops.launch 返 <0)→ consume 返回 1,app_ran=0,idx_out 命中(供 launch_fail_toast); * 9) picker 扩载荷:set_ex 武装 → take_picker 取回并清位。 * 只依赖 meow_ams_start.c + meow_ipc.h(纯 C),宿主 gcc 直编: * gcc -Wall -Wextra -std=gnu99 test_ams_start.c meow_ams_start.c -o test_ams_start */ #include #include #include #include #include "meow_ams_start.h" #include "meow_ipc.h" static int g_fails = 0; #define CHECK(cond, msg) do { \ if (cond) printf("PASS: %s\n", msg); \ else { printf("FAIL: %s\n", msg); g_fails++; } } while (0) /* ---- 假件 ---- */ static int g_launch_calls = 0; /* ops.launch 被调次数 */ static int g_launch_last_idx = -1; static char g_launch_last_chain[MEOW_CHAIN_MAX]; static int g_launch_ret = 0; /* 可注入:0=成功 / <0=启动失败 */ static int g_resolve_calls = 0; static int g_resolve_rc = MEOW_OK; /* 可注入:MEOW_OK / MEOW_ERR_NOENT */ static int g_log_calls = 0; static int fake_launch(int idx, const char *chain) { g_launch_calls++; g_launch_last_idx = idx; if (chain) snprintf(g_launch_last_chain, sizeof g_launch_last_chain, "%s", chain); else g_launch_last_chain[0] = 0; return g_launch_ret; } static int fake_resolve(const char *app_id, const char *activity, char *chain, int *depth) { (void)app_id; g_resolve_calls++; if (g_resolve_rc != MEOW_OK) return g_resolve_rc; snprintf(chain, MEOW_CHAIN_MAX, "%s", activity); /* 简化:chain=activity */ if (depth) *depth = 1; return MEOW_OK; } static void fake_log(const char *fmt, ...) { (void)fmt; g_log_calls++; } static void reset_fakes(void) { g_launch_calls = 0; g_launch_last_idx = -1; g_launch_last_chain[0] = 0; g_launch_ret = 0; g_resolve_calls = 0; g_resolve_rc = MEOW_OK; g_log_calls = 0; meow_ams_set_busy(0); } /* 应用表:id 主键串 + 用于 idx 断言 */ static const char *g_ids[3] = { "dev.player.ncm", "os.meow.files", "os.meow.hello" }; int main(void) { static struct meow_ams_ops ops; ops.launch = fake_launch; ops.resolve = fake_resolve; ops.log = fake_log; meow_ams_ops_init(&ops); char toast[64]; int app_ran = 0, idx = -1; /* ---- 1) 合法裸启动(act 空)→ consume → ops.launch ---- */ reset_fakes(); CHECK(meow_ams_pending_start_set("dev.player.ncm", "") == 0, "set(bare): 受理"); int r = meow_ams_start_consume(g_ids, 3, 0, toast, sizeof toast, &app_ran, &idx); CHECK(r == 1 && g_launch_calls == 1, "consume(bare): 拉起链路被调"); CHECK(g_launch_last_idx == 0, "consume(bare): idx 命中 dev.player.ncm"); CHECK(g_launch_last_chain[0] == 0, "consume(bare): chain 空(裸启动)"); CHECK(app_ran == 1, "consume(bare): app_ran=1"); /* ---- 2) activity 链 → resolve → consume → ops.launch(chain) ---- */ reset_fakes(); CHECK(meow_ams_pending_start_set("os.meow.files", "pick") == 0, "set(act): 受理"); r = meow_ams_start_consume(g_ids, 3, 0, toast, sizeof toast, &app_ran, &idx); CHECK(r == 1 && g_resolve_calls == 1, "consume(act): resolve 被调"); CHECK(g_launch_calls == 1 && g_launch_last_idx == 1, "consume(act): ops.launch(idx=1)"); CHECK(g_launch_last_chain[0] && strcmp(g_launch_last_chain, "pick") == 0, "consume(act): chain 透传 pick"); /* ---- 3) 参数非法(pkg 空)→ INVAL,不置位、不拉起 ---- */ reset_fakes(); CHECK(meow_ams_pending_start_set("", "") == MEOW_ERR_INVAL, "set(空 pkg): 返回负错误码 MEOW_ERR_INVAL"); CHECK(meow_ams_pending_start_set(NULL, NULL) == MEOW_ERR_INVAL, "set(NULL pkg): MEOW_ERR_INVAL"); r = meow_ams_start_consume(g_ids, 3, 0, toast, sizeof toast, &app_ran, &idx); CHECK(r == 0 && g_launch_calls == 0, "consume: 空 pkg 未置位 → 不拉起"); /* ---- 4) busy(有前台 App)→ BUSY,不拉起 ---- */ reset_fakes(); meow_ams_set_busy(1); CHECK(meow_ams_pending_start_set("os.meow.hello", "") == MEOW_ERR_BUSY, "set(busy): 返回负错误码 MEOW_ERR_BUSY(根因修复:旧实现返回 +8 被误判 OK)"); r = meow_ams_start_consume(g_ids, 3, 0, toast, sizeof toast, &app_ran, &idx); CHECK(r == 0 && g_launch_calls == 0, "consume(busy): 不拉起"); meow_ams_set_busy(0); /* ---- 5) 列表未就绪(state!=0)→ 保留 pending,后就绪拉起 ---- */ reset_fakes(); CHECK(meow_ams_pending_start_set("dev.player.ncm", "") == 0, "set: 受理"); r = meow_ams_start_consume(g_ids, 3, 2, toast, sizeof toast, &app_ran, &idx); CHECK(r == 2 && g_launch_calls == 0, "consume(state!=0): 返回 2 保留 pending,不拉起"); CHECK(toast[0] && strstr(toast, "not ready"), "consume(state!=0): toast=列表未就绪"); r = meow_ams_start_consume(g_ids, 3, 0, toast, sizeof toast, &app_ran, &idx); CHECK(r == 1 && g_launch_calls == 1 && g_launch_last_idx == 0, "consume(后就绪): pending 保留后拉起成功"); /* ---- 6) pkg 不在应用表 → NO APP,不拉起 ---- */ reset_fakes(); CHECK(meow_ams_pending_start_set("no.such.app", "") == 0, "set(未知app): 受理"); r = meow_ams_start_consume(g_ids, 3, 0, toast, sizeof toast, &app_ran, &idx); CHECK(r == 1 && g_launch_calls == 0, "consume(未知app): 不拉起"); CHECK(toast[0] && strstr(toast, "NO APP"), "consume(未知app): toast=NO APP"); /* ---- 7) activity 无路由 → NO ROUTE,不拉起 ---- */ reset_fakes(); g_resolve_rc = MEOW_ERR_NOENT; CHECK(meow_ams_pending_start_set("os.meow.files", "nope") == 0, "set(nope): 受理"); r = meow_ams_start_consume(g_ids, 3, 0, toast, sizeof toast, &app_ran, &idx); CHECK(r == 1 && g_launch_calls == 0, "consume(无路由): 不拉起"); CHECK(toast[0] && strstr(toast, "NO ROUTE"), "consume(无路由): toast=NO ROUTE"); g_resolve_rc = MEOW_OK; /* ---- 8) 启动失败(ops.launch<0)→ app_ran=0,idx_out 命中 ---- */ reset_fakes(); g_launch_ret = -2; /* MEOW_ERR_NOENT */ CHECK(meow_ams_pending_start_set("dev.player.ncm", "") == 0, "set: 受理"); r = meow_ams_start_consume(g_ids, 3, 0, toast, sizeof toast, &app_ran, &idx); CHECK(r == 1 && g_launch_calls == 1, "consume(启动失败): 拉起被调(失败)"); CHECK(app_ran == 0 && idx == 0, "consume(启动失败): app_ran=0 且 idx_out=0(供 launch_fail_toast)"); g_launch_ret = 0; /* ---- 9) picker 扩载荷:set_ex 武装 → take_picker 取回并清位 ---- */ reset_fakes(); CHECK(meow_ams_pending_start_set_ex("os.meow.files", "pick", "pick_file", 7, "dev.player.ncm") == 0, "set_ex(picker): 受理"); char act[24] = "", caller[MEOW_APP_ID_MAX] = ""; uint32_t req = 0; CHECK(meow_ams_take_picker(act, sizeof act, caller, sizeof caller, &req) == 1, "take_picker: 有上下文"); CHECK(strcmp(act, "pick_file") == 0 && req == 7 && strcmp(caller, "dev.player.ncm") == 0, "take_picker: action/req/caller 正确"); CHECK(meow_ams_take_picker(act, sizeof act, caller, sizeof caller, &req) == 0, "take_picker: 二次取回=0(一次性清位)"); /* 非 picker 启动:set(非 set_ex)不武装 picker */ reset_fakes(); CHECK(meow_ams_pending_start_set("dev.player.ncm", "") == 0, "set(bare): 受理"); CHECK(meow_ams_take_picker(act, sizeof act, caller, sizeof caller, &req) == 0, "take_picker: 普通启动无 picker 上下文"); printf("\n%s (%d fails)\n", g_fails ? "FAILED" : "ALL PASS", g_fails); return g_fails ? 1 : 0; }