diff --git a/crates/forge/tests/cli/ext_integration.rs b/crates/forge/tests/cli/ext_integration.rs index f0044848a198..76c497837be3 100644 --- a/crates/forge/tests/cli/ext_integration.rs +++ b/crates/forge/tests/cli/ext_integration.rs @@ -8,23 +8,34 @@ fn solmate() { #[test] #[cfg_attr(windows, ignore = "Windows cannot find installed programs")] fn prb_math() { - ExtTester::new("PaulRBerg", "prb-math", "5b6279a0cf7c1b1b6a5cc96082811f7ef620cf60").run(); + ExtTester::new("PaulRBerg", "prb-math", "5b6279a0cf7c1b1b6a5cc96082811f7ef620cf60") + .install_command(&["bun", "install", "--prefer-offline"]) + // Try npm if bun fails / is not installed. + .install_command(&["npm", "install", "--prefer-offline"]) + .run(); } #[test] #[cfg_attr(windows, ignore = "Windows cannot find installed programs")] fn prb_proxy() { - ExtTester::new("PaulRBerg", "prb-proxy", "fa13cf09fbf544a2d575b45884b8e94a79a02c06").run(); + ExtTester::new("PaulRBerg", "prb-proxy", "fa13cf09fbf544a2d575b45884b8e94a79a02c06") + .install_command(&["bun", "install", "--prefer-offline"]) + // Try npm if bun fails / is not installed. + .install_command(&["npm", "install", "--prefer-offline"]) + .run(); } #[test] #[cfg_attr(windows, ignore = "Windows cannot find installed programs")] fn sablier_v2() { ExtTester::new("sablier-labs", "v2-core", "84758a40077bf3ccb1c8f7bb8d00278e672fbfef") - // skip fork tests + // Skip fork tests. .args(["--nmc", "Fork"]) - // run tests without optimizations + // Run tests without optimizations. .env("FOUNDRY_PROFILE", "lite") + .install_command(&["bun", "install", "--prefer-offline"]) + // Try npm if bun fails / is not installed. + .install_command(&["npm", "install", "--prefer-offline"]) .run(); } @@ -49,7 +60,9 @@ fn stringutils() { #[test] fn lootloose() { - ExtTester::new("gakonst", "lootloose", "7b639efe97836155a6a6fc626bf1018d4f8b2495").run(); + ExtTester::new("gakonst", "lootloose", "7b639efe97836155a6a6fc626bf1018d4f8b2495") + .install_command(&["make", "install"]) + .run(); } #[test] @@ -60,14 +73,16 @@ fn lil_web3() { #[test] #[cfg_attr(windows, ignore = "Windows cannot find installed programs")] fn snekmate() { - ExtTester::new("pcaversaccio", "snekmate", "ed49a0454393673cdf9a4250dd7051c28e6ac35f").run(); + ExtTester::new("pcaversaccio", "snekmate", "ed49a0454393673cdf9a4250dd7051c28e6ac35f") + .install_command(&["pnpm", "install", "--prefer-offline"]) + // Try npm if pnpm fails / is not installed. + .install_command(&["npm", "install", "--prefer-offline"]) + .run(); } #[test] fn makerdao_multicall() { - ExtTester::new("makerdao", "multicall", "103a8a28e4e372d582d6539b30031bda4cd48e21") - .args(["--block-number", "1"]) - .run(); + ExtTester::new("makerdao", "multicall", "103a8a28e4e372d582d6539b30031bda4cd48e21").run(); } #[test] @@ -93,7 +108,7 @@ fn gunilev() { } #[test] -fn convex() { +fn convex_shutdown_simulation() { ExtTester::new( "mds1", "convex-shutdown-simulation", diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index beed4498b8b0..ec54b1ef6c65 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -60,6 +60,7 @@ pub struct ExtTester { pub fork_block: Option, pub args: Vec, pub envs: Vec<(String, String)>, + pub install_commands: Vec>, } impl ExtTester { @@ -73,6 +74,7 @@ impl ExtTester { fork_block: None, args: vec![], envs: vec![], + install_commands: vec![], } } @@ -121,6 +123,15 @@ impl ExtTester { self } + /// Adds a command to run after the project is cloned. + /// + /// Note that the command is run in the project's root directory, and it won't fail the test if + /// it fails. + pub fn install_command(mut self, command: &[&str]) -> Self { + self.install_commands.push(command.iter().map(|s| s.to_string()).collect()); + self + } + /// Runs the test. pub fn run(&self) { // Skip fork tests if the RPC url is not set. @@ -129,7 +140,7 @@ impl ExtTester { return; } - let (prj, mut cmd) = setup_forge(self.name, self.style.clone()); + let (prj, mut test_cmd) = setup_forge(self.name, self.style.clone()); // Wipe the default structure. prj.wipe(); @@ -141,10 +152,10 @@ impl ExtTester { // Checkout the revision. if self.rev.is_empty() { - let mut cmd = Command::new("git"); - cmd.current_dir(root).args(["log", "-n", "1"]); - eprintln!("$ {cmd:?}"); - let output = cmd.output().unwrap(); + let mut git = Command::new("git"); + git.current_dir(root).args(["log", "-n", "1"]); + eprintln!("$ {git:?}"); + let output = git.output().unwrap(); if !output.status.success() { panic!("git log failed: {output:?}"); } @@ -152,31 +163,44 @@ impl ExtTester { let commit = stdout.lines().next().unwrap().split_whitespace().nth(1).unwrap(); panic!("pin to latest commit: {commit}"); } else { - let mut cmd = Command::new("git"); - cmd.current_dir(root).args(["checkout", self.rev]); - eprintln!("$ {cmd:?}"); - let status = cmd.status().unwrap(); + let mut git = Command::new("git"); + git.current_dir(root).args(["checkout", self.rev]); + eprintln!("$ {git:?}"); + let status = git.status().unwrap(); if !status.success() { panic!("git checkout failed: {status}"); } } - // Run common installation commands. - run_install_commands(prj.root()); + // Run installation command. + for install_command in &self.install_commands { + let mut install_cmd = Command::new(&install_command[0]); + install_cmd.args(&install_command[1..]).current_dir(root); + eprintln!("cd {root}; {install_cmd:?}"); + match install_cmd.status() { + Ok(s) => { + eprintln!("\n\n{install_cmd:?}: {s}"); + if s.success() { + break; + } + } + Err(e) => eprintln!("\n\n{install_cmd:?}: {e}"), + } + } // Run the tests. - cmd.arg("test"); - cmd.args(&self.args); - cmd.args(["--fuzz-runs=256", "--ffi", "-vvvvv"]); + test_cmd.arg("test"); + test_cmd.args(&self.args); + test_cmd.args(["--fuzz-runs=32", "--ffi", "-vvvvv"]); - cmd.envs(self.envs.iter().map(|(k, v)| (k, v))); - cmd.env("FOUNDRY_FUZZ_RUNS", "1"); + test_cmd.envs(self.envs.iter().map(|(k, v)| (k, v))); if let Some(fork_block) = self.fork_block { - cmd.env("FOUNDRY_ETH_RPC_URL", foundry_common::rpc::next_http_archive_rpc_endpoint()); - cmd.env("FOUNDRY_FORK_BLOCK_NUMBER", fork_block.to_string()); + test_cmd + .env("FOUNDRY_ETH_RPC_URL", foundry_common::rpc::next_http_archive_rpc_endpoint()); + test_cmd.env("FOUNDRY_FORK_BLOCK_NUMBER", fork_block.to_string()); } - cmd.assert_non_empty_stdout(); + test_cmd.assert_non_empty_stdout(); } } @@ -252,45 +276,6 @@ pub fn clone_remote(repo_url: &str, target_dir: &str) { eprintln!(); } -/// Runs common installation commands, such as `make` and `npm`. Continues if any command fails. -pub fn run_install_commands(root: &Path) { - let root_files = - std::fs::read_dir(root).unwrap().flatten().map(|x| x.path()).collect::>(); - let contains = |path: &str| root_files.iter().any(|p| p.to_str().unwrap().contains(path)); - let run = |args: &[&str]| { - let mut cmd = Command::new(args[0]); - cmd.args(&args[1..]).current_dir(root); - eprintln!("cd {}; {cmd:?}", root.display()); - match cmd.status() { - Ok(s) => { - eprintln!("\n\n{cmd:?}: {s}"); - s.success() - } - Err(e) => { - eprintln!("\n\n{cmd:?}: {e}"); - false - } - } - }; - let maybe_run = |path: &str, args: &[&str]| { - if contains(path) { - run(args) - } else { - false - } - }; - - maybe_run("Makefile", &["make", "install"]); - - // Only run one of these for `node_modules`. - let mut nm = false; - nm = nm || maybe_run("bun.lockb", &["bun", "install", "--prefer-offline"]); - nm = nm || maybe_run("pnpm-lock.yaml", &["pnpm", "install", "--prefer-offline"]); - nm = nm || maybe_run("yarn.lock", &["yarn", "install", "--prefer-offline"]); - nm = nm || maybe_run("package.json", &["npm", "install", "--prefer-offline"]); - let _ = nm; -} - /// Setup an empty test project and return a command pointing to the forge /// executable whose CWD is set to the project's root. /// @@ -628,7 +613,8 @@ impl TestProject { /// Returns the path to the forge executable. pub fn forge_bin(&self) -> Command { - let forge = self.exe_root.join(format!("../forge{}", env::consts::EXE_SUFFIX)); + let forge = self.exe_root.join("../forge").with_extension(env::consts::EXE_SUFFIX); + let forge = forge.canonicalize().unwrap(); let mut cmd = Command::new(forge); cmd.current_dir(self.inner.root()); // disable color output for comparisons @@ -638,7 +624,8 @@ impl TestProject { /// Returns the path to the cast executable. pub fn cast_bin(&self) -> Command { - let cast = self.exe_root.join(format!("../cast{}", env::consts::EXE_SUFFIX)); + let cast = self.exe_root.join("../cast").with_extension(env::consts::EXE_SUFFIX); + let cast = cast.canonicalize().unwrap(); let mut cmd = Command::new(cast); // disable color output for comparisons cmd.env("NO_COLOR", "1");