test(ui): pin the _user_adjusted resize guard both ways; fix coalescing doc overstatement (PR #188 review)
Hoshe's finding: the flag introduced so auto-fit never fights a manual view had zero coverage on exactly that branch. Two tests drive the REAL _gui_input path (synthetic drag), then fire NOTIFICATION_RESIZED: user-adjusted view survives a resize untouched (zoom AND offset); an unadjusted view re-fits to the new viewport. 52/52. Non-blocking doc note also taken: layer_proxy's normalization comment claimed the twins would otherwise 'coalesce independently' — the coalescing key is (ConnectionId, body_id) and never carried center; rewritten to say what normalization actually buys on that path (the work item derives and echoes the canonical center). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -430,3 +430,66 @@ func test_center_one_column_past_the_seam_shares_a_cache_key_with_its_twin() ->
|
||||
SimBridge.atlas_layers_received.emit(_mock_response("GJ380c", window))
|
||||
assert_that(v1.get_district_window()).is_equal(window)
|
||||
assert_that(v2.get_district_window()).is_equal(window)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# _user_adjusted guard (PR #188 review) — the flag exists so auto-fit NEVER
|
||||
# fights a manually-adjusted view. The one branch that makes that true
|
||||
# (resize while user-adjusted) had no coverage; both directions pinned here,
|
||||
# driving the REAL _gui_input path (synthetic events), not the flag directly.
|
||||
# =============================================================================
|
||||
|
||||
|
||||
func _drag_viewer(v: AtlasWindowViewer, from: Vector2, to: Vector2) -> void:
|
||||
var down := InputEventMouseButton.new()
|
||||
down.button_index = MOUSE_BUTTON_LEFT
|
||||
down.pressed = true
|
||||
down.position = from
|
||||
down.global_position = from
|
||||
v._gui_input(down)
|
||||
var move := InputEventMouseMotion.new()
|
||||
move.position = to
|
||||
move.global_position = to
|
||||
v._gui_input(move)
|
||||
var up := InputEventMouseButton.new()
|
||||
up.button_index = MOUSE_BUTTON_LEFT
|
||||
up.pressed = false
|
||||
up.position = to
|
||||
up.global_position = to
|
||||
v._gui_input(up)
|
||||
|
||||
|
||||
func test_resize_after_manual_drag_keeps_user_view() -> void:
|
||||
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
|
||||
add_child(v)
|
||||
v.size = Vector2(1280.0, 720.0)
|
||||
v.enter({"body_id": "GJ380c", "body_radius_km": 6238.4}, {}, Vector2i(10, 20), 32)
|
||||
|
||||
_drag_viewer(v, Vector2(600.0, 400.0), Vector2(540.0, 380.0))
|
||||
var user_zoom: float = v.get_view_zoom()
|
||||
var user_offset: Vector2 = v.get_view_offset()
|
||||
|
||||
v.size = Vector2(1600.0, 900.0)
|
||||
v.notification(Control.NOTIFICATION_RESIZED)
|
||||
|
||||
assert_float(v.get_view_zoom()).override_failure_message(
|
||||
"resize while user-adjusted must NOT re-fit — zoom belongs to the user"
|
||||
).is_equal_approx(user_zoom, 0.0001)
|
||||
assert_vector(v.get_view_offset()).override_failure_message(
|
||||
"resize while user-adjusted must NOT re-center — offset belongs to the user"
|
||||
).is_equal_approx(user_offset, Vector2(0.001, 0.001))
|
||||
|
||||
|
||||
func test_resize_without_user_adjustment_refits() -> void:
|
||||
var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new())
|
||||
add_child(v)
|
||||
v.size = Vector2(1280.0, 720.0)
|
||||
v.enter({"body_id": "GJ380c", "body_radius_km": 6238.4}, {}, Vector2i(10, 20), 32)
|
||||
var fitted_zoom: float = v.get_view_zoom()
|
||||
|
||||
v.size = Vector2(640.0, 360.0)
|
||||
v.notification(Control.NOTIFICATION_RESIZED)
|
||||
|
||||
assert_float(v.get_view_zoom()).override_failure_message(
|
||||
"resize with no manual adjustment must re-fit to the new viewport"
|
||||
).is_not_equal(fitted_zoom)
|
||||
|
||||
@@ -821,9 +821,13 @@ fn normalize_window_center(params: &BodyParams, center: DistrictPos) -> District
|
||||
/// rather than only inside the former miss-branch: normalization needs
|
||||
/// `body_radius_km` to compute the wrap/clamp bounds, and it must happen
|
||||
/// before either key exists, or an insane request and its sane normalized
|
||||
/// twin would land in different cache entries / coalesce independently
|
||||
/// (exactly the bug this fix closes — a garbage `window_center` was cached
|
||||
/// standalone instead of collapsing onto its valid twin). The one-time cost
|
||||
/// twin would land in different cache entries (exactly the bug this fix
|
||||
/// closes — a garbage `window_center` was cached standalone instead of
|
||||
/// collapsing onto its valid twin). The coalescing key itself is
|
||||
/// `(ConnectionId, body_id)` — it never carried `center`, so coalescing
|
||||
/// was never at risk of diverging per-center; normalizing before
|
||||
/// `submit_window` matters only so the work item DERIVES (and echoes) the
|
||||
/// canonical center. The one-time cost
|
||||
/// (a single indexed `bodies` row read) is paid on every window request now,
|
||||
/// not just on a cache miss — a request whose normalized center hits the
|
||||
/// cache still needed this read to know WHICH key to check.
|
||||
|
||||
Reference in New Issue
Block a user