将菜单的架构改成 MVP,并且进一步优化视图层和模型层的逻辑
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user