harness.py

Part of HexGL.

 1"""SceneRunner-driven smoke harness for HexGL.
 2
 3Validates: track builds, ship moves around the loop, race manager ticks,
 4checkpoint detection, and lap completion firing.
 5
 6Run with:
 7    uv run python examples/ports/hexgl/harness.py
 8"""
 9
10from __future__ import annotations
11
12import sys
13from pathlib import Path
14
15_PORT_DIR = Path(__file__).parent
16if str(_PORT_DIR) not in sys.path:
17    sys.path.insert(0, str(_PORT_DIR))
18
19from main import HexGLRoot  # noqa: E402
20
21from simvx.core.testing import SceneRunner  # noqa: E402
22
23
24def main() -> None:
25    runner = SceneRunner()
26    root = HexGLRoot()
27    runner.load(root)
28
29    # Tick a few frames so on_ready runs.
30    runner.advance_frames(5)
31    assert root.track is not None, "Track did not build"
32    assert root.ship is not None, "Ship not added"
33    assert root.race is not None, "Race manager not added"
34    assert len(root.track.checkpoints) == 6
35    assert len(root.track.boost_pads) == 3
36
37    # Leave the title screen, skip the countdown and start with a high speed;
38    # advance a long way and verify the ship's t parameter wraps and laps complete.
39    root.begin_race()
40    root.race.skip_countdown()
41    root.ship.speed = 70.0
42    runner.advance_frames(60 * 12)  # 12 seconds @ 60 fps
43
44    # The ship should have advanced (track total_length is ~600 m, speed 70 → ~12s/lap).
45    assert root.ship.t != 0.0, "Ship didn't move"
46    assert root.race.elapsed > 0.0, "Race elapsed didn't increment"
47
48    print("Harness: PASS")
49    print(f"  ship.t={root.ship.t:.3f}  speed={root.ship.speed:.1f}  lap={root.race.lap}")
50    print(f"  total_length={root.track.total_length:.1f}  laps_completed={len(root.race.lap_times)}")
51    print(f"  shield={root.ship.shield:.2f}  destroyed={root.ship.destroyed}")
52
53
54if __name__ == "__main__":
55    main()