a38f862fb9
问题:ui-golden 是逐字节比对(check_content_pages.py 里 data == refdata), 而基准 simulator/tests/ref/ 整目录被 .gitignore 忽略、从未入库。后果不是 "少几个文件",而是这条门禁在任何新克隆 / 新并行工作树里必然报「缺黄金 参考」——只在有老副本的机器上它才是真的回归门禁。本会话已两次实测踩到 (并行工作树里只能现场 --update 造基准,而现场造出来的基准恰好失去意义)。 入库前先证明基线可复现,否则等于把一条天天误报的门禁写进仓库: - 主树 ref/ 生成于 2026-10-11,其间合入 B6~B9 与 UI 令牌化等多次改动; 今天用刚重建的 lvgl_sim 重跑 → 11 页 + 开页-关页往返池水位逐字节全过 - 主树与新工作树两份**独立构建**的 lvgl_sim(不同构建目录、不同绝对路径) 各自生成基准 → 11 个 .bin 的 SHA-256 全部一致 即渲染是 CPU 侧确定性输出,可以当仓库内容。 改动: - .gitignore:整目录忽略 → 只忽略派生的目检 PNG。另加一条 !simulator/tests/ref/*.bin 反选——第 115 行的全仓 *.bin 兜底(挡 sim_netcfg.bin 之类运行产物)会把基线一起挡掉,且反选必须排在它之后 才生效 - 不引入 LFS:11 x 3200 B = 34.4 KB(160x160 单色打包)。走 LFS 反而新增 失效面——没拉 LFS 的克隆会拿 pointer 文本去比像素,把"缺基准"换成 "比不中"。.gitattributes 已有 *.bin binary,check-attr 确认 text unset, 不会被行尾转换改写 - 基线不会被改写型钩子毁掉:trailing-whitespace / end-of-file-fixer 声明 types: [text],位图含 NUL 被判二进制 → 实测 Skipped;索引 blob、磁盘、 主树参照三方哈希 11/11 一致 - 新增 11 个基线;同步 5 份文档与 3 处脚本口径:缺基准的提示从"首次/换机 请加 --update"改为"检出不完整 → git checkout -- simulator/tests/ref/ 取回, 不要用 --update 就地造基准"(旧提示正是自我实现型基准的来源) - 顺带修掉 simulator/tests/README.md 两处与实现不符的描述:基准写成 PNG (实为 .bin)、regen_ref_png.py 写成"重建黄金参考"(它只出目检图,基准 由 check_content_pages.py --update 刷) - .pre-commit-config.yaml 只改注释(原文明确断言 ref/ "永远不会进 git 索引",已被本改动推翻);ui-golden 的 files 面覆盖 simulator/tests/, 基线被改即触发回归,刷参考从此在 git diff 里显形 历史口径按纪律冻结:UI_自动化测试流程 2026-09-10 的"整目录不入库"等 6 处 变更记录行不改写,以新行登记反转。 实测:@build 0 错 0 警(Code=213520,与内存台账一致);ui-golden Passed; pre-commit run --all-files = 36 Passed / 0 Failed(RC=0)。
72 lines
2.8 KiB
Python
72 lines
2.8 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
regen_ref_png.py — 由黄金参考 .bin 批量重生成目检用 PNG(simulator/tests/ref/*.png)。
|
||
|
||
背景:ref/*.png 是本地目检调试产物(.gitignore 忽略;入库的基准是同名 .bin),
|
||
**不会**随 .bin 参考自动更新。
|
||
刷过 `check_content_pages.py --update` 或改了字体/视觉后,必须重跑本脚本,否则 PNG
|
||
仍是旧渲染(缺字/错位,看似"乱码")。
|
||
|
||
用法(仓库根目录;PIL 依赖用 .venv,见 AGENTS.md「Python 环境」):
|
||
.venv\\Scripts\\python.exe simulator/tests/regen_ref_png.py
|
||
.venv\\Scripts\\python.exe simulator/tests/regen_ref_png.py --scale 4
|
||
.venv\\Scripts\\python.exe simulator/tests/regen_ref_png.py --no-invert # 白字黑底(设备原样)
|
||
|
||
默认 `--scale 6 --invert`(深字白底,便于目检)。
|
||
退出码:0 全部成功;1 无 .bin 参考 / 有文件转换失败。
|
||
"""
|
||
import argparse
|
||
import importlib.util
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
HERE = Path(__file__).resolve().parent # simulator/tests/
|
||
REF_DIR = HERE / "ref"
|
||
I1_TO_PNG = HERE.parent / "i1_to_png.py" # simulator/i1_to_png.py
|
||
|
||
|
||
def _load_i1_to_png():
|
||
"""从 simulator/i1_to_png.py 载入转换模块(目录不同,不能直接 import)。"""
|
||
spec = importlib.util.spec_from_file_location("i1_to_png", I1_TO_PNG)
|
||
mod = importlib.util.module_from_spec(spec)
|
||
spec.loader.exec_module(mod)
|
||
return mod
|
||
|
||
|
||
def main():
|
||
ap = argparse.ArgumentParser(description="由 ref/*.bin 批量重生成目检 PNG")
|
||
ap.add_argument("--scale", type=int, default=6, help="放大倍数(默认 6)")
|
||
ap.add_argument("--invert", dest="invert", action="store_true", default=True,
|
||
help="前景/背景互换:深字白底(默认开,便于目检)")
|
||
ap.add_argument("--no-invert", dest="invert", action="store_false",
|
||
help="关闭互换:白字黑底(与设备显示一致)")
|
||
args = ap.parse_args()
|
||
|
||
if not I1_TO_PNG.exists():
|
||
print("FAIL: 找不到 %s" % I1_TO_PNG)
|
||
return 1
|
||
|
||
bins = sorted(REF_DIR.glob("*.bin"))
|
||
if not bins:
|
||
print("FAIL: %s 下没有 .bin 参考(先运行 check_content_pages.py --update)" % REF_DIR)
|
||
return 1
|
||
|
||
i1_to_png = _load_i1_to_png()
|
||
ok = 0
|
||
for b in bins:
|
||
try:
|
||
out = i1_to_png.i1_to_png(str(b), scale=args.scale, invert=args.invert)
|
||
print(" %-24s -> %s" % (b.name, Path(out).name))
|
||
ok += 1
|
||
except Exception as e: # 单文件失败不中断其余
|
||
print(" %-24s FAIL: %s" % (b.name, e))
|
||
|
||
print("%s: %d/%d 张 PNG 已生成(scale=%d invert=%s)"
|
||
% ("PASS" if ok == len(bins) else "FAIL", ok, len(bins), args.scale, args.invert))
|
||
return 0 if ok == len(bins) else 1
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|