#include #include "test_common.h" #include "../src/Drv/key.h" #include "../src/Drv/pages/page_manager.h" /* ------------------------------------------------------------------------- * 结构体名: test_page_ctx_t * 功能: * 测试页面私有上下文,用于统计各生命周期回调与事件处理的触发次数。 * * 字段说明: * createCount - on_create 被调用次数 * enterCount - on_enter 被调用次数 * exitCount - on_exit 被调用次数 * destroyCount - on_destroy 被调用次数 * loopCount - on_loop 被调用次数 * eventResult - on_event 预设返回值(用于控制是否触发全局兜底) * ------------------------------------------------------------------------- */ typedef struct { int createCount; int enterCount; int exitCount; int destroyCount; int loopCount; event_result_t eventResult; } test_page_ctx_t; static test_page_ctx_t g_ctx_a; static test_page_ctx_t g_ctx_b; /* A 页生命周期/事件回调桩函数:将调用痕迹写入 page->model 对应的计数器 */ static void on_create_a(page_t *page) { ((test_page_ctx_t *)page->model)->createCount++; } static void on_enter_a(page_t *page) { ((test_page_ctx_t *)page->model)->enterCount++; } static void on_exit_a(page_t *page) { ((test_page_ctx_t *)page->model)->exitCount++; } static void on_destroy_a(page_t *page) { ((test_page_ctx_t *)page->model)->destroyCount++; } static event_result_t on_event_a(page_t *page, input_event_t *event) { (void)event; return ((test_page_ctx_t *)page->model)->eventResult; } static void on_loop_a(page_t *page) { ((test_page_ctx_t *)page->model)->loopCount++; } /* B 页生命周期/事件回调桩函数:用于验证多页切换与销毁链路 */ static void on_create_b(page_t *page) { ((test_page_ctx_t *)page->model)->createCount++; } static void on_enter_b(page_t *page) { ((test_page_ctx_t *)page->model)->enterCount++; } static void on_exit_b(page_t *page) { ((test_page_ctx_t *)page->model)->exitCount++; } static void on_destroy_b(page_t *page) { ((test_page_ctx_t *)page->model)->destroyCount++; } static event_result_t on_event_b(page_t *page, input_event_t *event) { (void)event; return ((test_page_ctx_t *)page->model)->eventResult; } static void on_loop_b(page_t *page) { ((test_page_ctx_t *)page->model)->loopCount++; } static void setup_page(page_t *page, page_id_t pageId, uint8_t isCached, test_page_ctx_t *ctx, void (*on_create)(page_t *), void (*on_enter)(page_t *), void (*on_exit)(page_t *), void (*on_destroy)(page_t *), event_result_t (*on_event)(page_t *, input_event_t *), void (*on_loop)(page_t *)) { memset(page, 0, sizeof(*page)); page->page_id = pageId; page->is_cached = isCached; page->on_create = on_create; page->on_enter = on_enter; page->on_exit = on_exit; page->on_destroy = on_destroy; page->on_event = on_event; page->on_loop = on_loop; page->model = ctx; } /* ------------------------------------------------------------------------- * 函数名: main * 功能: * 覆盖 PageManager 的核心行为链路,验证注册、导航、事件分发(含 ESC 兜底回退)、 * 生命周期回调顺序以及循环驱动是否符合预期。 * * 参数: * 无 * * 边界处理: * - 通过 memset 将页面对象和上下文清零,避免脏数据影响断言。 * - 使用 ASSERT_TRUE 先校验 GetTop 非空,再访问 page_id,防止空指针解引用。 * * 说明: * - pageA 设为缓存页(is_cached = 1),用于验证回退后可继续进入而不销毁。 * - pageB 设为非缓存页(is_cached = 0),用于验证 ESC 回退时触发 on_destroy。 * - 将 pageB.on_event 设为 EVENT_UNHANDLED,确保 DispatchEvent 走全局兜底, * 从而触发 KEY_ESC -> PageManager_Pop() 的系统行为。 * - 关键验证点: * 1) Navigate 到 A:A create/enter 各一次; * 2) Navigate 到 B:A exit 一次,B create/enter 各一次; * 3) ESC 事件:B exit + destroy,栈顶回到 A,A enter 再次触发; * 4) Loop:仅驱动当前栈顶 A 的 on_loop。 * * 返回值: * - 0:测试通过 * ------------------------------------------------------------------------- */ int main(void) { page_t pageA; page_t pageB; page_t pageA2; page_t pageB2; page_t pageA3; input_event_t event; page_t invalidNonePage; page_t invalidMaxPage; /* ===== Case 1: Init 后的默认行为与空栈边界 ===== */ memset(&g_ctx_a, 0, sizeof(g_ctx_a)); memset(&g_ctx_b, 0, sizeof(g_ctx_b)); PageManager_Init(); ASSERT_TRUE(PageManager_GetTop() == NULL); ASSERT_TRUE(PageManager_Find(PAGE_ID_MENU) == NULL); ASSERT_EQ_INT(PAGE_MANAGER_ERR_NOT_FOUND, PageManager_Navigate(PAGE_ID_MENU)); ASSERT_EQ_INT(PAGE_MANAGER_ERR_STACK_BOTTOM, PageManager_Pop()); ASSERT_EQ_INT(PAGE_MANAGER_ERR_NULL_PARAM, PageManager_Register(NULL)); ASSERT_EQ_INT(PAGE_MANAGER_ERR_NULL_PARAM, PageManager_Push(NULL)); ASSERT_EQ_INT(PAGE_MANAGER_ERR_NULL_PARAM, PageManager_DispatchEvent(NULL)); PageManager_Loop(); /* ===== Case 2: Register 参数边界与重复注册覆盖 ===== */ setup_page(&invalidNonePage, PAGE_ID_NONE, 1, &g_ctx_a, on_create_a, on_enter_a, on_exit_a, on_destroy_a, on_event_a, on_loop_a); setup_page(&invalidMaxPage, PAGE_ID_MAX, 1, &g_ctx_b, on_create_b, on_enter_b, on_exit_b, on_destroy_b, on_event_b, on_loop_b); ASSERT_EQ_INT(PAGE_MANAGER_ERR_INVALID_ID, PageManager_Register(&invalidNonePage)); ASSERT_EQ_INT(PAGE_MANAGER_ERR_INVALID_ID, PageManager_Register(&invalidMaxPage)); setup_page(&pageA, PAGE_ID_MENU, 1, &g_ctx_a, on_create_a, on_enter_a, on_exit_a, on_destroy_a, on_event_a, on_loop_a); setup_page(&pageB, PAGE_ID_APP_INFO, 0, &g_ctx_b, on_create_b, on_enter_b, on_exit_b, on_destroy_b, on_event_b, on_loop_b); setup_page(&pageA2, PAGE_ID_MENU, 1, &g_ctx_a, on_create_a, on_enter_a, on_exit_a, on_destroy_a, on_event_a, on_loop_a); setup_page(&pageB2, PAGE_ID_APP_INFO, 0, &g_ctx_b, on_create_b, on_enter_b, on_exit_b, on_destroy_b, on_event_b, on_loop_b); setup_page(&pageA3, PAGE_ID_MENU, 1, &g_ctx_a, on_create_a, on_enter_a, on_exit_a, on_destroy_a, on_event_a, on_loop_a); ASSERT_EQ_INT(PAGE_MANAGER_OK, PageManager_Register(&pageA)); ASSERT_EQ_INT(PAGE_MANAGER_OK, PageManager_Register(&pageB)); ASSERT_TRUE(PageManager_Find(PAGE_ID_MENU) == &pageA); ASSERT_TRUE(PageManager_Find(PAGE_ID_APP_INFO) == &pageB); ASSERT_EQ_INT(PAGE_MANAGER_OK, PageManager_Register(&pageA2)); /* 重复 ID 覆盖 */ ASSERT_TRUE(PageManager_Find(PAGE_ID_MENU) == &pageA2); /* ===== Case 3: Navigate + 生命周期 + GetTop ===== */ ASSERT_EQ_INT(PAGE_MANAGER_OK, PageManager_Navigate(PAGE_ID_MENU)); ASSERT_EQ_INT(1, g_ctx_a.createCount); ASSERT_EQ_INT(1, g_ctx_a.enterCount); ASSERT_TRUE(PageManager_GetTop() != NULL); ASSERT_EQ_INT(PAGE_ID_MENU, PageManager_GetTop()->page_id); ASSERT_EQ_INT(PAGE_MANAGER_OK, PageManager_Navigate(PAGE_ID_MENU)); /* 幂等导航到当前页 */ ASSERT_EQ_INT(1, g_ctx_a.enterCount); /* 不应重复 enter */ ASSERT_EQ_INT(PAGE_MANAGER_OK, PageManager_Navigate(PAGE_ID_APP_INFO)); ASSERT_EQ_INT(1, g_ctx_a.exitCount); ASSERT_EQ_INT(1, g_ctx_b.createCount); ASSERT_EQ_INT(1, g_ctx_b.enterCount); ASSERT_TRUE(PageManager_GetTop() != NULL); ASSERT_EQ_INT(PAGE_ID_APP_INFO, PageManager_GetTop()->page_id); /* ===== Case 4: DispatchEvent handled/unhandled 分支 ===== */ g_ctx_b.eventResult = EVENT_HANDLED; event.type = PAGE_EVENT_KEY; event.keyVal = KEY_ESC; ASSERT_EQ_INT(PAGE_MANAGER_OK, PageManager_DispatchEvent(&event)); ASSERT_TRUE(PageManager_GetTop() == &pageB); /* handled 时不走全局 Pop */ ASSERT_EQ_INT(0, g_ctx_b.exitCount); ASSERT_EQ_INT(0, g_ctx_b.destroyCount); g_ctx_b.eventResult = EVENT_UNHANDLED; event.type = PAGE_EVENT_KEY; event.keyVal = KEY_ESC; ASSERT_EQ_INT(PAGE_MANAGER_OK, PageManager_DispatchEvent(&event)); ASSERT_TRUE(PageManager_GetTop() == &pageA2); ASSERT_EQ_INT(PAGE_ID_MENU, PageManager_GetTop()->page_id); ASSERT_EQ_INT(1, g_ctx_b.exitCount); ASSERT_EQ_INT(1, g_ctx_b.destroyCount); ASSERT_EQ_INT(2, g_ctx_a.enterCount); /* 未处理但非键盘事件,不应触发全局行为 */ event.type = 0; event.keyVal = KEY_ESC; ASSERT_EQ_INT(PAGE_MANAGER_OK, PageManager_DispatchEvent(&event)); ASSERT_TRUE(PageManager_GetTop() == &pageA2); /* ===== Case 5: Loop 仅驱动栈顶页 ===== */ PageManager_Loop(); ASSERT_EQ_INT(1, g_ctx_a.loopCount); ASSERT_EQ_INT(0, g_ctx_b.loopCount); /* ===== Case 6: Push/Pop 边界(含栈满) ===== */ ASSERT_EQ_INT(PAGE_MANAGER_OK, PageManager_Push(&pageB2)); ASSERT_EQ_INT(2, g_ctx_b.createCount); /* 上次被 destroy,重新 push 应再 create */ ASSERT_EQ_INT(2, g_ctx_b.enterCount); ASSERT_TRUE(PageManager_GetTop() == &pageB2); ASSERT_EQ_INT(PAGE_MANAGER_OK, PageManager_Push(&pageA3)); ASSERT_TRUE(PageManager_GetTop() == &pageA3); /* 当前栈深推进到 4(A2 -> B2 -> A3 -> B) */ ASSERT_EQ_INT(PAGE_MANAGER_OK, PageManager_Push(&pageB)); ASSERT_TRUE(PageManager_GetTop() == &pageB); /* 再 Push 一次达到栈深 5(上限) */ ASSERT_EQ_INT(PAGE_MANAGER_OK, PageManager_Push(&pageA3)); ASSERT_TRUE(PageManager_GetTop() == &pageA3); /* 栈满后继续 Push 应失败 */ ASSERT_EQ_INT(PAGE_MANAGER_ERR_STACK_FULL, PageManager_Push(&pageA2)); /* 连续 Pop 到底页,最后一次应失败 */ ASSERT_EQ_INT(PAGE_MANAGER_OK, PageManager_Pop()); ASSERT_EQ_INT(PAGE_MANAGER_OK, PageManager_Pop()); ASSERT_EQ_INT(PAGE_MANAGER_OK, PageManager_Pop()); ASSERT_EQ_INT(PAGE_MANAGER_OK, PageManager_Pop()); ASSERT_EQ_INT(PAGE_MANAGER_ERR_STACK_BOTTOM, PageManager_Pop()); ASSERT_TRUE(PageManager_GetTop() == &pageA2); /* ===== Case 7: Navigate 未注册页 ===== */ ASSERT_EQ_INT(PAGE_MANAGER_ERR_NOT_FOUND, PageManager_Navigate(PAGE_ID_NONE)); return 0; }