facett-map3d-flight/tests/sweden_flythrough.rs
//! **End-to-end Sweden fly-through (headless, FC-7).** Composes the whole "fly the
//! whole of Sweden" chain and asserts it as behaviour, not "didn't panic":
//!
//! 1. a [`FlightController`] pilots a [`FlightCamera`] along a **scripted input
//! path** (WASD + mouse-look, injected `dt` — no clock);
//! 2. each frame drives a [`TileLoader`] over a synthetic Sweden [`InMemoryTileSource`]
//! (`frame` → priority requests, worker streams `TileLoaded`, `poll` drains);
//! 3. we assert the **camera moved** across the country AND that tiles **streamed in +
//! evicted** as the view shifted, staying inside the tile budget.
//!
//! Run: `cargo test -p facett-map3d-flight --test sweden_flythrough`
//! (add `--features testmatrix` to emit the functional row).
use std::sync::Arc;
use facett_core::Theme;
use facett_map::Way;
use facett_map3d_flight::sweden::SpatialKey;
use facett_map3d_flight::{
ControlFrame, FlightCamera, FlightController, InMemoryTileSource, Msg, TileLoader, WorldPos,
};
/// Fill an in-memory source with `per_tile` ways for every z-`z` tile in a lon/lat
/// window over south-central Sweden — the region the scripted flight crosses.
fn sweden_source(z: u8, per_tile: usize) -> InMemoryTileSource {
let mut src = InMemoryTileSource::new();
let mut lat = 58.6;
while lat <= 60.0 {
let mut lon = 16.5;
while lon <= 19.5 {
let k = SpatialKey::from_lonlat(lon, lat, z);
src.insert(k, ways_at(k, per_tile));
lon += 0.02;
}
lat += 0.02;
}
src
}
fn ways_at(key: SpatialKey, n: usize) -> Vec<Way> {
let (lon, lat) = key.center_lonlat();
let mut out = Vec::new();
for i in 0..n {
let d = 0.0003 * (i as f64 + 1.0);
let ring = [(lon, lat), (lon + d, lat), (lon + d, lat + d), (lon, lat + d), (lon, lat)];
let tags = vec![("building".to_string(), "yes".to_string())];
if let Some(w) = Way::from_lonlat(i as i64, &ring, &tags, &Theme::default()) {
out.push(w);
}
}
out
}
#[test]
fn scripted_flight_streams_sweden_tiles_as_the_camera_flies() {
let z = 13u8;
let radius = 6_000.0;
let budget = 12;
let src = sweden_source(z, 6);
let mut loader = TileLoader::new(Arc::new(src), 128);
let ctl = FlightController::default();
// Start over Norrköping-ish, look east (+X), a few km up.
let mut cam = FlightCamera::free_fly(WorldPos::from_lonlat(16.6, 58.9, 3_000.0));
let start = cam.position;
// Scripted path: hold W + Shift (boosted forward flight, due east since yaw=0
// looks +X), a fixed dt — deterministic, no wall-clock. A straight run so the
// path stays inside the fixture window while crossing many tiles.
let frame_input = ControlFrame { w: true, shift: true, ..Default::default() };
let dt = 1.0 / 60.0;
let mut ever_loaded: std::collections::HashSet<SpatialKey> = Default::default();
let mut ever_evicted: std::collections::HashSet<SpatialKey> = Default::default();
let mut total_streamed = 0usize;
for _ in 0..2500 {
ctl.apply(&mut cam, &frame_input, dt);
let origin = cam.make_origin(256.0);
let report = loader.frame(origin, z, radius, budget);
for k in report.evicted {
ever_evicted.insert(k);
}
// Deterministically settle this frame's requests.
for msg in loader.wait_idle() {
if let Msg::TileLoaded(k, _) = msg {
ever_loaded.insert(k);
total_streamed += 1;
}
}
// Budget is honoured every frame.
assert!(loader.resident_count() <= budget, "resident tiles within budget: {}", loader.resident_count());
}
// The camera actually flew across the country (tens of km east).
let flew = cam.position.ground_distance(start);
assert!(flew > 30_000.0, "the camera flew a long way across Sweden ({flew:.0} m)");
// Tiles streamed in as new ground came into view, and old tiles were evicted.
assert!(ever_loaded.len() > budget, "the flight streamed many distinct tiles ({})", ever_loaded.len());
assert!(!ever_evicted.is_empty(), "tiles behind the camera were evicted");
assert!(total_streamed >= ever_loaded.len(), "every distinct tile was streamed at least once");
// The final resident set is exactly what is in view now (nothing stale).
let in_view: std::collections::HashSet<SpatialKey> =
facett_map3d_flight::tiles_in_view(cam.make_origin(256.0), z, radius).into_iter().take(budget).collect();
let resident = loader.resident_keys();
assert!(resident.is_subset(&in_view) || in_view.is_subset(&resident), "resident set tracks the current view");
eprintln!(
"[sweden_flythrough] flew={flew:.0} m, distinct_tiles={}, streamed={total_streamed}, evicted={}, final_resident={}",
ever_loaded.len(), ever_evicted.len(), resident.len()
);
#[cfg(feature = "testmatrix")]
facett_core::testmatrix::emit(
"facett-osm3d-sweden",
"sweden_flythrough",
flew > 30_000.0 && ever_loaded.len() > budget && !ever_evicted.is_empty(),
&format!("flew_m={flew:.0} distinct_tiles={} evicted={} final_resident={}", ever_loaded.len(), ever_evicted.len(), resident.len()),
);
}