//! Laying a tree out again has to land where growing it that way would. //! //! Every case is one of `scenario`'s, over the trees `iris::random` grows //! from a seed. The fast test takes a handful of seeds and the ignored one //! takes as many as it is asked for; both run the same cases the shrinker //! does over the same trees, so a seed that fails here is reduced by //! //! SHRINK_SEED= SHRINK_DEPTH= SHRINK_CASE= \ //! cargo test --release --test shrink -- --ignored --nocapture //! //! `IRIS_GENERATED_SEED`, `IRIS_GENERATED_SEEDS` and `IRIS_GENERATED_DEPTH` //! select what the long run covers. #[path = "scenario/mod.rs"] mod scenario; use iris::random::{Edits, plan}; use scenario::{ALL, Case, diverges, env, over_seeds}; /// How deep the generator branches. The generator widens two to four ways per /// level, so depth is exponential in width and a deep narrow tree is not /// reachable by raising this -- it buys more overlap between dependency /// paths, not more ancestry. fn depth() -> usize { env("IRIS_GENERATED_DEPTH", 4) } /// The seeds the ordinary tests take. Seven that have never failed; 86, /// which a `Scroll` fixed point once settled differently on; and 20, which /// caught a locally redrawn widget being placed twice in the box its parent /// had already placed it in. const SEEDS: [u64; 10] = [1, 2, 3, 5, 8, 10, 13, 20, 86, 98]; fn check(seed: u64, depth: usize, case: Case) { let grown = plan(seed, depth, &Edits::default()); if let Some(how) = diverges(&grown, case, seed) { panic!( "seed {seed} at depth {depth} differs after {}: {how}\n\ reduce it with SHRINK_SEED={seed} SHRINK_DEPTH={depth} \ SHRINK_CASE={} cargo test --release --test shrink -- --ignored --nocapture", case.name(), case.name(), ); } } macro_rules! case { ($name:ident, $case:expr) => { #[test] fn $name() { for seed in SEEDS { check(seed, depth(), $case); } } }; } case!( many_widgets_redrawing_at_once_leaves_every_box_where_it_was, Case::RepaintSome ); case!( everything_redrawing_at_once_leaves_every_box_where_it_was, Case::Repaint ); case!( a_resize_lands_where_starting_at_that_size_would, Case::Resize ); case!( a_resize_and_a_repaint_land_where_starting_that_way_would, Case::ResizeRepaint ); case!( a_size_change_after_a_resize_lands_the_same_way, Case::ResizeSize ); case!( a_size_change_lands_where_growing_it_that_way_would, Case::Size ); case!( every_size_changing_at_once_lands_where_growing_it_that_way_would, Case::EverySize ); case!( an_alignment_change_lands_where_growing_it_that_way_would, Case::Align ); case!( giving_and_taking_a_movable_region_rebuilds_what_resolves_it, Case::RegionNode ); case!( reordering_a_span_lands_where_growing_it_that_way_would, Case::Reorder ); #[test] fn adding_and_removing_span_children_lands_where_growing_it_that_way_would() { for case in ALL { if matches!(case, Case::Shuffle(_)) { for seed in SEEDS { check(seed, depth(), case); } } } } #[test] #[ignore = "as many seeds as it is asked for, rather than the nine the others check"] fn a_long_run_of_seeds_agrees() { let depth = depth(); let seeds: Vec = match std::env::var("IRIS_GENERATED_SEED") .ok() .and_then(|v| v.parse().ok()) { Some(seed) => vec![seed], None => (1..=env("IRIS_GENERATED_SEEDS", 100_u64)).collect(), }; over_seeds(seeds, |seed| { for case in ALL { check(seed, depth, case); } }); }