diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a4071f3..1c2d76f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,17 +1,29 @@ image: "rust:latest" -lint:cargo: +lint: stage: build script: - rustc --version && cargo --version - rustup component add rustfmt - cargo fmt -build:cargo: +build: stage: build script: - rustc --version && cargo --version - cargo build --release artifacts: paths: - - target/release/swaysome \ No newline at end of file + - target/release/swaysome + +test:integration: + stage: test + script: + - apt update && apt install -y --no-install-recommends sway foot + - adduser test + - chown -R test:test . + - su test < 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(); + } +}