Skip to content

Commit

Permalink
test: compile testdata/ only once (#6305)
Browse files Browse the repository at this point in the history
* test: compile testdata/ only once

* comment
  • Loading branch information
DaniPopes authored Nov 14, 2023
1 parent 1a2e2e0 commit 8126b99
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 26 deletions.
5 changes: 1 addition & 4 deletions crates/forge/tests/cli/test_cmd.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Contains various tests for checking `forge test`
use foundry_config::Config;
use foundry_test_utils::util::{template_lock, OutputExt, OTHER_SOLC_VERSION, SOLC_VERSION};
use foundry_test_utils::util::{OutputExt, OTHER_SOLC_VERSION, SOLC_VERSION};
use foundry_utils::rpc;
use std::{path::PathBuf, process::Command, str::FromStr};

Expand Down Expand Up @@ -265,8 +265,6 @@ forgetest_init!(
#[serial_test::serial]
can_test_forge_std,
|prj, cmd| {
let mut lock = template_lock();
let write = lock.write().unwrap();
let forge_std_dir = prj.root().join("lib/forge-std");
let status = Command::new("git")
.current_dir(&forge_std_dir)
Expand All @@ -276,7 +274,6 @@ forgetest_init!(
if !status.success() {
panic!("failed to update forge-std");
}
drop(write);

// execute in subdir
cmd.cmd().current_dir(forge_std_dir);
Expand Down
8 changes: 4 additions & 4 deletions crates/forge/tests/it/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ impl TestConfig {
Self::with_filter(runner, Filter::matches_all())
}

pub async fn filter(filter: Filter) -> Self {
Self::with_filter(runner().await, filter)
}

pub fn with_filter(runner: MultiContractRunner, filter: Filter) -> Self {
init_tracing();
Self { runner, should_fail: false, filter, opts: test_opts() }
}

pub async fn filter(filter: Filter) -> Self {
Self::with_filter(runner().await, filter)
}

pub fn evm_spec(mut self, spec: SpecId) -> Self {
self.runner.evm_spec = spec;
self
Expand Down
28 changes: 26 additions & 2 deletions crates/forge/tests/it/test_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ use foundry_evm::{
opts::{Env, EvmOpts},
revm::db::DatabaseRef,
};
use foundry_test_utils::fd_lock;
use once_cell::sync::Lazy;
use std::{env, io::Write};

pub const RE_PATH_SEPARATOR: &str = "/";

const TESTDATA: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../testdata");
// const TESTDATA_LOCK: Lazy<PathBuf> = Lazy::new(|| {});

pub static PROJECT: Lazy<Project> = Lazy::new(|| {
let paths = ProjectPathsConfig::builder().root(TESTDATA).sources(TESTDATA).build().unwrap();
Expand All @@ -29,7 +30,30 @@ pub static PROJECT: Lazy<Project> = Lazy::new(|| {
});

pub static COMPILED: Lazy<ProjectCompileOutput> = Lazy::new(|| {
let out = PROJECT.compile().unwrap();
const LOCK: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../testdata/.lock");

let project = &*PROJECT;
assert!(project.cached);

// Compile only once per test run.
// We need to use a file lock because `cargo-nextest` runs tests in different processes.
// This is similar to [`foundry_test_utils::util::initialize`], see its comments for more
// details.
let mut lock = fd_lock::new_lock(LOCK);
let read = lock.read().unwrap();
let out;
if project.cache_path().exists() && std::fs::read(LOCK).unwrap() == b"1" {
out = project.compile();
drop(read);
} else {
drop(read);
let mut write = lock.write().unwrap();
write.write_all(b"1").unwrap();
out = project.compile();
drop(write);
};

let out = out.unwrap();
if out.has_compiler_errors() {
panic!("Compiled with errors:\n{out}");
}
Expand Down
21 changes: 21 additions & 0 deletions crates/test-utils/src/fd_lock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//! File locking utilities.
use crate::util::pretty_err;
use std::{
fs::{File, OpenOptions},
path::Path,
};

pub use fd_lock::*;

/// Creates a new lock file at the given path.
pub fn new_lock(lock_path: impl AsRef<Path>) -> RwLock<File> {
fn new_lock(lock_path: &Path) -> RwLock<File> {
let lock_file = pretty_err(
lock_path,
OpenOptions::new().read(true).write(true).create(true).open(lock_path),
);
RwLock::new(lock_file)
}
new_lock(lock_path.as_ref())
}
3 changes: 2 additions & 1 deletion crates/test-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ extern crate tracing;
// Macros useful for testing.
mod macros;

pub mod fd_lock;

mod filter;
pub use filter::Filter;

Expand All @@ -17,7 +19,6 @@ mod script;
pub use script::{ScriptOutcome, ScriptTester};

// re-exports for convenience
pub use fd_lock;
pub use foundry_compilers;

/// Initializes tracing for tests.
Expand Down
17 changes: 3 additions & 14 deletions crates/test-utils/src/util.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::init_tracing;
use eyre::{Result, WrapErr};
use fd_lock::RwLock;
use foundry_compilers::{
cache::SolFilesCache,
error::Result as SolcResult,
Expand Down Expand Up @@ -51,16 +50,6 @@ pub const SOLC_VERSION: &str = "0.8.23";
/// versions.
pub const OTHER_SOLC_VERSION: &str = "0.8.22";

/// Creates a file lock to the global template dir.
pub fn template_lock() -> RwLock<File> {
let lock_path = &*TEMPLATE_LOCK;
let lock_file = pretty_err(
lock_path,
fs::OpenOptions::new().read(true).write(true).create(true).open(lock_path),
);
RwLock::new(lock_file)
}

/// Initializes a project with `forge init` at the given path.
///
/// This should be called after an empty project is created like in
Expand All @@ -81,9 +70,9 @@ pub fn initialize(target: &Path) {
pretty_err(tpath, fs::create_dir_all(tpath));

// Initialize the global template if necessary.
let mut lock = template_lock();
let mut lock = crate::fd_lock::new_lock(TEMPLATE_LOCK.as_path());
let mut _read = Some(lock.read().unwrap());
if fs::read_to_string(&*TEMPLATE_LOCK).unwrap() != "1" {
if fs::read(&*TEMPLATE_LOCK).unwrap() != b"1" {
// We are the first to acquire the lock:
// - initialize a new empty temp project;
// - run `forge init`;
Expand Down Expand Up @@ -571,7 +560,7 @@ fn config_paths_exist(paths: &ProjectPathsConfig, cached: bool) {
pub fn pretty_err<T, E: std::error::Error>(path: impl AsRef<Path>, res: Result<T, E>) -> T {
match res {
Ok(t) => t,
Err(err) => panic!("{}: {err:?}", path.as_ref().display()),
Err(err) => panic!("{}: {err}", path.as_ref().display()),
}
}

Expand Down
3 changes: 2 additions & 1 deletion testdata/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Compiler files
cache/
out/
out/
.lock

0 comments on commit 8126b99

Please sign in to comment.