feat(engine): live server visual tests and gauntlet snapshot replay
Add live server lifecycle to tests/run-visual (start/stop server per
scenario, parse LISTENING:{port}). Add MessagePack snapshot replay to
visual_capture.gd via Protocol.decode_snapshot() — exercises the full
client pipeline from wire bytes to rendered fog. Three replay scenarios
(hub_spawn, fog_theater, hub_after_movement) plus one live scenario
(fog_live_hub). Add gen_gauntlet_fixtures.rs to produce .msgpack fixtures
from the Gauntlet test world. Add max_diff_pct threshold to visual-diff.
Makefile: add fixtures-gauntlet target, fix build-client double-import,
preserve .godot cache in clean.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+24
-7
@@ -239,18 +239,22 @@ def compare(
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def load_tolerance(config_path: Path | None) -> int:
|
||||
"""Read tolerance from config JSON, return DEFAULT_TOLERANCE on failure."""
|
||||
def load_config(config_path: Path | None) -> dict:
|
||||
"""Read visual test config, return dict with tolerance and max_diff_pct."""
|
||||
if config_path is None:
|
||||
config_path = DEFAULT_CONFIG
|
||||
defaults = {"tolerance": DEFAULT_TOLERANCE, "max_diff_pct": 0.0}
|
||||
if not config_path.exists():
|
||||
return DEFAULT_TOLERANCE
|
||||
return defaults
|
||||
try:
|
||||
with open(config_path) as f:
|
||||
data = json.load(f)
|
||||
return int(data.get("tolerance", DEFAULT_TOLERANCE))
|
||||
return {
|
||||
"tolerance": int(data.get("tolerance", DEFAULT_TOLERANCE)),
|
||||
"max_diff_pct": float(data.get("max_diff_pct", 0.0)),
|
||||
}
|
||||
except (json.JSONDecodeError, ValueError, OSError):
|
||||
return DEFAULT_TOLERANCE
|
||||
return defaults
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -270,6 +274,12 @@ def main() -> int:
|
||||
default=None,
|
||||
help="Per-channel pixel tolerance (default: from config or 5)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--max-diff-pct",
|
||||
type=float,
|
||||
default=None,
|
||||
help="Max allowed diff percentage (default: from config or 0.0)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--diff-output",
|
||||
default=None,
|
||||
@@ -282,9 +292,11 @@ def main() -> int:
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Resolve tolerance: CLI > config > fallback
|
||||
# Resolve settings: CLI > config > fallback
|
||||
config_path = Path(args.config) if args.config else None
|
||||
tolerance = args.tolerance if args.tolerance is not None else load_tolerance(config_path)
|
||||
cfg = load_config(config_path)
|
||||
tolerance = args.tolerance if args.tolerance is not None else cfg["tolerance"]
|
||||
max_diff_pct = args.max_diff_pct if args.max_diff_pct is not None else cfg["max_diff_pct"]
|
||||
|
||||
# Read images
|
||||
try:
|
||||
@@ -322,6 +334,11 @@ def main() -> int:
|
||||
return 0
|
||||
|
||||
pct = diff_count / total * 100
|
||||
|
||||
if pct <= max_diff_pct:
|
||||
print(f"PASS: {diff_count} of {total} pixels differ ({pct:.1f}%, within {max_diff_pct}% threshold)")
|
||||
return 0
|
||||
|
||||
print(f"FAIL: {diff_count} of {total} pixels differ ({pct:.1f}%)")
|
||||
|
||||
if args.diff_output and diff_buf:
|
||||
|
||||
Reference in New Issue
Block a user