niri-scratchpad: refresh patch with multi-monitor tests

Adds two synthetic multi-output tests (niri's layout harness supports several
outputs) proving scratchpad_show targets the FOCUSED output, plus a control
that it returns to the origin output when focus is unchanged. This is the
substitute for manual multi-monitor verification on a single-display machine.
117 layout tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
funman300
2026-07-18 10:12:55 -07:00
parent 0e0b5fcbe3
commit a9ce3a13cf
+78 -2
View File
@@ -302,7 +302,7 @@ index 5c4dd639..a90d9e6e 100644
let mut seen_workspace_name = Vec::<String>::new();
diff --git a/src/layout/tests.rs b/src/layout/tests.rs
index 31265dd9..65e02f2b 100644
index 31265dd9..9037ff08 100644
--- a/src/layout/tests.rs
+++ b/src/layout/tests.rs
@@ -753,6 +753,8 @@ enum Op {
@@ -327,7 +327,7 @@ index 31265dd9..65e02f2b 100644
}
}
}
@@ -3929,3 +3937,93 @@ proptest! {
@@ -3929,3 +3937,169 @@ proptest! {
check_ops_with_options(options, ops);
}
}
@@ -421,3 +421,79 @@ index 31265dd9..65e02f2b 100644
+ assert_eq!(layout.scratchpad.len(), 1, "closed window evicted from stash");
+ assert_eq!(layout.scratchpad.front().unwrap().tile.window().id(), &1);
+}
+
+#[test]
+fn scratchpad_shows_on_the_focused_output() {
+ // First, confirm that while stashed the window lives on no monitor at all (checked against
+ // a standalone run so `check_ops`'s post-op invariant checks still see a fully-formed op
+ // sequence).
+ let stash_only_ops = [
+ Op::AddOutput(1),
+ Op::AddOutput(2),
+ Op::FocusOutput(1),
+ Op::AddWindow { params: TestWindowParams::new(0) }, // lands on output 1
+ Op::ScratchpadStash(0),
+ ];
+ let stashed_layout = check_ops(stash_only_ops);
+ {
+ let MonitorSet::Normal { monitors, .. } = &stashed_layout.monitor_set else {
+ unreachable!()
+ };
+ assert!(
+ monitors.iter().all(|m| !m.windows().any(|w| *w.id() == 0)),
+ "stashed window must not be present on any monitor"
+ );
+ }
+
+ // Stash on output 1, then switch focus to output 2 before showing.
+ let ops = [
+ Op::AddOutput(1),
+ Op::AddOutput(2),
+ Op::FocusOutput(1),
+ Op::AddWindow { params: TestWindowParams::new(0) }, // lands on output 1
+ Op::ScratchpadStash(0),
+ Op::FocusOutput(2),
+ Op::ScratchpadShow,
+ ];
+ let layout = check_ops(ops);
+
+ let MonitorSet::Normal { monitors, active_monitor_idx, .. } = &layout.monitor_set else {
+ unreachable!()
+ };
+
+ // The window must appear on whichever output is currently focused (output 2), not on the
+ // output it was originally stashed from (output 1).
+ for (idx, monitor) in monitors.iter().enumerate() {
+ let has_window = monitor.windows().any(|w| *w.id() == 0);
+ if idx == *active_monitor_idx {
+ assert!(has_window, "shown window should be on the focused output");
+ } else {
+ assert!(!has_window, "shown window should not be on a non-focused output");
+ }
+ }
+}
+
+#[test]
+fn scratchpad_shows_on_origin_output_when_focus_unchanged() {
+ let ops = [
+ Op::AddOutput(1),
+ Op::AddOutput(2),
+ Op::FocusOutput(1),
+ Op::AddWindow { params: TestWindowParams::new(0) }, // lands on output 1
+ Op::ScratchpadStash(0),
+ // no focus change here
+ Op::ScratchpadShow,
+ ];
+ let layout = check_ops(ops);
+
+ let MonitorSet::Normal { monitors, active_monitor_idx, .. } = &layout.monitor_set else {
+ unreachable!()
+ };
+
+ // Focus never left output 1, so the window should reappear there.
+ assert_eq!(*active_monitor_idx, 0, "output 1 should still be active");
+ assert!(
+ monitors[*active_monitor_idx].windows().any(|w| *w.id() == 0),
+ "shown window should be on the still-focused origin output"
+ );
+}