Files

84 lines
4.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
r"""run_smoke.py — 冒烟测试运行器(脚本错误外部门禁)
用法:
python tools/run_smoke.py # 跑冒烟测试(game/tests/smoke_boot.tscn,本机 Godot 固定路径)
python tools/run_smoke.py --scene res://tests/smoke_boot.tscn
python tools/run_smoke.py --godot <引擎路径> --path <切片目录> --timeout 120
冒烟测试 = 极小启动场景(game/tests/smoke_boot.tscn/.gd):以项目运行目标拉起=真实开机路径
(autoload 注册在位、配置表加载、主屏场景实例化、启动态菜单可见),断言只查存活,零逻辑判定。
背景: GDScript 运行时脚本错误(SCRIPT ERROR)不改变进程退出码、不计入失败数,
且会静默中断所在断言函数的剩余断言——摘要零失败也可能断言未跑全。
进程内无脚本错误陷阱 API(GDScript 无 try/catch),故闸设在外部:本包装除退出码与摘要行外,
另扫输出中的 SCRIPT ERROR / Parse Error,命中即判失败。
三闸: ① 进程退出码 = 0;② 摘要行「[smoke] 摘要:N 项检查,0 项失败」在案且失败数为 0;
③ 输出无「SCRIPT ERROR」「Parse Error」标记行(标记清单供 tools/error_scan.py 复用,禁另写一份)。
"""
import argparse
import os
import re
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
# 引擎定位:GODOT_BIN 环境变量优先(远端 CI 与非 Windows 平台的注入口),Windows 本机固定路径保底。
DEFAULT_GODOT = os.environ.get("GODOT_BIN") or r"D:\code\godot\Godot_v4.7.2-stable_win64_console.exe"
DEFAULT_SLICE = ROOT / "game"
DEFAULT_SCENE = "res://tests/smoke_boot.tscn"
SUMMARY_RE = re.compile(r"\[smoke\] 摘要:(\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("--scene", default=DEFAULT_SCENE, help="冒烟场景(项目运行目标)")
ap.add_argument("--timeout", type=int, default=120, help="外部超时秒数(硬性要求)")
args = ap.parse_args()
cmd = [args.godot, "--headless", "--path", args.path, args.scene]
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——硬性要求)")
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("摘要行缺失(未检出「[smoke] 摘要: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())