测试:补启动段可观测性与模拟器差距清单,run_all_suites 加只编译模式(S10+S13+S6)
CI / L0 静态门禁 (pre-commit) (push) Successful in 1m10s
CI / L0.5 主机侧单测 (MSVC) (push) Successful in 25s

This commit is contained in:
2026-10-02 13:57:25 +08:00
parent fe963e7d91
commit d9425ddd23
12 changed files with 288 additions and 22 deletions
+8 -1
View File
@@ -10,7 +10,7 @@
|---|---|---|
| `board/` | 板端测试套件(编入 Keil 工程,占 RAM) | [`board/README.md`](board/README.md) |
| `host/` | PC 端测试/判读脚本(Python) | [`host/README.md`](host/README.md) |
| `run_all_suites.py` | **多套件回归编排**(改宏 → 构建 → 烧录 → 采集,`try/finally` 保证还原开关) | 本文 §多套件回归 |
| `run_all_suites.py` | **多套件回归编排**(改宏 → 构建 → 烧录 → 采集,`try/finally` 保证还原开关);`--check-build` = 只编译不烧录(纯离机) | 本文 §多套件回归 |
## 运行方式
@@ -25,6 +25,9 @@
& '.venv\Scripts\python.exe' test/run_all_suites.py
& '.venv\Scripts\python.exe' test/run_all_suites.py --suites STORAGE,GD5F
& '.venv\Scripts\python.exe' test/run_all_suites.py --dry-run # 只看计划
# 只编译不烧录(S6;不需要 MCU / 串口 / ST-LINK,验证 4 套件可编译性)
& '.venv\Scripts\python.exe' test/run_all_suites.py --check-build
```
退出码约定:`0`=PASS / `1`=FAIL / `2`=采集或烧录错误 / `3`=用法或预检错误。
@@ -38,6 +41,10 @@
⚠️ 每轮要真机烧录 + 采集,4 套件约数分钟起;破坏性套件还会改设备持久状态。
**仅回归窗口用,绝不挂 pre-commit**。
`--check-build` 则相反:**只改宏 + `@build`,不烧录不采集,无需任何硬件**,用于防止
"测试套件长期编不过而无人知"(2026-08~09 CH395F 套件实例)。改动 `test/board/` 下测试
代码后建议先跑它——实测 4 套件均 `BUILD_OK: 0 errors, 0 warnings`。
## ⚠️ 注意
- 本目录**被 pre-commit 门禁排除**(测试代码不受 C 规范约束)
+50 -6
View File
@@ -31,8 +31,15 @@
& '.venv\\Scripts\\python.exe' test/run_all_suites.py
& '.venv\\Scripts\\python.exe' test/run_all_suites.py --suites STORAGE,GD5F
& '.venv\\Scripts\\python.exe' test/run_all_suites.py --dry-run # 只打印计划
& '.venv\\Scripts\\python.exe' test/run_all_suites.py --check-build # 只编译不烧录(S6,无需 MCU)
& '.venv\\Scripts\\python.exe' test/run_all_suites.py --keep-config # 不还原(调试用,慎)
--check-build(S6 / 上游 P1-10 方案 A):
只做"改宏 + @build",不烧录不采集,故**不需要 MCU / 串口 / ST-LINK**。
用途:① 改动测试固件或 test_config.h 后自检 4 套件仍可编译;
② 防止"某套件长期编不过而无人知"(2026-08~09 CH395F 套件实例)。
注意 RAM 溢出(P1-9)会自然暴露为链接错误 L6406E——这正是想要的。
设计依据:docs/待处理内容.md T-5;docs/测试说明/测试开发标准流程.md §3.1
创建日期:2026-10-01
"""
@@ -121,6 +128,9 @@ def main(argv=None):
ap.add_argument("--build-timeout", type=int, default=300)
ap.add_argument("--flash-timeout", type=int, default=300)
ap.add_argument("--dry-run", action="store_true", help="只打印计划,不改配置不构建不烧录")
ap.add_argument("--check-build", action="store_true",
help="只编译不烧录(S6/P1-10):逐套件改宏 + @build,验证 4 套件可编译性;"
"不需要 MCU / 串口 / ST-LINK,可用于 CI 或改动测试固件后自检")
ap.add_argument("--keep-config", action="store_true",
help="结束后不还原 test_config.h(调试用;⚠️ 会留下测试固件配置)")
args = ap.parse_args(argv)
@@ -133,22 +143,28 @@ def main(argv=None):
",".join(bad), ",".join(SUITES)))
return 2
for p in (CONFIG, BUILD_BAT, DOWNLOAD_BAT, RUN_BOOT):
# --check-build 不需要串口/烧录工具,故不强求 run_boot_test.py 存在
required = (CONFIG, BUILD_BAT) if args.check_build else (CONFIG, BUILD_BAT, DOWNLOAD_BAT, RUN_BOOT)
for p in required:
if not p.is_file():
print("[ERROR] 缺文件:{}".format(p))
return 2
mode = "只编译(--check-build,不烧录)" if args.check_build else "完整回归(编译+烧录+采集)"
print("=== 多套件回归编排 ===")
print("模式:{}".format(mode))
print("套件:{}".format(", ".join(picked)))
print("构建:{}".format(BUILD_BAT))
print("烧录:{}".format(DOWNLOAD_BAT))
print("判读:{}".format(RUN_BOOT))
if not args.check_build:
print("烧录:{}".format(DOWNLOAD_BAT))
print("判读:{}".format(RUN_BOOT))
if args.dry_run:
print("[DRY-RUN] 仅打印计划,未改动任何文件。")
return 0
results = {} # 套件 -> "PASS"/"FAIL"/"BUILD_FAIL"
restored = False
baseline = None # 进入时的 test_config.h 基线(try 内赋值;finally 依赖它,故先置 None)
try:
# 先确保从产品构建(干净起点)开始,避免上一轮的残留干扰
@@ -172,6 +188,12 @@ def main(argv=None):
results[suite] = "BUILD_FAIL"
continue
# --check-build:编译通过即达标,不烧录不采集(S6 方案 A)
if args.check_build:
results[suite] = "PASS"
print("[{}] BUILD_OK -> PASS(--check-build,未烧录)".format(suite))
continue
# 2) 烧录
t0 = time.monotonic()
frc, fout = _run(["cmd", "/c", str(DOWNLOAD_BAT)], timeout=args.flash_timeout)
@@ -204,7 +226,7 @@ def main(argv=None):
_rewrite_config(None)
# 与进入时的基线一致性核对(防止还原逻辑本身出错)
now = CONFIG.read_bytes()
if now != baseline:
if baseline is not None and now != baseline:
print("[WARN] 还原后的配置与进入时基线不一致,请人工核对 {}".format(CONFIG))
else:
restored = True
@@ -213,9 +235,26 @@ def main(argv=None):
except Exception as e: # noqa: BLE001 - 还原失败必须显式报出
print("[ERROR] 还原 test_config.h 失败:{}".format(e), file=sys.stderr)
# 5) 复位构建产物为产品态(2026-10-02):最后一轮构建留下的是**测试套件**的
# .map/.axf,会污染 mem-numbers 体积门禁(下次提交即 FAIL)。仅还原源码不够
# ——必须再编一次产品固件,使 .map 与 test_config.h 一起回到产品态。
# --check-build 也要做:它每轮都 @build,同样会留下测试态 .map。
if restored and not args.keep_config:
print("")
print("[..] 复位构建产物为产品态(重建一次产品固件,避免 .map 停留在测试套件态)...")
rc, out = _run([str(BUILD_BAT)], timeout=args.build_timeout)
tail = "\n".join(out.strip().splitlines()[-4:])
if rc == 0:
print("[OK] 产品固件已重建(.map/.axf 复位)")
else:
# 不判死:配置已还原,仅提示手工重建
print("[WARN] 产品固件重建失败(rc={}),请手工跑 @build 复位 .map".format(rc))
if tail:
print(tail)
# 汇总
print("")
print("=== 编排结果 ===")
print("=== 编排结果{} ===".format("(--check-build)" if args.check_build else ""))
for s in picked:
print(" {:<8} {}".format(s, results.get(s, "SKIPPED")))
if not restored and not args.keep_config:
@@ -224,8 +263,13 @@ def main(argv=None):
failed = [s for s, r in results.items() if r != "PASS"]
if failed:
print("[FAIL] 未通过:{}".format(", ".join(failed)))
if args.check_build:
print(" (--check-build:失败 = 该套件编译不过,需修测试固件代码)")
return 1
print("[OK] 全部套件 PASS")
if args.check_build:
print("[OK] 全部套件编译通过(--check-build:未烧录、未采集)")
else:
print("[OK] 全部套件 PASS")
return 0