lesavka/testing/tests/client_output_display_include_contract.rs

219 lines
6.0 KiB
Rust
Raw Normal View History

//! Include-based coverage for client monitor enumeration logic.
//!
//! Scope: include `client/src/output/display.rs` with deterministic GTK/GDK
//! stubs to exercise sorting and filtering branches.
//! Targets: `client/src/output/display.rs`.
//! Why: monitor-layout selection should remain stable even when CI has no real
//! display server attached.
#[allow(dead_code)]
mod gtk {
pub mod gdk {
use std::cell::RefCell;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Rectangle {
pub x: i32,
pub y: i32,
pub w: i32,
pub h: i32,
}
impl Rectangle {
pub fn new(x: i32, y: i32, w: i32, h: i32) -> Self {
Self { x, y, w, h }
}
pub fn width(&self) -> i32 {
self.w
}
pub fn height(&self) -> i32 {
self.h
}
}
#[derive(Clone, Debug)]
pub struct Monitor {
connector: Option<String>,
model: Option<String>,
geometry: Rectangle,
scale_factor: i32,
}
impl Monitor {
pub fn new(
connector: Option<&str>,
model: Option<&str>,
geometry: Rectangle,
scale_factor: i32,
) -> Self {
Self {
connector: connector.map(str::to_owned),
model: model.map(str::to_owned),
geometry,
scale_factor,
}
}
pub fn connector(&self) -> Option<String> {
self.connector.clone()
}
pub fn model(&self) -> Option<String> {
self.model.clone()
}
pub fn geometry(&self) -> Rectangle {
self.geometry
}
pub fn scale_factor(&self) -> i32 {
self.scale_factor
}
}
#[derive(Clone, Debug)]
pub enum Object {
Monitor(Monitor),
Other,
}
impl Object {
pub fn downcast<T>(self) -> Result<Monitor, Self> {
match self {
Self::Monitor(monitor) => Ok(monitor),
other => Err(other),
}
}
}
#[derive(Clone, Debug, Default)]
pub struct MonitorList {
pub items: Vec<Object>,
}
#[derive(Clone, Debug)]
pub struct Display {
monitors: MonitorList,
}
impl Display {
pub fn default() -> Option<Self> {
DISPLAY.with(|slot| slot.borrow().clone())
}
pub fn monitors(&self) -> MonitorList {
self.monitors.clone()
}
}
thread_local! {
static DISPLAY: RefCell<Option<Display>> = const { RefCell::new(None) };
}
pub fn set_mock_display(display: Option<Display>) {
DISPLAY.with(|slot| {
*slot.borrow_mut() = display;
});
}
pub fn display_from_items(items: Vec<Object>) -> Display {
Display {
monitors: MonitorList { items },
}
}
}
pub mod prelude {
use super::gdk::{MonitorList, Object};
pub trait ListModelExt {
fn n_items(&self) -> u32;
fn item(&self, idx: u32) -> Option<Object>;
}
impl ListModelExt for MonitorList {
fn n_items(&self) -> u32 {
self.items.len() as u32
}
fn item(&self, idx: u32) -> Option<Object> {
self.items.get(idx as usize).cloned()
}
}
}
}
#[allow(warnings)]
mod display_include_contract {
use crate::gtk;
include!(env!("LESAVKA_CLIENT_OUTPUT_DISPLAY_SRC"));
use crate::gtk::gdk as mock_gdk;
use serial_test::serial;
#[test]
#[serial]
fn enumerate_monitors_falls_back_when_display_is_missing() {
mock_gdk::set_mock_display(None);
let monitors = enumerate_monitors();
assert_eq!(monitors.len(), 1);
assert_eq!(monitors[0].geometry.width(), 1920);
assert_eq!(monitors[0].geometry.height(), 1080);
assert!(!monitors[0].is_internal);
}
#[test]
#[serial]
fn enumerate_monitors_sorts_external_monitors_first() {
let items = vec![
mock_gdk::Object::Monitor(mock_gdk::Monitor::new(
Some("eDP-1"),
Some("internal"),
mock_gdk::Rectangle::new(0, 0, 1920, 1200),
2,
)),
mock_gdk::Object::Monitor(mock_gdk::Monitor::new(
Some("HDMI-A-1"),
Some("external"),
mock_gdk::Rectangle::new(1920, 0, 1920, 1080),
1,
)),
mock_gdk::Object::Monitor(mock_gdk::Monitor::new(
Some("my-internal-panel"),
Some("alt"),
mock_gdk::Rectangle::new(-1920, 0, 1280, 720),
1,
)),
];
mock_gdk::set_mock_display(Some(mock_gdk::display_from_items(items)));
let monitors = enumerate_monitors();
assert_eq!(monitors.len(), 3);
assert!(!monitors[0].is_internal, "external monitor should be first");
assert!(monitors[1].is_internal);
assert!(monitors[2].is_internal);
}
#[test]
#[serial]
fn enumerate_monitors_ignores_non_monitor_objects() {
let items = vec![
mock_gdk::Object::Other,
mock_gdk::Object::Monitor(mock_gdk::Monitor::new(
Some("DP-1"),
Some("dock"),
mock_gdk::Rectangle::new(0, 0, 2560, 1440),
1,
)),
];
mock_gdk::set_mock_display(Some(mock_gdk::display_from_items(items)));
let monitors = enumerate_monitors();
assert_eq!(monitors.len(), 1);
assert_eq!(monitors[0].geometry.width(), 2560);
assert_eq!(monitors[0].scale_factor, 1);
}
}