From 3cf57dccc82229d44a7fd7bea54bd2361f22ff14 Mon Sep 17 00:00:00 2001 From: Barret Rennie Date: Mon, 21 Sep 2020 18:11:28 -0400 Subject: [PATCH] Build a fake firefox.exe for integration-tests --- Cargo.lock | 16 +++++-- Cargo.toml | 1 + fakefox/Cargo.toml | 13 ++++++ fakefox/src/main.rs | 76 +++++++++++++++++++++++++++++++++ fxrunner/src/lib/zip.rs | 13 ------ integration-tests/Cargo.toml | 3 ++ integration-tests/build.rs | 49 +++++++++++++++++++++ integration-tests/src/mocks.rs | 12 ++---- integration-tests/src/util.rs | 16 +++---- test/README.md | 5 --- test/firefox.zip | Bin 300 -> 0 bytes 11 files changed, 164 insertions(+), 40 deletions(-) create mode 100644 fakefox/Cargo.toml create mode 100644 fakefox/src/main.rs create mode 100644 integration-tests/build.rs delete mode 100644 test/firefox.zip diff --git a/Cargo.lock b/Cargo.lock index d7d9330..bdff4ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -326,6 +326,13 @@ dependencies = [ "syn", ] +[[package]] +name = "fakefox" +version = "0.1.0" +dependencies = [ + "structopt", +] + [[package]] name = "flate2" version = "1.0.14" @@ -684,6 +691,7 @@ dependencies = [ "tempfile", "tokio", "url", + "zip", ] [[package]] @@ -1402,9 +1410,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" -version = "0.3.14" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "863246aaf5ddd0d6928dfeb1a9ca65f505599e4e1b399935ef7e75107516b4ef" +checksum = "6cc388d94ffabf39b5ed5fadddc40147cb21e605f53db6f8f36a625d27489ac5" dependencies = [ "clap", "lazy_static", @@ -1413,9 +1421,9 @@ dependencies = [ [[package]] name = "structopt-derive" -version = "0.4.7" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d239ca4b13aee7a2142e6795cbd69e457665ff8037aed33b3effdc430d2f927a" +checksum = "5e2513111825077552a6751dfad9e11ce0fba07d7276a3943a037d7e93e64c5f" dependencies = [ "heck", "proc-macro-error", diff --git a/Cargo.toml b/Cargo.toml index d59f865..a5029da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "fakefox", "fxrecorder", "fxrunner", "libfxrecord", diff --git a/fakefox/Cargo.toml b/fakefox/Cargo.toml new file mode 100644 index 0000000..a0c3e72 --- /dev/null +++ b/fakefox/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "fakefox" +version = "0.1.0" +authors = ["barret Rennie "] +edition = "2018" +license = "MPL-2.0" + +[[bin]] +name = "fakefox" +path = "src/main.rs" + +[dependencies] +structopt = "0.3.17" diff --git a/fakefox/src/main.rs b/fakefox/src/main.rs new file mode 100644 index 0000000..d53ce05 --- /dev/null +++ b/fakefox/src/main.rs @@ -0,0 +1,76 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use std::env; +use std::process::{exit, Command}; +use std::thread::sleep; +use std::time::Duration; + +use structopt::StructOpt; + +/// Mimic the behaviour of `firefox.exe` on Windows. +/// +/// By default, the first run of firefox.exe starts the "Launcher Process", which +/// does a bunch of work before re-executing `firefox.exe` as the main (parent) +/// process. + +/// Matches the options passed to `firefox.exe` by `fxrunner. +#[derive(StructOpt)] +struct LauncherOptions { + #[structopt(long = "profile")] + _profile: String, + + #[structopt(long)] + new_instance: bool, + + #[structopt(long)] + wait_for_browser: bool, +} + +/// Options to distinguish main process mode from launcher mode. +#[derive(StructOpt)] +struct MainOptions { + #[structopt(long)] + main: bool, +} + +fn main() { + let args: Vec = env::args().collect(); + + if let Ok(opts) = LauncherOptions::from_iter_safe(&args) { + eprintln!("[launcher] args: {:?}", args); + // Launcher process. + // Launch this executable with different command + assert!(opts.wait_for_browser); + assert!(opts.new_instance); + + let mut child = Command::new(&args[0]) + .arg("--main") + .spawn() + .expect("Could not spawn child"); + + eprintln!("[launcher] spawned child process {}", child.id()); + + let exit_status = child.wait().expect("wait()"); + let code = exit_status.code().unwrap(); + + eprintln!("[launcher] child exited: {}", code); + + exit(code); + } else if let Ok(opts) = MainOptions::from_iter_safe(&args) { + if opts.main { + eprintln!("[main] args: {:?}", args); + assert!(opts.main); + + // Main process. + // Just spin the even loop waiting to be terminated. + loop { + sleep(Duration::from_secs(30)); + } + } + } + + eprintln!("[fakefox] args: {:?}", args); + panic!("[fakefox] not executed as child or parent"); +} diff --git a/fxrunner/src/lib/zip.rs b/fxrunner/src/lib/zip.rs index 4fd86c5..28a5453 100644 --- a/fxrunner/src/lib/zip.rs +++ b/fxrunner/src/lib/zip.rs @@ -166,19 +166,6 @@ mod test { assert_eq!(stats.top_level_dir, None); } - { - let zip = test_dir.join("firefox.zip"); - let tempdir = TempDir::new().unwrap(); - - let stats = unzip(&zip, tempdir.path()).unwrap(); - - let firefox_dir = tempdir.path().join("firefox"); - assert!(firefox_dir.join("firefox.exe").is_file()); - - assert_eq!(stats.extracted, 1); - assert_eq!(stats.top_level_dir, Some(PathBuf::from("firefox"))); - } - { let zip = test_dir.join("profile.zip"); let tempdir = TempDir::new().unwrap(); diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 755064c..15daf17 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -9,6 +9,9 @@ license = "MPL-2.0" name = "integration-tests" path = "src/test.rs" +[build-dependencies] +zip = "0.5.6" + [dev-dependencies] assert_matches = "1.3.0" async-trait = "0.1.36" diff --git a/integration-tests/build.rs b/integration-tests/build.rs new file mode 100644 index 0000000..4128950 --- /dev/null +++ b/integration-tests/build.rs @@ -0,0 +1,49 @@ +use std::env; +use std::fs::File; +use std::io; +use std::path::Path; +use std::process::Command; + +use zip::write::FileOptions; +use zip::ZipWriter; + +fn main() { + // We need to build fakefox in a separate target directory, or else nested + // cargo build will hang forever waiting for a lock on the target directory. + let fakefox_target_path = env::current_dir() + .expect("no cwd") + .parent() + .expect("no parent diorectory") + .join("target") + .join("nested"); + + let cargo = env::var("CARGO").expect("no CARGO during cargo build"); + let cargo_status = Command::new(&cargo) + .args(&["build", "-p", "fakefox", "--target-dir"]) + .arg(&fakefox_target_path) + .status() + .expect("could not execute `cargo build -p fakefox`."); + assert!( + cargo_status.success(), + "Failed to run `cargo build -p fakefox`." + ); + + let out_dir = env::var("OUT_DIR").expect("no OUT_DIR during cargo build"); + let out_dir = Path::new(&out_dir); + + let fakefox_path = fakefox_target_path.join("debug").join("fakefox.exe"); + let zip_path = out_dir.join("firefox.zip"); + + let mut zip_file = File::create(&zip_path).expect("could not create firefox.zip"); + let mut fakefox_file = File::open(&fakefox_path).expect("could not open fakefox.exe"); + + let mut zip = ZipWriter::new(&mut zip_file); + zip.add_directory("firefox", FileOptions::default()) + .unwrap(); + zip.start_file("firefox/firefox.exe", FileOptions::default()) + .unwrap(); + io::copy(&mut fakefox_file, &mut zip).unwrap(); + zip.finish().unwrap(); + + println!("wrote firefox.zip to {}", zip_path.display()); +} diff --git a/integration-tests/src/mocks.rs b/integration-tests/src/mocks.rs index 361899e..880d4e0 100644 --- a/integration-tests/src/mocks.rs +++ b/integration-tests/src/mocks.rs @@ -18,7 +18,7 @@ use libfxrunner::taskcluster::Taskcluster; use tempfile::TempDir; use tokio::fs; -use crate::util::{test_dir, touch, AssertInvoked}; +use crate::util::{firefox_zip_path, test_dir, AssertInvoked}; /// The only valid session ID for TestSessionManager. pub const VALID_SESSION_ID: &str = "REQUESTID"; @@ -80,7 +80,7 @@ impl Taskcluster for TestTaskcluster { } Some(TaskclusterFailureMode::BadZip) => test_dir().join("test.zip"), Some(TaskclusterFailureMode::NotZip) => test_dir().join("README.md"), - None => test_dir().join("firefox.zip"), + None => firefox_zip_path(), }; let dest = download_dir.join("firefox.zip"); @@ -289,12 +289,8 @@ impl SessionManager for TestSessionManager { fs::create_dir(&session_info.path.join("profile")) .await .unwrap(); - fs::create_dir(&session_info.path.join("firefox")) - .await - .unwrap(); - touch(&session_info.path.join("firefox").join("firefox.exe")) - .await - .unwrap(); + + libfxrunner::zip::unzip(&firefox_zip_path(), &session_info.path).unwrap(); *self.handle.last_session_info.lock().unwrap() = Some(session_info.clone()); Ok(session_info) diff --git a/integration-tests/src/util.rs b/integration-tests/src/util.rs index bfb26a1..9568668 100644 --- a/integration-tests/src/util.rs +++ b/integration-tests/src/util.rs @@ -3,7 +3,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. use std::fs::File; -use std::io::{self, Read}; +use std::io::Read; use std::path::{Path, PathBuf}; /// A test helper that is used to assert an operation either occurred or did not @@ -50,6 +50,11 @@ pub fn test_dir() -> PathBuf { .join("test") } +/// Return the path of the build-generated `firefox.zip`. +pub fn firefox_zip_path() -> PathBuf { + PathBuf::from(env!("OUT_DIR")).join("firefox.zip") +} + pub fn directory_is_empty(path: &Path) -> bool { path.read_dir() .unwrap() @@ -80,12 +85,3 @@ pub fn assert_file_contents_eq(path: &Path, expected: &'static str) { }; assert_eq!(contents, expected); } - -pub async fn touch(path: &Path) -> Result<(), io::Error> { - tokio::fs::OpenOptions::new() - .create(true) - .write(true) - .open(path) - .await - .map(drop) -} diff --git a/test/README.md b/test/README.md index ca73e0f..3cce37c 100644 --- a/test/README.md +++ b/test/README.md @@ -2,11 +2,6 @@ This directory contains artifacts used for testing. -## firefox.zip - -This is a sample build artifact containing a "Firefox executable" (an empty -file) used for mocking Taskcluster responses. - ## profile.zip This file is a sample profile containing some files present in Firefox diff --git a/test/firefox.zip b/test/firefox.zip deleted file mode 100644 index 287026ed7cde62d9b7dcaa470a05c84330a8c0c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 300 zcmWIWW@Zs#00Gn5R(~)9N^k(_w9KN^wEPPF0H7)^u(%1DDq$p5P}O>=6{!K)PNXfGLqAf iEm@_anF2DjaBmIRG8jL=o0SdZ5+)$r52V9E90mZB=Q*wb