Files
settled-reach/client/tests/test_atlas_window_tile_set.gd
T
jpmschweitzer 6195fd28b3 fix(client): T-1153/T-1152 round 3 — unified rung coverage model, reselect clamp mirror, orbital tile-set mosaic
Bug A (zoom reselection dead): select_rung() redesigned from the two-gate
split to a unified per-rung coverage-ceiling walk (MAX_COVERAGE_M,
finest-first) — which also restores District as a reachable rung
(33-131km extent band; the two-gate model had made it structurally
unreachable). Plus the THIRD instance of the PR #191 C1 clamp-mirror bug
class: _maybe_reselect_rung fed the old rung's _held_n raw into the new
rung's request — the server clamped, the held value didn't, every
cross-rung response stale-dropped. Re-clamped at the boundary; the
leaving_tile_mode flag also fixes a stale request-granularity edge case.

Bug B (orbital = whole body): genuine tile-set model. compute_tile_grid
composes ceil-divided Region-tile centers (canonicalized column wrap,
row clamp, Dictionary-set dedup; Lendel = 3x2 = 6 tiles);
AtlasWindowTileSet owns one AtlasWindowRequest per tile (reusing all
tested request/cache/debounce machinery), fans responses out, per-tile
tile_ready — genuinely progressive; the overlay draws arrived tiles as a
mosaic under the one view transform. enter_orbital branches to tile mode
when >1 tile; single-window path unchanged below the coverage ceiling.
E/W wrap at tile seams live-confirmed (col -6400 wraps to 12739). Pole-
row dedup verified correct but flagged honestly as currently-unreachable
defensive logic (no real radius triggers row collision).

All fixes + fallout verified via revert/confirm-fails/restore. Targeted
suites 256/256; gdlint clean; new 20-test tile-set suite.
2026-07-22 13:03:38 +02:00

205 lines
8.4 KiB
GDScript

## T-1153, live round 3 (Jeroen's ruling, design doc §4): tests for
## AtlasWindowTileSet — the orbital rest-state multi-window mosaic
## orchestration. Same hand-built-response-dict conventions as
## test_atlas_window_request.gd/test_atlas_zoom_ladder.gd; this file is
## about the ORCHESTRATION (N tiles, progressive per-tile arrival,
## teardown), not the tile-grid MATH (already covered directly against
## AtlasWindowGeometry.compute_tile_grid() in test_atlas_window_geometry.gd).
class_name TestAtlasWindowTileSet
extends GdUnitTestSuite
const AtlasWindowTileSet := preload("res://ui/implant/apps/atlas/atlas_window_tile_set.gd")
const AtlasWindowRequest := preload("res://ui/implant/apps/atlas/atlas_window_request.gd")
static func _mock_window(center: Vector2i, n: int) -> Dictionary:
return {
"center": [center.x, center.y],
"n": n,
"granularity_v2": "Region",
"morphology": PackedByteArray([1, 2, 3, 4]),
"elev_q": PackedByteArray([10, 20, 30, 40]),
"temp_dc": [0, 0, 0, 0],
"moisture_q": PackedByteArray([0, 0, 0, 0]),
"vegetation": PackedByteArray([0, 0, 0, 0]),
"glaciation": PackedByteArray([0, 0, 0, 0]),
}
static func _mock_response(body_id: String, window: Variant) -> Dictionary:
return {"body_id": body_id, "status": "Ready", "district_window": window}
func _make_tile_set() -> Variant:
var owner_stub := RefCounted.new()
var ts = auto_free(AtlasWindowTileSet.new(owner_stub))
add_child(ts)
return ts
# =============================================================================
# enter() — tile grid computation + one request per tile
# =============================================================================
## enter() on a real, tiling-sized body must produce the SAME tile count
## compute_tile_grid() would — 6 for GJ380c/Lendel, the coordinator's own
## live-round number.
func test_enter_produces_the_expected_tile_count_for_lendel() -> void:
var ts = _make_tile_set()
ts.enter("GJ380c", 6238.4)
assert_int(ts.get_tile_count()).is_equal(6)
assert_bool(ts.is_multi_tile()).is_true()
## A tiny (non-tiling) body produces exactly ONE tile — the degenerate case
## compute_tile_grid() itself already covers; this confirms the ORCHESTRATION
## (not just the grid math) handles it without crashing or requesting zero
## tiles.
func test_enter_tiny_body_produces_one_tile() -> void:
var ts = _make_tile_set()
ts.enter("TinyBody", 50.0)
assert_int(ts.get_tile_count()).is_equal(1)
assert_bool(ts.is_multi_tile()).is_false()
## Every tile must start with a null window (nothing has arrived yet) and
## the tile set must not report "fully arrived" before any response lands.
func test_enter_all_tiles_start_unarrived() -> void:
var ts = _make_tile_set()
ts.enter("GJ380c", 6238.4)
for tile: Dictionary in ts.get_tiles():
assert_that(tile["window"]).is_null()
assert_bool(ts.is_fully_arrived()).is_false()
## An empty tile set (never entered) must not report "fully arrived" either
## — an empty AND-over-nothing must not vacuously read true.
func test_empty_tile_set_is_not_fully_arrived() -> void:
var ts = _make_tile_set()
assert_bool(ts.is_fully_arrived()).is_false()
# =============================================================================
# Progressive per-tile arrival (design doc §4: "with visible refinement as
# tiles complete") — each tile's response is independent of every other's.
# =============================================================================
## Delivering ONE tile's response must populate ONLY that tile's window,
## leaving every other tile still null — the direct "progressive, not
## block-on-all" regression.
func test_one_tile_arriving_does_not_affect_the_others() -> void:
var ts = _make_tile_set()
ts.enter("GJ380c", 6238.4)
var tiles: Array = ts.get_tiles()
var first_center: Vector2i = tiles[0]["center"]
var window: Dictionary = _mock_window(first_center, AtlasWindowRequest.SERVER_DISTRICT_WINDOW_MAX_N_REGION)
SimBridge.atlas_layers_received.emit(_mock_response("GJ380c", window))
var updated_tiles: Array = ts.get_tiles()
assert_that(updated_tiles[0]["window"]).override_failure_message(
"the tile whose response arrived must have its window populated"
).is_equal(window)
for i in range(1, updated_tiles.size()):
assert_that(updated_tiles[i]["window"]).override_failure_message(
"tile %d must still be unarrived — only tile 0's response was delivered" % i
).is_null()
## tile_ready must fire with the INDEX of the tile that actually arrived —
## the viewer/overlay needs this to know WHICH tile to redraw, not just
## "something changed".
func test_tile_ready_signal_fires_with_the_correct_index() -> void:
var ts = _make_tile_set()
ts.enter("GJ380c", 6238.4)
var received_indices: Array = []
ts.tile_ready.connect(func(index: int) -> void: received_indices.append(index))
var tiles: Array = ts.get_tiles()
var second_center: Vector2i = tiles[1]["center"]
var window: Dictionary = _mock_window(second_center, AtlasWindowRequest.SERVER_DISTRICT_WINDOW_MAX_N_REGION)
SimBridge.atlas_layers_received.emit(_mock_response("GJ380c", window))
assert_int(received_indices.size()).is_equal(1)
assert_int(received_indices[0]).is_equal(1)
## Delivering EVERY tile's response must flip is_fully_arrived() to true —
## the mosaic-complete signal the viewer/legend chrome can use.
func test_all_tiles_arriving_flips_fully_arrived() -> void:
var ts = _make_tile_set()
ts.enter("GJ380c", 6238.4)
var tiles: Array = ts.get_tiles()
for tile: Dictionary in tiles:
var window: Dictionary = _mock_window(
tile["center"], AtlasWindowRequest.SERVER_DISTRICT_WINDOW_MAX_N_REGION
)
SimBridge.atlas_layers_received.emit(_mock_response("GJ380c", window))
assert_bool(ts.is_fully_arrived()).override_failure_message(
"once every tile's response has arrived, the tile set must report fully arrived"
).is_true()
## A response for a body the tile set is NOT currently showing (a stale
## response from a body the player has since navigated away from) must not
## be adopted by any tile — the SAME body_id staleness guard every other
## AtlasWindowRequest-based path already relies on (this is inherited for
## free since each tile IS an AtlasWindowRequest, but pinned here as an
## orchestration-level regression too).
func test_response_for_a_different_body_is_ignored() -> void:
var ts = _make_tile_set()
ts.enter("GJ380c", 6238.4)
var tiles: Array = ts.get_tiles()
var window: Dictionary = _mock_window(
tiles[0]["center"], AtlasWindowRequest.SERVER_DISTRICT_WINDOW_MAX_N_REGION
)
SimBridge.atlas_layers_received.emit(_mock_response("GJ_wrong_body", window))
assert_that(ts.get_tiles()[0]["window"]).is_null()
# =============================================================================
# Teardown — re-entering (a fresh body, or the same body again) must not
# leave stale tile request nodes wired up.
# =============================================================================
## Calling enter() a SECOND time (e.g. re-entering the orbital frame, or
## switching to a different body) must replace the tile set entirely — the
## OLD tiles' indices/centers must not linger.
func test_second_enter_replaces_the_tile_set() -> void:
var ts = _make_tile_set()
ts.enter("GJ380c", 6238.4)
var first_count: int = ts.get_tile_count()
assert_int(first_count).is_equal(6)
ts.enter("TinyBody", 50.0)
assert_int(ts.get_tile_count()).override_failure_message(
"a second enter() must fully replace the tile set, not append to it"
).is_equal(1)
## A response matching an OLD tile set's (body, center) — arriving AFTER a
## second enter() has already torn it down — must not be adopted (or crash):
## the old tile's AtlasWindowRequest node is queue_free()'d, and _tiles no
## longer references it, so a stale signal (if it could somehow still fire)
## has no live entry left to update.
func test_stale_response_after_second_enter_does_not_crash_or_leak() -> void:
var ts = _make_tile_set()
ts.enter("GJ380c", 6238.4)
var old_tiles: Array = ts.get_tiles()
var old_center: Vector2i = old_tiles[0]["center"]
ts.enter("GJ380c", 50.0) # same body_id, different (tiny) radius -> different tile grid
# A response shaped like it's answering the OLD tile set's first tile —
# must not crash, and must not corrupt the NEW tile set's single tile.
var stale_window: Dictionary = _mock_window(
old_center, AtlasWindowRequest.SERVER_DISTRICT_WINDOW_MAX_N_REGION
)
SimBridge.atlas_layers_received.emit(_mock_response("GJ380c", stale_window))
assert_int(ts.get_tile_count()).is_equal(1)