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

nordisk/facett

facett-map3d/tests/lane_flag.rs

//! **"A flag saying GPU or CPU when rendering osm 3d, so i know whats being used"** —
//! The owner, 2026-08-23. This is the guard that it is really ON SCREEN, and that its
//! third state exists.
//!
//! # Why the assertion is the a11y tree and not a pixel
//!
//! `facett_core::devid::badge` learned this the hard way and says so: *"Painted-only text
//! is invisible to every check we have — which is how a badge ships green and never
//! appears."* So the chip is emitted as a real AccessKit node and the oracle is
//! `get_by_label` / `query_by_label` over that tree — which proves the chip was laid out
//! at a real rect in a real pass, not that some code path could have painted it.
//!
//! # Three states, and the third is the point
//!
//! `Map3D::used_gpu_observed()` is `None` until a frame has been through the backend.
//! Rendering that as `CPU` is the exact lie this codebase keeps getting caught by: on
//! 2026-08-06 a pre-paint read of `used_gpu()` published `false` — the spelling of the
//! real defect — on a build with the GPU lane compiled in, the renderer installed and a
//! discrete card selected, and three people read it as the defect. So `Pending` is
//! asserted as its own answer, distinct from both.
//!
//! Run (the GPU arm needs the real device and opens NO window):
//! ```text
//! env -u WAYLAND_DISPLAY -u DISPLAY \
//!   cargo test -p facett-map3d --features wgpu --test lane_flag -- --test-threads=1 --nocapture
//! ```

use egui_kittest::kittest::Queryable;
use facett_core::render::lane::{lane_flag_atom, LaneFlag};
use facett_core::Theme;
use facett_map::Way;
use facett_map3d::Map3D;

const CANVAS: egui::Vec2 = egui::vec2(640.0, 480.0);

/// Six little buildings in central Stockholm — the same shape
/// `gpu_lane_before_the_first_frame::scene` uses, so both files probe one scene.
fn scene() -> Map3D {
    let theme = Theme::default();
    let ring = |lon: f64, lat: f64, h: f64| {
        vec![(lon - h, lat - h), (lon + h, lat - h), (lon + h, lat + h), (lon - h, lat + h), (lon - h, lat - h)]
    };
    let mut ways: Vec<Way> = Vec::new();
    for i in 0..6 {
        let d = f64::from(i) * 0.0004;
        ways.extend(Way::from_lonlat(
            100 + i64::from(i),
            &ring(18.0686 + d, 59.3293 + d, 0.00008),
            &[("building", "yes")],
            &theme,
        ));
    }
    Map3D::from_geometry("lane probe", &ways)
}

/// The three atom labels, so a "present" assertion cannot accidentally match the wrong
/// one and a "absent" assertion covers every alternative.
fn atoms() -> [String; 3] {
    [lane_flag_atom(LaneFlag::Gpu), lane_flag_atom(LaneFlag::Cpu), lane_flag_atom(LaneFlag::Pending)]
}

/// Assert the pane shows EXACTLY `want` and none of the other two — the part that makes
/// this non-vacuous. "the GPU label is there" is satisfied by a pane that paints all
/// three; "exactly one, and it is this one" is not.
fn assert_shows_exactly(harness: &egui_kittest::Harness<'_>, want: LaneFlag) {
    let wanted = lane_flag_atom(want);
    let present: Vec<String> =
        atoms().into_iter().filter(|a| harness.query_by_label(a.as_str()).is_some()).collect();
    assert_eq!(
        present,
        vec![wanted.clone()],
        "the pane must carry EXACTLY the {want:?} flag ({wanted:?}); the a11y tree carries \
         {present:?}"
    );
}

// ─────────────────────────────────────────────────────────────────────────────

/// **PENDING is a real state, and it is not CPU.** Before the scene has been through
/// `view` there is no measurement, and `lane_flag()` must say so.
///
/// Non-vacuous both ways: the same scene must STOP being pending once it has painted, or
/// this would pass on a `lane_flag()` hardwired to `Pending`.
///
/// RED: make `Map3D::lane_flag` read `used_gpu()` (the `bool` reader that collapses "not
/// measured" into `false`) instead of `used_gpu_observed()` — the first assertion then
/// reports `Cpu` before a single frame has existed, which is the 2026-08-06 defect
/// reproduced exactly.
#[test]
fn before_any_paint_the_flag_is_pending_and_after_one_it_is_not() {
    let mut map = scene();
    assert_eq!(
        map.lane_flag(),
        LaneFlag::Pending,
        "a scene that has never been painted has no lane to report — it must not claim the \
         CPU painter drew it"
    );
    assert_eq!(map.used_gpu_observed(), None, "…and that is exactly what the raw reader says");

    let mut harness = egui_kittest::Harness::builder()
        .with_size(CANVAS)
        .build_ui(move |ui| {
            facett_core::set_theme(ui.ctx(), Theme::default());
            // **The call korp makes**, not the inherent `Map3D::view`. korp paints this
            // pane with `facett_core::Facet::ui(map, ui)` (korp/src/map_views.rs:2210),
            // which the `impl_facet_via_elm!` bridge writes as the FC-9 loop
            // `for m in view(ui) { update(m) }`. Driving the inherent method would prove
            // the FUNCTION paints the chip and leave the PROGRAM unproven — the same
            // function-vs-program gap that made `run_watch`'s degrade promise true of the
            // function and false of the binary.
            facett_core::Facet::ui(&mut map, ui);
            // Read INSIDE the closure: the flag is a fact about the paint that just
            // happened, and `map` is moved in here.
            FLAG_AFTER.with(|c| c.set(Some(map.lane_flag())));
        });
    // `run_steps`, never `run()`: `Map3D::view` keeps requesting repaints (the idle
    // animation at lib.rs:4884), so `run()` trips its max-steps guard — that is a
    // property of the pane, not a failure of this probe.
    harness.run_steps(3);

    let after = FLAG_AFTER.with(std::cell::Cell::get).expect("the closure ran at least once");
    assert_ne!(
        after,
        LaneFlag::Pending,
        "after a real pass the pane must have an ANSWER — a `lane_flag` stuck on Pending \
         would satisfy the first assertion and mean nothing"
    );
}

thread_local! {
    static FLAG_AFTER: std::cell::Cell<Option<LaneFlag>> = const { std::cell::Cell::new(None) };
}

/// **The CPU painter shows CPU.** The default backend IS the CPU painter (`wgpu` is off by
/// default and no renderer is installed here), so this is the shipped answer for every
/// host without the GPU lane — and it must be on the pane, not only in a log.
///
/// RED: change `paint_lane_flag`'s call site in `Map3D::view` to sit BEFORE
/// `self.backend.paint(..)` — the flag then reads `—` on every frame forever, which is
/// the same non-answer `log_what_it_is_drawing` printed on 41 consecutive draws.
#[test]
fn the_cpu_painter_shows_cpu_on_the_pane() {
    let mut map = scene();
    assert!(!map.gpu_lane_selected(), "fixture: no GPU lane is selected in this build/config");
    let mut harness = egui_kittest::Harness::builder()
        .with_size(CANVAS)
        .build_ui(move |ui| {
            facett_core::set_theme(ui.ctx(), Theme::default());
            facett_core::Facet::ui(&mut map, ui);
        });
    harness.run_steps(3);
    assert_shows_exactly(&harness, LaneFlag::Cpu);
}

/// **The GPU lane shows GPU** — on the real device, through facett's OWN adapter policy.
///
/// Deliberately NOT `egui_kittest::wgpu::default_wgpu_setup()`: its selector prefers
/// software rasterisers, so a green there would have said "the GPU lane ran" about
/// llvmpipe while an RTX 4090 sat beside it (the argument
/// `gpu_lane_before_the_first_frame` makes at length).
///
/// RED: pass `with_gpu(false)` — the pane then shows `lane: CPU` and this fails, which is
/// also the proof that the two arms of this file are not both reading one constant.
#[cfg(feature = "wgpu")]
#[test]
fn the_gpu_lane_shows_gpu_on_the_pane() {
    use egui_kittest::wgpu::{create_render_state, WgpuTestRenderer};

    let setup = facett_core::render::gpu::adapter_wgpu::facett_wgpu_options().wgpu_setup;
    let rs = create_render_state(setup, Default::default());
    let info = rs.adapter.get_info();
    eprintln!("LOOK: adapter {} [{:?}/{:?}]", info.name, info.device_type, info.backend);
    facett_map3d::gpu::install_renderer(&rs);

    let mut map = scene().with_gpu(true);
    assert!(map.gpu_lane_selected(), "fixture: the wgpu lane is selected");
    assert_eq!(map.lane_flag(), LaneFlag::Pending, "…and nothing has been observed yet");

    let mut harness = egui_kittest::Harness::builder()
        .with_size(CANVAS)
        .renderer(WgpuTestRenderer::from_render_state(rs))
        .build_ui(move |ui| {
            facett_core::set_theme(ui.ctx(), Theme::default());
            facett_core::Facet::ui(&mut map, ui);
        });
    harness.run_steps(3);
    let _img = harness.render().expect("the wgpu backend produced a frame");
    // One more laid-out pass AFTER the render, so the tree carries the label the pane
    // painted from an observation the device had already answered.
    harness.run_steps(1);
    assert_shows_exactly(&harness, LaneFlag::Gpu);
}