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.
This commit is contained in:
2026-07-22 13:03:38 +02:00
parent 11f7927f24
commit 6195fd28b3
8 changed files with 1330 additions and 533 deletions
+189 -110
View File
@@ -268,84 +268,75 @@ func test_pole_wall_rows_half_matches_canonicalize_rows_half() -> void:
# =============================================================================
# T-1153: select_rung() — the §5 rung-selection rule, split into TWO tests
# per select_rung()'s own doc: a COVERAGE ceiling decides Region (can a
# District window even span this much world), and the `2x` visual-tolerance
# rule (design doc §5: "select the coarsest rung whose cell spacing <=
# 2*(E/C)") decides District vs. Quarter for whatever's under that ceiling.
# T-1153: select_rung() — REDESIGNED (live round 3 finding) per-rung
# single-window COVERAGE CEILING model, superseding the original
# `2x`-visual-tolerance-only reading of design doc §5. Select the FINEST
# rung whose own single-window coverage ceiling (MAX_COVERAGE_M) still
# covers the current world extent: Quarter <= 32,768 m; District <=
# 131,072 m; Region otherwise (including tiled coverage beyond its own
# single-window ceiling, a viewer-level concern — see select_rung()'s own
# doc for the full derivation and why this REPLACES the earlier two-gate
# design entirely, not just patches it).
# =============================================================================
## A tight sample spacing (deep zoom-in — small E over a large C) must select
## Quarter (512 m), the finest legal rung — 2*(E/C) is far below District's
## 2,048 m spacing at this ratio.
func test_select_rung_picks_quarter_at_a_tight_sample_spacing() -> void:
# E=2000m over C=1000px -> sample spacing 2 m/px -> threshold 4 m. Even
# Quarter (512 m) is coarser than the threshold, so select_rung() falls
# through to the FINEST legal rung (its own documented fallback) rather
# than returning something even finer that doesn't exist — Quarter.
## Deep zoom-in (a tiny extent) selects Quarter — comfortably under its own
## 32,768 m ceiling.
func test_select_rung_picks_quarter_well_under_its_ceiling() -> void:
var rung: String = AtlasWindowGeometry.select_rung(2000.0, 1000.0)
assert_str(rung).is_equal("Quarter")
## A sample spacing that satisfies BOTH District's own `2x` band AND the
## coverage ceiling selects District — the coarsest rung whose spacing still
## satisfies the fine-end rule, without exceeding what a District window can
## physically cover.
func test_select_rung_picks_district_at_a_moderate_sample_spacing() -> void:
# E=120,000m (under the 64*2048=131,072m coverage ceiling) over C=100px ->
# threshold = 2*120000/100 = 2,400m — satisfies District's 2,048m spacing.
var rung: String = AtlasWindowGeometry.select_rung(120_000.0, 100.0)
## An extent past Quarter's own ceiling but under District's selects
## District — the finest rung that can still cover it in one window.
func test_select_rung_picks_district_between_the_two_ceilings() -> void:
# 60,000 m is past Quarter's 32,768 m ceiling but well under District's
# 131,072 m one.
var rung: String = AtlasWindowGeometry.select_rung(60_000.0, 100.0)
assert_str(rung).is_equal("District")
## An extent past the COVERAGE ceiling (more world than a District window can
## physically span, regardless of how generous the visual tolerance would
## otherwise be) must select Region — the coverage test, not the `2x` visual
## one, is what decides this (select_rung()'s own doc: "the coverage ceiling
## wins whenever the two disagree").
func test_select_rung_picks_region_past_the_coverage_ceiling() -> void:
# E = full Earth-like circumference (~40,075 km) — far past the
# 64*2048=131,072m District coverage ceiling regardless of canvas_px.
## An extent past BOTH Quarter's and District's ceilings selects Region —
## neither finer rung's single window can cover this much world.
func test_select_rung_picks_region_past_both_finer_ceilings() -> void:
var rung: String = AtlasWindowGeometry.select_rung(40_075_264.0, 1920.0)
assert_str(rung).is_equal("Region")
## Exactly AT the coverage ceiling (E == 64*2048 = 131,072m) must still
## select District if the `2x` band also agrees — the ceiling is `>`, not
## `>=`, so the boundary value itself stays under District's own test.
func test_select_rung_coverage_ceiling_boundary_stays_district() -> void:
## Exactly AT Quarter's own ceiling (32,768 m) must still select Quarter —
## the rule is `<=`, not `<`.
func test_select_rung_quarter_ceiling_boundary_is_inclusive() -> void:
var rung: String = AtlasWindowGeometry.select_rung(32_768.0, 100.0)
assert_str(rung).is_equal("Quarter")
## One metre past Quarter's ceiling must flip to District — confirms the
## ceiling bites right at its own boundary, not one cell short of it.
func test_select_rung_one_past_quarter_ceiling_is_district() -> void:
var rung: String = AtlasWindowGeometry.select_rung(32_769.0, 100.0)
assert_str(rung).is_equal("District")
## Exactly AT District's own ceiling (131,072 m) must still select District.
func test_select_rung_district_ceiling_boundary_is_inclusive() -> void:
var rung: String = AtlasWindowGeometry.select_rung(131_072.0, 100.0)
assert_str(rung).is_equal("District")
## One metre past the coverage ceiling must flip to Region — confirms the
## ceiling actually bites right at its own boundary, not one district-window
## short of it.
func test_select_rung_one_past_the_coverage_ceiling_is_region() -> void:
## One metre past District's ceiling must flip to Region.
func test_select_rung_one_past_district_ceiling_is_region() -> void:
var rung: String = AtlasWindowGeometry.select_rung(131_073.0, 100.0)
assert_str(rung).is_equal("Region")
## Exactly AT District's `2x` threshold (spacing_m == 2*(E/C)) must select
## District, not the next-finer rung — the rule is `<=`, not `<`.
func test_select_rung_district_threshold_boundary_is_inclusive() -> void:
# District spacing = 2048 m. Choose E/C such that 2*(E/C) == 2048 exactly:
# E=1024, C=1.0 -> E/C=1024 -> threshold=2048. E=1024 is also comfortably
# under the coverage ceiling (131,072), so the `2x` test is what's
# actually being exercised here.
var rung: String = AtlasWindowGeometry.select_rung(1024.0, 1.0)
assert_str(rung).is_equal("District")
## Degenerate canvas_px (<=0, an unlaid-out viewport) must fall back to the
## FINEST rung, never crash or pick the coarsest by dividing by zero — the
## documented "under-resolve is the safe failure direction" disposition (and
## must be checked BEFORE the coverage ceiling could otherwise route a
## degenerate small extent toward Region by accident).
func test_select_rung_degenerate_canvas_px_falls_back_to_finest() -> void:
var rung: String = AtlasWindowGeometry.select_rung(1000.0, 0.0)
assert_str(rung).is_equal("Quarter")
## canvas_px is unused by the coverage rule (kept for signature stability,
## see select_rung()'s own doc) — degenerate/zero values must not change the
## selected rung at all, unlike the old `2x`-tolerance design's special-cased
## fallback.
func test_select_rung_canvas_px_does_not_affect_selection() -> void:
var with_real_canvas: String = AtlasWindowGeometry.select_rung(2000.0, 1000.0)
var with_zero_canvas: String = AtlasWindowGeometry.select_rung(2000.0, 0.0)
assert_str(with_zero_canvas).is_equal(with_real_canvas)
## spacing_for_rung() is select_rung()'s inverse lookup — pin the three known
@@ -363,16 +354,26 @@ func test_spacing_for_rung_unknown_tag_falls_back_to_district() -> void:
assert_float(AtlasWindowGeometry.spacing_for_rung("Nonsense")).is_equal_approx(2048.0, 0.001)
## The exact scenario that surfaced the coverage-vs-visual-tolerance
## distinction (live-testing enter_orbital()'s own fit zoom): a whole
## Earth-like body's circumference (~40,075 km, matching
## AtlasDescendGeometry.district_extent()'s own cols*DISTRICT_M for
## radius=6371km) fitted to a 1920px-wide viewport at CELL_PIXEL_SIZE=16 must
## select Region — this is the direct regression guard for the bug this
## implementation found and fixed (an earlier version of select_rung()
## selected District here, which would have meant the canonical orbital
## frame requests a District-tier derive spanning an entire planet — the
## exact R1-catastrophe cost scenario the design doc §4 rejects).
## MAX_COVERAGE_M's three values, pinned directly against the formulas
## select_rung()'s own doc derives them from — a regression guard
## independent of select_rung()'s own boundary tests above, so a future
## accidental edit to the constants table itself (not just the selection
## logic) is caught here too.
func test_max_coverage_m_matches_derived_formulas() -> void:
assert_float(AtlasWindowGeometry.MAX_COVERAGE_M["Quarter"]).is_equal_approx(32_768.0, 0.001)
assert_float(AtlasWindowGeometry.MAX_COVERAGE_M["District"]).is_equal_approx(131_072.0, 0.001)
assert_float(AtlasWindowGeometry.MAX_COVERAGE_M["Region"]).is_equal_approx(13_107_200.0, 0.001)
## The exact scenario that surfaced the original design flaw
## (live-testing enter_orbital()'s own fit zoom): a whole Earth-like body's
## circumference (~40,075 km, matching AtlasDescendGeometry.district_extent()'s
## own cols*DISTRICT_M for radius=6371km) fitted to a 1920px-wide viewport at
## CELL_PIXEL_SIZE=16 must select Region — the direct regression guard for
## the bug an early version of select_rung() had (picking District here,
## which would have meant the canonical orbital frame requests a
## District-tier derive spanning an entire planet — the exact R1-catastrophe
## cost scenario the design doc §4 rejects).
func test_select_rung_at_orbital_fit_zoom_selects_region() -> void:
var radius_km := 6371.0
var extent: Dictionary = AtlasDescendGeometry.district_extent(radius_km)
@@ -390,50 +391,29 @@ func test_select_rung_at_orbital_fit_zoom_selects_region() -> void:
).is_equal("Region")
## Pinned capture-resolution boundary numbers (1600x900, the coordinator's
## requested eyeball-capture viewport) — a live executable regression guard
## for select_rung()'s own doc's worked example. Region releases District's
## coverage ceiling at _view_zoom ~= 1.5625; District's own `2x` band edge
## sits at _view_zoom ~= 0.125 — i.e. BELOW (not above) the coverage-ceiling
## crossing, confirming the two never overlap at this (or any real) canvas
## size — see select_rung()'s "Tuning knobs" paragraph for what would need
## to change (DISTRICT_WINDOW_MAX_N, a server-side wire-budget change) to
## open a real District band.
func test_select_rung_1600x900_region_district_boundary_zoom() -> void:
var viewport := Vector2(1600.0, 900.0)
var canvas_px: float = maxf(viewport.x, viewport.y)
var boundary_zoom := 1.5625
var just_inside: float = AtlasWindowGeometry.world_extent_m(
CELL_PIXEL_SIZE, boundary_zoom * 1.001, viewport
)
var just_outside: float = AtlasWindowGeometry.world_extent_m(
CELL_PIXEL_SIZE, boundary_zoom * 0.999, viewport
)
assert_str(AtlasWindowGeometry.select_rung(just_inside, canvas_px)).override_failure_message(
"zoomed IN past ~1.5625 at 1600x900 must have released the Region coverage ceiling"
).is_not_equal("Region")
assert_str(AtlasWindowGeometry.select_rung(just_outside, canvas_px)).override_failure_message(
"zoomed OUT past ~1.5625 at 1600x900 must still be under the Region coverage ceiling"
).is_equal("Region")
func test_select_rung_1600x900_district_quarter_boundary_zoom_confirms_no_overlap() -> void:
var viewport := Vector2(1600.0, 900.0)
var canvas_px: float = maxf(viewport.x, viewport.y)
var boundary_zoom := 0.125
var just_inside: float = AtlasWindowGeometry.world_extent_m(
CELL_PIXEL_SIZE, boundary_zoom * 1.001, viewport
)
var just_outside: float = AtlasWindowGeometry.world_extent_m(
CELL_PIXEL_SIZE, boundary_zoom * 0.999, viewport
)
# Both sides of the District/Quarter `2x`-band boundary read "Region" at
# 1600x900, NOT "District" — confirming the coverage ceiling (which
# releases at zoom~=1.5625, far above this boundary) has already forced
# Region long before the `2x` band's own edge is reached. This is the
# literal "no overlap" finding, pinned as an executable assertion.
assert_str(AtlasWindowGeometry.select_rung(just_inside, canvas_px)).is_equal("Region")
assert_str(AtlasWindowGeometry.select_rung(just_outside, canvas_px)).is_equal("Region")
## **Live round 3 regression, the direct fix target:** at 1600x900 (the
## coordinator's capture viewport), zooming IN from the orbital fit all the
## way to Quarter's own ceiling must pass through District along the way —
## a wheel-zoom gesture crossing world_extent_m from Region's territory down
## to Quarter's must select District for SOME real span of extent in
## between, not skip straight from Region to Quarter (the exact "money shot"
## the coordinator wants capture-worthy: a visible SHARPEN in place, not a
## jump).
func test_select_rung_district_is_reachable_between_region_and_quarter() -> void:
# An extent comfortably between District's and Quarter's ceilings (e.g.
# the midpoint) must select District — proving the band is non-empty,
# unlike the old two-gate design where it was empty by construction at
# every real viewport (see git history / the coordinator's live-round
# finding for the retired analysis).
var midpoint: float = (
(AtlasWindowGeometry.MAX_COVERAGE_M["Quarter"] as float)
+ (AtlasWindowGeometry.MAX_COVERAGE_M["District"] as float)
) * 0.5
var rung: String = AtlasWindowGeometry.select_rung(midpoint, 1600.0)
assert_str(rung).override_failure_message(
"District must be reachable between Quarter's and District's own"
+ " coverage ceilings — the redesigned rule must not skip it"
).is_equal("District")
# =============================================================================
@@ -598,3 +578,102 @@ func test_edge_scroll_direction_points_west_near_left_edge() -> void:
)
assert_float(direction.x).is_less(0.0)
assert_float(direction.y).is_equal_approx(0.0, 0.001)
# =============================================================================
# T-1153, live round 3 (Jeroen's ruling, design doc §4): compute_tile_grid()
# — the orbital rest state's multi-window mosaic.
# =============================================================================
## The exact live-round scenario: GJ380c/Lendel (radius 6238.4 km) needs a
## 3x2 = 6-tile grid — the coordinator's own estimate, confirmed here as an
## executable regression.
func test_compute_tile_grid_lendel_produces_six_tiles() -> void:
var tiles: Array = AtlasWindowGeometry.compute_tile_grid(6238.4)
assert_int(tiles.size()).override_failure_message(
"GJ380c/Lendel must tile into 3x2=6 windows, matching the coordinator's own"
+ " live-round finding (13,107.2 km single-window coverage vs. 39,198 km"
+ " circumference)"
).is_equal(6)
## A tiny body whose whole circumference fits in ONE Region window's
## coverage ceiling must produce exactly ONE tile — tiling degenerates
## gracefully to the pre-existing single-window behavior when it isn't
## actually needed.
func test_compute_tile_grid_tiny_body_produces_one_tile() -> void:
# radius small enough that circumference << MAX_COVERAGE_M["Region"]
# (13,107,200 m) — a few hundred km radius comfortably qualifies.
var tiles: Array = AtlasWindowGeometry.compute_tile_grid(50.0)
assert_int(tiles.size()).is_equal(1)
assert_that(tiles[0]).is_equal(Vector2i.ZERO)
## A no-radius body (tiny test body) must produce exactly one tile at the
## canonical origin — matching enter_orbital()'s own no-radius fallback
## disposition (no circumference/tiling concept without a radius).
func test_compute_tile_grid_no_radius_produces_single_origin_tile() -> void:
var tiles: Array = AtlasWindowGeometry.compute_tile_grid(0.0)
assert_int(tiles.size()).is_equal(1)
assert_that(tiles[0]).is_equal(Vector2i.ZERO)
## Every tile center must be a LEGAL canonicalized DistrictPos — column
## wrapped into [0, cols), row clamped into [-rows_half, rows_half] — the
## same range canonicalize_district_center() enforces everywhere else in
## this cluster (pan refetch, entry, rung-reselect). A raw, uncanonicalized
## tile center would fail the server's own normalize_window_center() (or
## silently alias to a different tile than intended).
func test_compute_tile_grid_tiles_are_all_canonicalized() -> void:
var radius_km := 6238.4
var extent: Dictionary = AtlasDescendGeometry.district_extent(radius_km)
var cols: int = int(extent["cols"])
var rows_half: int = int(extent["rows_half"])
var tiles: Array = AtlasWindowGeometry.compute_tile_grid(radius_km)
for tile: Vector2i in tiles:
assert_int(tile.x).override_failure_message(
"tile column %d must be wrapped into [0, %d)" % [tile.x, cols]
).is_greater_equal(0)
assert_int(tile.x).is_less(cols)
assert_int(tile.y).override_failure_message(
"tile row %d must be clamped into [-%d, %d]" % [tile.y, rows_half, rows_half]
).is_greater_equal(-rows_half)
assert_int(tile.y).is_less_equal(rows_half)
## No two tiles may share the same canonicalized center — compute_tile_grid()
## must dedupe (a pole-row clamp or column-wrap collision producing the exact
## same DistrictPos twice would otherwise request/draw the same tile twice,
## wasting a request and drawing one tile over another).
func test_compute_tile_grid_has_no_duplicate_centers() -> void:
var tiles: Array = AtlasWindowGeometry.compute_tile_grid(6238.4)
var seen: Dictionary = {}
for tile: Vector2i in tiles:
assert_bool(seen.has(tile)).override_failure_message(
"tile center %s appears more than once in the grid" % str(tile)
).is_false()
seen[tile] = true
## The tile grid's own center of mass must land on the canonical origin
## (0,0) — the tile-set's symmetric layout (each axis' centers computed as
## `(index - (count-1)/2) * TILE_N`) is centered on the SAME canonical origin
## enter_orbital() uses, so the tile-set's overall framing agrees with
## single-window enter_orbital()'s own "center on (0,0)" contract.
func test_compute_tile_grid_is_centered_on_the_canonical_origin() -> void:
var tiles: Array = AtlasWindowGeometry.compute_tile_grid(6238.4)
var sum_col := 0
var sum_row := 0
for tile: Vector2i in tiles:
sum_col += tile.x
sum_row += tile.y
# Column centers wrap (periodic), so a raw average isn't meaningful there
# the way it is for rows — assert row symmetry directly instead (rows
# never wrap, so their average must be very close to 0 for a
# symmetric grid).
var avg_row: float = float(sum_row) / float(tiles.size())
assert_float(avg_row).override_failure_message(
"the tile grid's row centers must average to ~0 (symmetric around the"
+ " canonical origin's equator row)"
).is_equal_approx(0.0, float(AtlasWindowGeometry.TILE_N))
+11 -4
View File
@@ -23,10 +23,14 @@ static func _mock_window(n: int = 2) -> Dictionary:
## Minimal viewer stub — AtlasWindowOverlay only reaches the viewer through
## get_district_window()/is_overlay_visible()/get_cell_pixel_size(), so a
## bare stub with just those three methods is a legitimate "viewer" for
## these tests, matching the duck-typed-viewer precedent this whole overlay
## cluster already relies on (atlas_overlay_bar.gd/atlas_legend_panel.gd).
## get_district_window()/is_overlay_visible()/get_cell_pixel_size()/
## is_tile_mode(), so a bare stub with just those methods is a legitimate
## "viewer" for these tests, matching the duck-typed-viewer precedent this
## whole overlay cluster already relies on (atlas_overlay_bar.gd/
## atlas_legend_panel.gd). is_tile_mode() always returns false — this suite
## covers the single-window composite-cache path only; the tile mosaic path
## is covered separately by test_atlas_window_tile_set.gd + the viewer's own
## is_tile_mode()-branching tests.
class _ViewerStub:
var window: Variant = null
var active_overlay: String = ""
@@ -40,6 +44,9 @@ class _ViewerStub:
func get_cell_pixel_size() -> float:
return 16.0
func is_tile_mode() -> bool:
return false
func test_composite_smooth_defaults_true() -> void:
assert_bool(AtlasWindowOverlay.COMPOSITE_SMOOTH).override_failure_message(
+204
View File
@@ -0,0 +1,204 @@
## 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)
+142 -87
View File
@@ -73,81 +73,64 @@ func test_enter_orbital_requests_region_granularity() -> void:
assert_str(v._held_granularity_v2).is_equal("Region")
## enter_orbital()'s `n` intent is the body's full equatorial circumference
## in districts (district_extent().cols) — the whole body fitted to the
## canvas, per Jeroen's HARD condition wording — BUT `_held_n` is what
## actually gets STORED/SENT, and that must be the CLAMPED value
## (live-round finding: the raw cols value, routinely tens of thousands at
## Region granularity, was stored unclamped while AtlasWindowRequest clamped
## before sending — see _enter_at_rung()'s own doc for the full C1-one-layer-up
## story). GJ380c's real cols (~19,139 per the live repro) exceeds
## DISTRICT_WINDOW_MAX_N_REGION's clamp ceiling, so this body is the exact
## regression case, not a hypothetical.
func test_enter_orbital_n_is_the_clamped_value_not_raw_circumference() -> void:
## **Superseded by live round 3's tiling fix — retargeted, not deleted.**
## GJ380c/Lendel (radius 6238.4 km) was the ORIGINAL single-window C1 repro
## (raw cols ~19,139 vs. the 6,400 clamp ceiling) — but that SAME threshold
## (`DISTRICT_WINDOW_MAX_N_REGION * DISTRICT_M` = the coverage ceiling
## `compute_tile_grid()` tiles past) means any body needing the n-clamp ALSO
## needs tiling: there is no real body where enter_orbital() takes the
## single-window path with a raw `n` big enough to require clamping.
## GJ380c now correctly enters TILE mode (test_enter_orbital_n_is_the_clamped_value_not_raw_circumference's
## old assertion on a single clamped `_held_n` no longer applies — see
## test_enter_orbital_tile_mode_held_n_is_the_whole_body_extent below for
## what `_held_n` means in tile mode instead). The single-window clamp-mirror
## fix itself remains covered: `_enter_at_rung()`'s own doc/the clamp
## mirror's unit tests (test_atlas_window_request.gd) pin the formula
## directly, and test_zoom_crossing_fires_request_and_accepts_wire_accurate_refinement
## exercises the SAME clamp-mirror lesson at the reselect (not entry)
## boundary, which single-window mode still reaches on the way DOWN from a
## tile-mode zoom-in.
func test_enter_orbital_tile_mode_held_n_is_the_whole_body_extent() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
var radius_km := 6238.4 # GJ380c (Lendel) — the live-repro body
var radius_km := 6238.4 # GJ380c (Lendel)
var extent: Dictionary = AtlasDescendGeometry.district_extent(radius_km)
var raw_cols: int = int(extent["cols"])
v.enter_orbital({"body_id": "GJ380c", "body_radius_km": radius_km}, {})
var expected_clamped: int = AtlasWindowRequest._clamp_window_n_mirror_v2(raw_cols, "Region")
var failure_msg: String = (
"_held_n must be the CLAMPED n (%d), matching what the server will echo —"
+ " not the raw circumference (%d), which the server would clamp down and"
+ " every response would then fail the w_n != _held_n staleness check"
) % [expected_clamped, raw_cols]
assert_int(v._held_n).override_failure_message(failure_msg).is_equal(expected_clamped)
# GJ380c's raw circumference must actually exceed the clamp — otherwise this
# test would pass trivially without exercising the clamp at all.
assert_int(raw_cols).override_failure_message(
"GJ380c's raw district-column count must exceed the Region clamp ceiling"
+ " for this to be a real regression guard, not a no-op"
).is_greater(expected_clamped)
assert_bool(v.is_tile_mode()).override_failure_message(
"GJ380c/Lendel needs tiling — enter_orbital() must have entered tile mode"
).is_true()
# In TILE mode, _held_n is the WHOLE body's extent (unclamped) — each
# TILE clamps its own request independently inside AtlasWindowTileSet
# (see that file's own tests), so _held_n here is NOT expected to equal
# any single clamped value the way single-window mode's is.
assert_int(v._held_n).is_equal(raw_cols)
## **The live-round regression, end to end (fix #1: the n-clamp mirror one
## layer up):** enter_orbital() on a real-sized body (GJ380c/Lendel, radius
## 6238.4 km, raw cols far past the Region clamp ceiling) followed by a
## server response echoing the CLAMPED n + "Region" granularity must be
## ACCEPTED and become the held window — not silently dropped as stale
## forever (the exact live bug: `wv._held_n = 19139` vs. echoed `6400`,
## blank ladder on every real-sized body). This is the round-trip the
## existing suite never exercised — every prior enter_orbital() test
## asserted on request-side state only, never delivered a response.
##
## **WIRE-ACCURATE response shape (fix #2, second live-round finding):** the
## response dict below carries `"granularity":
## SERVER_LEGACY_GRANULARITY_REGION_SENTINEL` explicitly — the ACTUAL byte a
## real server sends, not the field's absence. The first version of this
## test omitted the legacy key entirely, which let `w.get("granularity",
## DEFAULT)` silently default to `1` (matching `_granularity`'s own pinned
## value) — an ACCIDENTAL pass that never exercised the real sentinel
## mismatch, exactly the class of gap the live round exists to catch. This
## version fails without the v2-authoritative-when-present fix in
## `_echoed_granularity_matches()`.
func test_enter_orbital_oversized_body_accepts_the_clamped_region_response() -> void:
## **The live-round-3 regression, end to end for TILE mode:** enter_orbital()
## on GJ380c/Lendel followed by delivering ONE tile's wire-accurate response
## (clamped n=6,400, "Region" granularity_v2, the legacy sentinel in the old
## granularity slot — exactly what a real server sends) must be ACCEPTED
## into that tile's own slot — not silently dropped. This exercises BOTH
## live-round fixes (the v2-authoritative precedence AND per-tile clamping)
## through the tile-set path specifically, complementing
## test_atlas_window_tile_set.gd's own more granular orchestration tests.
func test_enter_orbital_tile_mode_accepts_a_wire_accurate_tile_response() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
var radius_km := 6238.4 # GJ380c (Lendel) — the live-repro body
var extent: Dictionary = AtlasDescendGeometry.district_extent(radius_km)
var raw_cols: int = int(extent["cols"])
var clamped_n: int = AtlasWindowRequest._clamp_window_n_mirror_v2(raw_cols, "Region")
# Sanity: this body must actually need clamping, or the test proves nothing.
assert_int(raw_cols).is_greater(clamped_n)
v.enter_orbital({"body_id": "GJ380c", "body_radius_km": radius_km}, {})
assert_that(v.get_district_window()).override_failure_message(
"no response delivered yet — must still be null"
).is_null()
assert_bool(v.is_tile_mode()).is_true()
# The server's REAL response: echoes the CLAMPED n, "Region" granularity_v2
# (String), center (0,0), AND the legacy sentinel in "granularity" — exactly
# what handle_atlas_request/clamp_window_n_v2 actually produces on the wire
# for an oversized orbital request (confirmed against Dudley's contract).
var region_window: Dictionary = {
"center": [0, 0],
"n": clamped_n,
var tile_set = v.get_tile_set()
var tiles: Array = tile_set.get_tiles()
assert_int(tiles.size()).is_greater(1)
var first_tile_center: Vector2i = tiles[0]["center"]
var tile_window: Dictionary = {
"center": [first_tile_center.x, first_tile_center.y],
"n": AtlasWindowRequest.SERVER_DISTRICT_WINDOW_MAX_N_REGION,
"granularity": SERVER_LEGACY_GRANULARITY_REGION_SENTINEL,
"granularity_v2": "Region",
"morphology": PackedByteArray([8, 14, 0, 1]),
@@ -157,19 +140,12 @@ func test_enter_orbital_oversized_body_accepts_the_clamped_region_response() ->
"vegetation": PackedByteArray([2, 1, 6, 3]),
"glaciation": PackedByteArray([0, 0, 1, 2]),
}
SimBridge.atlas_layers_received.emit(_mock_response("GJ380c", region_window))
SimBridge.atlas_layers_received.emit(_mock_response("GJ380c", tile_window))
var failure_msg: String = (
"a response echoing the server's own clamped n + Region granularity_v2 (with"
+ " the legacy sentinel u32::MAX in the old granularity slot) must be ACCEPTED"
+ " and become the held window — the live bug left this permanently null"
+ " (w_n=%d never matched a stale unclamped _held_n=%d, THEN the legacy"
+ " sentinel never matched the stored _granularity=1) on every real-sized body"
) % [clamped_n, raw_cols]
assert_that(v.get_district_window()).override_failure_message(failure_msg).is_equal(
region_window
)
assert_str(v._held_granularity_v2).is_equal("Region")
assert_that(tile_set.get_tiles()[0]["window"]).override_failure_message(
"a wire-accurate response (clamped n, Region granularity_v2, the legacy"
+ " sentinel) for the first tile must be ACCEPTED into that tile's slot"
).is_equal(tile_window)
## A no-radius body (tiny test body) has no circumference concept —
@@ -364,19 +340,20 @@ func test_zoom_out_past_district_threshold_requests_a_coarser_rung() -> void:
assert_that(v.get_district_window()).is_equal(district_window)
## Zooming IN on a District-rung window (well within its own legal spacing
## band) must NOT trigger a rung change — this is the "zoom is client-side on
## the already-held composite" case, unchanged for in-rung zoom. Sets
## _view_zoom DIRECTLY to a value inside District's legal band (rather than
## relying on enter()'s COVER auto-fit, which for a small n can already sit
## right at Quarter's own threshold — a fit's zoom level is a display-density
## choice independent of what rung selection would pick from scratch, and
## this test is specifically about a SINGLE zoom-in STEP not crossing a
## boundary, not about where the auto-fit itself lands). District's legal
## band (select_rung()'s own doc: the coverage ceiling and the `2x` visual
## band only overlap at small viewports — `canvas_px <= DISTRICT_WINDOW_MAX_N
## * DISTRICT_SPACING_M / 1024 = 128px`) requires a SMALL viewport here,
## unlike most of this suite's 800x600/1920x1080 fixtures.
## Zooming IN on a District-rung window (well within its own legal coverage
## band, `(32,768 m, 131,072 m]` per select_rung()'s redesigned per-rung
## ceiling model — viewport-independent since `canvas_px` no longer affects
## selection) must NOT trigger a rung change — this is the "zoom is
## client-side on the already-held composite" case, unchanged for in-rung
## zoom. Sets _view_zoom DIRECTLY to a value inside District's band (rather
## than relying on enter()'s COVER auto-fit, which for a small n can already
## sit right at Quarter's own threshold — a fit's zoom level is a
## display-density choice independent of what rung selection would pick from
## scratch, and this test is specifically about a SINGLE zoom-in STEP not
## crossing a boundary, not about where the auto-fit itself lands). The
## small 100x80 viewport here is incidental (any size works under the new
## viewport-independent model) — kept small only because that's what the
## original version of this test used.
func test_zoom_in_within_district_threshold_does_not_change_rung() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
@@ -394,6 +371,84 @@ func test_zoom_in_within_district_threshold_does_not_change_rung() -> void:
).is_equal("District")
## **Live round 3 regression, the direct end-to-end fix target:** a real
## wheel-zoom gesture (many `_zoom_at()` ticks, matching the shape a
## continuous mouse-wheel scroll actually produces) crossing from the
## Region rest state down through District into Quarter territory must (i)
## fire a request at the NEW granularity — `_window_request.get_granularity_v2()`
## must have changed by the end of the gesture — and (ii) accept a
## WIRE-ACCURATE response for that request: echoing the REQUEST's own
## (already re-centered, already re-clamped) center/n, which the live round
## found DIFFERS from the ORIGINAL held center (screen-center-anchored
## refinement re-centers on wherever the cursor currently maps to, not
## wherever the player started) — this is the "second latent drop" the
## coordinator specifically flagged: comparing the echo against a STALE
## `_held_center` (frozen at the pre-crossing value) rather than the
## request's own center would silently drop this response too.
## **Live round 3 update:** GJ380c/Lendel now enters TILE mode via
## enter_orbital() (bug B's fix), so this test starts from THERE — zooming
## in far enough crosses Region's coverage ceiling and must LEAVE tile mode
## for the single-window path at the new (finer) rung, exactly the
## `_maybe_reselect_rung()` "leaving_tile_mode" branch this test exercises.
func test_zoom_crossing_fires_request_and_accepts_wire_accurate_refinement() -> void:
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
add_child(v)
v.size = Vector2(1600.0, 900.0)
var radius_km := 6238.4 # GJ380c (Lendel) — the live-repro body
v.enter_orbital({"body_id": "GJ380c", "body_radius_km": radius_km}, {})
assert_bool(v.is_tile_mode()).override_failure_message(
"GJ380c/Lendel must enter tile mode at the orbital rest state (live round 3)"
).is_true()
# A real wheel-zoom gesture: many ticks, cursor OFF-CENTER (so cursor-
# anchored zoom genuinely drifts the screen-to-district mapping away from
# the canonical origin, not just scaling in place) — matching the live
# drive's actual input shape, not a single synthetic jump. Zooming in far
# enough must cross OUT of Region's coverage ceiling, leaving tile mode.
var cursor_pos := Vector2(1100.0, 300.0) # off-center, biased toward one quadrant
for _i in range(60):
v._zoom_at(cursor_pos, 1.15)
if not v.is_tile_mode():
break
# (i) Tile mode must have been LEFT, and a request must have gone out at
# a NEW (finer) granularity via the single-window path.
assert_bool(v.is_tile_mode()).override_failure_message(
"zooming in far enough must leave tile mode for the single-window path"
).is_false()
var request_granularity: String = v._window_request.get_granularity_v2()
assert_str(request_granularity).override_failure_message(
"leaving tile mode must fire a request at a new (finer) granularity"
).is_not_equal("Region")
# (ii) The request's own center/n — read AFTER leaving tile mode, so this
# is whatever _maybe_reselect_rung() actually computed — is what a
# wire-accurate response must echo to be accepted.
var request_center: Vector2i = v._window_request._center
var request_n: int = v._window_request._n
var refinement_window: Dictionary = {
"center": [request_center.x, request_center.y],
"n": request_n,
"granularity_v2": request_granularity,
"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]),
}
SimBridge.atlas_layers_received.emit(_mock_response("GJ380c", refinement_window))
assert_that(v.get_district_window()).override_failure_message(
"a wire-accurate refinement response (echoing the REQUEST's own center/n/"
+ " granularity after leaving tile mode) must be ACCEPTED — comparing"
+ " against a stale/wrong reference instead of the request's own would"
+ " silently drop this response forever"
).is_equal(refinement_window)
assert_str(v._held_granularity_v2).is_equal(request_granularity)
# =============================================================================
# T-1153: E/W wrap and pole-wall clamps at EVERY rung — both are extent-
# relative (CELL_PIXEL_SIZE-based district-space math, unchanged regardless