diff --git a/tests/drift.rs b/tests/drift.rs new file mode 100644 index 0000000..fe2059b --- /dev/null +++ b/tests/drift.rs @@ -0,0 +1,45 @@ +//! What a retained drawing costs in accuracy when it is moved instead of made +//! again. A subtree's stored regions are the only record of where it is, so a +//! move that works from the last answer rather than from the box it is now in +//! integrates its own rounding, and nothing later recomputes it. Re-expressing +//! each part as the same fraction of the new box is what keeps a long-lived +//! layout on the one a cold start produces. + +use iris::harness::Harness; +use iris::prelude::*; + +/// A row of a fixed height under a bar, so changing the bar's height moves the +/// row without changing the box it is given: the move path, repeatedly. +fn plant(h: &mut Harness, bar_height: f32) -> (WeakWidget, WeakWidget) { + let bar = rect(Color::RED).height(bar_height).add(&mut h.rsc); + let inner = rect(Color::BLUE).add(&mut h.rsc); + let row = (inner, rect(Color::GREEN)).span(Dir::RIGHT).height(100); + h.set_root((bar, row).span(Dir::DOWN)); + (bar, inner) +} + +/// Enough moves to pass the 0.05 physical pixels layout treats as the same +/// place, for a move that adds an offset to the last answer. Measured on this +/// fixture on 2026-09-15: adding the offset to both ends of a span shortened +/// the row by 0.071 over this many moves and by 0.712 over ten times as many, +/// growing with the count rather than settling. Placing the far end from the +/// near one instead left 0.069, because the length is re-derived either way. +const MOVES: usize = 20_000; + +#[test] +fn a_subtree_moved_many_times_stays_where_a_cold_layout_puts_it() { + let mut warm = Harness::new((640, 900)); + let (bar, inner) = plant(&mut warm, 40.0); + let mut height = 40.0; + for step in 0..MOVES { + height = 40.0 + (step % 300) as f32 * 0.37; + warm.rsc[bar].y = Some(Len::px(height)); + warm.frame(); + } + + let mut cold = Harness::new((640, 900)); + let (_, cold_inner) = plant(&mut cold, height); + cold.frame(); + + assert_eq!(warm.region(&inner), cold.region(&cold_inner)); +}