Files
settled-reach/client/tests/test_step_canvas_viewer.gd
T
jpmschweitzerandClaude Fable 5 d28d24fd26 feat(ui): step-canvas map component — RTT terrain + stepped zoom (D-255, T-1182)
The two-layer client rebuild per D-255(a)(b)(e), replacing the
_canvas.scale continuous-zoom model with one viewer, one path, all six
rungs:

- step_canvas_protocol.gd: StepCanvasRequest/Response codec against
  the T-1181 wire contract — incl. the discovered png_bytes subtlety
  (rmp_serde without serde_bytes emits a msgpack int-array, not bin;
  decode repacks via PackedByteArray before load_png_from_buffer) and
  the extent-echo rule (read the server-clamped extent, never assume
  the requested one).
- step_canvas/ component: transport (six-rung ladder, cursor-anchored
  scroll steps, edge-scroll/WASD pan with re-request on edge crossing,
  hard reset-to-Global), RTT terrain layer (Image.set_pixel colorize
  per the c1 measured ruling, texture.update reuse on step-cross,
  NEAREST coarse / LINEAR fine per rung), unscaled screen-space
  annotation sibling (courses + settlement markers at literal px),
  in-memory LRU cache (Tier 1; T-1183 layers the disk tiers beneath),
  request lifecycle (pending retry, staleness gate, extent echo).
- Full _canvas.scale retirement in the same change: the zoom-scaled
  canvas model, the _zs compensation family, select_rung /
  MAX_COVERAGE_M / compute_tile_grid, the orbital-mosaic-vs-window
  two-path split, _view_zoom/_canonical_fit_zoom — 10 source files
  deleted; their 14 test suites deleted with them (T-1157 dead-goldens
  rule; replacement visual-capture coverage is re-scoped T-1157).
- Surviving surfaces kept per the ticket: atlas_window_cache.gd's LRU
  shape (the ticket's named file atlas_window_tile_set.gd was the
  retiring orchestrator; the real LRU shape lives in
  atlas_window_cache.gd — cited in step_canvas_cache.gd), overlay
  colors, legend/overlay-bar chrome, AtlasViewer descend geometry.

Determinism boundary per D-255(e): the client interpolates only within
the closed server-supplied input set. 7 new gdUnit suites (164 cases)
incl. a real extent-echo bug caught by its own test during
implementation. Full client suite green (exit 0) with the live-gated
suites running against a worktree server build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 08:53:48 +02:00

111 lines
4.6 KiB
GDScript

## T-1182 tests: StepCanvasViewer — the rung transport state machine
## (enter() lands on the Global opener, scroll steps through the ladder,
## overlay toggle wiring) and RegionalScreen's re-entry guard against the
## new viewer. test_mode (SimBridge default outside SR_LIVE=1) means
## request_step_canvas() is a silent no-op — these tests exercise
## client-side state only, matching test_atlas_view_api.gd's own
## no-live-server convention for viewer-internals tests.
class_name TestStepCanvasViewer
extends GdUnitTestSuite
const StepCanvasTransport := preload("res://ui/implant/apps/atlas/step_canvas/step_canvas_transport.gd")
func test_enter_lands_on_the_global_opener() -> void:
var v: StepCanvasViewer = auto_free(StepCanvasViewer.new())
add_child(v)
v.enter({"body_id": "GJ380c", "body_radius_km": 6238.4}, {})
assert_str(v.get_held_rung()).is_equal(StepCanvasTransport.RUNG_GLOBAL)
func test_get_body_id_reflects_the_entered_body() -> void:
var v: StepCanvasViewer = auto_free(StepCanvasViewer.new())
add_child(v)
assert_str(v.get_body_id()).is_equal("")
v.enter({"body_id": "GJ380c", "body_radius_km": 6238.4}, {})
assert_str(v.get_body_id()).is_equal("GJ380c")
## Scrolling one notch descends the ladder — cursor-anchored, so a cursor
## position must be supplied; the rung index advances by exactly one.
func test_scroll_rung_descends_one_notch() -> void:
var v: StepCanvasViewer = auto_free(StepCanvasViewer.new())
add_child(v)
v.enter({"body_id": "GJ380c", "body_radius_km": 6238.4}, {})
v._scroll_rung(1, Vector2(400.0, 300.0))
assert_str(v.get_held_rung()).is_equal(StepCanvasTransport.RUNG_REGION)
func test_scroll_rung_clamps_at_the_deepest_rung() -> void:
var v: StepCanvasViewer = auto_free(StepCanvasViewer.new())
add_child(v)
v.enter({"body_id": "GJ380c", "body_radius_km": 6238.4}, {})
for _i in range(10):
v._scroll_rung(1, Vector2(400.0, 300.0))
assert_str(v.get_held_rung()).is_equal(StepCanvasTransport.RUNG_CHUNK)
func test_reset_to_global_returns_from_a_deep_rung() -> void:
var v: StepCanvasViewer = auto_free(StepCanvasViewer.new())
add_child(v)
v.enter({"body_id": "GJ380c", "body_radius_km": 6238.4}, {})
v._scroll_rung(1, Vector2(400.0, 300.0))
v._scroll_rung(1, Vector2(400.0, 300.0))
v._reset_to_global()
assert_str(v.get_held_rung()).is_equal(StepCanvasTransport.RUNG_GLOBAL)
func test_overlay_visibility_defaults_to_off_for_every_toggle() -> void:
var v: StepCanvasViewer = auto_free(StepCanvasViewer.new())
add_child(v)
for def: Dictionary in v.get_overlay_defs():
assert_bool(v.is_overlay_visible(def["id"])).is_false()
func test_set_overlay_visible_updates_state() -> void:
var v: StepCanvasViewer = auto_free(StepCanvasViewer.new())
add_child(v)
v.set_overlay_visible("gen_dw_temp", true)
assert_bool(v.is_overlay_visible("gen_dw_temp")).is_true()
func test_set_overlay_visible_unknown_id_is_a_no_op() -> void:
var v: StepCanvasViewer = auto_free(StepCanvasViewer.new())
add_child(v)
v.set_overlay_visible("not_a_real_overlay", true)
assert_bool(v.is_overlay_visible("not_a_real_overlay")).is_false()
# =============================================================================
# RegionalScreen re-entry guard (BUG 2 lineage, carried forward from the
# retired AtlasWindowViewer-era regression) — now against StepCanvasViewer.
# =============================================================================
func test_regional_screen_repeat_enter_for_the_same_body_is_a_no_op() -> void:
var screen: RegionalScreen = auto_free(RegionalScreen.new())
add_child(screen)
var body: Dictionary = {"body_id": "GJ380c", "body_radius_km": 6238.4}
screen.enter({"body": body, "system": {}})
screen._viewer._scroll_rung(1, Vector2(400.0, 300.0))
assert_str(screen._viewer.get_held_rung()).is_equal(StepCanvasTransport.RUNG_REGION)
screen.enter({"body": body, "system": {}})
# A no-op re-entry must NOT reset the held rung back to Global — that
# would be the exact "repeat enter tears down in-flight state" class the
# retired viewer's own cold-start guard existed to prevent.
assert_str(screen._viewer.get_held_rung()).override_failure_message(
"a repeat enter() for the SAME body must not reset the held rung"
).is_equal(StepCanvasTransport.RUNG_REGION)
func test_regional_screen_different_body_still_re_enters() -> void:
var screen: RegionalScreen = auto_free(RegionalScreen.new())
add_child(screen)
screen.enter({"body": {"body_id": "GJ380c", "body_radius_km": 6238.4}, "system": {}})
screen.enter({"body": {"body_id": "OtherBody", "body_radius_km": 100.0}, "system": {}})
assert_str(screen._viewer.get_body_id()).is_equal("OtherBody")