facett-demo/tests/osm_tabs.rs
//! **OSM tab non-blank render PROOF** (CPU path, headless — no GPU in CI).
//!
//! The "200k points in state but the pane is BLACK" class of bug: a map view can
//! report a fat `state_json` (lots of points/ways) yet draw *nothing* (a blank
//! pane). This test renders both OSM tabs through facett's **CPU** headless
//! harness (egui's software tessellator — no wgpu, so it runs on a GPU-less CI
//! box) and asserts that real geometry actually reached the tessellator:
//! `vertices > 0` **and** the view's own `state_json` reports the geometry it
//! should be drawing (`geometry > 0`). A blank pane → `vertices == 0` → RED.
//!
//! It runs on **real liechtenstein GeoParquet** when present on `/home` (the
//! default the demo ships with), and otherwise on the synthetic in-memory sample
//! so the test always runs and always asserts. Either way each tab emits one
//! `nornir_testmatrix::functional_status` row (the EMIT-DOCTRINE "did the map
//! render non-blank" check) under `--features testmatrix`.
//!
//! Run:
//! cargo test -p facett-demo --test osm_tabs
//! cargo test -p facett-demo --test osm_tabs --features testmatrix # feeds the matrix
use facett::Facet;
use facett::core::harness;
use facett_demo::osm_tabs::{self, OsmSource};
/// Render a facet through the CPU headless harness and return `(vertices, geometry,
/// state)`. `geometry` is the view's own count of what it should draw — points +
/// ways for 2D, triangles for 3D — read from its `state_json`.
fn render_geometry(facet: &mut dyn Facet, geom_keys: &[&str]) -> (usize, usize, serde_json::Value) {
let r = harness::headless_render(facet);
let geometry: usize = geom_keys
.iter()
.filter_map(|k| r.state.get(*k).and_then(|v| v.as_u64()))
.map(|n| n as usize)
.sum();
(r.vertices, geometry, r.state)
}
#[test]
fn osm_2d_and_3d_tabs_render_non_blank() {
// Load the real liechtenstein data if it's on /home (the demo's default),
// else the synthetic sample. Either way we render + assert non-blank.
let geo = osm_tabs::load();
let on_real = geo.source == OsmSource::Liechtenstein || geo.source == OsmSource::Env;
eprintln!(
"osm_tabs test: source = {:?} ({} points, {} ways)",
geo.source,
geo.points.len(),
geo.ways_2d.len()
);
// ── 2D OSM tab ────────────────────────────────────────────────────────────
// Force the CPU painter lane for the headless proof: the GPU line-ways
// fast-path emits a paint callback that no-ops without an installed renderer
// (no GPU in CI), which would leave the line layer undrawn in the harness. The
// CPU lane is the one we can PROVE non-blank here; the GPU lane is verified on
// a real GPU box (see the report). `with_gpu` only exists under `map-wgpu`.
let mut v2 = osm_tabs::osm2d_view(&geo);
#[cfg(feature = "map-wgpu")]
{
v2 = v2.with_gpu(false);
}
let (verts2, geom2, st2) = render_geometry(&mut v2, &["points", "ways"]);
eprintln!(" OSM 2D → {verts2} verts, geometry={geom2}, visible_points={}", st2["visible_points"]);
// The non-blank contract: real tessellated vertices AND real geometry behind
// them (this is exactly what catches "fat state, black pane").
assert!(geom2 > 0, "OSM 2D has geometry to draw (points + ways)");
assert!(verts2 > 0, "OSM 2D tessellated to vertices — the pane is NOT blank");
// After the camera fits the data, some points fall inside the viewport (so the
// dots layer actually drew, not just the background rect).
assert!(
st2["visible_points"].as_u64().unwrap() > 0,
"OSM 2D shows points after the fit (the dot layer is live, not a black pane)"
);
// ── 3D OSM tab ────────────────────────────────────────────────────────────
let mut v3 = osm_tabs::osm3d_view(&geo);
let (verts3, geom3, st3) = render_geometry(&mut v3, &["triangles"]);
eprintln!(
" OSM 3D → {verts3} verts, geometry={geom3} tris, visible_triangles={}",
st3["visible_triangles"]
);
assert!(geom3 > 0, "OSM 3D extruded a real mesh (triangles)");
assert!(verts3 > 0, "OSM 3D tessellated to vertices — the pane is NOT blank");
assert!(
st3["visible_triangles"].as_u64().unwrap() > 0,
"OSM 3D camera projected visible triangles (the lit model drew, not a black pane)"
);
// If we DID load the real liechtenstein data, prove it's the big real set, not
// a fallback masquerading as real (353k nodes / 41k ways).
if on_real {
assert!(
st2["points"].as_u64().unwrap() > 100_000,
"real liechtenstein 2D loaded its ~353k nodes, got {}",
st2["points"]
);
assert!(
st2["ways"].as_u64().unwrap() > 5_000,
"real liechtenstein 2D loaded thousands of ways, got {}",
st2["ways"]
);
}
// EMIT-DOCTRINE: one functional row per tab — "did the map render non-blank".
// OK iff vertices > 0 AND the view reports geometry (emit_render's contract).
#[cfg(feature = "testmatrix")]
{
facett::core::testmatrix::emit_render("facett_demo.osm_2d", verts2, geom2);
facett::core::testmatrix::emit_render("facett_demo.osm_3d", verts3, geom3);
// A second, sharper row carrying the source + the visible-feature count, so
// the matrix shows the map was alive (visible features), not merely drawn.
facett::core::testmatrix::emit(
"facett_demo.osm_2d",
"renders_non_blank",
verts2 > 0 && st2["visible_points"].as_u64().unwrap() > 0,
&format!(
"source={:?} points={} ways={} visible_points={} verts={verts2}",
geo.source, st2["points"], st2["ways"], st2["visible_points"]
),
);
facett::core::testmatrix::emit(
"facett_demo.osm_3d",
"renders_non_blank",
verts3 > 0 && st3["visible_triangles"].as_u64().unwrap() > 0,
&format!(
"source={:?} triangles={} visible_triangles={} verts={verts3}",
geo.source, st3["triangles"], st3["visible_triangles"]
),
);
}
}