Live round 2 (the real bug): every nature-overlay marker size was a raw screen-space constant drawn inside _canvas, whose scale IS view_zoom — at Lendel's orbital fit zoom (0.0063) a 2.2px trunk dot rendered at ~0.014px, invisible; the same code at District's 3.75 zoom produced the correctly-visible mouth ring, which is why one capture worked and the headline rung didn't. Fixed via AtlasWindowGeometry.zoom_compensated_ size() (pure, floor-guarded) wired through every radius/line-width; basin FILL points are positions and correctly stay unscaled. Suspect tile-mode-rung-detection was ruled out live (granularity_v2=Region confirmed in tile mode) but pinned with a named regression test anyway. +8 pure-function tests incl. a numeric pin of the pre-fix magnitude (<0.02px at orbital zoom); revert-verified by name. Draw-smoke suite documented as supplementary (the shared SubViewport background harness can pass vacuously under X11 BadMatch — the pure suite is the gate). Live round 3 (drive-script bug, no product change): the lead's scratch drive passed the button LABEL to set_overlay_visible() and the unknown- id guard silently no-op'd — but the chase banked a real pin: test_set_overlay_visible_gen_basins_flips_gate_and_redraws_nature_ overlay (draw-counting spy per the cold-start precedent; is_queued_for_ redraw does not exist in this build). Revert-verified. Basins verified live: 7 Lendel watershed boundaries render at the ruling's alphas. Suites: viewer 76/76, geometry-nature 42/42, nature- overlay 22/22, zoom-ladder 50/50, no regressions across the cluster. Tickets: T-1156 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
282 lines
12 KiB
GDScript
282 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.
|
|
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}
|
|
|
|
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))
|
|
|
|
|
|
## 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)
|