Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

facett-geomap/tests/geomap_deck_scale.rs

//! **`GeoMap`'s scale, measured through the deck the app ships** — the twin of
//! `osm2d_deck_scale.rs`.
//!
//! `GeoMap` (facett-geomap/src/lib.rs) had the identical defect `OsmView` did, and for
//! the identical reason. It reports its viewport zoom as the deck's uniform scale knob:
//!
//! ```ignore
//! fn scale(&self) -> f32 { self.view.zoom as f32 }
//! fn set_scale(&mut self, s: f32) { let _ = self.update(Msg::SetZoom(s as f64)); }
//! ```
//!
//! That zoom is clamped `0.25..=5e6` by `update` — a twenty-million-fold range — while
//! `FacetDeck::scale_active` used to clamp every facet into `[0.25, 4.0]`, a document
//! viewer's sixteen-fold range. The wheel (`Msg::ZoomBy`) drives the viewport DIRECTLY
//! and never passes through `set_scale`, so the moment a user wheels past `zoom = 4.0`
//! the next `+` click computes `scale() * 1.1`, saturates the clamp at `4.0`, and writes
//! the viewport BACKWARDS — a zoom-IN button that visibly zooms OUT.
//!
//! It hid at start-up because `GeoViewport::default()` is `zoom = 1.0`: the one value at
//! which a `[0.25, 4.0]` band is accidentally correct. Exactly the trap that kept
//! `OsmView`'s unit guard green through a 400× bug.
//!
//! These assert on the **applied** viewport — what the camera ends up at after a real
//! click on the real toolbar — not on a field written and read straight back.

use egui_kittest::Harness;
use egui_kittest::kittest::Queryable;
use facett_core::{Facet, FacetDeck};
use facett_geomap::{GeoMap, Msg};

const TAB: &str = "Geo";

/// A deck holding exactly one `GeoMap`, driven through `FacetDeck::ui` — the same call a
/// host makes. Two frames so the pane gets a real rect and the toolbar settles.
fn deck_harness() -> Harness<'static, FacetDeck> {
    let map = GeoMap::new(
        TAB,
        vec![(9.52, 47.14), (9.55, 47.16), (9.50, 47.12)],
        Vec::new(),
    );
    let deck = FacetDeck::new(vec![Box::new(map) as Box<dyn Facet>]);
    let mut harness = Harness::builder()
        .with_size(egui::vec2(1200.0, 900.0))
        .build_ui_state(|ui, deck: &mut FacetDeck| deck.ui(ui), deck);
    harness.run();
    harness.run();
    harness
}

fn zoom_of(deck: &mut FacetDeck) -> f64 {
    let map: &mut GeoMap = deck.facet_mut(TAB).expect("the deck is holding the GeoMap facet");
    map.state().zoom
}

/// **RED-when-broken: after the user wheels in past `zoom = 4`, the toolbar's `+` still
/// zooms IN.** The visible bug, asserted on the applied viewport zoom.
#[test]
fn zoom_in_after_a_wheel_gesture_still_zooms_in() {
    let mut harness = deck_harness();

    // The wheel path: `Msg::ZoomBy`, straight into the viewport, clamped only by the
    // map's own 0.25..=5e6 — never through `Facet::set_scale`.
    {
        let map: &mut GeoMap = harness.state_mut().facet_mut(TAB).expect("the GeoMap facet");
        let _ = map.update(Msg::ZoomBy(50.0));
    }
    harness.run();

    let before = zoom_of(harness.state_mut());
    assert!(before > 4.0, "precondition: the wheel took the viewport past 4.0 (got {before})");

    harness.get_by_label("+").click();
    harness.run();
    let after = zoom_of(harness.state_mut());

    assert!(
        after > before,
        "clicking ZOOM IN (+) must raise the viewport zoom, but it went {before} → {after} \
         (a {:.1}× COLLAPSE). FacetDeck::scale_active clamped {before} × 1.1 into the \
         document-viewer band [0.25, 4.0] and wrote the viewport backwards. The band has \
         to come from the facet (Facet::scale_range).",
        before / after.max(f64::MIN_POSITIVE),
    );
}

/// **RED-when-broken: the toolbar can drive the whole zoom band the map allows.** `+`
/// must keep zooming in past the deck's old `4.0` ceiling, all the way toward the map's
/// own 5e6, instead of stalling a couple of clicks past the default.
#[test]
fn the_toolbar_can_drive_the_whole_zoom_band() {
    let mut harness = deck_harness();
    let start = zoom_of(harness.state_mut());

    let mut prev = start;
    for i in 1..=20 {
        harness.get_by_label("+").click();
        harness.run();
        let now = zoom_of(harness.state_mut());
        assert!(
            now > prev,
            "click #{i} of ZOOM IN must still zoom in ({prev} → {now}). It stalled at \
             {:.2}× the opening viewport — the deck's [0.25, 4.0] clamp capping a map \
             whose own `update` allows zoom up to 5e6.",
            prev / start,
        );
        prev = now;
    }
    assert!(prev / start > 6.0, "20 × 1.1 = 6.7×; got {:.2}×", prev / start);
}