swaysome/tests/utils/mod.rs
Skia 75806d081f tests: make sure sway's subprocesses are killed before killing sway
This fixes the tests when run within Debian's autopkgtest. Since there
were still some living child processes, autopkgtest was stuck waiting
for them forever.
2025-09-09 12:57:47 +02:00

129 lines
4.5 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.
// 200ms 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(500));
self.send_command(["exec", "foot -T TERM2"].as_slice());
std::thread::sleep(std::time::Duration::from_millis(500));
self.send_command(["exec", "foot -T TERM3"].as_slice());
std::thread::sleep(std::time::Duration::from_millis(500));
}
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) {
// in case some apps were spawned, kill them, and give them some time to be killed
self.send_command(["[all] kill"].as_slice());
std::thread::sleep(std::time::Duration::from_millis(500));
// now terminate sway
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();
}
}