fix(sim): UI 回归改为自洽运行——修 netcfg_save 假失败
现象:check_content_pages.py 只有 netcfg_save 一页 FAIL(保存行值列 显示 ERR 而非 OK),其余 10 页像素与两处往返池水位全过。 真因不是代码回归:模拟器按**当前工作目录**读写 sim_config.ini,而 --cfgtest 的 E-01 场景 2 会伪造一份 4000 B 纯注释"雷"文件、并断言 "拒写后原文件不动"(size == 4000)——连正常通过路径也不删除它。 实测仓库根那份是 2026-10-11 08:37 留下的。之后每次运行都会加载它 → 外科式保存要在 4000 B 注释后补写受管段 → 输出必超 CFG_FILE_MAX_LEN → 引擎按设计拒写 → 保存行显示 ERR,与"工厂默认 态"的黄金参考对不上。引擎行为是对的,错在自检留雷 + 回归不自洽。 修三处: - simulator/main.c:run_cfgtest 拆成 scenarios + 外壳,无论成败一律 cfg_store_remove 配置路径与临时路径,让配置回到"无文件 = 工厂默认" - check_menu_rows.py / check_content_pages.py:以空临时目录当工作 目录跑 lvgl_sim(cwd=tmp),调用目录里的残留档不再影响结果 - check_content_pages.py:作废"删 sim_netcfg.bin 残留"的旧兼容清理 (旧档迁移 2026-10-10 已删),改为只提示、不删别人的档 验证:--cfgtest PASS 且跑完不再留下 sim_config.ini; check_menu_rows.py PASS(8 状态 + 池基线 5,368 B); check_content_pages.py "PASS: 11 内容页 + 往返池水位全部通过", 往返 5,448 B。**未刷任何黄金参考**——参考本来就是对的。顺带删掉 本地两份 4000 B 残留(仓库根与 build/Debug,均 gitignore 产物)。 未跑 @build:本次只改 simulator/ 侧代码,固件工程无涉及;模拟器 CMake 构建 0 错 0 警,cppcheck 钩子覆盖 simulator/main.c。 文档:UI_自动化测试流程 §五 加排查行 + 变更记录;simulator/README §3.7 说明删除保证与原因;lvgl-simulator skill 同步;订正审查报告 R-9 那条把该差异误归给 cfg_file 改动的表述。
This commit is contained in:
+3
-1
@@ -232,7 +232,7 @@ UI 改动的自动化门禁脚本(完整流程见 `docs/UI设计/UI_自动化
|
||||
|
||||
### 3.7 配置文件自检(--cfgtest)
|
||||
|
||||
配置引擎"加载 → 轮询 → 重载 → 保存"的进程内闭环验证(无需窗口 / LVGL,退出码 0=PASS / 非 0=FAIL),在当前工作目录产生 `sim_config.ini`:
|
||||
配置引擎"加载 → 轮询 → 重载 → 保存"的进程内闭环验证(无需窗口 / LVGL,退出码 0=PASS / 非 0=FAIL);自检期间在当前工作目录**临时**使用 `sim_config.ini`,**结束时无论成败一律删除**(2026-10-15 起,原因见下文 ⚠):
|
||||
|
||||
```powershell
|
||||
.\build\Debug\lvgl_sim.exe --cfgtest
|
||||
@@ -242,6 +242,8 @@ UI 改动的自动化门禁脚本(完整流程见 `docs/UI设计/UI_自动化
|
||||
|
||||
空目录运行 = 验证出厂模板生成;超限文件(>4096 B)= 判损坏并重建出厂模板;超限文档保存 = 拒绝且不动原文件。窗口模式主循环以与 MCU netTask 相同的 2s 周期调用 `cfg_file_poll()`:外部文件变更沉降后自动重载 `[network]` 段(模拟器无网络栈,变更回调为空操作,仅日志可见)。
|
||||
|
||||
⚠ **为什么必须删掉伪造档**:超限文档保存那条断言要求"拒写后原文件不动"(`size == 4000`),所以**连通过路径也会把这份 4000 B 纯注释档留在工作目录**。之后每一次运行都会加载它 → 外科式保存要在 4000 B 注释后补写受管段 → 输出必超 `CFG_FILE_MAX_LEN` → 引擎按设计拒写 → 网口设置页保存行显示 `ERR`,把 UI 像素回归带偏(2026-10-15 实测踩中)。故 `run_cfgtest()` 只是外壳,跑完一律删除;`simulator/tests/` 两套回归另以**空临时目录当工作目录**运行做双保险 → `docs/UI设计/UI_自动化测试流程.md` §五。
|
||||
|
||||
---
|
||||
|
||||
## 四、已知坑(构建/运行)
|
||||
|
||||
+22
-1
@@ -259,7 +259,7 @@ static const uint8_t s_cfgtest_ip[4] = { 10u, 0u, 0u, 1u };
|
||||
* 说明:验证链 net_cfg_load(只读 INI)→ poll 建基线 → 伪造外部编辑
|
||||
* → poll 两次(沉降去抖)→ 重载并更新 RAM 副本
|
||||
*/
|
||||
static int run_cfgtest(void) {
|
||||
static int run_cfgtest_scenarios(void) {
|
||||
int i;
|
||||
const net_cfg_t *p_cfg;
|
||||
|
||||
@@ -380,6 +380,27 @@ static int run_cfgtest(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:配置自检外壳——保证不在工作目录留下伪造的配置档
|
||||
* 入口参数:无
|
||||
* 返回值:run_cfgtest_scenarios 的判定 int
|
||||
* 说明:E-01 场景 2 会写一个 4000 B 纯注释"雷"文件,并断言"拒写后原文件不动"
|
||||
* (size == 4000)——**因此连正常通过路径也会把它留在原地**。该文件是
|
||||
* gitignore 的本地产物,但之后每一次 UI 回归都会加载它:外科手术保存要在
|
||||
* 4000 B 注释后补写受管段,输出必然超 CFG_FILE_MAX_LEN → 引擎按设计拒写
|
||||
* → 网口设置页保存行显示 ERR → 黄金参考(工厂默认态)比对失败
|
||||
* (2026-10-15 实测踩中)。故无论成败一律删除,让配置路径回到
|
||||
* "无文件 = 工厂默认"这一确定态;回归脚本另以空工作目录运行做双保险
|
||||
* → docs/UI设计/UI_自动化测试流程.md §三
|
||||
*/
|
||||
static int run_cfgtest(void) {
|
||||
int rc = run_cfgtest_scenarios();
|
||||
|
||||
(void)cfg_store_remove(cfg_store_config_path());
|
||||
(void)cfg_store_remove(cfg_store_tmp_path());
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:日志引擎自检(无窗口、进程内闭环)
|
||||
* 入口参数:无
|
||||
|
||||
@@ -111,8 +111,16 @@ STRIDE = W // 8 # I1:20 字节/行
|
||||
POOL_AFTER_ROUNDTRIP = 5448
|
||||
|
||||
|
||||
def run_sim(seq, out):
|
||||
r = subprocess.run([str(EXE), "--keys", seq, out], capture_output=True)
|
||||
def run_sim(seq, out, cwd):
|
||||
"""在指定工作目录里跑一次无头截图。
|
||||
|
||||
模拟器按**工作目录**读写 `sim_config.ini`:目录里残留一份配置(尤其
|
||||
`--cfgtest` E-01 场景 2 伪造的 4000 B 纯注释"雷"文件)会让引擎按设计
|
||||
拒绝保存 → 网口设置页保存行显示 ERR → 与"工厂默认态"的黄金参考对不上。
|
||||
因此一律在空临时目录里跑,回到"无配置文件 = 工厂默认"这一确定态。
|
||||
"""
|
||||
r = subprocess.run([str(EXE), "--keys", seq, out], capture_output=True,
|
||||
cwd=str(cwd))
|
||||
if r.returncode != 0:
|
||||
raise RuntimeError("lvgl_sim --keys %r rc=%d: %s"
|
||||
% (seq, r.returncode, r.stderr.decode(errors="replace")[:200]))
|
||||
@@ -263,17 +271,20 @@ def main():
|
||||
tmp = tempfile.mkdtemp(prefix="dtu_ui_pages_")
|
||||
ok = True
|
||||
|
||||
# 清理手工运行残留的配置存储文件(模拟器工作目录),防"工厂默认值"像素参考被污染
|
||||
# (sim_netcfg.bin 是旧档时代的产物:旧档迁移已于 2026-10-10 删除,此处只做兼容清理)
|
||||
stale = Path.cwd() / "sim_netcfg.bin"
|
||||
if stale.exists():
|
||||
stale.unlink()
|
||||
# 回归一律在空临时目录里跑(原因见 run_sim 的说明)。调用目录里残留的配置档
|
||||
# 不再影响像素,只提示一句,便于手工跑 lvgl_sim 的人排查。
|
||||
# (旧 sim_netcfg.bin 时代的"删除残留"做法已作废:旧档迁移 2026-10-10 删除,
|
||||
# 且真正会污染结果的是 --cfgtest 留下的 sim_config.ini,删别人的档不合适)
|
||||
for leftover in ("sim_config.ini", "sim_netcfg.bin"):
|
||||
if (Path.cwd() / leftover).exists():
|
||||
print("[提示] 当前目录存在 %s(本脚本不读它;手工运行 lvgl_sim 时留意,"
|
||||
"--cfgtest 会伪造 4000 B 配置档)" % leftover)
|
||||
|
||||
# 1) 跑全部页面 + 文本级断言预检(先于像素比对/刷新:失败即中止,避免误刷参考)
|
||||
shots = {}
|
||||
for name, seq in PAGES:
|
||||
shot = os.path.join(tmp, "shot_%s.bin" % name)
|
||||
run_sim(seq, shot)
|
||||
run_sim(seq, shot, tmp)
|
||||
shots[name] = open(shot, "rb").read()
|
||||
|
||||
for name, _seq in PAGES:
|
||||
@@ -316,7 +327,7 @@ def main():
|
||||
|
||||
# 开页-关页往返池水位门禁(info 页 e/s;密码页 ddee e/s 后 ←← 弹框回根框)
|
||||
for rt_name, rt_seq in (("info", "es"), ("pwd", "ddeeesll")):
|
||||
out = run_sim(rt_seq, os.path.join(tmp, "shot_rt_%s.bin" % rt_name))
|
||||
out = run_sim(rt_seq, os.path.join(tmp, "shot_rt_%s.bin" % rt_name), tmp)
|
||||
m = re.search(r"pool after keys: used=(\d+)", out.decode(errors="replace"))
|
||||
if not m:
|
||||
print("FAIL: 往返输出未找到 'pool after keys'(%s %r)" % (rt_name, rt_seq))
|
||||
|
||||
@@ -126,7 +126,10 @@ def main():
|
||||
try:
|
||||
for name, seq in STATES:
|
||||
out = os.path.join(tmp, "rows_%s.bin" % name)
|
||||
r = subprocess.run([str(EXE), "--keys", seq, out], capture_output=True)
|
||||
# 空临时目录当工作目录:模拟器按 cwd 读写 sim_config.ini,
|
||||
# 残留档(--cfgtest 会伪造 4000 B)会改变页面内容 → 回归必须自洽
|
||||
r = subprocess.run([str(EXE), "--keys", seq, out], capture_output=True,
|
||||
cwd=tmp)
|
||||
if r.returncode != 0:
|
||||
print("[%s] 运行失败 rc=%d: %s" % (name, r.returncode,
|
||||
r.stderr.decode(errors="replace")[:200]))
|
||||
|
||||
Reference in New Issue
Block a user