From d65f0bbedc460f47c6d8745e06e0f29e054ea8e3 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Mon, 19 Sep 2022 12:11:51 -0500 Subject: [PATCH] Avoid example tests overwriting each other's platforms Closes https://github.com/roc-lang/roc/issues/4038 --- Cargo.lock | 6 ++++-- crates/cli/Cargo.toml | 2 ++ crates/cli/tests/cli_run.rs | 36 ++++++++++++++++++++++++++++++++++-- 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b3eca3518..54d499edbd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2615,9 +2615,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" +checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" [[package]] name = "oorandom" @@ -3458,6 +3458,8 @@ dependencies = [ "libloading", "memexec", "mimalloc", + "once_cell", + "parking_lot 0.12.1", "pretty_assertions", "roc_build", "roc_builtins", diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 44d86eaa0d..dc443b6d23 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -101,6 +101,8 @@ criterion = { git = "https://github.com/Anton-4/criterion.rs"} cli_utils = { path = "../cli_utils" } strum = "0.24.0" strum_macros = "0.24" +once_cell = "1.14.0" +parking_lot = "0.12" # Wasmer singlepass compiler only works on x86_64. [target.'cfg(target_arch = "x86_64")'.dev-dependencies] diff --git a/crates/cli/tests/cli_run.rs b/crates/cli/tests/cli_run.rs index 7db8f33f69..29639a3e5a 100644 --- a/crates/cli/tests/cli_run.rs +++ b/crates/cli/tests/cli_run.rs @@ -16,11 +16,14 @@ mod cli_run { }; use const_format::concatcp; use indoc::indoc; + use once_cell::sync::Lazy; + use parking_lot::{Mutex, RwLock}; use roc_cli::{CMD_BUILD, CMD_CHECK, CMD_FORMAT, CMD_RUN}; use roc_test_utils::assert_multiline_str_eq; use serial_test::serial; use std::iter; - use std::path::Path; + use std::path::{Path, PathBuf}; + use std::sync::Once; use strum::IntoEnumIterator; use strum_macros::EnumIter; @@ -31,8 +34,19 @@ mod cli_run { #[allow(dead_code)] const TARGET_FLAG: &str = concatcp!("--", roc_cli::FLAG_TARGET); - use std::sync::Once; static BENCHMARKS_BUILD_PLATFORM: Once = Once::new(); + static POPULATED_EXAMPLE_LOCKS: Once = Once::new(); + + use std::collections::HashMap; + static EXAMPLE_PLATFORM_LOCKS: Lazy>>> = + once_cell::sync::Lazy::new(|| RwLock::new(HashMap::default())); + + fn populate_example_locks(examples: impl Iterator) { + let mut locks = EXAMPLE_PLATFORM_LOCKS.write(); + for example in examples { + locks.insert(example, Default::default()); + } + } #[derive(Debug, EnumIter)] enum CliMode { @@ -270,12 +284,19 @@ mod cli_run { /// add a test for it here! macro_rules! examples { ($($test_name:ident:$name:expr => $example:expr,)+) => { + static EXAMPLE_NAMES: &[&str] = &[$($name,)+]; + $( #[test] #[allow(non_snake_case)] fn $test_name() { + POPULATED_EXAMPLE_LOCKS.call_once( || { + populate_example_locks(EXAMPLE_NAMES.iter().map(|name| examples_dir(name))) + }); + let dir_name = $name; let example = $example; + let example_dir = examples_dir(dir_name); let file_name = example_file(dir_name, example.filename); let mut app_args: Vec = vec![]; @@ -318,6 +339,17 @@ mod cli_run { _ => {} } + // To avoid concurrent examples tests overwriting produced host binaries, lock + // on the example's directory, so that only one example per directory runs at a + // time. + // NOTE: we are assuming that each example corresponds to one platform, under + // the subdirectory. This is not necessarily true, and moreover is too + // restrictive. To increase throughput we only need to lock the produced host + // file, however, it is not trivial to recover what that file is today (without + // enumerating all examples and their platforms). + let locks = EXAMPLE_PLATFORM_LOCKS.read(); + let _example_guard = locks.get(&example_dir).unwrap().lock(); + // Check with and without optimizations check_output_with_stdin( &file_name,