Every atlas capture scenario built its body dict without body_radius_km. Radius is Global's ONLY spacing input (2*pi*R / width), so every Global golden has been a degenerate block since these scenarios were created — the map rendered at 0.000 km/gridunit. The fixed rungs were unaffected, since they derive spacing from the rung's own cell size rather than the body. That means the "eyeball check against .cache/screenshots baselines" discipline has been hollow at Global specifically: a baseline that is a solid rectangle diffs clean against a new solid rectangle. It is how a 2x1 canvas survived in front of two verification layers. Adds the real radii to all ten scenarios (Lendel 6238.4, Vethis 6959.3, Arbour 6711.0, Ferrath 6062.0, Threshold 5503.5), and makes the capture log course and settlement counts alongside canvas_cells — an empty annotation layer was previously indistinguishable from a populated one in the log, which is exactly the signal needed to tell "no rivers" from "rivers not drawn". Also updates three suites to the corrected Global cache contract: the key now honours extent (collapsing it meant a resize could never miss), so the shared _land_global_canvas helper must cache under the extent the viewer will actually request, and the two make_key tests now assert the real rule — centre collapsed, extent honoured — instead of the retired sentinel. Client suite 1830 total / 1804 passed / 0 failed / 26 skipped. Pair session with Jeroen, 2026-07-27. Co-Authored-By: Claude <noreply@anthropic.com>
142 lines
6.3 KiB
GDScript
142 lines
6.3 KiB
GDScript
## T-1182 tests: step_canvas_cache.gd — the client-side in-memory LRU cache
|
|
## for decoded step-canvas payloads. Keyed on (body_id, rung, center, extent,
|
|
## min_wl_m), the exact tuple server/src/atlas/step_canvas.rs's own
|
|
## StepCanvasCache keys on. Mirrors test_atlas_window_cache.gd's own
|
|
## conventions (the surviving LRU shape this file is adapted from).
|
|
class_name TestStepCanvasCache
|
|
extends GdUnitTestSuite
|
|
|
|
const StepCanvasCache := preload("res://ui/implant/apps/atlas/step_canvas/step_canvas_cache.gd")
|
|
|
|
|
|
func test_make_key_distinguishes_body_rung_center_and_extent() -> void:
|
|
var k1 := StepCanvasCache.make_key("GJ1c", "District", Vector2i(10, 20), Vector2i(64, 64))
|
|
var k2 := StepCanvasCache.make_key("GJ1d", "District", Vector2i(10, 20), Vector2i(64, 64))
|
|
var k3 := StepCanvasCache.make_key("GJ1c", "Chunk", Vector2i(10, 20), Vector2i(64, 64))
|
|
var k4 := StepCanvasCache.make_key("GJ1c", "District", Vector2i(11, 20), Vector2i(64, 64))
|
|
var k5 := StepCanvasCache.make_key("GJ1c", "District", Vector2i(10, 20), Vector2i(32, 32))
|
|
assert_str(k1).is_not_equal(k2)
|
|
assert_str(k1).is_not_equal(k3)
|
|
assert_str(k1).is_not_equal(k4)
|
|
assert_str(k1).is_not_equal(k5)
|
|
|
|
|
|
## Global collapses the CENTRE to a sentinel (its canvas is whole-body and
|
|
## origin-anchored, so the server genuinely ignores `center`) but NOT the
|
|
## extent. Zeroing the extent was correct while Global had exactly one
|
|
## possible size per body; since the D-255 extent inversion made it
|
|
## viewport-sized, collapsing it meant the first canvas cached for a body
|
|
## answered every later request — so a window resize could never take effect,
|
|
## because the differently-sized request was a HIT on the stale one.
|
|
func test_make_key_global_ignores_center_but_honours_extent() -> void:
|
|
var same_size_a := StepCanvasCache.make_key(
|
|
"GJ1c", "Global", Vector2i(10, 20), Vector2i(64, 64)
|
|
)
|
|
var same_size_b := StepCanvasCache.make_key(
|
|
"GJ1c", "Global", Vector2i(999, -999), Vector2i(64, 64)
|
|
)
|
|
assert_str(same_size_a).override_failure_message(
|
|
"Global must ignore centre — its canvas is origin-anchored"
|
|
).is_equal(same_size_b)
|
|
|
|
var other_size := StepCanvasCache.make_key(
|
|
"GJ1c", "Global", Vector2i(10, 20), Vector2i(960, 480)
|
|
)
|
|
assert_str(same_size_a).override_failure_message(
|
|
"Global must NOT ignore extent — a resize has to be able to miss"
|
|
).is_not_equal(other_size)
|
|
|
|
|
|
func test_miss_returns_null_and_has_reports_false() -> void:
|
|
var cache := StepCanvasCache.new()
|
|
assert_that(cache.get_canvas("GJ1c", "District", Vector2i.ZERO, Vector2i(64, 64))).is_null()
|
|
assert_bool(cache.has("GJ1c", "District", Vector2i.ZERO, Vector2i(64, 64))).is_false()
|
|
|
|
|
|
func test_put_then_get_round_trips_exact_canvas() -> void:
|
|
var cache := StepCanvasCache.new()
|
|
var canvas := {"width": 64, "height": 64}
|
|
cache.put("GJ1c", "District", Vector2i(10, 20), Vector2i(64, 64), canvas)
|
|
assert_bool(cache.has("GJ1c", "District", Vector2i(10, 20), Vector2i(64, 64))).is_true()
|
|
assert_that(cache.get_canvas("GJ1c", "District", Vector2i(10, 20), Vector2i(64, 64))).is_equal(
|
|
canvas
|
|
)
|
|
|
|
|
|
## D-227: a canvas fetched once is valid FOREVER for that exact key — no
|
|
## expiry, no invalidation path.
|
|
func test_cached_canvas_never_expires() -> void:
|
|
var cache := StepCanvasCache.new()
|
|
var canvas := {"width": 1}
|
|
cache.put("GJ1c", "Chunk", Vector2i.ZERO, Vector2i(1, 1), canvas)
|
|
for _i in range(50):
|
|
assert_that(cache.get_canvas("GJ1c", "Chunk", Vector2i.ZERO, Vector2i(1, 1))).is_equal(canvas)
|
|
|
|
|
|
func test_different_rungs_at_identical_center_extent_do_not_collide() -> void:
|
|
var cache := StepCanvasCache.new()
|
|
var district_canvas := {"rung": "District"}
|
|
var chunk_canvas := {"rung": "Chunk"}
|
|
cache.put("GJ1c", "District", Vector2i(10, 20), Vector2i(64, 64), district_canvas)
|
|
cache.put("GJ1c", "Chunk", Vector2i(10, 20), Vector2i(64, 64), chunk_canvas)
|
|
assert_int(cache.size()).is_equal(2)
|
|
assert_that(cache.get_canvas("GJ1c", "District", Vector2i(10, 20), Vector2i(64, 64))).is_equal(
|
|
district_canvas
|
|
)
|
|
assert_that(cache.get_canvas("GJ1c", "Chunk", Vector2i(10, 20), Vector2i(64, 64))).is_equal(
|
|
chunk_canvas
|
|
)
|
|
|
|
|
|
func test_put_overwrites_existing_key() -> void:
|
|
var cache := StepCanvasCache.new()
|
|
cache.put("GJ1c", "District", Vector2i.ZERO, Vector2i(64, 64), {"v": 1})
|
|
cache.put("GJ1c", "District", Vector2i.ZERO, Vector2i(64, 64), {"v": 2})
|
|
assert_int(cache.size()).is_equal(1)
|
|
assert_that(cache.get_canvas("GJ1c", "District", Vector2i.ZERO, Vector2i(64, 64))).is_equal(
|
|
{"v": 2}
|
|
)
|
|
|
|
|
|
func test_eviction_drops_least_recently_used_on_overflow() -> void:
|
|
var cache := StepCanvasCache.new(2)
|
|
cache.put("GJ1c", "District", Vector2i(0, 0), Vector2i(64, 64), {"id": "a"})
|
|
cache.put("GJ1c", "District", Vector2i(1, 0), Vector2i(64, 64), {"id": "b"})
|
|
cache.put("GJ1c", "District", Vector2i(2, 0), Vector2i(64, 64), {"id": "c"})
|
|
|
|
assert_int(cache.size()).is_equal(2)
|
|
assert_bool(cache.has("GJ1c", "District", Vector2i(0, 0), Vector2i(64, 64))).override_failure_message(
|
|
"oldest entry should have been evicted"
|
|
).is_false()
|
|
assert_bool(cache.has("GJ1c", "District", Vector2i(2, 0), Vector2i(64, 64))).is_true()
|
|
|
|
|
|
func test_get_touches_entry_and_protects_it_from_eviction() -> void:
|
|
var cache := StepCanvasCache.new(2)
|
|
cache.put("GJ1c", "District", Vector2i(0, 0), Vector2i(64, 64), {"id": "a"})
|
|
cache.put("GJ1c", "District", Vector2i(1, 0), Vector2i(64, 64), {"id": "b"})
|
|
cache.get_canvas("GJ1c", "District", Vector2i(0, 0), Vector2i(64, 64))
|
|
cache.put("GJ1c", "District", Vector2i(2, 0), Vector2i(64, 64), {"id": "c"})
|
|
|
|
assert_bool(cache.has("GJ1c", "District", Vector2i(0, 0), Vector2i(64, 64))).override_failure_message(
|
|
"touched entry should survive eviction"
|
|
).is_true()
|
|
assert_bool(cache.has("GJ1c", "District", Vector2i(1, 0), Vector2i(64, 64))).override_failure_message(
|
|
"untouched entry should be the one evicted"
|
|
).is_false()
|
|
|
|
|
|
func test_max_entries_clamped_to_at_least_one() -> void:
|
|
var cache := StepCanvasCache.new(0)
|
|
cache.put("GJ1c", "District", Vector2i(0, 0), Vector2i(64, 64), {"id": "a"})
|
|
cache.put("GJ1c", "District", Vector2i(1, 0), Vector2i(64, 64), {"id": "b"})
|
|
assert_int(cache.size()).is_equal(1)
|
|
|
|
|
|
func test_clear_empties_the_cache() -> void:
|
|
var cache := StepCanvasCache.new()
|
|
cache.put("GJ1c", "District", Vector2i.ZERO, Vector2i(64, 64), {"id": "a"})
|
|
cache.clear()
|
|
assert_int(cache.size()).is_equal(0)
|
|
assert_bool(cache.has("GJ1c", "District", Vector2i.ZERO, Vector2i(64, 64))).is_false()
|