Files
settled-reach/client/tests/test_atlas_window_nature_overlay_draw_smoke.gd
T
jpmschweitzerandClaude Fable 5 9914dc0d91 fix(ui): T-1172 — clip river dots/confluences/mouths against the drawn waterline
Jeroen's hands-on report: rivers continuing under the ocean. Tyre's
ruling implemented: the skeleton is rung-independent, the drawn coast
is rung-indexed (warp cutoff admits more octaves per rung), so
reconciliation is a presentation-frame operation — a draw-time clip
against the SAME per-cell morphology verdict the terrain painter used,
at the rung on screen. New pure module atlas_window_water_clip.gd:
cell resolution across both paths (single-window direct; tile mode
selects the containing tile by each tile's OWN echoed n with nearest-
wrap re-expression against that tile's canonical center — response-is-
source-of-truth + wrap discipline reused, not reinvented). Strict drop
(no snap); offshore mouths suppressed (return with real termini in
T-1170 — retirement markers at the clip sites); basins untouched;
fail-open wherever no composite data has arrived (the clip refines
presentation, never gates data). The _pos split feeds the SAME wrap-
resolved district to both draw position and clip test so they can
never disagree about the wrap image. 49 new tests incl. antimeridian
and mid-progressive-arrival fail-open; revert-verified with precise
attribution (breaking water detection fails exactly the 3 water tests,
fail-open/land tests stay green); smoke-suite stub crash under a real
driver caught and fixed (headless skip-gating masked it). 7 suites
regression-free; gdlint clean.

Tickets: T-1172

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 10:37:56 +02:00

300 lines
12 KiB
GDScript

## T-1156 wave 1: a REAL draw smoke test for AtlasWindowNatureOverlay,
## matching test_atlas_window_overlay_draw_smoke.gd's established pattern —
## `draw_circle()`/`draw_rect()`/`draw_arc()`/`draw_colored_polygon()` calls
## require a live render pass under this engine version (confirmed directly:
## a plain unit test calling `_draw()` outside one throws "Drawing is only
## allowed inside this node's `_draw()`..."). Render into a REAL SubViewport,
## force a settle wait, and assert visible non-background pixels — proving
## the river-dot/mouth-ring/basin-polygon/attractor-glyph draw calls actually
## paint, not just that the state feeding them is correct (that half is
## test_atlas_window_nature_overlay.gd's job).
##
## **REQUIRES A REAL RENDERING DRIVER — SKIPS (not fails) under
## `tests/run-godot`'s hardcoded `--headless`**, same posture/rationale as
## test_atlas_window_overlay_draw_smoke.gd's own header doc (dummy driver,
## no GPU texture output — SubViewport.get_texture().get_image() returns an
## all-zero/unusable image under it).
##
## To actually exercise this file's assertions, run it with a real driver:
## godot4 --display-driver x11 --rendering-driver opengl3 \
## -s addons/gdUnit4/bin/GdUnitCmdTool.gd --ignoreHeadlessMode -c \
## -a res://tests/test_atlas_window_nature_overlay_draw_smoke.gd
##
## **Known limitation, verified directly (2026-07-23 zoom-compensation fix
## verification):** the shared _BackgroundRect/_render_to_image() harness
## (copied from test_atlas_window_overlay_draw_smoke.gd) occasionally fails
## to composite the COLOR_BG fill at all under a real X11/opengl3 run in this
## environment (sampled pixels read (0,0,0,0), not COLOR_BG — an unrelated,
## intermittent X11/SubViewport timing issue, confirmed via an XServer
## "BadMatch" warning in that same run's log). When that happens EVERY pixel
## reads as "non-background" regardless of what this overlay actually draws,
## making a low MIN_NON_BACKGROUND_FRACTION threshold pass VACUOUSLY (it
## would pass even with zoom-compensation deliberately broken — confirmed by
## direct revert-test). The terrain sibling suite is accidentally immune to
## this (its checkerboard composite covers most of the frame regardless of
## background correctness); this suite's SPARSE markers are not. **The
## reliable, environment-independent regression gate for the zoom-
## compensation fix is therefore the pure-function suite in
## test_atlas_window_geometry_nature.gd** (zoom_compensated_size()'s own
## tests) — this smoke suite is a supplementary "does it actually paint"
## check when the harness cooperates, not the primary gate.
class_name TestAtlasWindowNatureOverlayDrawSmoke
extends GdUnitTestSuite
const AtlasWindowNatureOverlay := preload("res://ui/implant/apps/atlas/atlas_window_nature_overlay.gd")
const COLOR_BG: Color = Color("#0d1117") # AtlasWindowViewer.COLOR_BG, mirrored (private const)
const VIEWPORT_SIZE: Vector2i = Vector2i(512, 512)
const MIN_NON_BACKGROUND_FRACTION: float = 0.0005 # river dots are sparse — a low bar is honest here
const SKIP_REASON: String = (
"no real rendering driver (dummy/headless) — run with e.g."
+ " `godot4 --display-driver x11 --rendering-driver opengl3"
+ " -s addons/gdUnit4/bin/GdUnitCmdTool.gd --ignoreHeadlessMode -c"
+ " -a res://tests/test_atlas_window_nature_overlay_draw_smoke.gd` to exercise this file"
)
## Matches test_atlas_window_overlay_draw_smoke.gd's own detection exactly.
static func _dummy_renderer_active() -> bool:
return DisplayServer.get_name() == "headless"
## Same duck-typed viewer contract as test_atlas_window_nature_overlay.gd's
## _ViewerStub, minus the SimBridge-signal machinery this smoke test doesn't
## need (layer1 is injected directly via _layer1, not through a response).
## view_zoom MUST be kept in sync with whatever zoom _render_to_image() is
## called with — the whole point of this smoke suite (post-live-eyeball,
## coordinator finding 2026-07-23) is proving markers stay visible at the
## REAL orbital fit zoom, not an artificially large test zoom that would
## mask the zoom-compensation bug the fix addresses. is_tile_mode()/
## get_district_window()/get_tile_set() added for T-1172 (the water clip) —
## defaults to single-window mode with NO arrived composite (district_window
## null), the fail-open case, so these smoke tests keep drawing every marker
## exactly as before the clip existed (this file's own job is proving the
## draw calls paint pixels at all, not exercising the clip's water-detection
## branch — that's test_atlas_window_nature_overlay.gd's job).
class _ViewerStub:
var held_granularity_v2: String = "Region"
var body_radius_km: float = 6371.0
var held_center: Vector2i = Vector2i.ZERO
var held_n: int = 64
var view_zoom: float = 1.0
var overlay_visibility: Dictionary = {"gen_rivers": true, "gen_basins": true, "gen_attractors": true}
var tile_mode: bool = false
var district_window: Variant = null
var tile_set: Variant = null
func get_held_granularity_v2() -> String:
return held_granularity_v2
func get_body_radius_km() -> float:
return body_radius_km
func get_held_center() -> Vector2i:
return held_center
func get_held_n() -> int:
return held_n
func get_cell_pixel_size() -> float:
return 16.0
func get_view_zoom() -> float:
return view_zoom
func is_overlay_visible(overlay_id: String) -> bool:
return bool(overlay_visibility.get(overlay_id, false))
func is_tile_mode() -> bool:
return tile_mode
func get_district_window() -> Variant:
return district_window
func get_tile_set() -> Variant:
return tile_set
## A dense scatter of river cells spanning the whole held window's canvas
## footprint (not clustered in one corner) — a genuine "does the composite
## draw something visible across the frame" check, same intent as the
## terrain smoke test's checkerboard morphology spread.
static func _mock_layer1_dense() -> Dictionary:
var river_cells: Array = []
var river_class: PackedByteArray = PackedByteArray()
for i in range(40):
river_cells.append([i * 3, i * 3])
river_class.append(2) # trunk — visible at every rung this suite exercises
return {
"river_network": {
"river_cells": river_cells,
"river_class": river_class,
"confluences": [[60, 60]],
"mouths": [[120, 120]],
},
"drainage_basins": [
{"basin_id": 1, "boundary": [[0, 0], [0, 40], [40, 40], [40, 0]]},
],
"attractors": [
{"position": [80, 80], "strength": 0.9, "attractor_type": "Oasis", "sub_biome": ""},
],
"grid_w": 256,
"grid_h": 128,
}
## Same _BackgroundRect/_render_to_image/_non_background_fraction shared
## rendering infrastructure as test_atlas_window_overlay_draw_smoke.gd —
## duplicated rather than imported since gdUnit4 test suites are not
## typically composed via inheritance in this codebase (no precedent for a
## shared test-infra base class in client/tests/), and the block is small.
class _BackgroundRect extends Node2D:
var fill_color: Color = Color.BLACK
var fill_size: Vector2 = Vector2.ZERO
func _draw() -> void:
draw_rect(Rect2(Vector2.ZERO, fill_size), fill_color)
func _render_to_image(overlay: Node2D, zoom: float = 1.0) -> Image:
var sub_viewport := SubViewport.new()
sub_viewport.size = VIEWPORT_SIZE
sub_viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS
sub_viewport.transparent_bg = false
add_child(sub_viewport)
auto_free(sub_viewport)
var bg := _BackgroundRect.new()
bg.fill_color = COLOR_BG
bg.fill_size = Vector2(VIEWPORT_SIZE)
sub_viewport.add_child(bg)
bg.queue_redraw()
var canvas := Node2D.new()
canvas.position = Vector2(VIEWPORT_SIZE) * 0.5
canvas.scale = Vector2(zoom, zoom)
sub_viewport.add_child(canvas)
canvas.add_child(overlay)
overlay.queue_redraw()
for _i in range(6):
await get_tree().process_frame
return sub_viewport.get_texture().get_image()
static func _non_background_fraction(image: Image) -> float:
var w: int = image.get_width()
var h: int = image.get_height()
if w <= 0 or h <= 0:
return 0.0
var total: int = w * h
var differing: int = 0
var bg_rgb: Color = Color(COLOR_BG.r, COLOR_BG.g, COLOR_BG.b, 1.0)
for y in range(h):
for x in range(w):
var px: Color = image.get_pixel(x, y)
var px_rgb: Color = Color(px.r, px.g, px.b, 1.0)
if not px_rgb.is_equal_approx(bg_rgb):
differing += 1
return float(differing) / float(total)
## Region rung, every toggle on: rivers + confluence + mouth + basin fill +
## attractor glyph must all draw SOMETHING — the "does the composite actually
## paint" proof the state-only unit suite cannot provide.
func test_region_rung_draws_visible_pixels() -> void:
if _dummy_renderer_active():
print(SKIP_REASON)
return
var overlay := AtlasWindowNatureOverlay.new()
var stub := _ViewerStub.new()
overlay.viewer = stub
overlay._layer1 = _mock_layer1_dense()
overlay._requested_body_id = "SmokeBody"
# Zoom chosen so the held window's canvas footprint (held_n * cell_px =
# 64 * 16 = 1024 units) comfortably fills the 512px viewport.
var zoom: float = float(VIEWPORT_SIZE.x) / (float(stub.held_n) * stub.get_cell_pixel_size())
stub.view_zoom = zoom
var image: Image = await _render_to_image(overlay, zoom)
var fraction: float = _non_background_fraction(image)
assert_float(fraction).override_failure_message(
(
"the Region-rung nature composite (rivers + confluence + mouth + basin +"
+ " attractor, every toggle on) must render VISIBLE non-background pixels —"
+ " got only %.4f%% of the frame differing from COLOR_BG"
)
% (fraction * 100.0)
).is_greater(MIN_NON_BACKGROUND_FRACTION)
## District rung: only trunk rivers (class 2, all cells in the dense fixture
## are trunk) + the mouth carve-out draw; basins/attractors/confluences are
## rung-gated off. Still must produce visible pixels — proving the "fade
## down, don't vanish" posture actually leaves SOMETHING on screen.
func test_district_rung_still_draws_visible_pixels() -> void:
if _dummy_renderer_active():
print(SKIP_REASON)
return
var overlay := AtlasWindowNatureOverlay.new()
var stub := _ViewerStub.new()
stub.held_granularity_v2 = "District"
overlay.viewer = stub
overlay._layer1 = _mock_layer1_dense()
overlay._requested_body_id = "SmokeBody"
var zoom: float = float(VIEWPORT_SIZE.x) / (float(stub.held_n) * stub.get_cell_pixel_size())
stub.view_zoom = zoom
var image: Image = await _render_to_image(overlay, zoom)
var fraction: float = _non_background_fraction(image)
assert_float(fraction).override_failure_message(
(
"the District-rung composite (trunk rivers + mouth carve-out only) must"
+ " still render VISIBLE non-background pixels — got only %.4f%% of the"
+ " frame differing from COLOR_BG"
)
% (fraction * 100.0)
).is_greater(MIN_NON_BACKGROUND_FRACTION)
## Coordinator live-eyeball regression (2026-07-23): the ORBITAL TILE MOSAIC
## rest state's REAL fit zoom on a Lendel-scale body (~0.0063, confirmed
## directly via a live drive script — held_n=19139 districts,
## cell_px=16, a ~1600px viewport) — NOT the comfortable ~0.5 zoom the
## sibling test above uses. Before the zoom-compensation fix, this exact
## zoom magnitude produced ZERO visible river/mouth pixels (a 2.2px trunk
## dot rasterized at ~0.014 screen px) despite RVR being on and the policy
## table correctly returning full Region visibility — the captures showed
## terrain-only with literally nothing drawn. Pins the regression at
## production scale, not a toy zoom that could accidentally still pass.
func test_orbital_scale_zoom_still_draws_visible_pixels() -> void:
if _dummy_renderer_active():
print(SKIP_REASON)
return
var overlay := AtlasWindowNatureOverlay.new()
var stub := _ViewerStub.new()
stub.held_n = 19139 # Lendel's own raw circumference, live drive script
stub.view_zoom = 0.0063 # Lendel's own live orbital fit zoom
overlay.viewer = stub
overlay._layer1 = _mock_layer1_dense()
overlay._requested_body_id = "SmokeBody"
var image: Image = await _render_to_image(overlay, stub.view_zoom)
var fraction: float = _non_background_fraction(image)
assert_float(fraction).override_failure_message(
(
"at Lendel's REAL orbital fit zoom (~0.0063), the Region-rung nature"
+ " composite must still render VISIBLE non-background pixels — got only"
+ " %.4f%% of the frame differing from COLOR_BG. This is exactly the"
+ " coordinator's live-eyeball finding: uncompensated screen-space marker"
+ " sizes get multiplied by the canvas's own zoom transform, vanishing"
+ " sub-pixel at the orbital rest state's tiny fit zoom."
)
% (fraction * 100.0)
).is_greater(MIN_NON_BACKGROUND_FRACTION)