背景:孤儿探针 ui_layout_probe 退役后1920 档布局几何无机器承载。重建但改形态——
「档位表(数据)+ 一份不变量断言(代码)+ 循环代入」,测试代码量与档数无关,
增删档位只改 VIEWPORTS 数组一行。这与业界 Playwright parametrize/projects 同构:
同一个 test body 跑多档,变的只是输入表与基线图张数(数据非代码)。
档位按 battle.md §验收现登记的两档:基座 1280×800、16:9 全屏 1920×1080。不新增档。
关键修正——旧探针那条「1920 档几何恒定」断言是假的:
设get_window().size = 1920×1080 → 逻辑视口仍是 1280×800(headless 下不生效)
设 content_scale_size = 1422×800 → 逻辑视口 = 1422×800(真生效)
Godot 文档指定的运行时改基座接口是 content_scale_size;旧探针用错了 API,
其断言永远为真、当年报 PASS 是假绿。本探针改用正确接口。
断言面(每档跑一遍):三区非零/不越视口/两两不重叠/board_view ⊆ board_zone/
格子边长 ≥36px(battle.md §棋盘格尺寸公式 1280 档 12×12 → 36px)。
变异验证(CLAUDE.md 第9条)两发:
① battle_stage_view B区 252→700 → 5 条红(两档都中)✓ 还原后 PASS
② 注入全幅 ColorRect → 未见红 ✗ —— 判据只点名三区不覆盖第四个兄弟,
该缺陷类不在拦截面内。已在代码注释标明边界,未过度设计补递归遍历
(递归全子树查重叠极易误报容器父子关系)。
挂 probe_gate 默认必跑段(轻闸即跑)。S10 文件头闸本批零新增。
157 lines
8.5 KiB
Bash
157 lines
8.5 KiB
Bash
#!/usr/bin/env bash
|
||
# 轻闸探针正主(2026-10-02 挂死灾害批落籍)——headless 安全段固化,禁再手拼同款 Godot 命令。
|
||
# 三闸:①每段 timeout 守卫(挂死=FAIL 不静默)②段前预清+段后清本 game 目录孤儿 Godot
|
||
# (按命令行精确匹配本 game 路径,不误杀其他 worktree 会话的合法进程)③末尾残留清点=批末闸(必须 0)。
|
||
# 段:ui_input_probe=默认必跑;ui_struct_probe=默认必跑(ui-struct-probe mini-loop 2026-10-05 注册——
|
||
# 全 UI 屏结构断言:按钮互压/等宽/出屏/零尺寸/标题屏双列);ui_contrast_probe=默认必跑(UI 设计对齐批
|
||
# T2 2026-10-06 注册——UI 文字对比度机检 ≥4.5:1 无区域例外,测点表随批维护);--click=加跑 ui_click_probe 行为走查
|
||
# (自退出+退出码闸;超时多半=探针未适配新停点层,归 probe适配批)。
|
||
# layout_probe=默认必跑(2026-10-15 建——分辨率几何断言,档位表是数据/断言是一份函数,
|
||
# 增删档位只改表内一行;用 content_scale_size 改逻辑视口,headless 下真生效——
|
||
# 旧 ui_layout_probe 用 get_window().size 在 headless 下不改变视口,其「1920 档恒定」
|
||
# 断言是永远为真的假断言,随该探针退役)。
|
||
# --full 段(迭代 19 卡① T3 收编 2026-10-08——重闸件):ui_probe/ui_tune_probe/t17_defer_probe/fx_probe
|
||
# 四件带断言探针收进(此前游离在一切闸外只能手敲);轻闸默认段断言面零删减零改动(--full 只增不改)。
|
||
# 渲染面归 render_gate.sh 正主(窗口化:shot_probe 七屏+fx_shot/board_shot 取证+棋盘 golden 逐像素)——
|
||
# 旧头注「shot_probe 禁入本闸……归真人目检」纪律句随三闸口径扩容改写退役(2026-10-07 用户授权=历史偷懒标记)。
|
||
# 用法: tools/probe_gate.sh [game目录] [--click] [--full] 缺省 game 目录=脚本所在仓库的 game/(worktree 内自动指向该 worktree)
|
||
# 依赖: Git Bash(coreutils timeout/taskkill) + PowerShell;Godot 路径可用环境变量 GODOT_BIN 覆盖。
|
||
set -u
|
||
G="${GODOT_BIN:-/d/code/godot/Godot_v4.7.2-stable_win64_console.exe}"
|
||
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
||
GAME=""; WITH_CLICK=0; WITH_FULL=0
|
||
for a in "$@"; do
|
||
case "$a" in
|
||
--click) WITH_CLICK=1 ;;
|
||
--full) WITH_FULL=1 ;;
|
||
*) GAME="$a" ;;
|
||
esac
|
||
done
|
||
GAME="${GAME:-$REPO/game}"
|
||
GAME_WIN="$(cygpath -m "$GAME")" # Godot --path 需 Windows 形式
|
||
TMP="$(mktemp -d /tmp/probegate_XXXXXX)"
|
||
trap 'rm -rf "$TMP"' EXIT
|
||
_rc=0
|
||
|
||
orphans() { # 列出命令行含本 game 路径的 Godot 进程 PID(console wrapper+真引擎都算)
|
||
printf "Get-CimInstance Win32_Process -Filter \"Name like 'Godot%%'\" | Where-Object { \$_.CommandLine -like '*%s*' } | ForEach-Object { \$_.ProcessId }\n" "$GAME_WIN" > "$TMP/k.ps1"
|
||
powershell -NoProfile -ExecutionPolicy Bypass -File "$(cygpath -w "$TMP/k.ps1")" 2>/dev/null | tr -d '\r'
|
||
}
|
||
kill_orphans() {
|
||
local p; for p in $(orphans); do taskkill //F //PID "$p" >/dev/null 2>&1; done
|
||
}
|
||
|
||
# 冷检出刷导入(.godot 不入 git,fresh worktree 首跑一次性付)
|
||
if [ ! -f "$GAME/.godot/uid_cache.bin" ]; then
|
||
echo "[probe_gate] 冷检出:editor --quit 刷导入(一次性,可到 1-2 分钟)"
|
||
timeout 300 "$G" --headless --path "$GAME_WIN" --editor --quit >/dev/null 2>&1
|
||
kill_orphans
|
||
fi
|
||
|
||
pre="$(orphans | wc -l | tr -d ' ')"
|
||
if [ "$pre" -gt 0 ]; then echo "[probe_gate] 预清本闸残留 Godot×$pre"; kill_orphans; fi
|
||
|
||
echo "======== ui_input_probe ========"
|
||
timeout 120 "$G" --headless --path "$GAME_WIN" res://smoke/ui_input_probe.tscn >"$TMP/uip.log" 2>&1
|
||
st=$?; kill_orphans
|
||
grep -E "PASS|FAIL|问题" "$TMP/uip.log" | head -12
|
||
if [ "$st" -eq 124 ] || [ "$st" -eq 137 ]; then
|
||
echo "[probe_gate] FAIL — ui_input_probe 超时挂死(exit=$st),孤儿已清"; _rc=1
|
||
elif ! grep -q "PASS" "$TMP/uip.log"; then
|
||
echo "[probe_gate] FAIL — ui_input_probe 未见 PASS(exit=$st)"; _rc=1
|
||
fi
|
||
|
||
echo "======== ui_struct_probe ========"
|
||
timeout 120 "$G" --headless --path "$GAME_WIN" res://smoke/ui_struct_probe.tscn >"$TMP/stp.log" 2>&1
|
||
st=$?; kill_orphans
|
||
grep -E "PASS|FAIL" "$TMP/stp.log" | head -12
|
||
if [ "$st" -eq 124 ] || [ "$st" -eq 137 ]; then
|
||
echo "[probe_gate] FAIL — ui_struct_probe 超时挂死(exit=$st),孤儿已清"; _rc=1
|
||
elif ! grep -q "PASS" "$TMP/stp.log"; then
|
||
echo "[probe_gate] FAIL — ui_struct_probe 未见 PASS(exit=$st)"; _rc=1
|
||
fi
|
||
|
||
echo "======== layout_probe(分辨率几何·两档) ========"
|
||
timeout 120 "$G" --headless --path "$GAME_WIN" res://smoke/layout_probe.tscn >"$TMP/lay.log" 2>&1
|
||
st=$?; kill_orphans
|
||
grep -E "PASS|FAIL" "$TMP/lay.log" | head -12
|
||
if [ "$st" -eq 124 ] || [ "$st" -eq 137 ]; then
|
||
echo "[probe_gate] FAIL — layout_probe 超时挂死(exit=$st),孤儿已清"; _rc=1
|
||
elif ! grep -q "PASS" "$TMP/lay.log"; then
|
||
echo "[probe_gate] FAIL — layout_probe 未见 PASS(exit=$st)"; _rc=1
|
||
fi
|
||
|
||
echo "======== ui_contrast_probe ========"
|
||
timeout 120 "$G" --headless --path "$GAME_WIN" res://smoke/ui_contrast_probe.tscn >"$TMP/ctp.log" 2>&1
|
||
st=$?; kill_orphans
|
||
grep -E "PASS|FAIL" "$TMP/ctp.log" | head -12
|
||
if [ "$st" -eq 124 ] || [ "$st" -eq 137 ]; then
|
||
echo "[probe_gate] FAIL — ui_contrast_probe 超时挂死(exit=$st),孤儿已清"; _rc=1
|
||
elif ! grep -q "PASS" "$TMP/ctp.log"; then
|
||
echo "[probe_gate] FAIL — ui_contrast_probe 未见 PASS(exit=$st)"; _rc=1
|
||
fi
|
||
|
||
if [ "$WITH_CLICK" -eq 1 ]; then
|
||
echo "======== ui_click_probe 行为走查 ========"
|
||
timeout 240 "$G" --headless --path "$GAME_WIN" res://smoke/ui_click_probe.tscn >"$TMP/clk.log" 2>&1
|
||
st=$?; kill_orphans
|
||
grep -E "click_probe" "$TMP/clk.log" | tail -4
|
||
if [ "$st" -eq 124 ] || [ "$st" -eq 137 ]; then
|
||
echo "[probe_gate] FAIL — ui_click_probe 超时(exit=$st):挂死或探针未适配新停点层(归 probe适配批),孤儿已清"; _rc=1
|
||
elif [ "$st" -ne 0 ]; then
|
||
echo "[probe_gate] FAIL — ui_click_probe 退出码=$st(done=false 或 问题>0)"; _rc=1
|
||
fi
|
||
fi
|
||
|
||
# ── --full 收编段(迭代 19 卡① T3 2026-10-08——四件带断言探针入闸;实测时长 ui_probe 17.4s/
|
||
# ui_tune_probe 49.6s/t17_defer_probe 63.0s/fx_probe 21.9s,timeout 120=2-7×余量) ──
|
||
if [ "$WITH_FULL" -eq 1 ]; then
|
||
echo "======== ui_probe(--full 收编) ========"
|
||
timeout 120 "$G" --headless --path "$GAME_WIN" res://smoke/ui_probe.tscn >"$TMP/flp.log" 2>&1
|
||
st=$?; kill_orphans
|
||
grep -E "\[ui_probe\]" "$TMP/flp.log" | tail -2
|
||
if [ "$st" -eq 124 ] || [ "$st" -eq 137 ]; then
|
||
echo "[probe_gate] FAIL — ui_probe 超时挂死(exit=$st),孤儿已清"; _rc=1
|
||
elif ! grep -q "PASS" "$TMP/flp.log"; then
|
||
echo "[probe_gate] FAIL — ui_probe 未见 PASS(exit=$st)"; _rc=1
|
||
fi
|
||
|
||
echo "======== ui_tune_probe(--full 收编) ========"
|
||
timeout 120 "$G" --headless --path "$GAME_WIN" res://smoke/ui_tune_probe.tscn >"$TMP/flt.log" 2>&1
|
||
st=$?; kill_orphans
|
||
grep -E "\[tune_probe\]" "$TMP/flt.log" | tail -2
|
||
if [ "$st" -eq 124 ] || [ "$st" -eq 137 ]; then
|
||
echo "[probe_gate] FAIL — ui_tune_probe 超时挂死(exit=$st),孤儿已清"; _rc=1
|
||
elif ! grep -q "PASS" "$TMP/flt.log"; then
|
||
echo "[probe_gate] FAIL — ui_tune_probe 未见 PASS(exit=$st)"; _rc=1
|
||
fi
|
||
|
||
echo "======== t17_defer_probe(--full 收编) ========"
|
||
timeout 120 "$G" --headless --path "$GAME_WIN" res://smoke/t17_defer_probe.tscn >"$TMP/fld.log" 2>&1
|
||
st=$?; kill_orphans
|
||
grep -E "\[t17_defer\]" "$TMP/fld.log" | tail -2
|
||
if [ "$st" -eq 124 ] || [ "$st" -eq 137 ]; then
|
||
echo "[probe_gate] FAIL — t17_defer_probe 超时挂死(exit=$st),孤儿已清"; _rc=1
|
||
elif ! grep -q "PASS" "$TMP/fld.log"; then
|
||
echo "[probe_gate] FAIL — t17_defer_probe 未见 PASS(exit=$st)"; _rc=1
|
||
fi
|
||
|
||
echo "======== fx_probe(--full 收编) ========"
|
||
timeout 120 "$G" --headless --path "$GAME_WIN" res://smoke/fx_probe.tscn >"$TMP/flx.log" 2>&1
|
||
st=$?; kill_orphans
|
||
grep -E "\[fx_probe\]" "$TMP/flx.log" | tail -2
|
||
if [ "$st" -eq 124 ] || [ "$st" -eq 137 ]; then
|
||
echo "[probe_gate] FAIL — fx_probe 超时挂死(exit=$st),孤儿已清"; _rc=1
|
||
elif ! grep -q "PASS" "$TMP/flx.log"; then
|
||
echo "[probe_gate] FAIL — fx_probe 未见 PASS(exit=$st)"; _rc=1
|
||
fi
|
||
fi
|
||
|
||
echo "======== 残留检查(批末闸) ========"
|
||
left="$(orphans | wc -l | tr -d ' ')"
|
||
total="$(tasklist 2>/dev/null | grep -ci '^Godot' || true)"
|
||
echo "本闸残留 Godot=$left(必须0) 全局 Godot 进程=$total(其他会话的仅报数不算账)"
|
||
if [ "$left" -ne 0 ]; then echo "[probe_gate] FAIL — 本闸残留未清零"; kill_orphans; _rc=1; fi
|
||
if [ "$_rc" -eq 0 ]; then echo "[probe_gate] 全绿"; else echo "[probe_gate] 存在 FAIL,见上"; fi
|
||
exit "$_rc"
|