From 147d42b2c50eed18b921fcd5d1b9098f90237e92 Mon Sep 17 00:00:00 2001 From: Brent Westbrook <36778786+ntBre@users.noreply.github.com> Date: Thu, 5 Dec 2024 15:05:13 -0500 Subject: [PATCH] Use Xvfb for automated tests (#37) * test buttonpress * wait for xephyr to start * install xephyr * start events module * try starting x * /usr/bin/X not found * Only console users are allowed to run the X server * need sudo * forgot sudo echo doesn't work * revert sudo stuff, try -ac * use xvfb not xephyr for automated tests * install libxcb * try a different one * print xcb connection error * specify display for xcb too * delete unused benches * use a labeled block to avoid clippy * try_wait after kill to handle clippy * bring back linux cfg for xcon stuff * xquartz might have xvfb * try lowercase xvfb on mac * more debugging on mac * both xvfb and Xvfb are shown in which but PATH isn't updating --- .github/workflows/test.yml | 6 +-- src/events.rs | 39 +++++++++++++++++ src/lib.rs | 1 + src/main.rs | 86 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 src/events.rs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 45633ba..3d9e8e8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,11 +24,13 @@ jobs: if: ${{ ! matrix.is_mac }} run: | sudo apt-get update - sudo apt-get install libxft-dev libxinerama-dev + sudo apt-get install libxft-dev libxinerama-dev xserver-xephyr libxcb1-dev - name: Install X libraries (mac) if: ${{ matrix.is_mac }} run: | brew install libxft libxinerama + brew install --cask xquartz + echo "PATH=$PATH:/opt/X11/bin" >> $GITHUB_ENV - name: Install ${{ matrix.toolchain }} uses: dtolnay/rust-toolchain@master with: @@ -39,5 +41,3 @@ jobs: # https://twitter.com/jonhoo/status/1571290371124260865 - name: cargo test --locked run: cargo test --locked --all-features --lib --bins --tests --examples -- --test-threads=1 - - name: cargo bench - run: cargo bench diff --git a/src/events.rs b/src/events.rs new file mode 100644 index 0000000..6131eab --- /dev/null +++ b/src/events.rs @@ -0,0 +1,39 @@ +use x11::xlib::{XButtonEvent, XEvent, _XDisplay}; + +pub enum Event { + Button(XButtonEvent), +} + +impl Event { + pub fn button( + window: u64, + button: u32, + x: i32, + display: *mut _XDisplay, + state: u32, + ) -> Self { + Self::Button(XButtonEvent { + type_: 0, + serial: 0, + send_event: 0, + display, + window, + root: 0, + subwindow: 0, + time: 0, + x, + y: 0, + x_root: 0, + y_root: 0, + state, + button, + same_screen: 0, + }) + } + + pub fn into_button(self) -> XEvent { + match self { + Event::Button(button) => XEvent { button }, + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 367792b..3f15ef6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ use std::ffi::{c_char, c_int, c_uint}; use enums::Clk; pub mod enums; +pub mod events; pub type Window = u64; diff --git a/src/main.rs b/src/main.rs index 0374966..987d4a1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2907,3 +2907,89 @@ fn main() { drop(Box::from_raw(XCON)); } } + +#[cfg(test)] +mod tests { + use rwm::events::Event; + use xlib::Button1; + + use super::*; + + use std::process::Command; + + #[test] + fn main() { + // setup xephyr + #[cfg(target_os = "linux")] + let mut cmd = Command::new("Xvfb").arg(":1").spawn().unwrap(); + + #[cfg(not(target_os = "linux"))] + let mut cmd = Command::new("xvfb").arg(":1").spawn().unwrap(); + + // wait for xephyr to start + unsafe { + while DPY.is_null() { + DPY = xlib::XOpenDisplay(c":1.0".as_ptr()); + } + } + + // goto for killing xephyr no matter what + let ok = 'defer: { + #[cfg(target_os = "linux")] + unsafe { + let xcon = match Connection::connect(Some(":1.0")) { + Ok((xcon, _)) => xcon, + Err(e) => { + eprintln!("rwm: cannot get xcb connection: {e:?}"); + break 'defer false; + } + }; + XCON = Box::into_raw(Box::new(xcon)); + } + checkotherwm(); + setup(); + scan(); + + // instead of calling `run`, manually send some XEvents + + // test that a mouse click on the initial (tiling) layout icon + // switches to floating mode + handlers::buttonpress( + &mut Event::button( + (unsafe { *SELMON }).barwin, + Button1, + CONFIG + .tags + .iter() + .map(|tag| textw(tag.as_ptr())) + .sum::() + + 5, + unsafe { DPY }, + 0, + ) + .into_button(), + ); + unsafe { + assert!((*(*SELMON).lt[(*SELMON).sellt as usize]) + .arrange + .is_none()); + } + + cleanup(); + unsafe { + xlib::XCloseDisplay(DPY); + + #[cfg(target_os = "linux")] + drop(Box::from_raw(XCON)); + } + + break 'defer true; + }; + + // kill xephyr when finished + cmd.kill().unwrap(); + cmd.try_wait().unwrap(); + + assert!(ok); + } +}