将菜单的架构改成 MVP,并且进一步优化视图层和模型层的逻辑
This commit is contained in:
@@ -7,7 +7,6 @@ set(DTU_TEST_COMMON_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/lcd/lcd.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/key.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/menu/app/menu.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/menu/model/display.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/TCP/tcp.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/thread_utils.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/remoteDisplay.c"
|
||||
@@ -70,11 +69,10 @@ add_dtu_test(
|
||||
# ------------------------------------------------------------
|
||||
# P1:业务核心计算/状态流转测试
|
||||
# ------------------------------------------------------------
|
||||
add_dtu_test(
|
||||
test_p1_key
|
||||
test_p1_key.c
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/key.c"
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# P1:lcd基本测试
|
||||
# ------------------------------------------------------------
|
||||
add_dtu_test(
|
||||
test_p1_lcd_basic
|
||||
test_p1_lcd_basic.c
|
||||
@@ -83,40 +81,40 @@ add_dtu_test(
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/lcd/lcd_text.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/lcd/ascii.c"
|
||||
)
|
||||
# ------------------------------------------------------------
|
||||
# P1:菜单管理器测试
|
||||
# ------------------------------------------------------------
|
||||
add_dtu_test(
|
||||
test_p1_page_manager
|
||||
test_p1_page_manager.c
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/pages/page_manager.c"
|
||||
)
|
||||
# ------------------------------------------------------------
|
||||
# P1:菜单测试
|
||||
# ------------------------------------------------------------
|
||||
add_dtu_test(
|
||||
test_p1_menu
|
||||
test_p1_menu.c
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/menu/model/menu_tree_builder.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/menu/view/menu_layout.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/lcd/lcd_text.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/lcd/lcd.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/lcd/ascii.c"
|
||||
)
|
||||
add_dtu_test(
|
||||
test_p1_menu_nav_legacy
|
||||
test_p1_menu_nav_legacy.c
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/menu/presenter/menu_navigator.c"
|
||||
)
|
||||
add_dtu_test(
|
||||
test_p1_menu_navigator
|
||||
test_p1_menu_navigator.c
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/menu/presenter/menu_navigator.c"
|
||||
)
|
||||
add_dtu_test(
|
||||
test_p1_menu_tree_builder
|
||||
test_p1_menu_tree_builder.c
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/menu/model/menu_tree_builder.c"
|
||||
)
|
||||
add_dtu_test(
|
||||
test_p1_menu_layout
|
||||
test_p1_menu_layout.c
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/menu/view/menu_layout.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/menu/app/menu.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/pages/menu/model.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/pages/menu/view.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/pages/menu/presenter.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/pages/menu/page.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/pages/global/renderer_lcd.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/lcd/lcd_text.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/lcd/lcd.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/lcd/lcd_draw.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/lcd/ascii.c"
|
||||
)
|
||||
|
||||
target_compile_definitions(test_p1_menu PRIVATE UNIT_TEST)
|
||||
# ------------------------------------------------------------
|
||||
# P1:key测试
|
||||
# ------------------------------------------------------------
|
||||
add_dtu_test(
|
||||
test_p1_key
|
||||
test_p1_key.c
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/key.c"
|
||||
)
|
||||
# ------------------------------------------------------------
|
||||
# P2:集成测试(网络回环等)
|
||||
# ------------------------------------------------------------
|
||||
@@ -126,21 +124,3 @@ add_dtu_test(
|
||||
"${CMAKE_SOURCE_DIR}/src/TCP/tcp.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/thread_utils.c"
|
||||
)
|
||||
add_dtu_test(
|
||||
test_p2_menu_runtime_startup
|
||||
test_p2_menu_runtime_startup.c
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/menu/app/menu.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/menu/model/display.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/menu/model/menu_model.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/menu/view/menu_view.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/menu/presenter/menu_presenter.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/menu/model/menu_tree_builder.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/menu/view/menu_layout.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/menu/presenter/menu_navigator.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/menu/view/menu_renderer_lcd.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/lcd/lcd.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/lcd/lcd_draw.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/lcd/lcd_text.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/lcd/ascii.c"
|
||||
"${CMAKE_SOURCE_DIR}/src/Drv/key.c"
|
||||
)
|
||||
|
||||
@@ -2,46 +2,199 @@
|
||||
|
||||
#include "test_common.h"
|
||||
|
||||
#include "../src/Drv/menu/view/menu_layout.h"
|
||||
#include "../src/Drv/menu/model/menu_tree_builder.h"
|
||||
|
||||
static int noop_proc(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#include "../src/Drv/pages/menu/view.h"
|
||||
#include "../src/Drv/pages/menu/model.h"
|
||||
#include "../src/Drv/pages/menu/page.h"
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* 函数名: main
|
||||
* 功能:
|
||||
* 覆盖菜单模块的白盒/黑盒关键能力,验证:
|
||||
* 1) Model 初始化与辅助函数
|
||||
* 2) 菜单树构建与显示名称装饰
|
||||
* 3) View 初始化后的端口与接口绑定
|
||||
* 4) MenuPage 生命周期入口是否完整绑定
|
||||
*
|
||||
* 参数:
|
||||
* 无
|
||||
*
|
||||
* 返回值:
|
||||
* - 0 : 测试通过
|
||||
* ------------------------------------------------------------------------- */
|
||||
int main(void)
|
||||
{
|
||||
tagMenuCtrl ctrl;
|
||||
tagMenuItem items[4];
|
||||
uint8_t menu_num[4] = {0};
|
||||
/* 用于 MenuView_CharLenCal / Position 相关白盒测试的临时数组 */
|
||||
tagPMenuItem first[4] = {0};
|
||||
tagPMenuItem index[4] = {0};
|
||||
uint8_t max_len;
|
||||
|
||||
const tagMenuModel model[4] = {
|
||||
{0, "Root", "", 0, 0, 0, (FUNCPTR)noop_proc},
|
||||
{1, "设置", "", 0, 0, 0, (FUNCPTR)noop_proc},
|
||||
{2, "子项", "", 0, 0, 0, (FUNCPTR)noop_proc},
|
||||
{1, "查看", "", 0, 0, 0, (FUNCPTR)noop_proc},
|
||||
/* 页面实例:用于验证 MenuPage_GetInstance 生命周期回调绑定 */
|
||||
page_t *menuPage;
|
||||
|
||||
/* 真实菜单模型对象:用于测试 MenuModel_Init 及其派生结果 */
|
||||
menu_model_t menuModel;
|
||||
|
||||
/* 视图对象:用于验证 MenuView_Init 的接口与渲染端口初始化 */
|
||||
MenuView view;
|
||||
|
||||
/* 自定义最小菜单树:用于单独测试 BuildTree / DecorateDisplayNames */
|
||||
tagMenuItem customItems[4];
|
||||
tagMenuItem customItems2[7];
|
||||
tagMenuModel customModelTab[4] =
|
||||
{
|
||||
{0, "Root", "", 0, 0x0000, 0, NULL},
|
||||
{1, "ChildA", "", 0, 0x0000, 0, NULL},
|
||||
{2, "Leaf", "", 0, 0x0000, 0, NULL},
|
||||
{1, "ChildB", "", 0, 0x0000, 0, NULL}
|
||||
};
|
||||
tagMenuModel customModelTab2[7] =
|
||||
{
|
||||
{0, "RootA", "", 0, 0x0000, 0, NULL},
|
||||
{1, "A1", "", 0, 0x0000, 0, NULL},
|
||||
{2, "A1a", "", 0, 0x0000, 0, NULL},
|
||||
{1, "A2", "", 0, 0x0000, 0, NULL},
|
||||
{0, "RootB", "", 0, 0x0000, 0, NULL},
|
||||
{1, "B1", "", 0, 0x0000, 0, NULL},
|
||||
{0, "RootC", "", 0, 0x0000, 0, NULL}
|
||||
};
|
||||
|
||||
memset(&ctrl, 0, sizeof(ctrl));
|
||||
memset(items, 0, sizeof(items));
|
||||
/* ---------------------------------------------------------------------
|
||||
* Case 1:
|
||||
* 测试 MenuModel_Init / MenuModel_CountTopLevel
|
||||
* - 初始化真实菜单模型
|
||||
* - 验证 0 级菜单数量统计结果有效
|
||||
* --------------------------------------------------------------------- */
|
||||
memset(&menuModel, 0, sizeof(menuModel));
|
||||
MenuModel_Init(&menuModel);
|
||||
|
||||
MenuTree_0LevelNumCal(&ctrl, model, 4);
|
||||
ASSERT_EQ_INT(1, ctrl.by0LevelNum);
|
||||
MenuTree_MainCreate(items, model, 4);
|
||||
/* ---------------------------------------------------------------------
|
||||
* Case 2:
|
||||
* 测试 MenuView_Utf8LenCal
|
||||
* - 验证英文与中文字符串长度计算逻辑
|
||||
* --------------------------------------------------------------------- */
|
||||
ASSERT_EQ_INT(3, MenuModel_Utf8LenCal((uint8_t *)"ABC"));
|
||||
ASSERT_EQ_INT(2, MenuModel_Utf8LenCal((uint8_t *)"你"));
|
||||
|
||||
ASSERT_EQ_INT(3, MenuLayout_Utf8LenCal((uint8_t *)"ABC"));
|
||||
ASSERT_EQ_INT(2, MenuLayout_Utf8LenCal((uint8_t *)"你"));
|
||||
|
||||
first[0] = &items[0];
|
||||
index[0] = &items[0];
|
||||
max_len = MenuLayout_CharLenCal(0, menu_num, first, index);
|
||||
/* ---------------------------------------------------------------------
|
||||
* Case 3:
|
||||
* 测试 MenuModel_IndexMenuItems / MenuModel_GetMenuMaxDisplayLen
|
||||
* - 使用真实菜单模型验证序号初始化、子节点数量统计与子菜单宽度统计
|
||||
* --------------------------------------------------------------------- */
|
||||
first[0] = &menuModel.menuItems[0];
|
||||
index[0] = &menuModel.menuItems[0];
|
||||
first[1] = menuModel.menuItems[0].links.lower;
|
||||
max_len = MenuModel_GetMenuMaxDisplayLen(first[1]);
|
||||
ASSERT_TRUE(max_len > 0);
|
||||
ASSERT_TRUE(menu_num[1] > 0);
|
||||
ASSERT_STREQ("设置", (const char *)items[1].byName);
|
||||
ASSERT_EQ_INT(0, menuModel.menuItems[0].rect.wPos);
|
||||
ASSERT_EQ_INT(0, first[1]->rect.wPos);
|
||||
ASSERT_TRUE(menuModel.menuItems[0].rect.wNum > 0);
|
||||
if (first[1]->links.lower == NULL)
|
||||
{
|
||||
ASSERT_EQ_INT(0, first[1]->rect.wNum);
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT_TRUE(first[1]->rect.wNum > 0);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
* Case 4:
|
||||
* 测试 MenuModel_DecorateDisplayNames 的真实初始化结果
|
||||
* - 验证存在子菜单的项名称非空,并被正确用于显示
|
||||
* --------------------------------------------------------------------- */
|
||||
/* 有子菜单的项会被装饰追加 '\x10'(右箭头标记) */
|
||||
ASSERT_TRUE(((const char *)menuModel.menuItems[1].menuDef.byName)[0] != '\0');
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
* Case 5:
|
||||
* 测试 MenuModel_BuildTree
|
||||
* - 使用自定义 4 节点菜单表验证父子、同层、闭环链路关系
|
||||
* --------------------------------------------------------------------- */
|
||||
memset(customItems, 0, sizeof(customItems));
|
||||
|
||||
MenuModel_BuildTree(customItems, customModelTab, 4);
|
||||
MenuModel_IndexMenuItems(customItems, 4);
|
||||
ASSERT_TRUE(customItems[0].links.lower == &customItems[1]);
|
||||
ASSERT_TRUE(customItems[1].links.higher == &customItems[0]);
|
||||
ASSERT_TRUE(customItems[1].links.lower == &customItems[2]);
|
||||
ASSERT_TRUE(customItems[2].links.higher == &customItems[1]);
|
||||
ASSERT_TRUE(customItems[1].links.behind == &customItems[3]);
|
||||
ASSERT_TRUE(customItems[3].links.before == &customItems[1]);
|
||||
ASSERT_TRUE(customItems[3].links.higher == &customItems[0]);
|
||||
ASSERT_TRUE(customItems[3].links.behind == &customItems[1]); /* 同层闭环 */
|
||||
ASSERT_EQ_INT(0, customItems[0].rect.wPos);
|
||||
ASSERT_EQ_INT(2, customItems[0].rect.wNum);
|
||||
ASSERT_EQ_INT(0, customItems[1].rect.wPos);
|
||||
ASSERT_EQ_INT(1, customItems[1].rect.wNum);
|
||||
ASSERT_EQ_INT(1, customItems[3].rect.wPos);
|
||||
ASSERT_EQ_INT(0, customItems[3].rect.wNum);
|
||||
ASSERT_EQ_INT(0, customItems[2].rect.wPos);
|
||||
ASSERT_EQ_INT(0, customItems[2].rect.wNum);
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
* Case 5.1:
|
||||
* 强化测试 MenuModel_IndexMenuItems
|
||||
* - 覆盖多顶层、多兄弟、跨层回退后的 wPos / wNum 统计
|
||||
* --------------------------------------------------------------------- */
|
||||
memset(customItems2, 0, sizeof(customItems2));
|
||||
MenuModel_BuildTree(customItems2, customModelTab2, 7);
|
||||
MenuModel_IndexMenuItems(customItems2, 7);
|
||||
ASSERT_EQ_INT(0, customItems2[0].rect.wPos);
|
||||
ASSERT_EQ_INT(2, customItems2[0].rect.wNum);
|
||||
ASSERT_EQ_INT(0, customItems2[1].rect.wPos);
|
||||
ASSERT_EQ_INT(1, customItems2[1].rect.wNum);
|
||||
ASSERT_EQ_INT(0, customItems2[2].rect.wPos);
|
||||
ASSERT_EQ_INT(0, customItems2[2].rect.wNum);
|
||||
ASSERT_EQ_INT(1, customItems2[3].rect.wPos);
|
||||
ASSERT_EQ_INT(0, customItems2[3].rect.wNum);
|
||||
ASSERT_EQ_INT(1, customItems2[4].rect.wPos);
|
||||
ASSERT_EQ_INT(1, customItems2[4].rect.wNum);
|
||||
ASSERT_EQ_INT(0, customItems2[5].rect.wPos);
|
||||
ASSERT_EQ_INT(0, customItems2[5].rect.wNum);
|
||||
ASSERT_EQ_INT(2, customItems2[6].rect.wPos);
|
||||
ASSERT_EQ_INT(0, customItems2[6].rect.wNum);
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
* Case 6:
|
||||
* 测试 MenuModel_DecorateDisplayNames
|
||||
* - 验证有子节点的项追加 '\x10'
|
||||
* - 验证叶子节点名称不被追加装饰符
|
||||
* --------------------------------------------------------------------- */
|
||||
MenuModel_DecorateDisplayNames(customItems, 4);
|
||||
ASSERT_TRUE(customItems[0].menuDef.byName[4] == '\x10');
|
||||
ASSERT_TRUE(customItems[0].menuDef.byName[5] == '\0');
|
||||
ASSERT_TRUE(customItems[1].menuDef.byName[6] == '\x10');
|
||||
ASSERT_TRUE(customItems[1].menuDef.byName[7] == '\0');
|
||||
ASSERT_TRUE(customItems[2].menuDef.byName[4] == '\0'); /* 叶子节点不追加 */
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
* Case 7:
|
||||
* 测试 MenuView_Init / MenuView_GetPortForTest
|
||||
* - 验证 View 初始化后渲染端口、布局参数、接口函数指针均可用
|
||||
* --------------------------------------------------------------------- */
|
||||
/* View 初始化后,渲染端口派生数据和对外接口必须可用 */
|
||||
memset(&view, 0, sizeof(view));
|
||||
ASSERT_TRUE(MenuView_GetPortForTest() == NULL);
|
||||
MenuView_Init(&view);
|
||||
ASSERT_TRUE(MenuView_GetPortForTest() != NULL);
|
||||
ASSERT_TRUE(view.update_selection_new_level != NULL);
|
||||
ASSERT_TRUE(view.update_selection_same_level != NULL);
|
||||
ASSERT_TRUE(view.full_refresh != NULL);
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
* Case 8:
|
||||
* 测试 MenuPage_GetInstance
|
||||
* - 验证页面生命周期入口已经完整绑定
|
||||
* --------------------------------------------------------------------- */
|
||||
/* 页面生命周期入口必须完整绑定,避免注册后生命周期不执行 */
|
||||
menuPage = MenuPage_GetInstance();
|
||||
ASSERT_TRUE(menuPage != NULL);
|
||||
ASSERT_TRUE(menuPage->on_create != NULL);
|
||||
ASSERT_TRUE(menuPage->on_enter != NULL);
|
||||
ASSERT_TRUE(menuPage->on_exit != NULL);
|
||||
ASSERT_TRUE(menuPage->on_destroy != NULL);
|
||||
ASSERT_TRUE(menuPage->on_event != NULL);
|
||||
ASSERT_TRUE(menuPage->on_loop != NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
#include "test_common.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "../src/Drv/lcd/lcd.h"
|
||||
#include "../src/Drv/menu/app/menu.h"
|
||||
#include "../src/Drv/menu/view/menu_layout.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
tagMenuItem root;
|
||||
tagMenuItem child;
|
||||
MenuLayoutConfig config = {
|
||||
LCD_SIZE_X,
|
||||
LCD_SIZE_Y,
|
||||
MENU_YMIN,
|
||||
MENU_YMAX_FROM_LCD(LCD_SIZE_Y),
|
||||
LINE_HEIGHT,
|
||||
MENU_WITDTH,
|
||||
MENU_XADD,
|
||||
MENU_YADD};
|
||||
|
||||
memset(&root, 0, sizeof(root));
|
||||
memset(&child, 0, sizeof(child));
|
||||
|
||||
memcpy(root.byName, "Root", 5);
|
||||
memcpy(child.byName, "Child", 6);
|
||||
root.byClass = 0;
|
||||
child.byClass = 1;
|
||||
|
||||
root.ptLower = &child;
|
||||
root.ptBehind = &root;
|
||||
root.ptBefore = &root;
|
||||
child.ptHigher = &root;
|
||||
child.ptBehind = &child;
|
||||
child.ptBefore = &child;
|
||||
|
||||
ASSERT_EQ_INT(3, MenuLayout_Utf8LenCal((uint8_t *)"ABC"));
|
||||
ASSERT_EQ_INT(2, MenuLayout_Utf8LenCal((uint8_t *)"你"));
|
||||
|
||||
MenuLayout_PositionCal(&root, 1, &config);
|
||||
ASSERT_TRUE(root.wEPosX > root.wSPosX);
|
||||
ASSERT_TRUE(root.wEPosY > root.wSPosY);
|
||||
ASSERT_TRUE(root.wEPosY <= LCD_SIZE_Y);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "test_common.h"
|
||||
|
||||
#include "../src/Drv/key.h"
|
||||
#include "../src/Drv/menu/presenter/menu_navigator.h"
|
||||
|
||||
static int g_exec_count = 0;
|
||||
|
||||
static int on_exec(void)
|
||||
{
|
||||
g_exec_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void build_legacy_like_tree(MenuNavState *nav, tagMenuItem *root, tagMenuItem *m1, tagMenuItem *m2, tagMenuItem *m1_sub)
|
||||
{
|
||||
memset(nav, 0, sizeof(*nav));
|
||||
memset(root, 0, sizeof(*root));
|
||||
memset(m1, 0, sizeof(*m1));
|
||||
memset(m2, 0, sizeof(*m2));
|
||||
memset(m1_sub, 0, sizeof(*m1_sub));
|
||||
|
||||
root->byClass = 0;
|
||||
root->ptLower = m1;
|
||||
root->ptBefore = root;
|
||||
root->ptBehind = root;
|
||||
root->wPos = 1;
|
||||
|
||||
m1->byClass = 1;
|
||||
m1->wPos = 1;
|
||||
m1->ptHigher = root;
|
||||
m1->ptBefore = m2;
|
||||
m1->ptBehind = m2;
|
||||
m1->ptLower = m1_sub;
|
||||
m1->pfnWinProc = on_exec;
|
||||
|
||||
m2->byClass = 1;
|
||||
m2->wPos = 2;
|
||||
m2->ptHigher = root;
|
||||
m2->ptBefore = m1;
|
||||
m2->ptBehind = m1;
|
||||
m2->pfnWinProc = on_exec;
|
||||
|
||||
m1_sub->byClass = 2;
|
||||
m1_sub->wPos = 1;
|
||||
m1_sub->ptHigher = m1;
|
||||
m1_sub->ptBefore = m1_sub;
|
||||
m1_sub->ptBehind = m1_sub;
|
||||
m1_sub->pfnWinProc = on_exec;
|
||||
|
||||
nav->ptHead = root;
|
||||
nav->ptCurrent = m1;
|
||||
nav->ptCurBak = m1;
|
||||
nav->ptRoute[0] = root;
|
||||
nav->ptRoute[1] = m1;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
MenuNavState nav;
|
||||
tagMenuItem root;
|
||||
tagMenuItem m1;
|
||||
tagMenuItem m2;
|
||||
tagMenuItem m1_sub;
|
||||
MenuNavResult result;
|
||||
|
||||
build_legacy_like_tree(&nav, &root, &m1, &m2, &m1_sub);
|
||||
|
||||
result = MenuNavigator_ProcessKey(&nav, KEY_D);
|
||||
ASSERT_EQ_INT(1, result.needRefresh);
|
||||
ASSERT_TRUE(nav.ptCurrent == &m2);
|
||||
|
||||
result = MenuNavigator_ProcessKey(&nav, KEY_U);
|
||||
ASSERT_EQ_INT(1, result.needRefresh);
|
||||
ASSERT_TRUE(nav.ptCurrent == &m1);
|
||||
|
||||
result = MenuNavigator_ProcessKey(&nav, KEY_ENT);
|
||||
ASSERT_EQ_INT(1, result.needRefresh);
|
||||
ASSERT_TRUE(nav.ptCurrent == &m1_sub);
|
||||
|
||||
result = MenuNavigator_ProcessKey(&nav, KEY_ESC);
|
||||
ASSERT_EQ_INT(1, result.needRefresh);
|
||||
ASSERT_TRUE(nav.ptCurrent == &m1);
|
||||
|
||||
g_exec_count = 0;
|
||||
nav.ptCurrent = &m2;
|
||||
result = MenuNavigator_ProcessKey(&nav, KEY_ENT);
|
||||
ASSERT_EQ_INT(0, result.needRefresh);
|
||||
ASSERT_EQ_INT(1, g_exec_count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
#include "test_common.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "../src/Drv/key.h"
|
||||
#include "../src/Drv/menu/presenter/menu_navigator.h"
|
||||
|
||||
static int g_exec_count = 0;
|
||||
|
||||
static int on_exec(void)
|
||||
{
|
||||
g_exec_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void build_two_level(MenuNavState *nav, tagMenuItem *root, tagMenuItem *child_a, tagMenuItem *child_b)
|
||||
{
|
||||
memset(nav, 0, sizeof(*nav));
|
||||
memset(root, 0, sizeof(*root));
|
||||
memset(child_a, 0, sizeof(*child_a));
|
||||
memset(child_b, 0, sizeof(*child_b));
|
||||
|
||||
root->byClass = 0;
|
||||
root->wPos = 1;
|
||||
root->ptLower = child_a;
|
||||
root->ptBefore = root;
|
||||
root->ptBehind = root;
|
||||
|
||||
child_a->byClass = 1;
|
||||
child_a->wPos = 1;
|
||||
child_a->ptHigher = root;
|
||||
child_a->ptBefore = child_b;
|
||||
child_a->ptBehind = child_b;
|
||||
child_a->pfnWinProc = on_exec;
|
||||
|
||||
child_b->byClass = 1;
|
||||
child_b->wPos = 2;
|
||||
child_b->ptHigher = root;
|
||||
child_b->ptBefore = child_a;
|
||||
child_b->ptBehind = child_a;
|
||||
child_b->pfnWinProc = on_exec;
|
||||
|
||||
nav->ptHead = root;
|
||||
nav->ptCurrent = child_a;
|
||||
nav->ptCurBak = child_a;
|
||||
nav->ptRoute[0] = root;
|
||||
nav->ptRoute[1] = child_a;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
MenuNavState nav;
|
||||
tagMenuItem root;
|
||||
tagMenuItem a;
|
||||
tagMenuItem b;
|
||||
MenuNavResult result;
|
||||
|
||||
build_two_level(&nav, &root, &a, &b);
|
||||
|
||||
result = MenuNavigator_ProcessKey(&nav, KEY_D);
|
||||
ASSERT_EQ_INT(1, result.needRefresh);
|
||||
ASSERT_TRUE(nav.ptCurrent == &b);
|
||||
|
||||
result = MenuNavigator_ProcessKey(&nav, KEY_U);
|
||||
ASSERT_EQ_INT(1, result.needRefresh);
|
||||
ASSERT_TRUE(nav.ptCurrent == &a);
|
||||
|
||||
g_exec_count = 0;
|
||||
result = MenuNavigator_ProcessKey(&nav, KEY_ENT);
|
||||
ASSERT_EQ_INT(0, result.needRefresh);
|
||||
ASSERT_EQ_INT(1, g_exec_count);
|
||||
|
||||
result = MenuNavigator_ProcessKey(&nav, KEY_ESC);
|
||||
ASSERT_EQ_INT(1, result.skipRenderThisRound);
|
||||
ASSERT_TRUE(nav.ptCurrent == root.ptLower);
|
||||
ASSERT_TRUE(nav.ptRoute[0] == &root);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
#include "test_common.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "../src/Drv/menu/model/menu_tree_builder.h"
|
||||
|
||||
static int noop_proc(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
tagMenuCtrl ctrl;
|
||||
tagMenuItem items[4];
|
||||
const tagMenuModel model[4] = {
|
||||
{0, "Root", "", 0, 0, 0, (FUNCPTR)noop_proc},
|
||||
{1, "A", "", 0, 0, 0, (FUNCPTR)noop_proc},
|
||||
{1, "B", "", 0, 0, 0, (FUNCPTR)noop_proc},
|
||||
{2, "C", "", 0, 0, 0, (FUNCPTR)noop_proc},
|
||||
};
|
||||
|
||||
memset(&ctrl, 0, sizeof(ctrl));
|
||||
memset(items, 0, sizeof(items));
|
||||
|
||||
MenuTree_0LevelNumCal(&ctrl, model, 4);
|
||||
ASSERT_EQ_INT(1, ctrl.by0LevelNum);
|
||||
|
||||
MenuTree_MainCreate(items, model, 4);
|
||||
ASSERT_TRUE(items[0].ptLower == &items[1]);
|
||||
ASSERT_TRUE(items[1].ptBehind == &items[2]);
|
||||
ASSERT_TRUE(items[2].ptBefore == &items[1]);
|
||||
ASSERT_TRUE(items[2].ptLower == &items[3]);
|
||||
ASSERT_TRUE(items[3].ptHigher == &items[2]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
292
tests/test_p1_page_manager.c
Normal file
292
tests/test_p1_page_manager.c
Normal file
@@ -0,0 +1,292 @@
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
#include "test_common.h"
|
||||
|
||||
#include "../src/Drv/key.h"
|
||||
#include "../src/Drv/menu/app/menu.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int decorated_found = 0;
|
||||
uint16_t itemCount = 0;
|
||||
const tagMenuItem *menuItems;
|
||||
|
||||
MenuApp_Init();
|
||||
Key_Init();
|
||||
menuItems = MenuApp_GetMenuItems(&itemCount);
|
||||
|
||||
for (uint16_t i = 0; i < itemCount; i++)
|
||||
{
|
||||
if (menuItems[i].ptLower != NULL)
|
||||
{
|
||||
uint8_t len = 0;
|
||||
while ((len < 50) && (menuItems[i].byName[len] != '\0'))
|
||||
{
|
||||
len++;
|
||||
}
|
||||
ASSERT_TRUE(len > 0);
|
||||
ASSERT_EQ_INT('\x10', menuItems[i].byName[len - 1]);
|
||||
decorated_found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(decorated_found == 1);
|
||||
|
||||
/* 首次路由应仅触发首帧绘制,不应崩溃 */
|
||||
MenuApp_PollInput();
|
||||
|
||||
/* 二次刷新路径也不应崩溃 */
|
||||
MenuApp_Render();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user