atagen

monolith

public
Star0
Watch0
8ae66f06204a73baa3ea104d94b6664f09a9de36
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git

Blame: crates/layout/src/tests.rs

9baaafa4b167atagen2026-10-16 10:04 UTC1
use super::*;
9baaafa4b167atagen2026-10-16 10:04 UTC2
use proptest::prelude::*;
9baaafa4b167atagen2026-10-16 10:04 UTC3
9baaafa4b167atagen2026-10-16 10:04 UTC4
const AREA: Rect = Rect::new(0.0, 0.0, 1000.0, 600.0);
9baaafa4b167atagen2026-10-16 10:04 UTC5
9baaafa4b167atagen2026-10-16 10:04 UTC6
#[derive(Clone, Debug)]
9baaafa4b167atagen2026-10-16 10:04 UTC7
enum Op {
9baaafa4b167atagen2026-10-16 10:04 UTC8
    Add(u8, bool),
9baaafa4b167atagen2026-10-16 10:04 UTC9
    Remove(u8),
9baaafa4b167atagen2026-10-16 10:04 UTC10
    Focus(u8),
9baaafa4b167atagen2026-10-16 10:04 UTC11
    Move(Dir),
9baaafa4b167atagen2026-10-16 10:04 UTC12
    Block(Dir),
4444c779eb48atagen2026-10-16 12:07 UTC13
    Merge(Dir),
9baaafa4b167atagen2026-10-16 10:04 UTC14
    View(u8),
9baaafa4b167atagen2026-10-16 10:04 UTC15
    ToggleView(u8),
9baaafa4b167atagen2026-10-16 10:04 UTC16
    Retag(u8, u8),
9baaafa4b167atagen2026-10-16 10:04 UTC17
    Float(u8),
9baaafa4b167atagen2026-10-16 10:04 UTC18
    Expand(u8),
9baaafa4b167atagen2026-10-16 10:04 UTC19
    Flip,
9baaafa4b167atagen2026-10-16 10:04 UTC20
    Resize(bool, i8),
9baaafa4b167atagen2026-10-16 10:04 UTC21
    Scratch(u8),
9baaafa4b167atagen2026-10-16 10:04 UTC22
    Summon,
9baaafa4b167atagen2026-10-16 10:04 UTC23
    Unplug,
9baaafa4b167atagen2026-10-16 10:04 UTC24
    Replug,
9baaafa4b167atagen2026-10-16 10:04 UTC25
}
9baaafa4b167atagen2026-10-16 10:04 UTC26
9baaafa4b167atagen2026-10-16 10:04 UTC27
fn dir() -> impl Strategy<Value = Dir> {
9baaafa4b167atagen2026-10-16 10:04 UTC28
    prop_oneof![
9baaafa4b167atagen2026-10-16 10:04 UTC29
        Just(Dir::Left),
9baaafa4b167atagen2026-10-16 10:04 UTC30
        Just(Dir::Right),
9baaafa4b167atagen2026-10-16 10:04 UTC31
        Just(Dir::Up),
9baaafa4b167atagen2026-10-16 10:04 UTC32
        Just(Dir::Down)
9baaafa4b167atagen2026-10-16 10:04 UTC33
    ]
9baaafa4b167atagen2026-10-16 10:04 UTC34
}
9baaafa4b167atagen2026-10-16 10:04 UTC35
9baaafa4b167atagen2026-10-16 10:04 UTC36
fn op() -> impl Strategy<Value = Op> {
9baaafa4b167atagen2026-10-16 10:04 UTC37
    prop_oneof![
9baaafa4b167atagen2026-10-16 10:04 UTC38
        4 => (0u8..4, prop::bool::weighted(0.15)).prop_map(|(t, f)| Op::Add(t, f)),
9baaafa4b167atagen2026-10-16 10:04 UTC39
        1 => any::<u8>().prop_map(Op::Remove),
9baaafa4b167atagen2026-10-16 10:04 UTC40
        3 => any::<u8>().prop_map(Op::Focus),
9baaafa4b167atagen2026-10-16 10:04 UTC41
        4 => dir().prop_map(Op::Move),
9baaafa4b167atagen2026-10-16 10:04 UTC42
        2 => dir().prop_map(Op::Block),
4444c779eb48atagen2026-10-16 12:07 UTC43
        3 => dir().prop_map(Op::Merge),
9baaafa4b167atagen2026-10-16 10:04 UTC44
        1 => (0u8..4).prop_map(Op::View),
9baaafa4b167atagen2026-10-16 10:04 UTC45
        2 => (0u8..4).prop_map(Op::ToggleView),
9baaafa4b167atagen2026-10-16 10:04 UTC46
        1 => (any::<u8>(), 0u8..4).prop_map(|(w, t)| Op::Retag(w, t)),
9baaafa4b167atagen2026-10-16 10:04 UTC47
        1 => any::<u8>().prop_map(Op::Float),
9baaafa4b167atagen2026-10-16 10:04 UTC48
        1 => (0u8..3).prop_map(Op::Expand),
9baaafa4b167atagen2026-10-16 10:04 UTC49
        1 => Just(Op::Flip),
9baaafa4b167atagen2026-10-16 10:04 UTC50
        1 => (any::<bool>(), any::<i8>()).prop_map(|(a, d)| Op::Resize(a, d)),
9baaafa4b167atagen2026-10-16 10:04 UTC51
        1 => any::<u8>().prop_map(Op::Scratch),
9baaafa4b167atagen2026-10-16 10:04 UTC52
        1 => Just(Op::Summon),
9baaafa4b167atagen2026-10-16 10:04 UTC53
        1 => Just(Op::Unplug),
9baaafa4b167atagen2026-10-16 10:04 UTC54
        1 => Just(Op::Replug),
9baaafa4b167atagen2026-10-16 10:04 UTC55
    ]
9baaafa4b167atagen2026-10-16 10:04 UTC56
}
9baaafa4b167atagen2026-10-16 10:04 UTC57
9baaafa4b167atagen2026-10-16 10:04 UTC58
fn nth(l: &Layout, i: u8) -> Option<WindowId> {
9baaafa4b167atagen2026-10-16 10:04 UTC59
    let ids: Vec<WindowId> = l.windows().map(|(id, _)| id).collect();
9baaafa4b167atagen2026-10-16 10:04 UTC60
    (!ids.is_empty()).then(|| ids[i as usize % ids.len()])
9baaafa4b167atagen2026-10-16 10:04 UTC61
}
9baaafa4b167atagen2026-10-16 10:04 UTC62
9baaafa4b167atagen2026-10-16 10:04 UTC63
fn apply(l: &mut Layout, tags: &[TagId], op: &Op) {
9baaafa4b167atagen2026-10-16 10:04 UTC64
    let active = l.active_output();
9baaafa4b167atagen2026-10-16 10:04 UTC65
    match *op {
9baaafa4b167atagen2026-10-16 10:04 UTC66
        Op::Add(t, f) => {
9baaafa4b167atagen2026-10-16 10:04 UTC67
            let w = l.add_window(
9baaafa4b167atagen2026-10-16 10:04 UTC68
                None,
9baaafa4b167atagen2026-10-16 10:04 UTC69
                Some(tags[t as usize]),
9baaafa4b167atagen2026-10-16 10:04 UTC70
                f.then(|| Rect::new(10.0, 10.0, 200.0, 100.0)),
9baaafa4b167atagen2026-10-16 10:04 UTC71
            );
9baaafa4b167atagen2026-10-16 10:04 UTC72
            if l.visible(w) {
9baaafa4b167atagen2026-10-16 10:04 UTC73
                l.focus(w);
9baaafa4b167atagen2026-10-16 10:04 UTC74
            }
9baaafa4b167atagen2026-10-16 10:04 UTC75
        }
9baaafa4b167atagen2026-10-16 10:04 UTC76
        Op::Remove(i) => nth(l, i).into_iter().for_each(|w| l.remove_window(w)),
9baaafa4b167atagen2026-10-16 10:04 UTC77
        Op::Focus(i) => {
9baaafa4b167atagen2026-10-16 10:04 UTC78
            if let Some(w) = nth(l, i).filter(|&w| l.visible(w)) {
9baaafa4b167atagen2026-10-16 10:04 UTC79
                l.focus(w);
9baaafa4b167atagen2026-10-16 10:04 UTC80
            }
9baaafa4b167atagen2026-10-16 10:04 UTC81
        }
9baaafa4b167atagen2026-10-16 10:04 UTC82
        Op::Move(d) => {
9baaafa4b167atagen2026-10-16 10:04 UTC83
            l.move_window(d);
9baaafa4b167atagen2026-10-16 10:04 UTC84
        }
9baaafa4b167atagen2026-10-16 10:04 UTC85
        Op::Block(d) => {
4444c779eb48atagen2026-10-16 12:07 UTC86
            l.move_container(d);
4444c779eb48atagen2026-10-16 12:07 UTC87
        }
4444c779eb48atagen2026-10-16 12:07 UTC88
        Op::Merge(d) => {
4444c779eb48atagen2026-10-16 12:07 UTC89
            l.merge_window(d);
9baaafa4b167atagen2026-10-16 10:04 UTC90
        }
9baaafa4b167atagen2026-10-16 10:04 UTC91
        Op::View(t) => active.into_iter().for_each(|o| l.view(o, tags[t as usize])),
9baaafa4b167atagen2026-10-16 10:04 UTC92
        Op::ToggleView(t) => active
9baaafa4b167atagen2026-10-16 10:04 UTC93
            .into_iter()
9baaafa4b167atagen2026-10-16 10:04 UTC94
            .for_each(|o| l.toggle_view(o, tags[t as usize])),
9baaafa4b167atagen2026-10-16 10:04 UTC95
        Op::Retag(i, t) => nth(l, i)
9baaafa4b167atagen2026-10-16 10:04 UTC96
            .into_iter()
9baaafa4b167atagen2026-10-16 10:04 UTC97
            .for_each(|w| l.set_tag(w, tags[t as usize])),
9baaafa4b167atagen2026-10-16 10:04 UTC98
        Op::Float(i) => nth(l, i).into_iter().for_each(|w| l.toggle_floating(w)),
9baaafa4b167atagen2026-10-16 10:04 UTC99
        Op::Expand(s) => {
4444c779eb48atagen2026-10-16 12:07 UTC100
            l.expand(
4444c779eb48atagen2026-10-16 12:07 UTC101
                [
4444c779eb48atagen2026-10-16 12:07 UTC102
                    ExpandScope::Window,
4444c779eb48atagen2026-10-16 12:07 UTC103
                    ExpandScope::Container,
4444c779eb48atagen2026-10-16 12:07 UTC104
                    ExpandScope::All,
4444c779eb48atagen2026-10-16 12:07 UTC105
                ][s as usize],
4444c779eb48atagen2026-10-16 12:07 UTC106
            );
9baaafa4b167atagen2026-10-16 10:04 UTC107
        }
9baaafa4b167atagen2026-10-16 10:04 UTC108
        Op::Flip => active.into_iter().for_each(|o| l.flip(o)),
9baaafa4b167atagen2026-10-16 10:04 UTC109
        Op::Resize(h, d) => {
9baaafa4b167atagen2026-10-16 10:04 UTC110
            let axis = if h { Axis::Horizontal } else { Axis::Vertical };
9baaafa4b167atagen2026-10-16 10:04 UTC111
            l.resize(axis, f64::from(d) / 200.0);
9baaafa4b167atagen2026-10-16 10:04 UTC112
        }
9baaafa4b167atagen2026-10-16 10:04 UTC113
        Op::Scratch(i) => nth(l, i).into_iter().for_each(|w| l.scratchpad_add(w, "s")),
9baaafa4b167atagen2026-10-16 10:04 UTC114
        Op::Summon => {
9baaafa4b167atagen2026-10-16 10:04 UTC115
            l.summon("s");
9baaafa4b167atagen2026-10-16 10:04 UTC116
        }
9baaafa4b167atagen2026-10-16 10:04 UTC117
        Op::Unplug => {
9baaafa4b167atagen2026-10-16 10:04 UTC118
            let b = l.outputs().find(|(_, out)| out.name == "b").map(|(o, _)| o);
9baaafa4b167atagen2026-10-16 10:04 UTC119
            if let Some(o) = b {
9baaafa4b167atagen2026-10-16 10:04 UTC120
                l.remove_output(o);
9baaafa4b167atagen2026-10-16 10:04 UTC121
            }
9baaafa4b167atagen2026-10-16 10:04 UTC122
        }
9baaafa4b167atagen2026-10-16 10:04 UTC123
        Op::Replug => {
9baaafa4b167atagen2026-10-16 10:04 UTC124
            if !l.outputs().any(|(_, out)| out.name == "b") {
9baaafa4b167atagen2026-10-16 10:04 UTC125
                l.add_output("b", Rect::new(1000.0, 0.0, 800.0, 600.0));
9baaafa4b167atagen2026-10-16 10:04 UTC126
            }
9baaafa4b167atagen2026-10-16 10:04 UTC127
        }
9baaafa4b167atagen2026-10-16 10:04 UTC128
    }
9baaafa4b167atagen2026-10-16 10:04 UTC129
}
9baaafa4b167atagen2026-10-16 10:04 UTC130
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC131
/// The area a tiled window gets, zero for floating or unplaced ones.
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC132
fn tiled_area(l: &Layout, w: WindowId) -> f64 {
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC133
    let Some(o) = l.window(w).and_then(|x| x.output) else {
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC134
        return 0.0;
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC135
    };
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC136
    l.arrange(o)
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC137
        .tiled
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC138
        .iter()
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC139
        .find(|p| p.window == w && p.visible)
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC140
        .map_or(0.0, |p| p.rect.w * p.rect.h)
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC141
}
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC142
9baaafa4b167atagen2026-10-16 10:04 UTC143
fn check_geometry(l: &Layout) {
9baaafa4b167atagen2026-10-16 10:04 UTC144
    for (o, out) in l.outputs() {
9baaafa4b167atagen2026-10-16 10:04 UTC145
        let arr = l.arrange(o);
38125b3487d7atagen2026-10-17 05:02 UTC146
        let mut drawn: Vec<WindowId> = arr
38125b3487d7atagen2026-10-17 05:02 UTC147
            .tiled
38125b3487d7atagen2026-10-17 05:02 UTC148
            .iter()
38125b3487d7atagen2026-10-17 05:02 UTC149
            .filter(|p| p.visible)
38125b3487d7atagen2026-10-17 05:02 UTC150
            .map(|p| p.window)
38125b3487d7atagen2026-10-17 05:02 UTC151
            .chain(arr.floating.iter().map(|&(w, _)| w))
38125b3487d7atagen2026-10-17 05:02 UTC152
            .chain(arr.fullscreen)
38125b3487d7atagen2026-10-17 05:02 UTC153
            .collect();
38125b3487d7atagen2026-10-17 05:02 UTC154
        drawn.sort();
38125b3487d7atagen2026-10-17 05:02 UTC155
        drawn.dedup();
38125b3487d7atagen2026-10-17 05:02 UTC156
        let mut visible: Vec<WindowId> = l
38125b3487d7atagen2026-10-17 05:02 UTC157
            .windows()
38125b3487d7atagen2026-10-17 05:02 UTC158
            .filter(|&(w, win)| win.output == Some(o) && l.visible(w))
38125b3487d7atagen2026-10-17 05:02 UTC159
            .map(|(w, _)| w)
38125b3487d7atagen2026-10-17 05:02 UTC160
            .collect();
38125b3487d7atagen2026-10-17 05:02 UTC161
        visible.sort();
38125b3487d7atagen2026-10-17 05:02 UTC162
        assert_eq!(drawn, visible, "arrangement disagrees with visible()");
9baaafa4b167atagen2026-10-16 10:04 UTC163
        let shown: Vec<Rect> = arr
9baaafa4b167atagen2026-10-16 10:04 UTC164
            .tiled
9baaafa4b167atagen2026-10-16 10:04 UTC165
            .iter()
9baaafa4b167atagen2026-10-16 10:04 UTC166
            .filter(|p| p.visible)
9baaafa4b167atagen2026-10-16 10:04 UTC167
            .map(|p| p.rect)
9baaafa4b167atagen2026-10-16 10:04 UTC168
            .collect();
1c8e1b4009f5atagen2026-10-16 23:19 UTC169
        for p in arr.tiled.iter().filter(|p| !p.visible) {
1c8e1b4009f5atagen2026-10-16 23:19 UTC170
            assert!(p.rect.w * p.rect.h <= 1e-6, "hidden {:?} has area", p.rect);
1c8e1b4009f5atagen2026-10-16 23:19 UTC171
        }
9baaafa4b167atagen2026-10-16 10:04 UTC172
        for r in &shown {
9baaafa4b167atagen2026-10-16 10:04 UTC173
            assert!(r.w >= 0.0 && r.h >= 0.0);
9baaafa4b167atagen2026-10-16 10:04 UTC174
            assert!(r.x >= out.area.x - 1e-6 && r.right() <= out.area.right() + 1e-6);
9baaafa4b167atagen2026-10-16 10:04 UTC175
            assert!(r.y >= out.area.y - 1e-6 && r.bottom() <= out.area.bottom() + 1e-6);
9baaafa4b167atagen2026-10-16 10:04 UTC176
        }
9baaafa4b167atagen2026-10-16 10:04 UTC177
        for (i, a) in shown.iter().enumerate() {
9baaafa4b167atagen2026-10-16 10:04 UTC178
            for b in &shown[i + 1..] {
9baaafa4b167atagen2026-10-16 10:04 UTC179
                let ox = a.right().min(b.right()) - a.x.max(b.x);
9baaafa4b167atagen2026-10-16 10:04 UTC180
                let oy = a.bottom().min(b.bottom()) - a.y.max(b.y);
9baaafa4b167atagen2026-10-16 10:04 UTC181
                assert!(ox <= 1e-6 || oy <= 1e-6, "{a:?} overlaps {b:?}");
9baaafa4b167atagen2026-10-16 10:04 UTC182
            }
9baaafa4b167atagen2026-10-16 10:04 UTC183
        }
9baaafa4b167atagen2026-10-16 10:04 UTC184
    }
9baaafa4b167atagen2026-10-16 10:04 UTC185
}
9baaafa4b167atagen2026-10-16 10:04 UTC186
9baaafa4b167atagen2026-10-16 10:04 UTC187
proptest! {
9baaafa4b167atagen2026-10-16 10:04 UTC188
    #![proptest_config(ProptestConfig::with_cases(2000))]
9baaafa4b167atagen2026-10-16 10:04 UTC189
9baaafa4b167atagen2026-10-16 10:04 UTC190
    #[test]
86ef802692f5atagen2026-10-19 07:43 UTC191
    fn random_commands_keep_the_model_sound(
86ef802692f5atagen2026-10-19 07:43 UTC192
        ops in prop::collection::vec(op(), 1..80),
86ef802692f5atagen2026-10-19 07:43 UTC193
        autotile in any::<bool>(),
86ef802692f5atagen2026-10-19 07:43 UTC194
    ) {
86ef802692f5atagen2026-10-19 07:43 UTC195
        let mut l = Layout {
86ef802692f5atagen2026-10-19 07:43 UTC196
            autotile,
86ef802692f5atagen2026-10-19 07:43 UTC197
            ..Layout::default()
86ef802692f5atagen2026-10-19 07:43 UTC198
        };
9baaafa4b167atagen2026-10-16 10:04 UTC199
        l.add_output("a", AREA);
9baaafa4b167atagen2026-10-16 10:04 UTC200
        l.add_output("b", Rect::new(1000.0, 0.0, 800.0, 600.0));
9baaafa4b167atagen2026-10-16 10:04 UTC201
        let tags = [l.homeless(), l.tag("1"), l.tag("2"), l.tag("3")];
9baaafa4b167atagen2026-10-16 10:04 UTC202
        for op in &ops {
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC203
            let moving = matches!(op, Op::Move(_) | Op::Merge(_) | Op::Block(_));
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC204
            let shown_before = l.focused().filter(|&w| tiled_area(&l, w) > 1.0);
9baaafa4b167atagen2026-10-16 10:04 UTC205
            apply(&mut l, &tags, op);
9baaafa4b167atagen2026-10-16 10:04 UTC206
            l.check();
9baaafa4b167atagen2026-10-16 10:04 UTC207
            check_geometry(&l);
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC208
            if moving && let Some(w) = shown_before {
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC209
                prop_assert!(tiled_area(&l, w) > 1.0, "{w:?} vanished after {op:?}");
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC210
            }
9baaafa4b167atagen2026-10-16 10:04 UTC211
        }
9baaafa4b167atagen2026-10-16 10:04 UTC212
    }
38125b3487d7atagen2026-10-17 05:02 UTC213
38125b3487d7atagen2026-10-17 05:02 UTC214
    #[test]
38125b3487d7atagen2026-10-17 05:02 UTC215
    fn replugging_restores_what_unplugging_moved(ops in prop::collection::vec(op(), 1..60)) {
38125b3487d7atagen2026-10-17 05:02 UTC216
        let mut l = Layout::default();
38125b3487d7atagen2026-10-17 05:02 UTC217
        l.add_output("a", AREA);
38125b3487d7atagen2026-10-17 05:02 UTC218
        l.add_output("b", Rect::new(1000.0, 0.0, 800.0, 600.0));
38125b3487d7atagen2026-10-17 05:02 UTC219
        let tags = [l.homeless(), l.tag("1"), l.tag("2"), l.tag("3")];
38125b3487d7atagen2026-10-17 05:02 UTC220
        for op in &ops {
38125b3487d7atagen2026-10-17 05:02 UTC221
            apply(&mut l, &tags, op);
38125b3487d7atagen2026-10-17 05:02 UTC222
        }
38125b3487d7atagen2026-10-17 05:02 UTC223
        let Some(b) = l.outputs().find(|(_, out)| out.name == "b").map(|(o, _)| o) else {
38125b3487d7atagen2026-10-17 05:02 UTC224
            return Ok(());
38125b3487d7atagen2026-10-17 05:02 UTC225
        };
38125b3487d7atagen2026-10-17 05:02 UTC226
        let before = |l: &Layout, o| {
38125b3487d7atagen2026-10-17 05:02 UTC227
            let out = l.output(o).unwrap();
38125b3487d7atagen2026-10-17 05:02 UTC228
            (out.tree.windows().collect::<Vec<_>>(), out.floating.clone(), out.viewed.clone())
38125b3487d7atagen2026-10-17 05:02 UTC229
        };
38125b3487d7atagen2026-10-17 05:02 UTC230
        let was = before(&l, b);
38125b3487d7atagen2026-10-17 05:02 UTC231
        l.remove_output(b);
38125b3487d7atagen2026-10-17 05:02 UTC232
        let b = l.add_output("b", Rect::new(1000.0, 0.0, 800.0, 600.0));
38125b3487d7atagen2026-10-17 05:02 UTC233
        l.check();
38125b3487d7atagen2026-10-17 05:02 UTC234
        prop_assert_eq!(before(&l, b), was);
38125b3487d7atagen2026-10-17 05:02 UTC235
    }
9baaafa4b167atagen2026-10-16 10:04 UTC236
}
9baaafa4b167atagen2026-10-16 10:04 UTC237
9baaafa4b167atagen2026-10-16 10:04 UTC238
/// The hidden-slot rules from planner.html, demo 11.
9baaafa4b167atagen2026-10-16 10:04 UTC239
#[test]
9baaafa4b167atagen2026-10-16 10:04 UTC240
fn hidden_windows_keep_their_slots() {
9baaafa4b167atagen2026-10-16 10:04 UTC241
    let (a, b, c, d, e) = (1, 2, 3, 4, 5);
9baaafa4b167atagen2026-10-16 10:04 UTC242
    let mut t: Tree<u32> = Tree::default();
9baaafa4b167atagen2026-10-16 10:04 UTC243
    t.insert_row(a);
9baaafa4b167atagen2026-10-16 10:04 UTC244
    t.insert_after(d, a);
9baaafa4b167atagen2026-10-16 10:04 UTC245
    t.insert_after(b, d);
9baaafa4b167atagen2026-10-16 10:04 UTC246
    t.insert_row(c);
9baaafa4b167atagen2026-10-16 10:04 UTC247
    t.insert_after(e, c);
9baaafa4b167atagen2026-10-16 10:04 UTC248
    t.check();
9baaafa4b167atagen2026-10-16 10:04 UTC249
    let hidden = |w: u32| w != d && w != e;
9baaafa4b167atagen2026-10-16 10:04 UTC250
    let all = |_| true;
9baaafa4b167atagen2026-10-16 10:04 UTC251
    let rect = |t: &Tree<u32>, w| {
9baaafa4b167atagen2026-10-16 10:04 UTC252
        t.arrange(AREA, 0.0, all)
9baaafa4b167atagen2026-10-16 10:04 UTC253
            .into_iter()
9baaafa4b167atagen2026-10-16 10:04 UTC254
            .find(|p| p.0 == w)
9baaafa4b167atagen2026-10-16 10:04 UTC255
            .unwrap()
9baaafa4b167atagen2026-10-16 10:04 UTC256
            .1
9baaafa4b167atagen2026-10-16 10:04 UTC257
    };
9baaafa4b167atagen2026-10-16 10:04 UTC258
9baaafa4b167atagen2026-10-16 10:04 UTC259
    assert!(t.move_window(a, Dir::Right, hidden));
9baaafa4b167atagen2026-10-16 10:04 UTC260
    assert!(rect(&t, b).x < rect(&t, d).x && rect(&t, d).x < rect(&t, a).x);
9baaafa4b167atagen2026-10-16 10:04 UTC261
9baaafa4b167atagen2026-10-16 10:04 UTC262
    assert!(t.move_window(c, Dir::Up, hidden));
9baaafa4b167atagen2026-10-16 10:04 UTC263
    t.check();
9baaafa4b167atagen2026-10-16 10:04 UTC264
    assert_eq!(rect(&t, e).w, AREA.w, "E is demoted to a row of its own");
9baaafa4b167atagen2026-10-16 10:04 UTC265
}
60f00dd4f2beatagen2026-10-16 11:50 UTC266
60f00dd4f2beatagen2026-10-16 11:50 UTC267
/// Moving out of a row toward a lane of hidden windows joins that lane, so
60f00dd4f2beatagen2026-10-16 11:50 UTC268
/// they share it when shown again instead of a new lane opening between.
60f00dd4f2beatagen2026-10-16 11:50 UTC269
#[test]
60f00dd4f2beatagen2026-10-16 11:50 UTC270
fn moving_toward_a_hidden_lane_joins_it() {
60f00dd4f2beatagen2026-10-16 11:50 UTC271
    let (a, b, c, d, e) = (1, 2, 3, 4, 5);
60f00dd4f2beatagen2026-10-16 11:50 UTC272
    let mut t: Tree<u32> = Tree::default();
60f00dd4f2beatagen2026-10-16 11:50 UTC273
    t.insert_row(a);
60f00dd4f2beatagen2026-10-16 11:50 UTC274
    t.insert_after(b, a);
60f00dd4f2beatagen2026-10-16 11:50 UTC275
    t.insert_after(c, b);
60f00dd4f2beatagen2026-10-16 11:50 UTC276
    t.insert_row(d);
60f00dd4f2beatagen2026-10-16 11:50 UTC277
    t.insert_after(e, c);
60f00dd4f2beatagen2026-10-16 11:50 UTC278
    let tag_two = |w: u32| w != a && w != d;
60f00dd4f2beatagen2026-10-16 11:50 UTC279
    assert!(t.move_window(e, Dir::Down, tag_two));
60f00dd4f2beatagen2026-10-16 11:50 UTC280
    t.check();
60f00dd4f2beatagen2026-10-16 11:50 UTC281
    let all = t.arrange(AREA, 0.0, |_| true);
60f00dd4f2beatagen2026-10-16 11:50 UTC282
    let rect = |w| all.iter().find(|p| p.0 == w).unwrap().1;
60f00dd4f2beatagen2026-10-16 11:50 UTC283
    assert_eq!(rect(e).y, rect(d).y);
60f00dd4f2beatagen2026-10-16 11:50 UTC284
    assert!(rect(d).x < rect(e).x);
60f00dd4f2beatagen2026-10-16 11:50 UTC285
    assert!(rect(a).y < rect(d).y);
60f00dd4f2beatagen2026-10-16 11:50 UTC286
}
91957f0ae29eatagen2026-10-17 09:24 UTC287
91957f0ae29eatagen2026-10-17 09:24 UTC288
#[test]
605c44c8c5fcatagen2026-10-17 09:57 UTC289
fn merging_steps_out_of_a_stack_before_joining_another() {
91957f0ae29eatagen2026-10-17 09:24 UTC290
    let (a, b, c) = (1, 2, 3);
605c44c8c5fcatagen2026-10-17 09:57 UTC291
    let stacked = || {
605c44c8c5fcatagen2026-10-17 09:57 UTC292
        let mut t: Tree<u32> = Tree::default();
605c44c8c5fcatagen2026-10-17 09:57 UTC293
        t.insert_row(a);
605c44c8c5fcatagen2026-10-17 09:57 UTC294
        t.insert_after(b, a);
605c44c8c5fcatagen2026-10-17 09:57 UTC295
        t.insert_after(c, b);
605c44c8c5fcatagen2026-10-17 09:57 UTC296
        assert!(t.merge_window(b, Dir::Left, |_| true));
605c44c8c5fcatagen2026-10-17 09:57 UTC297
        t
605c44c8c5fcatagen2026-10-17 09:57 UTC298
    };
605c44c8c5fcatagen2026-10-17 09:57 UTC299
    let rects = |t: &Tree<u32>| {
605c44c8c5fcatagen2026-10-17 09:57 UTC300
        t.check();
605c44c8c5fcatagen2026-10-17 09:57 UTC301
        let all = t.arrange(AREA, 0.0, |_| true);
605c44c8c5fcatagen2026-10-17 09:57 UTC302
        move |w| all.iter().find(|p: &&(u32, Rect)| p.0 == w).unwrap().1
605c44c8c5fcatagen2026-10-17 09:57 UTC303
    };
605c44c8c5fcatagen2026-10-17 09:57 UTC304
605c44c8c5fcatagen2026-10-17 09:57 UTC305
    let mut t = stacked();
605c44c8c5fcatagen2026-10-17 09:57 UTC306
    assert!(t.merge_window(a, Dir::Right, |_| true));
605c44c8c5fcatagen2026-10-17 09:57 UTC307
    let rect = rects(&t);
605c44c8c5fcatagen2026-10-17 09:57 UTC308
    assert!(rect(b).x < rect(a).x && rect(a).x < rect(c).x);
605c44c8c5fcatagen2026-10-17 09:57 UTC309
    assert_eq!(rect(a).h, AREA.h);
605c44c8c5fcatagen2026-10-17 09:57 UTC310
    assert!(t.merge_window(a, Dir::Right, |_| true));
605c44c8c5fcatagen2026-10-17 09:57 UTC311
    let rect = rects(&t);
605c44c8c5fcatagen2026-10-17 09:57 UTC312
    assert_eq!(rect(a).x, rect(c).x);
605c44c8c5fcatagen2026-10-17 09:57 UTC313
605c44c8c5fcatagen2026-10-17 09:57 UTC314
    let mut t = stacked();
91957f0ae29eatagen2026-10-17 09:24 UTC315
    assert!(t.merge_window(a, Dir::Up, |_| true));
605c44c8c5fcatagen2026-10-17 09:57 UTC316
    let rect = rects(&t);
91957f0ae29eatagen2026-10-17 09:24 UTC317
    assert_eq!(rect(a).w, AREA.w);
91957f0ae29eatagen2026-10-17 09:24 UTC318
    assert!(rect(a).bottom() <= rect(b).y && rect(b).y == rect(c).y);
91957f0ae29eatagen2026-10-17 09:24 UTC319
}
54559daf11c3atagen2026-10-17 13:38 UTC320
54559daf11c3atagen2026-10-17 13:38 UTC321
/// A window alone on screen in a container of hidden ones expands among the
54559daf11c3atagen2026-10-17 13:38 UTC322
/// windows it is shown beside, not inside the container nobody can see.
54559daf11c3atagen2026-10-17 13:38 UTC323
#[test]
54559daf11c3atagen2026-10-17 13:38 UTC324
fn expand_skips_containers_of_hidden_windows() {
54559daf11c3atagen2026-10-17 13:38 UTC325
    let (a, b, c) = (1, 2, 3);
54559daf11c3atagen2026-10-17 13:38 UTC326
    let mut t: Tree<u32> = Tree::default();
54559daf11c3atagen2026-10-17 13:38 UTC327
    t.insert_row(a);
54559daf11c3atagen2026-10-17 13:38 UTC328
    t.insert_after(b, a);
54559daf11c3atagen2026-10-17 13:38 UTC329
    t.insert_after(c, b);
54559daf11c3atagen2026-10-17 13:38 UTC330
    assert!(t.merge_window(a, Dir::Right, |_| true));
54559daf11c3atagen2026-10-17 13:38 UTC331
    let shown = |w: u32| w != a;
54559daf11c3atagen2026-10-17 13:38 UTC332
    assert!(t.toggle_expand(b, 0, 1, shown));
54559daf11c3atagen2026-10-17 13:38 UTC333
    assert!(t.maximized(b, shown));
54559daf11c3atagen2026-10-17 13:38 UTC334
    let all = t.arrange(AREA, 0.0, shown);
54559daf11c3atagen2026-10-17 13:38 UTC335
    let rect = |w| all.iter().find(|p| p.0 == w).unwrap().1;
54559daf11c3atagen2026-10-17 13:38 UTC336
    assert_eq!(rect(b).w, AREA.w);
54559daf11c3atagen2026-10-17 13:38 UTC337
}
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC338
e2443dec4dcdatagen2026-10-18 09:58 UTC339
/// Going in and out of a stack repeatedly leaves every window the size it was.
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC340
#[test]
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC341
fn stacking_in_and_out_does_not_drift() {
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC342
    for out in [Dir::Up, Dir::Down, Dir::Right] {
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC343
        let (a, b, c) = (1, 2, 3);
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC344
        let mut t: Tree<u32> = Tree::default();
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC345
        t.insert_row(a);
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC346
        t.insert_after(b, a);
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC347
        t.insert_after(c, b);
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC348
        assert!(t.merge_window(c, Dir::Left, |_| true));
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC349
        assert!(t.merge_window(c, out, |_| true));
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC350
        let once = t.arrange(AREA, 0.0, |_| true);
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC351
        for _ in 0..8 {
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC352
            t.merge_window(c, Dir::Left, |_| true);
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC353
            t.merge_window(c, out, |_| true);
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC354
        }
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC355
        t.check();
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC356
        assert_eq!(t.arrange(AREA, 0.0, |_| true), once, "stepping out {out:?}");
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC357
    }
3b1cf5ee2ea2atagen2026-10-17 13:53 UTC358
}
9c0e9500c151atagen2026-10-18 09:09 UTC359
e2443dec4dcdatagen2026-10-18 09:58 UTC360
/// Windows of other tags in the rows around a window must not change what
e2443dec4dcdatagen2026-10-18 09:58 UTC361
/// the user sees as it moves through them, nor drift over round trips.
9c0e9500c151atagen2026-10-18 09:09 UTC362
#[test]
9c0e9500c151atagen2026-10-18 09:09 UTC363
fn moving_past_hidden_windows_does_not_drift() {
9c0e9500c151atagen2026-10-18 09:09 UTC364
    let (a, b, h, h2) = (1, 2, 3, 4);
9c0e9500c151atagen2026-10-18 09:09 UTC365
    let mut t: Tree<u32> = Tree::default();
9c0e9500c151atagen2026-10-18 09:09 UTC366
    t.insert_row(h);
9c0e9500c151atagen2026-10-18 09:09 UTC367
    t.insert_row(a);
9c0e9500c151atagen2026-10-18 09:09 UTC368
    t.insert_after(h2, a);
9c0e9500c151atagen2026-10-18 09:09 UTC369
    t.insert_after(b, h2);
9c0e9500c151atagen2026-10-18 09:09 UTC370
    let vis = |w: u32| w != h && w != h2;
00c7b3db8a3fatagen2026-10-18 10:19 UTC371
    let mut once = None;
9c0e9500c151atagen2026-10-18 09:09 UTC372
    for _ in 0..4 {
9c0e9500c151atagen2026-10-18 09:09 UTC373
        for d in [Dir::Up, Dir::Up, Dir::Down] {
e2443dec4dcdatagen2026-10-18 09:58 UTC374
            t.move_window(b, d, vis);
e2443dec4dcdatagen2026-10-18 09:58 UTC375
            let shown = t.arrange(AREA, 0.0, vis);
e2443dec4dcdatagen2026-10-18 09:58 UTC376
            let r = shown.iter().find(|p| p.0 == b).unwrap().1;
e2443dec4dcdatagen2026-10-18 09:58 UTC377
            assert!(
e2443dec4dcdatagen2026-10-18 09:58 UTC378
                (r.w * r.h - AREA.w * AREA.h / 2.0).abs() < 1e-6,
e2443dec4dcdatagen2026-10-18 09:58 UTC379
                "{d:?} left {r:?}"
e2443dec4dcdatagen2026-10-18 09:58 UTC380
            );
9c0e9500c151atagen2026-10-18 09:09 UTC381
        }
9c0e9500c151atagen2026-10-18 09:09 UTC382
        t.check();
00c7b3db8a3fatagen2026-10-18 10:19 UTC383
        let all = t.arrange(AREA, 0.0, |_| true);
00c7b3db8a3fatagen2026-10-18 10:19 UTC384
        assert_eq!(*once.get_or_insert_with(|| all.clone()), all);
9c0e9500c151atagen2026-10-18 09:09 UTC385
    }
9c0e9500c151atagen2026-10-18 09:09 UTC386
}
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC387
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC388
/// Monitors sleeping together unplug one after another, so windows already
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC389
/// moved off the first must still go home to it, not to the second.
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC390
#[test]
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC391
fn windows_return_to_where_they_were_first_evicted_from() {
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC392
    let mut l = Layout::default();
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC393
    let a = l.add_output("a", AREA);
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC394
    l.add_output("b", Rect::new(1000.0, 0.0, 800.0, 600.0));
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC395
    let c = l.add_output("c", Rect::new(1800.0, 0.0, 800.0, 600.0));
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC396
    let w = l.add_window(Some(a), None, None);
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC397
    l.remove_output(a);
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC398
    let b = l.outputs().find(|(_, out)| out.name == "b").unwrap().0;
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC399
    l.remove_output(b);
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC400
    assert_eq!(l.window(w).unwrap().output, Some(c));
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC401
    l.add_output("b", Rect::new(1000.0, 0.0, 800.0, 600.0));
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC402
    let a = l.add_output("a", AREA);
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC403
    l.check();
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC404
    assert_eq!(l.window(w).unwrap().output, Some(a));
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC405
    assert!(l.output(a).unwrap().tree.contains(w));
8ffa3fa4d4a9atagen2026-10-18 09:38 UTC406
}
00c7b3db8a3fatagen2026-10-18 10:19 UTC407
00c7b3db8a3fatagen2026-10-18 10:19 UTC408
/// A stack showing one window looks like a lone window, so moving toward it
00c7b3db8a3fatagen2026-10-18 10:19 UTC409
/// swaps places instead of joining a stack the user cannot see.
00c7b3db8a3fatagen2026-10-18 10:19 UTC410
#[test]
00c7b3db8a3fatagen2026-10-18 10:19 UTC411
fn moving_toward_a_stack_showing_one_window_swaps() {
00c7b3db8a3fatagen2026-10-18 10:19 UTC412
    let (a, b, h) = (1, 2, 3);
00c7b3db8a3fatagen2026-10-18 10:19 UTC413
    let mut t: Tree<u32> = Tree::default();
00c7b3db8a3fatagen2026-10-18 10:19 UTC414
    t.insert_row(a);
00c7b3db8a3fatagen2026-10-18 10:19 UTC415
    t.insert_after(b, a);
00c7b3db8a3fatagen2026-10-18 10:19 UTC416
    t.insert_after(h, b);
00c7b3db8a3fatagen2026-10-18 10:19 UTC417
    assert!(t.merge_window(h, Dir::Left, |_| true));
00c7b3db8a3fatagen2026-10-18 10:19 UTC418
    let vis = |w: u32| w != h;
00c7b3db8a3fatagen2026-10-18 10:19 UTC419
    assert!(t.move_window(a, Dir::Right, vis));
00c7b3db8a3fatagen2026-10-18 10:19 UTC420
    t.check();
00c7b3db8a3fatagen2026-10-18 10:19 UTC421
    let shown = t.arrange(AREA, 0.0, vis);
00c7b3db8a3fatagen2026-10-18 10:19 UTC422
    let rect = |w| shown.iter().find(|p| p.0 == w).unwrap().1;
00c7b3db8a3fatagen2026-10-18 10:19 UTC423
    assert!(rect(b).x < rect(a).x);
00c7b3db8a3fatagen2026-10-18 10:19 UTC424
    assert_eq!(rect(a).h, AREA.h);
00c7b3db8a3fatagen2026-10-18 10:19 UTC425
}
8f1f743a2f6batagen2026-10-19 03:43 UTC426
ae47743b5745atagen2026-10-19 03:49 UTC427
/// A move either changes what is on screen or changes nothing at all, and a
ae47743b5745atagen2026-10-19 03:49 UTC428
/// window move never rearranges hidden windows among themselves. Steps around
ae47743b5745atagen2026-10-19 03:49 UTC429
/// hidden windows used to succeed invisibly and eat the keypress.
8f1f743a2f6batagen2026-10-19 03:43 UTC430
#[test]
8f1f743a2f6batagen2026-10-19 03:43 UTC431
fn moves_are_seen_or_undone() {
8f1f743a2f6batagen2026-10-19 03:43 UTC432
    let dirs = [Dir::Left, Dir::Right, Dir::Up, Dir::Down];
8f1f743a2f6batagen2026-10-19 03:43 UTC433
    let mut runner = proptest::test_runner::TestRunner::deterministic();
ae47743b5745atagen2026-10-19 03:49 UTC434
    let ops = prop::collection::vec((0u8..4, 0u8..4, 0u8..4, 0u8..32), 1..14);
8f1f743a2f6batagen2026-10-19 03:43 UTC435
    for _ in 0..2000 {
8f1f743a2f6batagen2026-10-19 03:43 UTC436
        let seq = proptest::strategy::ValueTree::current(&ops.new_tree(&mut runner).unwrap());
8f1f743a2f6batagen2026-10-19 03:43 UTC437
        let mut t: Tree<u32> = Tree::default();
8f1f743a2f6batagen2026-10-19 03:43 UTC438
        t.insert_row(0);
8f1f743a2f6batagen2026-10-19 03:43 UTC439
        let mut n = 1;
8f1f743a2f6batagen2026-10-19 03:43 UTC440
        for &(w, d, kind, hide) in &seq {
ae47743b5745atagen2026-10-19 03:49 UTC441
            let vis = |x: u32| hide & (1 << x) == 0;
8f1f743a2f6batagen2026-10-19 03:43 UTC442
            if kind == 0 && n < 5 {
8f1f743a2f6batagen2026-10-19 03:43 UTC443
                t.insert_after(n, u32::from(w) % n);
8f1f743a2f6batagen2026-10-19 03:43 UTC444
                n += 1;
8f1f743a2f6batagen2026-10-19 03:43 UTC445
                continue;
8f1f743a2f6batagen2026-10-19 03:43 UTC446
            }
8f1f743a2f6batagen2026-10-19 03:43 UTC447
            let w = u32::from(w) % n;
8f1f743a2f6batagen2026-10-19 03:43 UTC448
            let shown = |t: &Tree<u32>| {
8f1f743a2f6batagen2026-10-19 03:43 UTC449
                let mut v: Vec<_> = t
8f1f743a2f6batagen2026-10-19 03:43 UTC450
                    .arrange(AREA, 0.0, vis)
8f1f743a2f6batagen2026-10-19 03:43 UTC451
                    .into_iter()
8f1f743a2f6batagen2026-10-19 03:43 UTC452
                    .filter(|p| vis(p.0))
8f1f743a2f6batagen2026-10-19 03:43 UTC453
                    .collect();
8f1f743a2f6batagen2026-10-19 03:43 UTC454
                v.sort_by_key(|p| p.0);
8f1f743a2f6batagen2026-10-19 03:43 UTC455
                v
8f1f743a2f6batagen2026-10-19 03:43 UTC456
            };
8f1f743a2f6batagen2026-10-19 03:43 UTC457
            let before = shown(&t);
ae47743b5745atagen2026-10-19 03:49 UTC458
            let hidden = |t: &Tree<u32>| {
ae47743b5745atagen2026-10-19 03:49 UTC459
                let mut t = t.clone();
ae47743b5745atagen2026-10-19 03:49 UTC460
                t.retain(|x| !vis(x));
ae47743b5745atagen2026-10-19 03:49 UTC461
                t.shape()
ae47743b5745atagen2026-10-19 03:49 UTC462
            };
ae47743b5745atagen2026-10-19 03:49 UTC463
            let rest = hidden(&t);
ae47743b5745atagen2026-10-19 03:49 UTC464
            let d = dirs[usize::from(d)];
ae47743b5745atagen2026-10-19 03:49 UTC465
            let moved = match kind {
ae47743b5745atagen2026-10-19 03:49 UTC466
                1 => t.merge_window(w, d, vis),
ae47743b5745atagen2026-10-19 03:49 UTC467
                2 => t.move_window(w, d, vis),
ae47743b5745atagen2026-10-19 03:49 UTC468
                3 => t.move_container(w, d, vis),
ae47743b5745atagen2026-10-19 03:49 UTC469
                _ => continue,
8f1f743a2f6batagen2026-10-19 03:43 UTC470
            };
8f1f743a2f6batagen2026-10-19 03:43 UTC471
            t.check();
8f1f743a2f6batagen2026-10-19 03:43 UTC472
            assert_eq!(moved, shown(&t) != before, "{seq:?}");
ae47743b5745atagen2026-10-19 03:49 UTC473
            if kind != 3 {
ae47743b5745atagen2026-10-19 03:49 UTC474
                assert_eq!(hidden(&t), rest, "{seq:?}");
ae47743b5745atagen2026-10-19 03:49 UTC475
            }
8f1f743a2f6batagen2026-10-19 03:43 UTC476
        }
8f1f743a2f6batagen2026-10-19 03:43 UTC477
    }
8f1f743a2f6batagen2026-10-19 03:43 UTC478
}
b44dde08d7acatagen2026-10-19 07:38 UTC479
b44dde08d7acatagen2026-10-19 07:38 UTC480
/// A fullscreen window hides everything but its own transients, which stay
b44dde08d7acatagen2026-10-19 07:38 UTC481
/// reachable by focus and are not hidden when they map.
b44dde08d7acatagen2026-10-19 07:38 UTC482
#[test]
b44dde08d7acatagen2026-10-19 07:38 UTC483
fn fullscreen_leaves_its_transients_reachable() {
b44dde08d7acatagen2026-10-19 07:38 UTC484
    let mut l = Layout::default();
b44dde08d7acatagen2026-10-19 07:38 UTC485
    let o = l.add_output("a", AREA);
b44dde08d7acatagen2026-10-19 07:38 UTC486
    let beside = l.add_window(None, None, None);
b44dde08d7acatagen2026-10-19 07:38 UTC487
    l.focus(beside);
b44dde08d7acatagen2026-10-19 07:38 UTC488
    let game = l.add_window(None, None, None);
b44dde08d7acatagen2026-10-19 07:38 UTC489
    l.focus(game);
b44dde08d7acatagen2026-10-19 07:38 UTC490
    l.toggle_fullscreen(game);
b44dde08d7acatagen2026-10-19 07:38 UTC491
    let stranger = l.add_window(None, None, None);
b44dde08d7acatagen2026-10-19 07:38 UTC492
    assert!(l.covered(stranger));
b44dde08d7acatagen2026-10-19 07:38 UTC493
    let dialog = l.add_window(None, None, Some(Rect::new(300.0, 200.0, 200.0, 100.0)));
b44dde08d7acatagen2026-10-19 07:38 UTC494
    l.set_parent(dialog, Some(game));
b44dde08d7acatagen2026-10-19 07:38 UTC495
    assert!(!l.covered(dialog));
b44dde08d7acatagen2026-10-19 07:38 UTC496
    l.focus(dialog);
b44dde08d7acatagen2026-10-19 07:38 UTC497
b44dde08d7acatagen2026-10-19 07:38 UTC498
    let arr = l.arrange(o);
b44dde08d7acatagen2026-10-19 07:38 UTC499
    assert_eq!(arr.fullscreen, Some(game));
b44dde08d7acatagen2026-10-19 07:38 UTC500
    assert_eq!(arr.over, vec![dialog]);
b44dde08d7acatagen2026-10-19 07:38 UTC501
    for dir in [Dir::Left, Dir::Right, Dir::Up, Dir::Down] {
b44dde08d7acatagen2026-10-19 07:38 UTC502
        if let Some(w) = l.focus_dir(dir) {
b44dde08d7acatagen2026-10-19 07:38 UTC503
            assert!(w == game || w == dialog, "focus walked to a covered window");
b44dde08d7acatagen2026-10-19 07:38 UTC504
        }
b44dde08d7acatagen2026-10-19 07:38 UTC505
    }
b44dde08d7acatagen2026-10-19 07:38 UTC506
b44dde08d7acatagen2026-10-19 07:38 UTC507
    l.remove_window(game);
b44dde08d7acatagen2026-10-19 07:38 UTC508
    assert!(!l.covered(stranger));
b44dde08d7acatagen2026-10-19 07:38 UTC509
    assert_eq!(l.window(dialog).and_then(|w| w.parent), None);
b44dde08d7acatagen2026-10-19 07:38 UTC510
}
86ef802692f5atagen2026-10-19 07:43 UTC511
86ef802692f5atagen2026-10-19 07:43 UTC512
fn rect_of(t: &Tree<u32>, w: u32, vis: impl Fn(u32) -> bool) -> Rect {
86ef802692f5atagen2026-10-19 07:43 UTC513
    t.arrange(AREA, 0.0, vis)
86ef802692f5atagen2026-10-19 07:43 UTC514
        .into_iter()
86ef802692f5atagen2026-10-19 07:43 UTC515
        .find(|p| p.0 == w)
86ef802692f5atagen2026-10-19 07:43 UTC516
        .unwrap()
86ef802692f5atagen2026-10-19 07:43 UTC517
        .1
86ef802692f5atagen2026-10-19 07:43 UTC518
}
86ef802692f5atagen2026-10-19 07:43 UTC519
86ef802692f5atagen2026-10-19 07:43 UTC520
#[test]
86ef802692f5atagen2026-10-19 07:43 UTC521
fn splitting_a_lone_window_shares_its_space() {
86ef802692f5atagen2026-10-19 07:43 UTC522
    let (a, b, c) = (1, 2, 3);
86ef802692f5atagen2026-10-19 07:43 UTC523
    let mut t: Tree<u32> = Tree::default();
86ef802692f5atagen2026-10-19 07:43 UTC524
    t.insert_row(a);
86ef802692f5atagen2026-10-19 07:43 UTC525
    t.insert_split(b, a, Axis::Horizontal);
86ef802692f5atagen2026-10-19 07:43 UTC526
    t.check();
86ef802692f5atagen2026-10-19 07:43 UTC527
    let (ra, rb) = (rect_of(&t, a, |_| true), rect_of(&t, b, |_| true));
86ef802692f5atagen2026-10-19 07:43 UTC528
    assert_eq!((ra.w, rb.w, ra.h), (AREA.w / 2.0, AREA.w / 2.0, AREA.h));
86ef802692f5atagen2026-10-19 07:43 UTC529
    assert!(ra.x < rb.x);
86ef802692f5atagen2026-10-19 07:43 UTC530
86ef802692f5atagen2026-10-19 07:43 UTC531
    t.insert_split(c, b, Axis::Vertical);
86ef802692f5atagen2026-10-19 07:43 UTC532
    t.check();
86ef802692f5atagen2026-10-19 07:43 UTC533
    let (rb, rc) = (rect_of(&t, b, |_| true), rect_of(&t, c, |_| true));
86ef802692f5atagen2026-10-19 07:43 UTC534
    assert_eq!(
86ef802692f5atagen2026-10-19 07:43 UTC535
        (rb.h, rc.h, rb.w),
86ef802692f5atagen2026-10-19 07:43 UTC536
        (AREA.h / 2.0, AREA.h / 2.0, AREA.w / 2.0)
86ef802692f5atagen2026-10-19 07:43 UTC537
    );
86ef802692f5atagen2026-10-19 07:43 UTC538
    assert!(rb.y < rc.y);
86ef802692f5atagen2026-10-19 07:43 UTC539
    assert_eq!(rect_of(&t, a, |_| true), ra, "the sibling is undisturbed");
86ef802692f5atagen2026-10-19 07:43 UTC540
}
86ef802692f5atagen2026-10-19 07:43 UTC541
86ef802692f5atagen2026-10-19 07:43 UTC542
#[test]
86ef802692f5atagen2026-10-19 07:43 UTC543
fn splitting_along_the_parents_axis_appends_a_sibling() {
86ef802692f5atagen2026-10-19 07:43 UTC544
    let (a, b, c, d) = (1, 2, 3, 4);
86ef802692f5atagen2026-10-19 07:43 UTC545
    let mut t: Tree<u32> = Tree::default();
86ef802692f5atagen2026-10-19 07:43 UTC546
    t.insert_row(a);
86ef802692f5atagen2026-10-19 07:43 UTC547
    t.insert_split(b, a, Axis::Horizontal);
86ef802692f5atagen2026-10-19 07:43 UTC548
    t.insert_split(c, b, Axis::Vertical);
86ef802692f5atagen2026-10-19 07:43 UTC549
    let before = rect_of(&t, a, |_| true);
86ef802692f5atagen2026-10-19 07:43 UTC550
    t.insert_split(d, a, Axis::Horizontal);
86ef802692f5atagen2026-10-19 07:43 UTC551
    t.check();
86ef802692f5atagen2026-10-19 07:43 UTC552
    let all = |_| true;
86ef802692f5atagen2026-10-19 07:43 UTC553
    let (ra, rd, rb) = (
86ef802692f5atagen2026-10-19 07:43 UTC554
        rect_of(&t, a, all),
86ef802692f5atagen2026-10-19 07:43 UTC555
        rect_of(&t, d, all),
86ef802692f5atagen2026-10-19 07:43 UTC556
        rect_of(&t, b, all),
86ef802692f5atagen2026-10-19 07:43 UTC557
    );
86ef802692f5atagen2026-10-19 07:43 UTC558
    assert_eq!((ra.w, rd.w), (before.w / 2.0, before.w / 2.0));
86ef802692f5atagen2026-10-19 07:43 UTC559
    assert!(ra.x < rd.x && rd.x < rb.x);
86ef802692f5atagen2026-10-19 07:43 UTC560
    assert_eq!(rb.w, AREA.w / 2.0, "the neighbour keeps its share");
86ef802692f5atagen2026-10-19 07:43 UTC561
}
86ef802692f5atagen2026-10-19 07:43 UTC562
86ef802692f5atagen2026-10-19 07:43 UTC563
#[test]
86ef802692f5atagen2026-10-19 07:43 UTC564
fn splitting_beside_hidden_windows_leaves_their_slots_alone() {
86ef802692f5atagen2026-10-19 07:43 UTC565
    let (a, b, c, d) = (1, 2, 3, 4);
86ef802692f5atagen2026-10-19 07:43 UTC566
    let mut t: Tree<u32> = Tree::default();
86ef802692f5atagen2026-10-19 07:43 UTC567
    t.insert_row(a);
86ef802692f5atagen2026-10-19 07:43 UTC568
    t.insert_after(b, a);
86ef802692f5atagen2026-10-19 07:43 UTC569
    t.insert_after(c, a);
86ef802692f5atagen2026-10-19 07:43 UTC570
    let tag_one = |w: u32| w == a || w == d;
86ef802692f5atagen2026-10-19 07:43 UTC571
    assert_eq!(rect_of(&t, a, tag_one).h, AREA.h);
86ef802692f5atagen2026-10-19 07:43 UTC572
86ef802692f5atagen2026-10-19 07:43 UTC573
    t.insert_split(d, a, Axis::Vertical);
86ef802692f5atagen2026-10-19 07:43 UTC574
    t.check();
86ef802692f5atagen2026-10-19 07:43 UTC575
    let (ra, rd) = (rect_of(&t, a, tag_one), rect_of(&t, d, tag_one));
86ef802692f5atagen2026-10-19 07:43 UTC576
    assert_eq!((ra.h, rd.h, ra.w), (AREA.h / 2.0, AREA.h / 2.0, AREA.w));
86ef802692f5atagen2026-10-19 07:43 UTC577
    assert!(ra.y < rd.y);
86ef802692f5atagen2026-10-19 07:43 UTC578
86ef802692f5atagen2026-10-19 07:43 UTC579
    t.insert_split(d + 1, a, Axis::Horizontal);
86ef802692f5atagen2026-10-19 07:43 UTC580
    t.check();
86ef802692f5atagen2026-10-19 07:43 UTC581
    let shown = |w: u32| w == a || w == d + 1;
86ef802692f5atagen2026-10-19 07:43 UTC582
    let (ra, re) = (rect_of(&t, a, shown), rect_of(&t, d + 1, shown));
86ef802692f5atagen2026-10-19 07:43 UTC583
    assert_eq!((ra.w, re.w), (AREA.w / 2.0, AREA.w / 2.0));
86ef802692f5atagen2026-10-19 07:43 UTC584
    assert!(ra.x < re.x);
86ef802692f5atagen2026-10-19 07:43 UTC585
    assert_eq!(t.windows().count(), 5, "hidden windows stay in the tree");
86ef802692f5atagen2026-10-19 07:43 UTC586
}
86ef802692f5atagen2026-10-19 07:43 UTC587
86ef802692f5atagen2026-10-19 07:43 UTC588
#[test]
86ef802692f5atagen2026-10-19 07:43 UTC589
fn autotile_dwindles_by_the_anchors_shape() {
86ef802692f5atagen2026-10-19 07:43 UTC590
    let mut l = Layout {
86ef802692f5atagen2026-10-19 07:43 UTC591
        autotile: true,
86ef802692f5atagen2026-10-19 07:43 UTC592
        ..Layout::default()
86ef802692f5atagen2026-10-19 07:43 UTC593
    };
86ef802692f5atagen2026-10-19 07:43 UTC594
    let o = l.add_output("a", AREA);
86ef802692f5atagen2026-10-19 07:43 UTC595
    let tag = l.tag("1");
86ef802692f5atagen2026-10-19 07:43 UTC596
    l.view(o, tag);
86ef802692f5atagen2026-10-19 07:43 UTC597
    let mut ids = Vec::new();
86ef802692f5atagen2026-10-19 07:43 UTC598
    for _ in 0..4 {
86ef802692f5atagen2026-10-19 07:43 UTC599
        let w = l.add_window(Some(o), Some(tag), None);
86ef802692f5atagen2026-10-19 07:43 UTC600
        l.focus(w);
86ef802692f5atagen2026-10-19 07:43 UTC601
        ids.push(w);
86ef802692f5atagen2026-10-19 07:43 UTC602
        l.check();
86ef802692f5atagen2026-10-19 07:43 UTC603
    }
86ef802692f5atagen2026-10-19 07:43 UTC604
    let arr = l.arrange(o);
86ef802692f5atagen2026-10-19 07:43 UTC605
    let r = |w| arr.tiled.iter().find(|p| p.window == w).unwrap().rect;
86ef802692f5atagen2026-10-19 07:43 UTC606
    let [a, b, c, d] = [ids[0], ids[1], ids[2], ids[3]];
86ef802692f5atagen2026-10-19 07:43 UTC607
    assert!(
86ef802692f5atagen2026-10-19 07:43 UTC608
        r(a).x < r(b).x && r(a).h > r(b).h * 1.5,
86ef802692f5atagen2026-10-19 07:43 UTC609
        "{a:?} left, {b:?} right"
86ef802692f5atagen2026-10-19 07:43 UTC610
    );
86ef802692f5atagen2026-10-19 07:43 UTC611
    assert!(r(b).y < r(c).y && r(b).x == r(c).x);
86ef802692f5atagen2026-10-19 07:43 UTC612
    assert!(r(c).x < r(d).x && r(c).y == r(d).y);
86ef802692f5atagen2026-10-19 07:43 UTC613
}