本轮把"自研代码落在门禁之外"的口子收掉,同时落地 LVGL 特性裁剪与 L3 保护定值/元件框架。 门禁(.pre-commit-config.yaml,26 个钩子) - 新增 scripts/check_lv_conf_sync.py + 钩子 lv-conf-sync:比对两份 lv_conf.h 的**生效值**(未定义则取 lv_conf_internal.h 兜底默认), 抓 MISMATCH / LVGL 不认的宏名 / 同文件重复定义 - 顶层 exclude 删掉 simulator/:其下 21 个 tracked 文件全是本项目自研 代码,生成物(build/、SDL2/、tests/ref/、shot*)已在 .gitignore; doc-links 的 exclude 同步删除 - lv-conf-sync 的 files: 覆盖两端配置 → 只改 simulator/lv_conf.h 也触发 (pre-commit 4.x 已移除 always: true,实测只告警并跳过,无兜底开关) - 新增 prot-set-unit-test / prot-elm-unit-test 两个主机侧单测钩子 - S11(2.3.5) 判定细化:禁的是"用路径找文件",尖括号系统头带子目录 (如 <sys/stat.h>)不再误报;全仓库复核仅 2 处项目代码判定变化 模拟器代码整改(纳入钩子后暴露,全部修代码而非豁免) - cppcheck 4 处:sim_syslog.c 判空分支内解引用 p_epoch(真实 bug)、 2 处 struct tm 未初始化、1 处指针可加 const - coding-standard 61 处:44 处函数左大括号并到右括号同行、8 处补函数 注释、3 处补文件头注释块、2 处 __ 前缀保护宏改 XXX_H、2 处 sprintf 改 snprintf(写入字节数与原来完全一致) LVGL 特性裁剪(F-1/F-2/F-3) - 两端 lv_conf.h 对齐同一特性子集:删 26 个 LVGL 9.3 不存在的选项名、 3 处重复定义,10 项靠默认值隐式生效的改为显式固化 - 实测对镜像体积中性;价值在防漂移,且模拟器 golden 回归跑的正是固件 实际编译的配置 L3 软件侧 - App/algo/prot_set.* Q10 定值模型(范围校验 / 整表 all-or-nothing / 校验和封存-自检-闭锁)+ prot_elm.* 通用"滞回+定时限"元件框架 7 实例 - test/host/prot_set、test/host/prot_elm 主机侧单测;uvprojx 挂载两处 文档同步 - pre-commit使用说明:§2.8 S11 口径、§2.15 lv-conf-sync 详解、新增 §5.5 复盘(教训:exclude 只写真正不该进钩子的路径) - AGENTS.md、UI_设计架构 §1.4 第 10 条、RAM 分析、审查报告刷新与销项 验证 - @build BUILD_OK 0/0(Code=194276 RO=15872 RW=876 ZI=139892) - 模拟器重建:菜单 8 状态像素逐字节一致(池 5368 B)、11 内容页黄金参考 全过(往返 5448 B)、remo sink 0/25600 位差异、--logtest/--cfgtest PASS - 全仓库 cppcheck / coding-standard / no-mojibake --all-files 全过 - check_doc_links --all 129/0;check_mem_numbers --check 内存数字一致 - 端到端:往 simulator/lv_conf.h 注入一行漂移 → 钩子 FAIL(exit 1)
9.2 KiB
UI 自动化测试流程(模拟器回归)
用途:所有涉及 UI 的改动(菜单结构 / 内容页 / 字体 / 样式 / 几何)改完代码后,按本文跑一遍全部门禁再提交。脚本已入库,直接调用即可,无需临时重写。 定位:
UI_界面制作流程.mdP2/P3 门禁的可执行版本;设计阶段(P0 规格 / P1 编码)走UI_界面制作流程.md。
一、前置条件
| 项 | 说明 |
|---|---|
| 模拟器已构建 | 仓库根目录:& "simulator\build_sim.bat"(自动探测本机 VS2019/2022/2026 生成器,见 simulator/README.md §3.2) |
| Python | 统一入口 .venv\Scripts\python.exe(AGENTS.md「Python 环境」);两个回归脚本纯标准库,任意 python3 均可,为保持一致一律用 .venv |
二、流程总览
# 仓库根目录执行(python 统一用 .venv,见 AGENTS.md「Python 环境」)
cmake --build simulator/build --config Debug # 0) 构建模拟器(须 0 警告)
.venv\Scripts\python.exe simulator/tests/check_menu_rows.py # 1) 菜单 8 状态像素回归 + 池基线门禁
.venv\Scripts\python.exe simulator/tests/check_content_pages.py # 2) 内容页黄金参考回归 + 往返池门禁
& '.venv\Scripts\python.exe' .dsh\skills\keil-build\scripts\keil_build.py # 3) Keil 构建(BUILD_OK,0 错 0 警)
.venv\Scripts\python.exe scripts/check_coding_standard.py # 4) 代码规范机械检查(PASS)
| 步 | 脚本/命令 | 门禁内容 | 预期输出 |
|---|---|---|---|
| 1 | simulator/tests/check_menu_rows.py |
菜单 8 状态:选中行白带 / 非选行黑底白字 / 框体 1px 边框 / 往返像素一致(S3==S1、S6==S1、S7==S2)+ S0 池基线 | PASS: 8 状态全部断言通过(8 图,池基线 5368 B) |
| 2 | simulator/tests/check_content_pages.py |
① 文本级断言(字形模板匹配,50 条,独立于 ref/)→ ② 11 页截图与 ref/ 黄金参考逐字节比对 + 开页-关页往返池水位 |
PASS: 11 内容页 + 往返池水位全部通过(文本断言先于像素比对,失败即中止) |
| 3 | keil-build skill(keil_build.py) |
MCU 端编译(UI 源码双端共用,改一端必须过另一端) | 退出码 0 = BUILD_OK |
| 4 | scripts/check_coding_standard.py |
代码规范 V1.0 机械检查 | PASS |
文本级断言层(2026-09-10 新增):
check_content_pages.py内置字形模板匹配——直接解析ui_font_cn_12.c的 cmap/glyph_dsc/bitmap,把期望字符串(标题/标签/值/页脚,含新增易缺字文案) 渲染成墨点集合,在截图对应 y 带内做精确匹配(LVGL 基线对齐)。价值:缺字显示空白 / 标签写错 这类回归不再只靠像素位图(那需要刷参考,且无视觉后端时难以判读);文本断言不依赖ref/,且 先于像素比对运行,失败即中止(不误刷参考)。文本断言覆盖确定文案,像素参考仍负责几何/样式/ 光标反显等其余视觉。
退出码约定:两个脚本均为 0=PASS / 1=断言失败 / 2=环境错误(exe 缺失、参考缺失)。 失败时脚本打印差异明细(文本断言未命中项 / ink runs 对照 / 像素差数),内容页脚本保留现场截图于临时目录并提示路径。
三、内存水位门禁常量
| 常量 | 所在脚本 | 当前值 | 含义 |
|---|---|---|---|
MENU_POOL_BASELINE |
check_menu_rows.py | 5368 B | 菜单根框建完的 LVGL 池 used(x64 模拟器) |
POOL_AFTER_ROUNDTRIP |
check_content_pages.py | 5448 B | 开页-关页往返后水位 = 基线 + 一次性残留 80 B |
水位历史:4784 →(2026-09-14 待机模块常驻 +656)5440 / 5520 →(2026-10-11 裁剪 LVGL 控件类 −72)5368 / 5448。 后一次漂移已核实为有意且更优:像素逐字节未变(11 页黄金参考在裁剪前生成、裁剪后全部一致), 且一次性残留仍是 80 B(5448 − 5368)→ 泄漏特征未变,只是
lv_theme_apply不再为已不存在的控件类登记样式条目。
漂移处理:菜单/页面结构或样式变更导致门禁失败时,先确认新值合理(残留分解见
菜单页面设计.md §9.5),再更新脚本常量并在本文变更记录登记。禁止为过门禁盲目改常量——
无法解释的漂移 = 内存回归 bug。
四、黄金参考与目检 PNG(均为本地生成物,不入库)
simulator/tests/ref/ 下的 .bin(像素黄金参考)与 .png(目检图)都不入库(.gitignore)——
首次/换机后必须先生成一次:
.venv\Scripts\python.exe simulator/tests/check_content_pages.py --update # 生成 ref/*.bin
.venv\Scripts\python.exe simulator/tests/regen_ref_png.py # 生成 ref/*.png(--scale 6 --invert)
有意改视觉时的流程:
- 先肉眼确认新效果正确:窗口模式运行
simulator\build\Debug\lvgl_sim.exe,或刷新后跑regen_ref_png.py看图对照 mockup; - 刷新参考:
.venv\Scripts\python.exe simulator/tests/check_content_pages.py --update(重写ref/*.bin); - 重生成目检 PNG:
.venv\Scripts\python.exe simulator/tests/regen_ref_png.py。.png是本地调试产物、不会随.bin自动更新——不重跑则 PNG 仍是旧渲染(缺字/错位,看似"乱码")。 默认--scale 6 --invert(深字白底便于目检);--no-invert= 白字黑底(与设备一致); - 无需提交
ref/(已 gitignore)。
误刷保护:--update 会先跑文本级断言,断言失败即中止,避免把缺字/错标签的渲染"刷成"新基准。
何时不该更新:未有意改视觉而某页 FAIL → 是 bug(缺字、几何漂移、样式回归),修代码而不是刷参考。 菜单状态无黄金参考文件——其断言是结构性的(白带占比/边框/往返一致),不依赖入库位图。
五、误报排查
| 现象 | 常见原因与处置 |
|---|---|
| 多页大面积像素差异 | 字体重生成(ui_font_cn_12.c 变化)→ 确认字形正确后 --update 刷参考 |
| 单行/单字差异 | 几何或文案变更 → 先判断是否有意修改;无意 = bug |
| 池水位门禁失败但像素一致 | 样式/结构变更(如动态样式转常量、增删框体)→ 确认新值合理后更新常量并登记 |
找不到 lvgl_sim.exe |
模拟器未构建或 build/ 被删 → 先按 §一 重建 |
| 终端输出中文乱码 | PowerShell 代码页渲染问题,不影响判定(看 PASS/FAIL 与退出码) |
六、与其他文档的关系
UI_界面制作流程.md:P0 需求录入 + 设计规格 → P1 规格→代码 → P2 本文回归 → P3 MCU 上机目检 → P4 归档菜单页面设计.md§9:内容页对象树 / 几何表 / 内存预算(本文数字的来源)simulator/README.md§3.5/§3.6:无头模式(--keys/--screenshot)与回归脚本说明
变更记录
| 日期 | 变更 |
|---|---|
| 2026-09-09 | Python 解释器统一为仓库 .venv(新增 requirements.txt,.gitignore 忽略 .venv/);§1/§2/§4 命令同步改用 .venv\Scripts\python.exe |
| 2026-09-09 | 初版:菜单 8 状态像素回归 + 内容页黄金参考回归(ref/ 3 图,I1 原始位图入库)+ 池水位门禁(4784/4864 B)。脚本自会话临时目录整理入 simulator/tests/(相对路径定位 exe、输出改临时目录),替代每次重写 |
| 2026-09-10 | check_content_pages.py 新增文本级断言层(字形模板匹配 50 条,独立于 ref/,先于像素比对运行、失败即中止防误刷参考);内容页扩至 11 页(+网口设置 4 态);§二/§四同步 |
| 2026-09-10 | 新增 simulator/tests/regen_ref_png.py(由 ref/*.bin 批量重生成目检 PNG,默认 --scale 6 --invert);ref/*.png 加入 .gitignore(本地调试产物不入库);§四 补"重生成目检 PNG"步骤(修复"刷了 .bin 参考但 .png 仍是旧渲染 → 看似乱码") |
| 2026-09-10 | simulator/tests/ref/ 整目录不入库(.bin 黄金参考 + .png 目检图均为本地生成物):git rm --cached 原有 3 个 .bin,.gitignore 改为忽略整目录;首次/换机后先 --update 生成参考;--update 先跑文本断言防误刷;§四 重写 |
| 2026-09-13 | 管线同步:UI_界面制作流程.md 移除 HTML 样机阶段(五阶段 → 四阶段 P0~P4),本文定位改为"P2/P3 门禁的可执行版本";§六 关系描述更新为 P0 规格 → P1 编码 → P2 本文回归 → P3 上机目检 → P4 归档;移除 tools/ui_mock/ 引用 |
| 2026-10-11 | 裁剪 LVGL 死代码(F-1/F-2/F-3)时的门禁记录:先用未裁剪配置 --update 生成 11 页黄金参考,再切到裁剪配置重编重跑 → 逐字节全部一致(菜单 8 状态像素一致),证明裁剪零视觉影响。池水位门禁随之 5440/5520 → 5368/5448(−72 B;一次性残留仍 80 B,故非泄漏变化,见 §三 水位历史)。顺带揪出并修掉 Lib/lv_conf.h 与 simulator/lv_conf.h 的长期漂移(模拟器早已 I1-only、MCU 端未裁)→ 规则见 UI_设计架构.md §1.4 第 10 条,量化见 ../内存管理/RAM使用分析与优化方案.md §2.6.4 |