chore(tools): 冒烟脚本错误门禁批(台账 84,2026-09-19 用户质询「发现 bug 是否已修复」触发补修)——新增 tools/run_smoke.py 三闸包装器:①进程退出码 ②摘要行失败数=0 ③SCRIPT ERROR/Parse Error 输出扫描(GDScript 无脚本错误陷阱 API,脚本错误静默吞断言的盲区外部闸住);超时闸默认 180s。CLAUDE.md「运行与验证」冒烟命令行改指包装器(底层 headless 命令保留注记)。验证:真实冒烟 317 项 0 失败 PASS+故意触发 Parse Error 探针 FAIL(验证后即删)。
This commit is contained in:
1 parent
d236d9f8e1
commit
c595397ab2
2 files changed
+79
-1
No files matched your search
@@ -18,7 +18,7 @@
|
||||
|
||||
工作目录 = 切片目录本身(含 `project.godot` 的那一层):
|
||||
|
||||
- 冒烟(全量 headless 断言):`…console.exe --headless --path . --script res://smoke/smoke_all.gd`
|
||||
- 冒烟(全量 headless 断言):`python tools/run_smoke.py`(包装器三闸:退出码+摘要失败数+SCRIPT ERROR 扫描——脚本错误不计冒烟失败数且静默中断所在函数剩余断言,2026-09-19 台账 84 立闸)。底层命令 = `…console.exe --headless --path . --script res://smoke/smoke_all.gd`
|
||||
- 场景级探针(呈现层回归):`…console.exe --headless --path . res://smoke/ui_probe.tscn`;另有 `ui_tune_probe.tscn`(数值调整板)、`ui_click_probe.tscn`(合成鼠标事件行为探针)
|
||||
- 调参期批量工具:`--script res://smoke/level_monte_carlo.gd`(关卡期望扫描)· `--script res://smoke/reward_expectation.gd`(奖励获取期望)
|
||||
- 交付前验收 = smoke + 全部场景探针全绿(测试细则见 `docs/code-conventions.md`)
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python3
|
||||
r"""run_smoke.py — 冒烟运行器(脚本错误外部门禁)
|
||||
|
||||
用法:
|
||||
python tools/run_smoke.py # 默认跑切片全量冒烟(本机 Godot 固定路径)
|
||||
python tools/run_smoke.py --script res://smoke/smoke_all.gd
|
||||
python tools/run_smoke.py --godot <引擎路径> --path <切片目录> --timeout 180
|
||||
|
||||
背景(2026-09-19 台账 83 实证): GDScript 运行时脚本错误(SCRIPT ERROR)不改变进程退出码、
|
||||
不计入冒烟失败数,且会静默中断所在冒烟函数的剩余断言——首跑「309 项 0 失败」实为断言未跑全。
|
||||
进程内无脚本错误陷阱 API(GDScript 无 try/catch),故闸设在外部:本包装除退出码与摘要行外,
|
||||
另扫输出中的 SCRIPT ERROR / Parse Error,命中即判失败。
|
||||
|
||||
三闸: ① 进程退出码 = 0;② 摘要行「冒烟完成:N 项检查,0 项失败」在案且失败数为 0;
|
||||
③ 输出无「SCRIPT ERROR」「Parse Error」标记行。
|
||||
"""
|
||||
import argparse
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
DEFAULT_GODOT = r"D:\code\godot\Godot_v4.7.2-stable_win64_console.exe"
|
||||
DEFAULT_SLICE = ROOT / "prototypes" / "dungeon-sweeper-vertical-slice"
|
||||
DEFAULT_SCRIPT = "res://smoke/smoke_all.gd"
|
||||
SUMMARY_RE = re.compile(r"冒烟完成:(\d+) 项检查,(\d+) 项失败")
|
||||
ERROR_MARKERS = ("SCRIPT ERROR", "Parse Error")
|
||||
|
||||
|
||||
def main():
|
||||
if hasattr(sys.stdout, "reconfigure"):
|
||||
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
||||
ap = argparse.ArgumentParser(description="冒烟运行器(脚本错误外部门禁)")
|
||||
ap.add_argument("--godot", default=DEFAULT_GODOT)
|
||||
ap.add_argument("--path", default=str(DEFAULT_SLICE), help="切片项目目录(含 project.godot)")
|
||||
ap.add_argument("--script", default=DEFAULT_SCRIPT)
|
||||
ap.add_argument("--timeout", type=int, default=180, help="外部超时秒数(ADR-0004 硬性要求)")
|
||||
args = ap.parse_args()
|
||||
|
||||
cmd = [args.godot, "--headless", "--path", args.path, "--script", args.script]
|
||||
try:
|
||||
proc = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8",
|
||||
errors="replace", timeout=args.timeout)
|
||||
except subprocess.TimeoutExpired:
|
||||
print(f"[run_smoke] FAIL:外部超时(>{args.timeout}s——ADR-0004 硬性要求)")
|
||||
return 1
|
||||
out = (proc.stdout or "") + (proc.stderr or "")
|
||||
sys.stdout.write(out)
|
||||
if out and not out.endswith("\n"):
|
||||
print()
|
||||
|
||||
gates = []
|
||||
if proc.returncode != 0:
|
||||
gates.append(f"退出码 = {proc.returncode}(≠0)")
|
||||
m = SUMMARY_RE.search(out)
|
||||
if m is None:
|
||||
gates.append("摘要行缺失(未检出「冒烟完成:N 项检查,M 项失败」——入口跑完前中断?)")
|
||||
elif int(m.group(2)) != 0:
|
||||
gates.append(f"冒烟失败 {m.group(2)} 项 / 共 {m.group(1)} 项")
|
||||
hits = [ln.strip() for ln in out.splitlines() if any(k in ln for k in ERROR_MARKERS)]
|
||||
if hits:
|
||||
gates.append(f"脚本错误标记命中 {len(hits)} 行(首行: {hits[0][:140]}——脚本错误不计冒烟失败数,"
|
||||
"且中断所在函数的剩余断言,必须人工排查)")
|
||||
|
||||
if m is not None:
|
||||
print(f"[run_smoke] 摘要: {m.group(0)}")
|
||||
if gates:
|
||||
print("[run_smoke] FAIL——")
|
||||
for g in gates:
|
||||
print(f" - {g}")
|
||||
return 1
|
||||
print("[run_smoke] PASS(退出码 / 摘要失败数 / 脚本错误扫描 三闸全过)")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in new issue
Block a user