test(client): T-1146 — full-suite isolation for the occlusion-gate suites

Unit suite: pin facing to North via the T-1088 facing_angle_provider seam.
D-054 makes movement facing-relative, and _update_facing_from_mouse() derives
facing from engine/session globals (viewport mouse position vs the
GameState.player_position anchor) — in full-suite order 'press W' resolved to
MOVE_NORTHWEST (the gate failure at line 187).

Live suite, three independent leaks from the shared autoloads, each of which
passed in isolation and failed in gate order:
- snapshot starvation: poll_snapshot() is consume-and-clear; a node leaked
  into the gdUnit tree by an earlier suite steals every snapshot during our
  awaits. Capture snapshot_received (fires on every consume, ours or theirs)
  alongside a direct poll + explicit SimBridge._process pump.
- tick poisoning: SimBridge left test_mode=true + CONNECTED lets the leaked
  poller apply TestHarness mock snapshots (~tick 15+) into
  GameState.current_tick during our spawn awaits; the fresh server's own
  ticks (1..5) then stamp backward and its InputQueue panics ('tick ordering
  violated', last=16 new=3). Neutralize SimBridge before the first await,
  zero current_tick, clear _last_snapshot/_outbound_buffer both directions.
- crash-on-assert-failure: gdUnit asserts don't halt; guard the player-entity
  dereferences so a missing Player reports cleanly instead of a runtime error.

Full suite: 3368/3368.
This commit is contained in:
2026-07-21 19:49:42 +02:00
parent cd52fe8843
commit 4af3dd2607
2 changed files with 87 additions and 4 deletions
@@ -25,7 +25,17 @@ func before_test() -> void:
InputMapper._gameplay_occluded = false
InputMapper._suppress_move_until_release = false
InputMapper._last_move_msec = 0
InputMapper._last_sent_octant = InputMapper.facing_octant
# D-054 movement is facing-relative, and _update_facing_from_mouse() derives
# facing from engine/session globals (viewport mouse position vs the
# GameState.player_position screen anchor under the live canvas transform) at
# the top of every _process. Earlier suites move all three, so in full-suite
# order "press W" can resolve to a diagonal (gate run: MOVE_NORTHWEST).
# Pin facing to North through the T-1088 provider seam so poll assertions
# are order-independent.
InputMapper.facing_angle_provider = func() -> float: return -PI / 2.0
InputMapper.facing_angle = -PI / 2.0
InputMapper.facing_octant = "North"
InputMapper._last_sent_octant = "North"
_release_all_movement_keys()
@@ -33,6 +43,7 @@ func after_test() -> void:
InputMapper.input_queue.clear()
InputMapper._gameplay_occluded = false
InputMapper._suppress_move_until_release = false
InputMapper.facing_angle_provider = Callable()
_release_all_movement_keys()
# Restore HudGroups to a clean gameplay state regardless of what a test left
# behind — mirrors test_implant_app_lifecycle.gd's after_test convention.