a lightweight, interval-based utility to combat digital strain through "Ma" (intentional pauses) for the eyes and body.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

test: regression and manual matrix for multi-monitor overlay

+152 -18
+81 -9
src/overlay/layer_shell.rs
··· 417 417 418 418 #[test] 419 419 fn cmd_enum_is_debug() { 420 - let c = Cmd::Show { is_dark: true }; 421 - assert!(format!("{c:?}").contains("Show")); 422 - let c = Cmd::Hide; 423 - assert!(format!("{c:?}").contains("Hide")); 424 - let c = Cmd::Quit; 425 - assert!(format!("{c:?}").contains("Quit")); 420 + assert!(format!("{:?}", Cmd::Show { is_dark: true }).contains("Show")); 421 + assert!(format!("{:?}", Cmd::Hide).contains("Hide")); 422 + assert!(format!("{:?}", Cmd::Quit).contains("Quit")); 426 423 } 427 424 428 425 #[test] 429 - fn barrier_state_skips_primary_output() { 430 - let state = BarrierState::new(0, 0); 431 - assert_eq!(state.primary_x, 0); 426 + fn barrier_state_initial_state() { 427 + let state = BarrierState::new(1920, 0); 428 + assert_eq!(state.primary_x, 1920); 432 429 assert_eq!(state.primary_y, 0); 433 430 assert!(state.surfaces.is_empty()); 434 431 assert!(state.outputs.is_empty()); 432 + assert!(state.compositor.is_none()); 433 + assert!(state.shm.is_none()); 434 + assert!(state.layer_shell.is_none()); 435 435 } 436 + 437 + #[test] 438 + fn anchor_bitmask_covers_all_edges() { 439 + // top=1, bottom=2, left=4, right=8 → all = 0b1111 = 15 440 + let anchor = Anchor::from_bits_truncate(0b1111); 441 + assert_eq!(anchor.bits(), 15); 442 + // Each individual edge must be set. 443 + assert!(anchor.contains(Anchor::from_bits_truncate(1))); // top 444 + assert!(anchor.contains(Anchor::from_bits_truncate(2))); // bottom 445 + assert!(anchor.contains(Anchor::from_bits_truncate(4))); // left 446 + assert!(anchor.contains(Anchor::from_bits_truncate(8))); // right 447 + } 448 + 449 + #[test] 450 + fn primary_skip_predicate() { 451 + let (px, py) = (0i32, 0i32); 452 + let cases: &[(i32, i32, bool)] = &[ 453 + (0, 0, true), // primary → skip 454 + (1920, 0, false), // right of primary → include 455 + (-1920, 0, false), // left of primary → include 456 + (0, 1080, false), // below primary → include 457 + ]; 458 + for &(x, y, should_skip) in cases { 459 + assert_eq!(x == px && y == py, should_skip, "failed for ({x},{y})"); 460 + } 461 + } 462 + 463 + #[test] 464 + fn try_new_returns_none_without_wayland() { 465 + // WAYLAND_DISPLAY must not be set (or point to a non-existent socket) in 466 + // the test environment. try_new should return None gracefully. 467 + if std::env::var("WAYLAND_DISPLAY").is_ok() { 468 + // Running inside a real Wayland session — skip this test. 469 + return; 470 + } 471 + let result = LayerShellBarrier::try_new(0, 0); 472 + assert!(result.is_none()); 473 + } 474 + 475 + // ── Manual test matrix (run with `cargo test -- --ignored`) ───────────── 476 + 477 + /// Dual-monitor X11: trigger a break and confirm both monitors are covered 478 + /// by full-screen Slint overlay windows. 479 + #[test] 480 + #[ignore] 481 + fn manual_x11_dual_monitor_both_covered() {} 482 + 483 + /// Dual-monitor Wayland (wlroots compositor): trigger a break and confirm 484 + /// primary monitor shows the Slint overlay and secondary shows the dark 485 + /// layer-shell barrier. Verify the secondary barrier cannot be dismissed. 486 + #[test] 487 + #[ignore] 488 + fn manual_wayland_dual_monitor_barrier_on_secondary() {} 489 + 490 + /// Wayland GNOME session: trigger a break; log should contain 491 + /// "wlr-layer-shell not available". Primary monitor should still be covered. 492 + #[test] 493 + #[ignore] 494 + fn manual_wayland_gnome_fallback_primary_only() {} 495 + 496 + /// Single-monitor setup (any session): overlay appears correctly and 497 + /// countdown timer runs to zero before auto-dismissing. 498 + #[test] 499 + #[ignore] 500 + fn manual_single_monitor_overlay_and_countdown() {} 501 + 502 + /// Enforced mode + password: overlay appears on all monitors, Alt+F4 is 503 + /// blocked on every window, password field auto-focuses when unlock is 504 + /// clicked, Enter key submits the password. 505 + #[test] 506 + #[ignore] 507 + fn manual_enforced_mode_password_all_monitors() {} 436 508 }
+71 -9
src/overlay/mod.rs
··· 50 50 None 51 51 }; 52 52 53 - let slint_monitors: &[monitors::MonitorInfo] = match session { 54 - SessionType::Wayland => { 55 - // On Wayland, Slint handles the primary monitor only; LayerShellBarrier 56 - // covers secondary monitors via wlr-layer-shell. 57 - log::info!("Wayland session: Slint overlay on primary monitor only"); 58 - &all_monitors[..1] 59 - } 60 - SessionType::X11 | SessionType::Windows => &all_monitors, 61 - }; 53 + let slint_monitors = slint_monitor_slice(session, &all_monitors); 62 54 63 55 let backend = Box::new(multi_slint::MultiSlintBackend::new( 64 56 is_dark, slint_monitors, cmd_tx, cfg_arc, ··· 107 99 format!("{}:{:02}", s / 60, s % 60) 108 100 } 109 101 } 102 + 103 + /// Returns the subset of monitors that the Slint backend should cover. 104 + /// On Wayland, Slint only covers the primary monitor; secondary monitors 105 + /// are handled by `LayerShellBarrier`. On X11/Windows all monitors are used. 106 + fn slint_monitor_slice<'a>( 107 + session: SessionType, 108 + monitors: &'a [monitors::MonitorInfo], 109 + ) -> &'a [monitors::MonitorInfo] { 110 + match session { 111 + SessionType::Wayland => { 112 + log::info!("Wayland session: Slint overlay on primary monitor only"); 113 + &monitors[..1.min(monitors.len())] 114 + } 115 + SessionType::X11 | SessionType::Windows => monitors, 116 + } 117 + } 118 + 119 + #[cfg(test)] 120 + mod tests { 121 + use super::*; 122 + use crate::overlay::monitors::MonitorInfo; 123 + 124 + fn make_monitors(count: usize) -> Vec<MonitorInfo> { 125 + (0..count) 126 + .map(|i| MonitorInfo { 127 + x: (i as i32) * 1920, 128 + y: 0, 129 + width: 1920, 130 + height: 1080, 131 + scale_factor: 1.0, 132 + is_primary: i == 0, 133 + name: format!("monitor-{i}"), 134 + }) 135 + .collect() 136 + } 137 + 138 + #[test] 139 + fn x11_uses_all_monitors() { 140 + let monitors = make_monitors(3); 141 + let slice = slint_monitor_slice(SessionType::X11, &monitors); 142 + assert_eq!(slice.len(), 3); 143 + } 144 + 145 + #[test] 146 + fn wayland_uses_primary_only() { 147 + let monitors = make_monitors(3); 148 + let slice = slint_monitor_slice(SessionType::Wayland, &monitors); 149 + assert_eq!(slice.len(), 1); 150 + assert!(slice[0].is_primary); 151 + } 152 + 153 + #[test] 154 + fn single_monitor_same_for_all_sessions() { 155 + let monitors = make_monitors(1); 156 + assert_eq!(slint_monitor_slice(SessionType::X11, &monitors).len(), 1); 157 + assert_eq!(slint_monitor_slice(SessionType::Wayland, &monitors).len(), 1); 158 + } 159 + 160 + #[test] 161 + fn fmt_dur_under_60s() { 162 + assert_eq!(fmt_dur(Duration::from_secs(5)), "0:05"); 163 + assert_eq!(fmt_dur(Duration::from_secs(59)), "0:59"); 164 + } 165 + 166 + #[test] 167 + fn fmt_dur_over_60s() { 168 + assert_eq!(fmt_dur(Duration::from_secs(90)), "1:30"); 169 + assert_eq!(fmt_dur(Duration::from_secs(600)), "10:00"); 170 + } 171 + }