From e3eb5e67525ebff2e830fa5efbce57ddb77442dd Mon Sep 17 00:00:00 2001 From: Ximon Eighteen <3304436+ximon18@users.noreply.github.com> Date: Mon, 8 Mar 2021 11:27:23 +0100 Subject: [PATCH] Add a CTRL-C handler to Cypress UI tests to prevent the Cypress Docker container being left running if the test is aborted while running. (#431) --- Cargo.lock | 27 +++++++++- Cargo.toml | 1 + tests/ui/mod.rs | 141 ++++++++++++++++++++++++++++-------------------- 3 files changed, 108 insertions(+), 61 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8ce92e85..f70903bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -422,6 +422,16 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "ctrlc" +version = "3.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c15b8ec3b5755a188c141c1f6a98e76de31b936209bf066b647979e2a84764a9" +dependencies = [ + "nix", + "winapi 0.3.9", +] + [[package]] name = "deunicode" version = "0.4.3" @@ -1083,6 +1093,7 @@ dependencies = [ "chrono", "clap", "clokwerk", + "ctrlc", "fern", "futures 0.3.7", "futures-util", @@ -1151,9 +1162,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.80" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" +checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c" [[package]] name = "libflate" @@ -1338,6 +1349,18 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +[[package]] +name = "nix" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" +dependencies = [ + "bitflags", + "cc", + "cfg-if 1.0.0", + "libc", +] + [[package]] name = "num-integer" version = "0.1.43" diff --git a/Cargo.toml b/Cargo.toml index 6e6fa1fa..c0fe2946 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,7 @@ panic = "abort" [dev-dependencies] # for user management tiny_http = "^0.7" +ctrlc = "^3.1" # ------------------------------------------------------------------------------ # START DEBIAN PACKAGING diff --git a/tests/ui/mod.rs b/tests/ui/mod.rs index c694ce8d..734308c2 100644 --- a/tests/ui/mod.rs +++ b/tests/ui/mod.rs @@ -3,7 +3,7 @@ mod openid_connect_mock; use tokio::task; -use std::env; +use std::{env, process::ExitStatus}; use std::process::Command; use krill::daemon::config::Config; @@ -25,68 +25,91 @@ pub async fn run_krill_ui_test(test_name: &str, _with_openid_server: bool, testb } } +struct CypressRunner { + status: ExitStatus +} +impl CypressRunner { + pub async fn run(test_name: &str) -> Self { + let test_name = test_name.to_string(); + + ctrlc::set_handler(move || { + // If `cargo test` is stopped with CTRL-C the background Cypress Docker container continues to run. This + // prevents the next run of `cargo test` from working as the container unexpectedly already exists. Tell + // Docker to kill it to avoid leaving it lying around. + Command::new("docker").arg("kill").arg("cypress").spawn().expect("Failed to kill Cypress Docker container"); + }).expect("Error setting Ctrl-C handler"); + + let task = task::spawn_blocking(move || { + // NOTE: the directory mentioned here must be the same as the directory + // mentioned in the tests/ui/cypress/plugins/index.js file in the + // "integrationFolder" property otherwise Cypress mysteriously complains + // that it cannot find the spec file. + let cypress_spec_path = format!("tests/ui/cypress/specs/{}.js", test_name); + + let mut cmd = Command::new("docker"); + + cmd + .arg("run") + .arg("--name").arg("cypress") + .arg("--rm") + .arg("--net=host") + .arg("--ipc=host") + .arg("-v").arg(format!("{}:/e2e", env::current_dir().unwrap().display())) + .arg("-w").arg("/e2e"); + + if let Ok(debug_level) = std::env::var("CYPRESS_DEBUG") { + // Example values: + // - To get LOTS of Cypress logging: CYPRESS_DEBUG=cypress:* + // - To get logging relating to HTTP requests: CYPRESS_DEBUG=cypress:proxy:http:* + cmd + .arg("-e").arg(format!("DEBUG={}", debug_level)); + } + + if std::env::var("CYPRESS_INTERACTIVE").is_ok() { + // After running `cargo test` a Chrome browser should open from the Cypress Docker container on your local + // X server. For this to work you might need to run this command in your shell prior to `cargo test`: + // xhost + + cmd + .arg("-v").arg(format!("/tmp/.X11-unix:/tmp/.X11-unix")) + .arg("-e").arg("DISPLAY") + .arg("--entrypoint").arg("cypress"); + } + + cmd.arg("cypress/included:6.2.0"); + + if std::env::var("CYPRESS_INTERACTIVE").is_ok() { + cmd + .arg("open") + .arg("--project").arg("."); + } else { + cmd + .arg("--spec").arg(cypress_spec_path); + } + + cmd + .arg("--browser").arg("chrome") + .status() + .expect("Failed to run Cypress Docker UI test suite") + }).await; + + Self { + status: task.unwrap() + } + } + + pub fn success(self) -> bool { + self.status.success() + } +} + async fn do_run_krill_ui_test(test_name: &str, testbed_enabled: bool) { krill::constants::enable_test_mode(); let config_path = &format!("test-resources/ui/{}.conf", test_name); let config = Config::read_config(&config_path).unwrap(); + + // Start Krill as a Tokio task in the background and wait just until we can tell that it has started. start_krill(Some(config), testbed_enabled).await; - let test_name = test_name.to_string(); - - let cypress_task = task::spawn_blocking(move || { - // NOTE: the directory mentioned here must be the same as the directory - // mentioned in the tests/ui/cypress/plugins/index.js file in the - // "integrationFolder" property otherwise Cypress mysteriously complains - // that it cannot find the spec file. - let cypress_spec_path = format!("tests/ui/cypress/specs/{}.js", test_name); - - let mut cmd = Command::new("docker"); - - cmd - .arg("run") - .arg("--name").arg("cypress") - .arg("--rm") - .arg("--net=host") - .arg("--ipc=host") - .arg("-v").arg(format!("{}:/e2e", env::current_dir().unwrap().display())) - .arg("-w").arg("/e2e"); - - if let Ok(debug_level) = std::env::var("CYPRESS_DEBUG") { - // Example values: - // - To get LOTS of Cypress logging: CYPRESS_DEBUG=cypress:* - // - To get logging relating to HTTP requests: CYPRESS_DEBUG=cypress:proxy:http:* - cmd - .arg("-e").arg(format!("DEBUG={}", debug_level)); - } - - if std::env::var("CYPRESS_INTERACTIVE").is_ok() { - // After running `cargo test` a Chrome browser should open from the Cypress Docker container on your local - // X server. For this to work you might need to run this command in your shell prior to `cargo test`: - // xhost + - cmd - .arg("-v").arg(format!("/tmp/.X11-unix:/tmp/.X11-unix")) - .arg("-e").arg("DISPLAY") - .arg("--entrypoint").arg("cypress"); - } - - cmd.arg("cypress/included:6.2.0"); - - if std::env::var("CYPRESS_INTERACTIVE").is_ok() { - cmd - .arg("open") - .arg("--project").arg("."); - } else { - cmd - .arg("--spec").arg(cypress_spec_path); - } - - cmd - .arg("--browser").arg("chrome") - .status() - .expect("Failed to run Cypress Docker UI test suite") - }); - - let cypress_exit_status = cypress_task.await.unwrap(); - - assert!(cypress_exit_status.success()); + // Run the specified Cypress UI test suite and wait for it to finish + assert!(CypressRunner::run(test_name).await.success()); }