swaysome/tests/utils/mod.rs
2025-04-13 14:58:42 +02:00

125 lines
4.2 KiB
Rust

use std::{
fs::File,
io::Read,
path::PathBuf,
process::{Child, Command, Output},
};
fn signal(pid: u32, sig: &str) {
Command::new("kill")
.arg("-s")
.arg(sig)
.arg(format!("{}", pid))
.status()
.expect("failed to execute 'kill'");
}
pub struct Sway {
pub sock: PathBuf,
process: Child,
}
impl Sway {
pub fn start() -> Sway {
let tmp = std::env::temp_dir()
.join("swaysome_tests")
.join(std::thread::current().name().unwrap());
std::fs::create_dir_all(&tmp).expect("Unable to create temporary working directory");
let pwd = std::env::current_dir().expect("Unable to get current dir");
let conf_path = pwd.join("tests/sway.conf");
let swaysock_path = tmp.join("swaysock");
let sway_log =
File::create(tmp.join("sway.log")).expect("Unable to create sway's log file");
let sway = Command::new("sway")
.arg("-c")
.arg(conf_path.clone())
.env_clear()
.env("WLR_BACKENDS", "headless")
.env("WLR_LIBINPUT_NO_DEVICES", "1")
.env("XDG_RUNTIME_DIR", &tmp)
.env("SWAYSOCK", &swaysock_path)
.stderr(sway_log)
.spawn()
.expect("failed to execute sway");
// check that sway works correctly without using swaysome
let sway = Sway {
sock: swaysock_path,
process: sway,
};
match sway.check_connection("loaded_config_file_name") {
Ok(()) => {
// Let's do some common initialization of the desktop
sway.send_command(["create_output"].as_slice());
sway.send_command(["create_output"].as_slice());
return sway;
}
Err(()) => {
eprintln!("Failed to start 'sway', aborting the tests");
eprintln!("---- sway stderr ----");
let mut buffer = String::new();
let mut sway_stderr =
File::open(tmp.join("sway.log")).expect("Unable to open sway's log file");
sway_stderr.read_to_string(&mut buffer).unwrap();
eprintln!("{}", buffer);
eprintln!("---------------------");
panic!();
}
}
}
pub fn send_command(&self, commands: &[&str]) -> Output {
Command::new("swaymsg")
.args(commands)
.env_clear()
.env("SWAYSOCK", self.sock.clone())
.output()
.expect("Couldn't run swaymsg")
}
pub fn spawn_some_apps(&self) {
self.send_command(["exec", "foot -T TERM1"].as_slice());
// Make sure the app are created in the right order.
// 50ms would still sometimes be racy on my Ryzen 5 PRO 4650U, so let's
// take a safe bet and give plenty of time for shared CI runners.
std::thread::sleep(std::time::Duration::from_millis(200));
self.send_command(["exec", "foot -T TERM2"].as_slice());
std::thread::sleep(std::time::Duration::from_millis(200));
self.send_command(["exec", "foot -T TERM3"].as_slice());
std::thread::sleep(std::time::Duration::from_millis(200));
}
fn check_connection(&self, flag: &str) -> Result<(), ()> {
let mut retries = 100; // wait for max 10s
while retries > 0 {
let version = self.send_command(["-t", "get_version"].as_slice());
if String::from_utf8(version.stdout).unwrap().contains(flag)
|| String::from_utf8(version.stderr).unwrap().contains(flag)
{
return Ok(());
}
std::thread::sleep(std::time::Duration::from_millis(100));
retries -= 1;
}
return Err(());
}
fn stop(&mut self) {
signal(self.process.id(), "TERM");
match self.check_connection("Unable to connect to") {
Ok(()) => eprintln!("Sway terminated correctly on its own"),
Err(_) => {
self.process.kill().expect("Failed to kill sway");
eprintln!("Sway had to be killed");
}
}
}
}
impl Drop for Sway {
fn drop(&mut self) {
self.stop();
}
}