diff --git a/crates/common/src/term.rs b/crates/common/src/term.rs index 22e9730de5d3..573bfb2f5e61 100644 --- a/crates/common/src/term.rs +++ b/crates/common/src/term.rs @@ -184,16 +184,16 @@ impl Reporter for SpinnerReporter { /// Invoked before a new [`Solc`] bin is installed fn on_solc_installation_start(&self, version: &Version) { - self.send_msg(format!("Installing solc version {version}")); + self.send_msg(format!("Installing Solc version {version}")); } /// Invoked before a new [`Solc`] bin was successfully installed fn on_solc_installation_success(&self, version: &Version) { - self.send_msg(format!("Successfully installed solc {version}")); + self.send_msg(format!("Successfully installed Solc {version}")); } fn on_solc_installation_error(&self, version: &Version, error: &str) { - self.send_msg(Paint::red(format!("Failed to install solc {version}: {error}")).to_string()); + self.send_msg(Paint::red(format!("Failed to install Solc {version}: {error}")).to_string()); } fn on_unresolved_imports(&self, imports: &[(&Path, &Path)], remappings: &[Remapping]) { diff --git a/crates/forge/tests/cli/cache.rs b/crates/forge/tests/cli/cache.rs index b8f93701430c..b2f6484262bd 100644 --- a/crates/forge/tests/cli/cache.rs +++ b/crates/forge/tests/cli/cache.rs @@ -16,11 +16,11 @@ forgetest!(can_list_specific_chain, |_prj, cmd| { }); forgetest_init!(can_test_no_cache, |prj, cmd| { - let _ = std::fs::remove_dir_all(prj.cache_path()); + prj.clear_cache(); cmd.args(["test", "--no-cache"]).assert_success(); - assert!(!prj.cache_path().exists(), "cache file should not exist"); + assert!(!prj.cache().exists(), "cache file should not exist"); - cmd.forge_fuse().args(["test"]).assert_success(); - assert!(prj.cache_path().exists(), "cache file should exist"); + cmd.forge_fuse().arg("test").assert_success(); + assert!(prj.cache().exists(), "cache file should exist"); }); diff --git a/crates/forge/tests/cli/cmd.rs b/crates/forge/tests/cli/cmd.rs index 4b5f89b340f0..3753c4d75fe6 100644 --- a/crates/forge/tests/cli/cmd.rs +++ b/crates/forge/tests/cli/cmd.rs @@ -1,11 +1,7 @@ //! Contains various tests for checking forge's commands use crate::constants::*; -use foundry_compilers::{ - artifacts::{BytecodeHash, Metadata}, - remappings::Remapping, - ConfigurableContractArtifact, -}; +use foundry_compilers::{artifacts::Metadata, remappings::Remapping, ConfigurableContractArtifact}; use foundry_config::{ parse_with_profile, BasicConfig, Chain, Config, NamedChain, SolidityErrorCode, }; @@ -527,24 +523,17 @@ forgetest_init!(can_emit_multiple_extra_output, |prj, cmd| { }); forgetest!(can_print_warnings, |prj, cmd| { - prj.inner() - .add_source( - "Foo", - r" -// SPDX-License-Identifier: UNLICENSED -pragma solidity >0.8.9; + prj.add_source( + "Foo", + r" contract Greeter { function foo(uint256 a) public { uint256 x = 1; } } ", - ) - .unwrap(); - - // explicitly set to run with 0.8.10 - let config = Config { solc: Some("0.8.10".into()), ..Default::default() }; - prj.write_config(config); + ) + .unwrap(); cmd.arg("build"); @@ -575,12 +564,9 @@ Warning (5667): Warning: Unused function parameter. Remove or comment out the va // | ^^^^ #[cfg(not(target_os = "windows"))] forgetest!(can_handle_direct_imports_into_src, |prj, cmd| { - prj.inner() - .add_source( - "Foo", - r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; + prj.add_source( + "Foo", + r#" import {FooLib} from "src/FooLib.sol"; struct Bar { uint8 x; @@ -596,23 +582,20 @@ contract Foo { } } "#, - ) - .unwrap(); + ) + .unwrap(); - prj.inner() - .add_source( - "FooLib", - r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; + prj.add_source( + "FooLib", + r#" import {Foo, Bar} from "src/Foo.sol"; library FooLib { function check(Bar memory b) public {} function check2(Foo f) public {} } "#, - ) - .unwrap(); + ) + .unwrap(); cmd.arg("build"); @@ -625,17 +608,11 @@ Compiler run successful! // tests that the `inspect` command works correctly forgetest!(can_execute_inspect_command, |prj, cmd| { - // explicitly set to include the ipfs bytecode hash - let config = Config { bytecode_hash: BytecodeHash::Ipfs, ..Default::default() }; - prj.write_config(config); let contract_name = "Foo"; let path = prj - .inner() .add_source( contract_name, r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; contract Foo { event log_string(string); function run() external { @@ -646,15 +623,9 @@ contract Foo { ) .unwrap(); - // Remove the ipfs hash from the metadata - let mut dynamic_bytecode = "0x608060405234801561001057600080fd5b5060c08061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063c040622614602d575b600080fd5b60336035565b005b7f0b2e13ff20ac7b474198655583edf70dedd2c1dc980e329c4fbb2fc0748b796b6040516080906020808252600a908201526939b1b934b83a103930b760b11b604082015260600190565b60405180910390a156fea264697066735822122065c066d19101ad1707272b9a884891af8ab0cf5a0e0bba70c4650594492c14be64736f6c634300080a0033\n".to_string(); - let ipfs_start = dynamic_bytecode.len() - (24 + 64); - let ipfs_end = ipfs_start + 65; - dynamic_bytecode.replace_range(ipfs_start..ipfs_end, ""); - - let check_output = |mut output: String| { - output.replace_range(ipfs_start..ipfs_end, ""); - assert_eq!(dynamic_bytecode, output); + let check_output = |output: String| { + let output = output.trim(); + assert!(output.starts_with("0x") && hex::decode(output).is_ok(), "{output}"); }; cmd.arg("inspect").arg(contract_name).arg("bytecode"); @@ -672,12 +643,9 @@ forgetest!( |prj, cmd| { prj.insert_ds_test(); - prj.inner() - .add_source( - "ATest.t.sol", - r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; + prj.add_source( + "ATest.t.sol", + r#" import "./test.sol"; contract ATest is DSTest { function testExample() public { @@ -685,8 +653,8 @@ contract ATest is DSTest { } } "#, - ) - .unwrap(); + ) + .unwrap(); cmd.arg("snapshot"); @@ -707,35 +675,30 @@ forgetest!(can_compile_without_warnings, |prj, cmd| { ..Default::default() }; prj.write_config(config); - prj.inner() - .add_source( - "A", - r" -pragma solidity 0.8.10; + prj.add_raw_source( + "A", + r" +pragma solidity *; contract A { function testExample() public {} } ", - ) - .unwrap(); + ) + .unwrap(); cmd.args(["build", "--force"]); let out = cmd.stdout_lossy(); // no warnings - assert!(out.trim().contains("Compiler run successful!")); - assert!(!out.trim().contains("Compiler run successful with warnings:")); + assert!(out.contains("Compiler run successful!")); + assert!(!out.contains("Compiler run successful with warnings:")); // don't ignore errors let config = Config { ignored_error_codes: vec![], ..Default::default() }; prj.write_config(config); let out = cmd.stdout_lossy(); - assert!(out.trim().contains("Compiler run successful with warnings:")); - assert!( - out.contains( - r#"Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: " to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information."# - ) - ); + assert!(out.contains("Compiler run successful with warnings:"), "{out}"); + assert!(out.contains("Warning") && out.contains("SPDX-License-Identifier"), "{out}"); }); // test that `forge build` compiles when severity set to error, fails when set to warning, and @@ -743,23 +706,21 @@ contract A { forgetest!(can_fail_compile_with_warnings, |prj, cmd| { let config = Config { ignored_error_codes: vec![], deny_warnings: false, ..Default::default() }; prj.write_config(config); - prj.inner() - .add_source( - "A", - r" -pragma solidity 0.8.10; + prj.add_raw_source( + "A", + r" +pragma solidity *; contract A { function testExample() public {} } ", - ) - .unwrap(); + ) + .unwrap(); + // there are no errors cmd.args(["build", "--force"]); let out = cmd.stdout_lossy(); - // there are no errors - assert!(out.trim().contains("Compiler run successful")); - assert!(out.trim().contains("Compiler run successful with warnings:")); + assert!(out.contains("Compiler run successful with warnings:"), "{out}"); // warning fails to compile let config = Config { ignored_error_codes: vec![], deny_warnings: true, ..Default::default() }; @@ -775,8 +736,8 @@ contract A { prj.write_config(config); let out = cmd.stdout_lossy(); - assert!(out.trim().contains("Compiler run successful!")); - assert!(!out.trim().contains("Compiler run successful with warnings:")); + assert!(out.contains("Compiler run successful!")); + assert!(!out.contains("Compiler run successful with warnings:")); }); // test against a local checkout, useful to debug with local ethers-rs patch @@ -814,12 +775,9 @@ forgetest!( forgetest!(can_build_after_failure, |prj, cmd| { prj.insert_ds_test(); - prj.inner() - .add_source( - "ATest.t.sol", - r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; + prj.add_source( + "ATest.t.sol", + r#" import "./test.sol"; contract ATest is DSTest { function testExample() public { @@ -827,14 +785,11 @@ contract ATest is DSTest { } } "#, - ) - .unwrap(); - prj.inner() - .add_source( - "BTest.t.sol", - r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; + ) + .unwrap(); + prj.add_source( + "BTest.t.sol", + r#" import "./test.sol"; contract BTest is DSTest { function testExample() public { @@ -842,8 +797,8 @@ contract BTest is DSTest { } } "#, - ) - .unwrap(); + ) + .unwrap(); cmd.arg("build"); cmd.assert_non_empty_stdout(); @@ -851,8 +806,6 @@ contract BTest is DSTest { prj.assert_artifacts_dir_exists(); let syntax_err = r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; import "./test.sol"; contract CTest is DSTest { function testExample() public { @@ -862,7 +815,7 @@ contract CTest is DSTest { "#; // introduce contract with syntax error - prj.inner().add_source("CTest.t.sol", syntax_err).unwrap(); + prj.add_source("CTest.t.sol", syntax_err).unwrap(); // `forge build --force` which should fail cmd.arg("--force"); @@ -870,19 +823,16 @@ contract CTest is DSTest { // but ensure this cleaned cache and artifacts assert!(!prj.paths().artifacts.exists()); - assert!(!prj.cache_path().exists()); + assert!(!prj.cache().exists()); // still errors cmd.forge_fuse().arg("build"); cmd.assert_err(); // resolve the error by replacing the file - prj.inner() - .add_source( - "CTest.t.sol", - r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; + prj.add_source( + "CTest.t.sol", + r#" import "./test.sol"; contract CTest is DSTest { function testExample() public { @@ -890,22 +840,22 @@ contract CTest is DSTest { } } "#, - ) - .unwrap(); + ) + .unwrap(); cmd.assert_non_empty_stdout(); prj.assert_cache_exists(); prj.assert_artifacts_dir_exists(); // ensure cache is unchanged after error - let cache = fs::read_to_string(prj.cache_path()).unwrap(); + let cache = fs::read_to_string(prj.cache()).unwrap(); // introduce the error again but building without force - prj.inner().add_source("CTest.t.sol", syntax_err).unwrap(); + prj.add_source("CTest.t.sol", syntax_err).unwrap(); cmd.assert_err(); // ensure unchanged cache file - let cache_after = fs::read_to_string(prj.cache_path()).unwrap(); + let cache_after = fs::read_to_string(prj.cache()).unwrap(); assert_eq!(cache, cache_after); }); @@ -1054,18 +1004,15 @@ forgetest!( // create test file that uses the top-level dependency; if the sub-dependency is updated, // compilation will fail - prj.inner() - .add_source( - "CounterCopy", - r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; + prj.add_source( + "CounterCopy", + r#" import "forge-5980-test/Counter.sol"; contract CounterCopy is Counter { } "#, - ) - .unwrap(); + ) + .unwrap(); // build and check output cmd.forge_fuse().arg("build"); @@ -1076,12 +1023,10 @@ contract CounterCopy is Counter { forgetest!(gas_report_all_contracts, |prj, cmd| { prj.insert_ds_test(); - prj.inner() - .add_source( - "Contracts.sol", - r#" + prj.add_source( + "Contracts.sol", + r#" //SPDX-license-identifier: MIT -pragma solidity ^0.8.0; import "./test.sol"; @@ -1164,8 +1109,8 @@ contract ContractThreeTest is DSTest { } } "#, - ) - .unwrap(); + ) + .unwrap(); // report for all prj.write_config(Config { @@ -1205,12 +1150,10 @@ contract ContractThreeTest is DSTest { forgetest!(gas_report_some_contracts, |prj, cmd| { prj.insert_ds_test(); - prj.inner() - .add_source( - "Contracts.sol", - r#" + prj.add_source( + "Contracts.sol", + r#" //SPDX-license-identifier: MIT -pragma solidity ^0.8.0; import "./test.sol"; @@ -1293,8 +1236,8 @@ contract ContractThreeTest is DSTest { } } "#, - ) - .unwrap(); + ) + .unwrap(); // report for One prj.write_config(Config { @@ -1331,12 +1274,10 @@ contract ContractThreeTest is DSTest { forgetest!(gas_ignore_some_contracts, |prj, cmd| { prj.insert_ds_test(); - prj.inner() - .add_source( - "Contracts.sol", - r#" + prj.add_source( + "Contracts.sol", + r#" //SPDX-license-identifier: MIT -pragma solidity ^0.8.0; import "./test.sol"; @@ -1419,8 +1360,8 @@ contract ContractThreeTest is DSTest { } } "#, - ) - .unwrap(); + ) + .unwrap(); // ignore ContractOne prj.write_config(Config { @@ -1471,40 +1412,34 @@ forgetest_init!(can_use_absolute_imports, |prj, cmd| { }; prj.write_config(config); - prj.inner() - .add_lib( - "myDepdendency/src/interfaces/IConfig.sol", - r" - pragma solidity ^0.8.10; - + prj.add_lib( + "myDepdendency/src/interfaces/IConfig.sol", + r" + interface IConfig {} ", - ) - .unwrap(); + ) + .unwrap(); - prj.inner() - .add_lib( - "myDepdendency/src/Config.sol", - r#" - pragma solidity ^0.8.10; - import "src/interfaces/IConfig.sol"; + prj.add_lib( + "myDepdendency/src/Config.sol", + r#" + import "src/interfaces/IConfig.sol"; contract Config {} "#, - ) - .unwrap(); + ) + .unwrap(); - prj.inner() - .add_source( - "Greeter", - r#" - pragma solidity ^0.8.10; - import "myDepdendency/src/Config.sol"; + prj.add_source( + "Greeter", + r#" + import "myDepdendency/src/Config.sol"; contract Greeter {} "#, - ) - .unwrap(); + ) + .unwrap(); cmd.arg("build"); let stdout = cmd.stdout_lossy(); @@ -1513,55 +1448,45 @@ forgetest_init!(can_use_absolute_imports, |prj, cmd| { // forgetest_init!(can_use_absolute_imports_from_test_and_script, |prj, cmd| { - prj.inner() - .add_script( - "IMyScript.sol", - r" - pragma solidity ^0.8.10; - - interface IMyScript {} - ", - ) - .unwrap(); - - prj.inner() - .add_script( - "MyScript.sol", - r#" - pragma solidity ^0.8.10; - import "script/IMyScript.sol"; - - contract MyScript is IMyScript {} - "#, - ) - .unwrap(); - - prj.inner() - .add_test( - "IMyTest.sol", - r" - pragma solidity ^0.8.10; - - interface IMyTest {} - ", - ) - .unwrap(); - - prj.inner() - .add_test( - "MyTest.sol", - r#" - pragma solidity ^0.8.10; - import "test/IMyTest.sol"; - - contract MyTest is IMyTest {} - "#, - ) - .unwrap(); + prj.add_script( + "IMyScript.sol", + r" +interface IMyScript {} + ", + ) + .unwrap(); + + prj.add_script( + "MyScript.sol", + r#" +import "script/IMyScript.sol"; + +contract MyScript is IMyScript {} + "#, + ) + .unwrap(); + + prj.add_test( + "IMyTest.sol", + r" +interface IMyTest {} + ", + ) + .unwrap(); + + prj.add_test( + "MyTest.sol", + r#" +import "test/IMyTest.sol"; + +contract MyTest is IMyTest {} + "#, + ) + .unwrap(); cmd.arg("build"); let stdout = cmd.stdout_lossy(); - assert!(stdout.contains("Compiler run successful")); + assert!(stdout.contains("Compiler run successful"), "{stdout}"); }); // checks `forge inspect irOptimized works @@ -1599,54 +1524,50 @@ forgetest_init!(can_install_missing_deps_build, |prj, cmd| { let output = cmd.stdout_lossy(); assert!(output.contains("Missing dependencies found. Installing now"), "{}", output); - assert!(output.contains("Compiler run successful"), "{}", output); + assert!(output.contains("No files changed, compilation skipped"), "{}", output); }); // checks that extra output works forgetest_init!(can_build_skip_contracts, |prj, cmd| { - // explicitly set to run with 0.8.17 for consistent output - let config = Config { solc: Some("0.8.17".into()), ..Default::default() }; - prj.write_config(config); + prj.clear_cache(); // only builds the single template contract `src/*` cmd.args(["build", "--skip", "tests", "--skip", "scripts"]); - cmd.unchecked_output().stdout_matches_path( PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("tests/fixtures/can_build_skip_contracts.stdout"), ); + // re-run command let out = cmd.stdout_lossy(); // unchanged - assert!(out.trim().contains("No files changed, compilation skipped"), "{}", out); + assert!(out.contains("No files changed, compilation skipped"), "{}", out); }); forgetest_init!(can_build_skip_glob, |prj, cmd| { - // explicitly set to run with 0.8.17 for consistent output - let config = Config { solc: Some("0.8.17".into()), ..Default::default() }; - prj.write_config(config); - prj.inner() - .add_test( - "Foo", - r" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.17; + prj.clear_cache(); + + prj.add_test( + "Foo", + r" contract TestDemo { function test_run() external {} }", - ) - .unwrap(); + ) + .unwrap(); + // only builds the single template contract `src/*` even if `*.t.sol` or `.s.sol` is absent cmd.args(["build", "--skip", "*/test/**", "--skip", "*/script/**"]); - cmd.unchecked_output().stdout_matches_path( PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/can_build_skip_glob.stdout"), ); }); // checks that build --sizes includes all contracts even if unchanged -forgetest_init!(can_build_sizes_repeatedly, |_prj, cmd| { +forgetest_init!(can_build_sizes_repeatedly, |prj, cmd| { + prj.clear_cache(); + cmd.args(["build", "--sizes"]); let out = cmd.stdout_lossy(); @@ -1661,7 +1582,9 @@ forgetest_init!(can_build_sizes_repeatedly, |_prj, cmd| { }); // checks that build --names includes all contracts even if unchanged -forgetest_init!(can_build_names_repeatedly, |_prj, cmd| { +forgetest_init!(can_build_names_repeatedly, |prj, cmd| { + prj.clear_cache(); + cmd.args(["build", "--names"]); let out = cmd.stdout_lossy(); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 4c454ff83750..96140c525585 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -10,7 +10,7 @@ use foundry_config::{ use foundry_evm::opts::EvmOpts; use foundry_test_utils::{ foundry_compilers::{remappings::Remapping, EvmVersion}, - util::{pretty_err, OutputExt, TestCommand}, + util::{pretty_err, OutputExt, TestCommand, OTHER_SOLC_VERSION}, }; use path_slash::PathBufExt; use pretty_assertions::assert_eq; @@ -319,72 +319,62 @@ forgetest_init!(can_set_config_values, |prj, _cmd| { // tests that solc can be explicitly set forgetest!(can_set_solc_explicitly, |prj, cmd| { - prj.inner() - .add_source( - "Foo", - r" -// SPDX-License-Identifier: UNLICENSED -pragma solidity >0.8.9; + prj.add_source( + "Foo", + r" +pragma solidity *; contract Greeter {} ", - ) - .unwrap(); + ) + .unwrap(); - // explicitly set to run with 0.8.10 - let config = Config { solc: Some("0.8.10".into()), ..Default::default() }; + let config = Config { solc: Some(OTHER_SOLC_VERSION.into()), ..Default::default() }; prj.write_config(config); cmd.arg("build"); - assert!(cmd.stdout_lossy().ends_with( - " -Compiler run successful! -", - )); + assert!(cmd.stdout_lossy().contains("Compiler run successful!")); }); // tests that `--use ` works forgetest!(can_use_solc, |prj, cmd| { - prj.inner() - .add_source( - "Foo", - r" -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.7.0; + prj.add_raw_source( + "Foo", + r" +pragma solidity *; contract Foo {} ", - ) - .unwrap(); - - cmd.args(["build", "--use", "0.7.1"]); + ) + .unwrap(); + cmd.args(["build", "--use", OTHER_SOLC_VERSION]); let stdout = cmd.stdout_lossy(); assert!(stdout.contains("Compiler run successful")); - cmd.forge_fuse().args(["build", "--force", "--use", "solc:0.7.1"]).root_arg(); - + cmd.forge_fuse() + .args(["build", "--force", "--use", &format!("solc:{OTHER_SOLC_VERSION}")]) + .root_arg(); + let stdout = cmd.stdout_lossy(); assert!(stdout.contains("Compiler run successful")); // fails to use solc that does not exist cmd.forge_fuse().args(["build", "--use", "this/solc/does/not/exist"]); assert!(cmd.stderr_lossy().contains("this/solc/does/not/exist does not exist")); - // 0.7.1 was installed in previous step, so we can use the path to this directly - let local_solc = foundry_compilers::Solc::find_svm_installed_version("0.7.1") + // `OTHER_SOLC_VERSION` was installed in previous step, so we can use the path to this directly + let local_solc = foundry_compilers::Solc::find_svm_installed_version(OTHER_SOLC_VERSION) .unwrap() - .expect("solc 0.7.1 is installed"); + .expect("solc is installed"); cmd.forge_fuse().args(["build", "--force", "--use"]).arg(local_solc.solc).root_arg(); + let stdout = cmd.stdout_lossy(); assert!(stdout.contains("Compiler run successful")); }); // test to ensure yul optimizer can be set as intended forgetest!(can_set_yul_optimizer, |prj, cmd| { - prj.inner() - .add_source( - "Foo", - r" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; + prj.add_source( + "Foo", + r" contract Foo { function bar() public pure { assembly { @@ -393,8 +383,8 @@ contract Foo { } } ", - ) - .unwrap(); + ) + .unwrap(); cmd.arg("build"); cmd.unchecked_output().stderr_matches_path( diff --git a/crates/forge/tests/cli/coverage.rs b/crates/forge/tests/cli/coverage.rs index 3631450bc14e..df44a1cf6a50 100644 --- a/crates/forge/tests/cli/coverage.rs +++ b/crates/forge/tests/cli/coverage.rs @@ -17,13 +17,9 @@ forgetest!(report_file_coverage, |prj, cmd| { forgetest!(test_setup_coverage, |prj, cmd| { prj.insert_ds_test(); - prj.inner() - .add_source( - "AContract.sol", - r#" -// SPDX-license-identifier: MIT -pragma solidity ^0.8.0; - + prj.add_source( + "AContract.sol", + r#" contract AContract { int public i; @@ -36,15 +32,12 @@ contract AContract { } } "#, - ) - .unwrap(); + ) + .unwrap(); - prj.inner() - .add_source( - "AContractTest.sol", - r#" -// SPDX-License-Identifier: MIT -pragma solidity 0.8.10; + prj.add_source( + "AContractTest.sol", + r#" import "./test.sol"; import {AContract} from "./AContract.sol"; @@ -61,8 +54,8 @@ contract AContractTest is DSTest { } } "#, - ) - .unwrap(); + ) + .unwrap(); let lcov_info = prj.root().join("lcov.info"); cmd.arg("coverage").args([ diff --git a/crates/forge/tests/cli/create.rs b/crates/forge/tests/cli/create.rs index fca462196036..89ea4e47da79 100644 --- a/crates/forge/tests/cli/create.rs +++ b/crates/forge/tests/cli/create.rs @@ -31,11 +31,9 @@ fn setup_with_simple_remapping(prj: &TestProject) -> String { }; prj.write_config(config); - prj.inner() - .add_source( - "LinkTest", - r#" -// SPDX-License-Identifier: MIT + prj.add_source( + "LinkTest", + r#" import "remapping/MyLib.sol"; contract LinkTest { function foo() public returns (uint256) { @@ -43,22 +41,20 @@ contract LinkTest { } } "#, - ) - .unwrap(); + ) + .unwrap(); - prj.inner() - .add_lib( - "remapping/MyLib", - r" -// SPDX-License-Identifier: MIT + prj.add_lib( + "remapping/MyLib", + r" library MyLib { function foobar(uint256 a) public view returns (uint256) { return a * 100; } } ", - ) - .unwrap(); + ) + .unwrap(); "src/LinkTest.sol:LinkTest".to_string() } @@ -73,12 +69,9 @@ fn setup_oracle(prj: &TestProject) -> String { }; prj.write_config(config); - prj.inner() - .add_source( - "Contract", - r#" -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; + prj.add_source( + "Contract", + r#" import {ChainlinkTWAP} from "./libraries/ChainlinkTWAP.sol"; contract Contract { function getPrice() public view returns (int latest) { @@ -86,24 +79,20 @@ contract Contract { } } "#, - ) - .unwrap(); - - prj.inner() - .add_source( - "libraries/ChainlinkTWAP", - r" -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; + ) + .unwrap(); + prj.add_source( + "libraries/ChainlinkTWAP", + r" library ChainlinkTWAP { function getLatestPrice(address base) public view returns (int256) { return 0; } } ", - ) - .unwrap(); + ) + .unwrap(); "src/Contract.sol:Contract".to_string() } @@ -144,12 +133,12 @@ forgetest_async!( #[serial_test::serial] can_create_template_contract, |prj, cmd| { + foundry_test_utils::util::initialize(prj.root()); + let (_api, handle) = spawn(NodeConfig::test()).await; let rpc = handle.http_endpoint(); let wallet = handle.dev_wallets().next().unwrap(); let pk = hex::encode(wallet.signer().to_bytes()); - cmd.args(["init", "--force"]); - cmd.assert_non_empty_stdout(); // explicitly byte code hash for consistent checks let config = Config { bytecode_hash: BytecodeHash::None, ..Default::default() }; @@ -158,8 +147,6 @@ forgetest_async!( cmd.forge_fuse().args([ "create", format!("./src/{TEMPLATE_CONTRACT}.sol:{TEMPLATE_CONTRACT}").as_str(), - "--use", - "solc:0.8.15", "--rpc-url", rpc.as_str(), "--private-key", @@ -183,11 +170,11 @@ forgetest_async!( #[serial_test::serial] can_create_using_unlocked, |prj, cmd| { + foundry_test_utils::util::initialize(prj.root()); + let (_api, handle) = spawn(NodeConfig::test()).await; let rpc = handle.http_endpoint(); let dev = handle.dev_accounts().next().unwrap(); - cmd.args(["init", "--force"]); - cmd.assert_non_empty_stdout(); // explicitly byte code hash for consistent checks let config = Config { bytecode_hash: BytecodeHash::None, ..Default::default() }; @@ -196,8 +183,6 @@ forgetest_async!( cmd.forge_fuse().args([ "create", format!("./src/{TEMPLATE_CONTRACT}.sol:{TEMPLATE_CONTRACT}").as_str(), - "--use", - "solc:0.8.15", "--rpc-url", rpc.as_str(), "--from", @@ -222,23 +207,20 @@ forgetest_async!( #[serial_test::serial] can_create_with_constructor_args, |prj, cmd| { + foundry_test_utils::util::initialize(prj.root()); + let (_api, handle) = spawn(NodeConfig::test()).await; let rpc = handle.http_endpoint(); let wallet = handle.dev_wallets().next().unwrap(); let pk = hex::encode(wallet.signer().to_bytes()); - cmd.args(["init", "--force"]); - cmd.assert_non_empty_stdout(); // explicitly byte code hash for consistent checks let config = Config { bytecode_hash: BytecodeHash::None, ..Default::default() }; prj.write_config(config); - prj.inner() - .add_source( - "ConstructorContract", - r#" -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; + prj.add_source( + "ConstructorContract", + r#" contract ConstructorContract { string public name; @@ -247,14 +229,12 @@ contract ConstructorContract { } } "#, - ) - .unwrap(); + ) + .unwrap(); cmd.forge_fuse().args([ "create", "./src/ConstructorContract.sol:ConstructorContract", - "--use", - "solc:0.8.15", "--rpc-url", rpc.as_str(), "--private-key", @@ -268,13 +248,9 @@ contract ConstructorContract { .join("tests/fixtures/can_create_with_constructor_args.stdout"), ); - prj.inner() - .add_source( - "TupleArrayConstructorContract", - r#" -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; - + prj.add_source( + "TupleArrayConstructorContract", + r#" struct Point { uint256 x; uint256 y; @@ -284,14 +260,12 @@ contract TupleArrayConstructorContract { constructor(Point[] memory _points) {} } "#, - ) - .unwrap(); + ) + .unwrap(); cmd.forge_fuse().args([ "create", "./src/TupleArrayConstructorContract.sol:TupleArrayConstructorContract", - "--use", - "solc:0.8.15", "--rpc-url", rpc.as_str(), "--private-key", diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 1619baf73f88..c8c931508eed 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -3,7 +3,6 @@ use crate::constants::TEMPLATE_CONTRACT; use alloy_primitives::Address; use anvil::{spawn, NodeConfig}; -use foundry_config::Config; use foundry_test_utils::{util::OutputExt, ScriptOutcome, ScriptTester}; use foundry_utils::{rpc, types::ToEthers}; use regex::Regex; @@ -16,13 +15,9 @@ forgetest_init!( can_use_fork_cheat_codes_in_script, |prj, cmd| { let script = prj - .inner() .add_source( "Foo", r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.10; - import "forge-std/Script.sol"; contract ContractScript is Script { @@ -46,12 +41,9 @@ contract ContractScript is Script { // Tests that the `run` command works correctly forgetest!(can_execute_script_command2, |prj, cmd| { let script = prj - .inner() .add_source( "Foo", r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; contract Demo { event log_string(string); function run() external { @@ -72,12 +64,9 @@ contract Demo { // Tests that the `run` command works correctly when path *and* script name is specified forgetest!(can_execute_script_command_fqn, |prj, cmd| { let script = prj - .inner() .add_source( "Foo", r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; contract Demo { event log_string(string); function run() external { @@ -98,12 +87,9 @@ contract Demo { // Tests that the run command can run arbitrary functions forgetest!(can_execute_script_command_with_sig, |prj, cmd| { let script = prj - .inner() .add_source( "Foo", r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; contract Demo { event log_string(string); function myFunction() external { @@ -125,12 +111,9 @@ contract Demo { forgetest_async!(can_execute_script_command_with_manual_gas_limit_unlocked, |prj, cmd| { foundry_test_utils::util::initialize(prj.root()); let deploy_script = prj - .inner() .add_source( "Foo", r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; import "forge-std/Script.sol"; contract GasWaster { @@ -181,12 +164,9 @@ contract DeployScript is Script { forgetest_async!(can_execute_script_command_with_manual_gas_limit, |prj, cmd| { foundry_test_utils::util::initialize(prj.root()); let deploy_script = prj - .inner() .add_source( "Foo", r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; import "forge-std/Script.sol"; contract GasWaster { @@ -236,12 +216,9 @@ contract DeployScript is Script { // Tests that the run command can run functions with arguments forgetest!(can_execute_script_command_with_args, |prj, cmd| { let script = prj - .inner() .add_source( "Foo", r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; contract Demo { event log_string(string); event log_uint(uint); @@ -265,12 +242,9 @@ contract Demo { // Tests that the run command can run functions with return values forgetest!(can_execute_script_command_with_returned, |prj, cmd| { let script = prj - .inner() .add_source( "Foo", r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; contract Demo { event log_string(string); function run() external returns (uint256 result, uint8) { @@ -291,12 +265,9 @@ forgetest_async!(can_broadcast_script_skipping_simulation, |prj, cmd| { foundry_test_utils::util::initialize(prj.root()); // This example script would fail in on-chain simulation let deploy_script = prj - .inner() .add_source( "DeployScript", r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; import "forge-std/Script.sol"; contract HashChecker { @@ -359,8 +330,6 @@ contract DeployScript is Script { .to_string(); let run_code = r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; import "forge-std/Script.sol"; import { HashChecker } from "./DeployScript.sol"; @@ -379,7 +348,7 @@ contract RunScript is Script { }"# .replace("CONTRACT_ADDRESS", contract_address); - let run_script = prj.inner().add_source("RunScript", run_code).unwrap(); + let run_script = prj.add_source("RunScript", &run_code).unwrap(); let run_contract = run_script.display().to_string() + ":RunScript"; cmd.forge_fuse(); @@ -788,13 +757,9 @@ forgetest_async!(can_execute_script_with_arguments, |prj, cmd| { cmd.forge_fuse(); let (_api, handle) = spawn(NodeConfig::test()).await; - let script = prj - .inner() - .add_script( + let script = prj .add_script( "Counter.s.sol", r#" -pragma solidity ^0.8.15; - import "forge-std/Script.sol"; struct Point { @@ -873,12 +838,9 @@ forgetest_async!(can_execute_script_with_arguments_nested_deploy, |prj, cmd| { let (_api, handle) = spawn(NodeConfig::test()).await; let script = prj - .inner() .add_script( "Counter.s.sol", r#" -pragma solidity ^0.8.13; - import "forge-std/Script.sol"; contract A { @@ -954,17 +916,10 @@ contract Script0 is Script { // checks that skipping build forgetest_init!(can_execute_script_and_skip_contracts, |prj, cmd| { - // explicitly set to run with 0.8.17 for consistent output - let config = Config { solc: Some("0.8.17".into()), ..Default::default() }; - prj.write_config(config); - let script = prj - .inner() .add_source( "Foo", r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.17; contract Demo { event log_string(string); function run() external returns (uint256 result, uint8) { @@ -1000,12 +955,9 @@ forgetest_async!(assert_tx_origin_is_not_overritten, |prj, cmd| { cmd.forge_fuse(); let script = prj - .inner() .add_script( "ScriptTxOrigin.s.sol", r#" -pragma solidity ^0.8.13; - import { Script } from "forge-std/Script.sol"; contract ScriptTxOrigin is Script { @@ -1066,12 +1018,9 @@ forgetest_async!(assert_can_create_multiple_contracts_with_correct_nonce, |prj, cmd.forge_fuse(); let script = prj - .inner() .add_script( "ScriptTxOrigin.s.sol", r#" -pragma solidity ^0.8.17; - import {Script, console} from "forge-std/Script.sol"; contract Contract { @@ -1111,12 +1060,9 @@ contract NestedCreateFail is Script { forgetest_async!(assert_can_detect_target_contract_with_interfaces, |prj, cmd| { let script = prj - .inner() .add_script( "ScriptWithInterface.s.sol", r#" -pragma solidity ^0.8.22; - contract Script { function run() external {} } diff --git a/crates/forge/tests/cli/svm.rs b/crates/forge/tests/cli/svm.rs index 71ec37aeafc8..7b0b38bce59d 100644 --- a/crates/forge/tests/cli/svm.rs +++ b/crates/forge/tests/cli/svm.rs @@ -3,13 +3,13 @@ use semver::Version; use svm::Platform; -/// The latest solc release +/// The latest Solc release. /// -/// solc to foundry release process: -/// 1. new solc release -/// 2. svm updated with all build info -/// 3. svm bumped in ethers-rs -/// 4. ethers bumped in foundry + update the `LATEST_SOLC` +/// Solc to Foundry release process: +/// 1. new solc release +/// 2. svm updated with all build info +/// 3. svm bumped in ethers-rs +/// 4. ethers bumped in foundry + update the `LATEST_SOLC` const LATEST_SOLC: Version = Version::new(0, 8, 23); macro_rules! ensure_svm_releases { @@ -56,6 +56,6 @@ contract CounterTest is Test {{ }} "# ); - prj.inner().add_test("Counter", src).unwrap(); + prj.add_test("Counter", &src).unwrap(); cmd.arg("test").stdout_lossy().contains("[PASS]"); }); diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 4de10924c475..19cb7355c10b 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -1,6 +1,6 @@ //! Contains various tests for checking `forge test` use foundry_config::Config; -use foundry_test_utils::util::{template_lock, OutputExt}; +use foundry_test_utils::util::{template_lock, OutputExt, OTHER_SOLC_VERSION, SOLC_VERSION}; use foundry_utils::rpc; use std::{path::PathBuf, process::Command, str::FromStr}; @@ -33,17 +33,13 @@ forgetest!(can_set_filter_values, |prj, cmd| { // tests that warning is displayed when there are no tests in project forgetest!(warn_no_tests, |prj, cmd| { - prj.inner() - .add_source( - "dummy", - r" -// SPDX-License-Identifier: UNLICENSED -pragma solidity =0.8.13; - + prj.add_source( + "dummy", + r" contract Dummy {} ", - ) - .unwrap(); + ) + .unwrap(); // set up command cmd.args(["test"]); @@ -55,17 +51,13 @@ contract Dummy {} // tests that warning is displayed with pattern when no tests match forgetest!(warn_no_tests_match, |prj, cmd| { - prj.inner() - .add_source( - "dummy", - r" -// SPDX-License-Identifier: UNLICENSED -pragma solidity =0.8.13; - + prj.add_source( + "dummy", + r" contract Dummy {} ", - ) - .unwrap(); + ) + .unwrap(); // set up command cmd.args(["test", "--match-test", "testA.*", "--no-match-test", "testB.*"]); @@ -81,20 +73,16 @@ contract Dummy {} // tests that suggestion is provided with pattern when no tests match forgetest!(suggest_when_no_tests_match, |prj, cmd| { // set up project - prj.inner() - .add_source( - "TestE.t.sol", - r" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; - + prj.add_source( + "TestE.t.sol", + r" contract TestC { function test1() public { } } ", - ) - .unwrap(); + ) + .unwrap(); // set up command cmd.args(["test", "--match-test", "testA.*", "--no-match-test", "testB.*"]); @@ -112,12 +100,9 @@ contract TestC { forgetest!(can_fuzz_array_params, |prj, cmd| { prj.insert_ds_test(); - prj.inner() - .add_source( - "ATest.t.sol", - r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; + prj.add_source( + "ATest.t.sol", + r#" import "./test.sol"; contract ATest is DSTest { function testArray(uint64[2] calldata values) external { @@ -125,8 +110,8 @@ contract ATest is DSTest { } } "#, - ) - .unwrap(); + ) + .unwrap(); cmd.arg("test"); cmd.stdout_lossy().contains("[PASS]"); @@ -136,11 +121,9 @@ contract ATest is DSTest { forgetest!(can_test_pre_bytecode_hash, |prj, cmd| { prj.insert_ds_test(); - prj.inner() - .add_source( - "ATest.t.sol", - r#" -// SPDX-License-Identifier: UNLICENSED + prj.add_source( + "ATest.t.sol", + r#" // pre bytecode hash version, was introduced in 0.6.0 pragma solidity 0.5.17; import "./test.sol"; @@ -150,8 +133,8 @@ contract ATest is DSTest { } } "#, - ) - .unwrap(); + ) + .unwrap(); cmd.arg("test"); cmd.stdout_lossy().contains("[PASS]"); @@ -161,12 +144,9 @@ contract ATest is DSTest { forgetest!(can_test_with_match_path, |prj, cmd| { prj.insert_ds_test(); - prj.inner() - .add_source( - "ATest.t.sol", - r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; + prj.add_source( + "ATest.t.sol", + r#" import "./test.sol"; contract ATest is DSTest { function testArray(uint64[2] calldata values) external { @@ -174,15 +154,12 @@ contract ATest is DSTest { } } "#, - ) - .unwrap(); - - prj.inner() - .add_source( - "FailTest.t.sol", - r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; + ) + .unwrap(); + + prj.add_source( + "FailTest.t.sol", + r#" import "./test.sol"; contract FailTest is DSTest { function testNothing() external { @@ -190,8 +167,8 @@ contract FailTest is DSTest { } } "#, - ) - .unwrap(); + ) + .unwrap(); cmd.args(["test", "--match-path", "*src/ATest.t.sol"]); assert!(cmd.stdout_lossy().contains("[PASS]") && !cmd.stdout_lossy().contains("[FAIL]")); @@ -207,12 +184,9 @@ forgetest!(can_run_test_in_custom_test_folder, |prj, cmd| { let config = cmd.config(); assert_eq!(config.test, PathBuf::from("nested/forge-tests")); - prj.inner() - .add_source( - "nested/forge-tests/MyTest.t.sol", - r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.10; + prj.add_source( + "nested/forge-tests/MyTest.t.sol", + r#" import "../../test.sol"; contract MyTest is DSTest { function testTrue() public { @@ -220,8 +194,8 @@ contract MyTest is DSTest { } } "#, - ) - .unwrap(); + ) + .unwrap(); cmd.arg("test"); cmd.unchecked_output().stdout_matches_path( @@ -247,13 +221,13 @@ forgetest_init!(can_test_repeatedly, |_prj, cmd| { forgetest!(runs_tests_exactly_once_with_changed_versions, |prj, cmd| { prj.insert_ds_test(); - prj.inner() - .add_source( - "Contract.t.sol", - r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.10; + prj.add_source( + "Contract.t.sol", + r#" +pragma solidity *; + import "./test.sol"; + contract ContractTest is DSTest { function setUp() public {} @@ -262,26 +236,26 @@ contract ContractTest is DSTest { } } "#, - ) - .unwrap(); + ) + .unwrap(); // pin version - let config = Config { solc: Some("0.8.10".into()), ..Default::default() }; + let config = Config { solc: Some(SOLC_VERSION.into()), ..Default::default() }; prj.write_config(config); cmd.arg("test"); cmd.unchecked_output().stdout_matches_path( PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures/runs_tests_exactly_once_with_changed_versions.0.8.10.stdout"), + .join("tests/fixtures/runs_tests_exactly_once_with_changed_versions.1.stdout"), ); // pin version - let config = Config { solc: Some("0.8.13".into()), ..Default::default() }; + let config = Config { solc: Some(OTHER_SOLC_VERSION.into()), ..Default::default() }; prj.write_config(config); cmd.unchecked_output().stdout_matches_path( PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("tests/fixtures/runs_tests_exactly_once_with_changed_versions.0.8.13.stdout"), + .join("tests/fixtures/runs_tests_exactly_once_with_changed_versions.2.stdout"), ); }); @@ -316,13 +290,9 @@ forgetest_init!( // tests that libraries are handled correctly in multiforking mode forgetest_init!(can_use_libs_in_multi_fork, |prj, cmd| { prj.wipe_contracts(); - prj.inner() - .add_source( - "Contract.sol", - r" -// SPDX-License-Identifier: UNLICENSED -pragma solidity =0.8.13; - + prj.add_source( + "Contract.sol", + r" library Library { function f(uint256 a, uint256 b) public pure returns (uint256) { return a + b; @@ -337,18 +307,14 @@ contract Contract { } } ", - ) - .unwrap(); + ) + .unwrap(); let endpoint = rpc::next_http_archive_rpc_endpoint(); - prj.inner() - .add_test( - "Contract.t.sol", - r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity =0.8.13; - + prj.add_test( + "Contract.t.sol", + &r#" import "forge-std/Test.sol"; import "src/Contract.sol"; @@ -362,9 +328,9 @@ contract ContractTest is Test { } } "# - .replace("", &endpoint), - ) - .unwrap(); + .replace("", &endpoint), + ) + .unwrap(); cmd.arg("test"); cmd.unchecked_output().stdout_matches_path( @@ -374,9 +340,6 @@ contract ContractTest is Test { }); static FAILING_TEST: &str = r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.17; - import "forge-std/Test.sol"; contract FailingTest is Test { @@ -388,7 +351,7 @@ contract FailingTest is Test { forgetest_init!(exit_code_error_on_fail_fast, |prj, cmd| { prj.wipe_contracts(); - prj.inner().add_source("failing_test", FAILING_TEST).unwrap(); + prj.add_source("failing_test", FAILING_TEST).unwrap(); // set up command cmd.args(["test", "--fail-fast"]); @@ -400,7 +363,7 @@ forgetest_init!(exit_code_error_on_fail_fast, |prj, cmd| { forgetest_init!(exit_code_error_on_fail_fast_with_json, |prj, cmd| { prj.wipe_contracts(); - prj.inner().add_source("failing_test", FAILING_TEST).unwrap(); + prj.add_source("failing_test", FAILING_TEST).unwrap(); // set up command cmd.args(["test", "--fail-fast", "--json"]); diff --git a/crates/forge/tests/cli/verify.rs b/crates/forge/tests/cli/verify.rs index 33b58f7d823b..8c916db1deb3 100644 --- a/crates/forge/tests/cli/verify.rs +++ b/crates/forge/tests/cli/verify.rs @@ -12,37 +12,30 @@ use foundry_utils::Retry; /// `import {Unique} from "./unique.sol";` fn add_unique(prj: &TestProject) { let timestamp = utils::millis_since_epoch(); - prj.inner() - .add_source( - "unique", - format!( - r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.4.0; - + prj.add_source( + "unique", + &format!( + r#" contract Unique {{ uint public _timestamp = {timestamp}; }} "# - ), - ) - .unwrap(); + ), + ) + .unwrap(); } fn add_verify_target(prj: &TestProject) { - prj.inner() - .add_source( - "Verify.sol", - r#" -// SPDX-License-Identifier: UNLICENSED -pragma solidity =0.8.10; + prj.add_source( + "Verify.sol", + r#" import {Unique} from "./unique.sol"; contract Verify is Unique { function doStuff() external {} } "#, - ) - .unwrap(); + ) + .unwrap(); } fn parse_verification_result(cmd: &mut TestCommand, retries: u32) -> eyre::Result<()> { diff --git a/crates/forge/tests/fixtures/can_build_skip_contracts.stdout b/crates/forge/tests/fixtures/can_build_skip_contracts.stdout index 74ddb6b4e49f..43949fcce59e 100644 --- a/crates/forge/tests/fixtures/can_build_skip_contracts.stdout +++ b/crates/forge/tests/fixtures/can_build_skip_contracts.stdout @@ -1,3 +1,3 @@ -Compiling 1 files with 0.8.17 -Solc 0.8.17 finished in 34.45ms +Compiling 1 files with 0.8.23 +Solc 0.8.23 finished in 34.45ms Compiler run successful! diff --git a/crates/forge/tests/fixtures/can_build_skip_glob.stdout b/crates/forge/tests/fixtures/can_build_skip_glob.stdout index 522beb3e2b6c..3213db81a21d 100644 --- a/crates/forge/tests/fixtures/can_build_skip_glob.stdout +++ b/crates/forge/tests/fixtures/can_build_skip_glob.stdout @@ -1,3 +1,3 @@ -Compiling 1 files with 0.8.17 -Solc 0.8.17 finished in 33.25ms +Compiling 1 files with 0.8.23 +Solc 0.8.23 finished in 33.25ms Compiler run successful! diff --git a/crates/forge/tests/fixtures/can_check_snapshot.stdout b/crates/forge/tests/fixtures/can_check_snapshot.stdout index 6a5df56b3fca..dffc5df49634 100644 --- a/crates/forge/tests/fixtures/can_check_snapshot.stdout +++ b/crates/forge/tests/fixtures/can_check_snapshot.stdout @@ -1,5 +1,5 @@ -Compiling 2 files with 0.8.10 -Solc 0.8.10 finished in 424.55ms +Compiling 2 files with 0.8.23 +Solc 0.8.23 finished in 424.55ms Compiler run successful! Running 1 test for src/ATest.t.sol:ATest diff --git a/crates/forge/tests/fixtures/can_create_template_contract.stdout b/crates/forge/tests/fixtures/can_create_template_contract.stdout index a67e4dd96aad..fd625cee305f 100644 --- a/crates/forge/tests/fixtures/can_create_template_contract.stdout +++ b/crates/forge/tests/fixtures/can_create_template_contract.stdout @@ -1,5 +1,5 @@ -Compiling 22 files with 0.8.15 -Solc 0.8.15 finished in 2.27s +Compiling 22 files with 0.8.23 +Solc 0.8.23 finished in 2.27s Compiler run successful! Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 Deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3 diff --git a/crates/forge/tests/fixtures/can_create_using_unlocked.stdout b/crates/forge/tests/fixtures/can_create_using_unlocked.stdout index ab309752469b..2e35831523fb 100644 --- a/crates/forge/tests/fixtures/can_create_using_unlocked.stdout +++ b/crates/forge/tests/fixtures/can_create_using_unlocked.stdout @@ -1,5 +1,5 @@ -Compiling 22 files with 0.8.15 -Solc 0.8.15 finished in 1.95s +Compiling 22 files with 0.8.23 +Solc 0.8.23 finished in 1.95s Compiler run successful! Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 Deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3 diff --git a/crates/forge/tests/fixtures/can_create_with_constructor_args.stdout b/crates/forge/tests/fixtures/can_create_with_constructor_args.stdout index ef198e784668..525e68ac13f8 100644 --- a/crates/forge/tests/fixtures/can_create_with_constructor_args.stdout +++ b/crates/forge/tests/fixtures/can_create_with_constructor_args.stdout @@ -1,5 +1,5 @@ -Compiling 23 files with 0.8.15 -Solc 0.8.15 finished in 2.82s +Compiling 23 files with 0.8.23 +Solc 0.8.23 finished in 2.82s Compiler run successful! Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 Deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3 diff --git a/crates/forge/tests/fixtures/can_create_with_tuple_constructor_args.stdout b/crates/forge/tests/fixtures/can_create_with_tuple_constructor_args.stdout index c70251f9b269..a0a574c959db 100644 --- a/crates/forge/tests/fixtures/can_create_with_tuple_constructor_args.stdout +++ b/crates/forge/tests/fixtures/can_create_with_tuple_constructor_args.stdout @@ -1,5 +1,5 @@ -Compiling 1 files with 0.8.15 -Solc 0.8.15 finished in 26.44ms +Compiling 1 files with 0.8.23 +Solc 0.8.23 finished in 26.44ms Compiler run successful! Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 Deployed to: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 diff --git a/crates/forge/tests/fixtures/can_execute_script_and_skip_contracts.stdout b/crates/forge/tests/fixtures/can_execute_script_and_skip_contracts.stdout index 46f580d03a5b..fdc8e739fbd6 100644 --- a/crates/forge/tests/fixtures/can_execute_script_and_skip_contracts.stdout +++ b/crates/forge/tests/fixtures/can_execute_script_and_skip_contracts.stdout @@ -1,5 +1,5 @@ -Compiling 1 files with 0.8.17 -Solc 0.8.17 +Compiling 1 files with 0.8.23 +Solc 0.8.23 Compiler run successful! Script ran successfully. Gas used: 22900 diff --git a/crates/forge/tests/fixtures/can_execute_script_command.stdout b/crates/forge/tests/fixtures/can_execute_script_command.stdout index 459bf7369bf9..a9717c19df4c 100644 --- a/crates/forge/tests/fixtures/can_execute_script_command.stdout +++ b/crates/forge/tests/fixtures/can_execute_script_command.stdout @@ -1,5 +1,5 @@ -Compiling 1 files with 0.8.10 -Solc 0.8.10 finished in 23.34ms +Compiling 1 files with 0.8.23 +Solc 0.8.23 finished in 23.34ms Compiler run successful! Script ran successfully. Gas used: 22815 diff --git a/crates/forge/tests/fixtures/can_execute_script_command_fqn.stdout b/crates/forge/tests/fixtures/can_execute_script_command_fqn.stdout index 6b2a06b30748..1156e916e40d 100644 --- a/crates/forge/tests/fixtures/can_execute_script_command_fqn.stdout +++ b/crates/forge/tests/fixtures/can_execute_script_command_fqn.stdout @@ -1,5 +1,5 @@ -Compiling 1 files with 0.8.10 -Solc 0.8.10 finished in 23.70ms +Compiling 1 files with 0.8.23 +Solc 0.8.23 finished in 23.70ms Compiler run successful! Script ran successfully. Gas used: 22815 diff --git a/crates/forge/tests/fixtures/can_execute_script_command_with_args.stdout b/crates/forge/tests/fixtures/can_execute_script_command_with_args.stdout index 882fe3a47c51..feb193073eb9 100644 --- a/crates/forge/tests/fixtures/can_execute_script_command_with_args.stdout +++ b/crates/forge/tests/fixtures/can_execute_script_command_with_args.stdout @@ -1,5 +1,5 @@ -Compiling 1 files with 0.8.10 -Solc 0.8.10 finished in 35.28ms +Compiling 1 files with 0.8.23 +Solc 0.8.23 finished in 35.28ms Compiler run successful! Script ran successfully. Gas used: 25301 diff --git a/crates/forge/tests/fixtures/can_execute_script_command_with_returned.stdout b/crates/forge/tests/fixtures/can_execute_script_command_with_returned.stdout index 0cc0e7f97b1c..62aa01b138ba 100644 --- a/crates/forge/tests/fixtures/can_execute_script_command_with_returned.stdout +++ b/crates/forge/tests/fixtures/can_execute_script_command_with_returned.stdout @@ -1,5 +1,5 @@ -Compiling 1 files with 0.8.10 -Solc 0.8.10 finished in 1.27s +Compiling 1 files with 0.8.23 +Solc 0.8.23 finished in 1.27s Compiler run successful! Script ran successfully. Gas used: 22900 diff --git a/crates/forge/tests/fixtures/can_execute_script_command_with_sig.stdout b/crates/forge/tests/fixtures/can_execute_script_command_with_sig.stdout index 54f954f36daa..13a94c698e96 100644 --- a/crates/forge/tests/fixtures/can_execute_script_command_with_sig.stdout +++ b/crates/forge/tests/fixtures/can_execute_script_command_with_sig.stdout @@ -1,5 +1,5 @@ -Compiling 1 files with 0.8.10 -Solc 0.8.10 finished in 24.49ms +Compiling 1 files with 0.8.23 +Solc 0.8.23 finished in 24.49ms Compiler run successful! Script ran successfully. Gas used: 22815 diff --git a/crates/forge/tests/fixtures/can_run_test_in_custom_test_folder.stdout b/crates/forge/tests/fixtures/can_run_test_in_custom_test_folder.stdout index 28a027349260..5cf274ebc7de 100644 --- a/crates/forge/tests/fixtures/can_run_test_in_custom_test_folder.stdout +++ b/crates/forge/tests/fixtures/can_run_test_in_custom_test_folder.stdout @@ -1,5 +1,5 @@ -Compiling 2 files with 0.8.10 -Solc 0.8.10 finished in 185.25ms +Compiling 2 files with 0.8.23 +Solc 0.8.23 finished in 185.25ms Compiler run successful! Running 1 test for src/nested/forge-tests/MyTest.t.sol:MyTest diff --git a/crates/forge/tests/fixtures/can_use_libs_in_multi_fork.stdout b/crates/forge/tests/fixtures/can_use_libs_in_multi_fork.stdout index 499648933b44..4c954e6c37c9 100644 --- a/crates/forge/tests/fixtures/can_use_libs_in_multi_fork.stdout +++ b/crates/forge/tests/fixtures/can_use_libs_in_multi_fork.stdout @@ -1,9 +1,9 @@ -Compiling 20 files with 0.8.13 -Solc 0.8.13 finished in 1.95s +Compiling 2 files with 0.8.23 +Solc 0.8.23 finished in 1.95s Compiler run successful! Running 1 test for test/Contract.t.sol:ContractTest -[PASS] test() (gas: 70351) +[PASS] test() (gas: 70360) Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.21s Ran 1 test suites: 1 tests passed, 0 failed, 0 skipped (1 total tests) diff --git a/crates/forge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.0.8.10.stdout b/crates/forge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.1.stdout similarity index 79% rename from crates/forge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.0.8.10.stdout rename to crates/forge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.1.stdout index 3a027e1825bb..aee6fb691ce3 100644 --- a/crates/forge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.0.8.10.stdout +++ b/crates/forge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.1.stdout @@ -1,5 +1,5 @@ -Compiling 2 files with 0.8.10 -Solc 0.8.10 finished in 185.25ms +Compiling 2 files with 0.8.23 +Solc 0.8.23 finished in 185.25ms Compiler run successful! Running 1 test for src/Contract.t.sol:ContractTest diff --git a/crates/forge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.0.8.13.stdout b/crates/forge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.2.stdout similarity index 79% rename from crates/forge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.0.8.13.stdout rename to crates/forge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.2.stdout index 006c3cfc478c..691af81679df 100644 --- a/crates/forge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.0.8.13.stdout +++ b/crates/forge/tests/fixtures/runs_tests_exactly_once_with_changed_versions.2.stdout @@ -1,5 +1,5 @@ -Compiling 2 files with 0.8.13 -Solc 0.8.13 finished in 185.25ms +Compiling 2 files with 0.8.22 +Solc 0.8.22 finished in 185.25ms Compiler run successful! Running 1 test for src/Contract.t.sol:ContractTest diff --git a/crates/forge/tests/fixtures/suggest_when_no_tests_match.stdout b/crates/forge/tests/fixtures/suggest_when_no_tests_match.stdout index 3c58603b91bc..1c27d8005cdc 100644 --- a/crates/forge/tests/fixtures/suggest_when_no_tests_match.stdout +++ b/crates/forge/tests/fixtures/suggest_when_no_tests_match.stdout @@ -1,5 +1,5 @@ -Compiling 1 files with 0.8.10 -Solc 0.8.10 finished in 185.25ms +Compiling 1 files with 0.8.23 +Solc 0.8.23 finished in 185.25ms Compiler run successful! No tests match the provided pattern: diff --git a/crates/forge/tests/fixtures/warn_no_tests.stdout b/crates/forge/tests/fixtures/warn_no_tests.stdout index 3af59c7af14e..9b2b8bff4748 100644 --- a/crates/forge/tests/fixtures/warn_no_tests.stdout +++ b/crates/forge/tests/fixtures/warn_no_tests.stdout @@ -1,5 +1,5 @@ -Compiling 1 files with 0.8.13 -Solc 0.8.13 +Compiling 1 files with 0.8.23 +Solc 0.8.23 Compiler run successful! No tests found in project! Forge looks for functions that starts with `test`. diff --git a/crates/forge/tests/fixtures/warn_no_tests_match.stdout b/crates/forge/tests/fixtures/warn_no_tests_match.stdout index 6eede4274858..56f068238d26 100644 --- a/crates/forge/tests/fixtures/warn_no_tests_match.stdout +++ b/crates/forge/tests/fixtures/warn_no_tests_match.stdout @@ -1,5 +1,5 @@ -Compiling 1 files with 0.8.13 -Solc 0.8.13 +Compiling 1 files with 0.8.23 +Solc 0.8.23 Compiler run successful! No tests match the provided pattern: diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 2496e89a4e31..1d0493497667 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -3,6 +3,7 @@ use eyre::{Result, WrapErr}; use fd_lock::RwLock; use foundry_compilers::{ cache::SolFilesCache, + error::Result as SolcResult, project_util::{copy_dir, TempProject}, ArtifactOutput, ConfigurableArtifacts, PathStyle, ProjectPathsConfig, }; @@ -27,21 +28,30 @@ use std::{ static CURRENT_DIR_LOCK: Lazy> = Lazy::new(|| Mutex::new(())); -// This stores `true` if the current terminal is a tty +/// Stores whether `stdout` is a tty / terminal. pub static IS_TTY: Lazy = Lazy::new(|| std::io::stdout().is_terminal()); -/// Global default template path. -pub static TEMPLATE_PATH: Lazy = +/// Global default template path. Contains the global template project from which all other +/// temp projects are initialized. See [`initialize()`] for more info. +static TEMPLATE_PATH: Lazy = Lazy::new(|| env::temp_dir().join("foundry-forge-test-template")); -/// Global default template lock. -pub static TEMPLATE_LOCK: Lazy = +/// Global default template lock. If its contents are not exactly `"1"`, the global template will +/// be re-initialized. See [`initialize()`] for more info. +static TEMPLATE_LOCK: Lazy = Lazy::new(|| env::temp_dir().join("foundry-forge-test-template.lock")); -// identifier for tests +/// Global test identifier. static NEXT_ID: AtomicUsize = AtomicUsize::new(0); -/// Acquires a lock on the global template dir. +/// The default Solc version used when compiling tests. +pub const SOLC_VERSION: &str = "0.8.23"; + +/// Another Solc version used when compiling tests. Necessary to avoid downloading multiple +/// versions. +pub const OTHER_SOLC_VERSION: &str = "0.8.22"; + +/// Creates a file lock to the global template dir. pub fn template_lock() -> RwLock { let lock_path = &*TEMPLATE_LOCK; let lock_file = pretty_err( @@ -51,30 +61,63 @@ pub fn template_lock() -> RwLock { RwLock::new(lock_file) } -/// Copies an initialized project to the given path +/// Initializes a project with `forge init` at the given path. +/// +/// This should be called after an empty project is created like in +/// [some of this crate's macros](crate::forgetest_init). +/// +/// ## Note +/// +/// This doesn't always run `forge init`, instead opting to copy an already-initialized template +/// project from a global template path. This is done to speed up tests. +/// +/// This used to use a `static` [`Lazy`], but this approach does not with `cargo-nextest` because it +/// runs each test in a separate process. Instead, we use a global lock file to ensure that only one +/// test can initialize the template at a time. pub fn initialize(target: &Path) { - eprintln!("initialize {}", target.display()); + eprintln!("initializing {}", target.display()); - let tpath = &*TEMPLATE_PATH; + let tpath = TEMPLATE_PATH.as_path(); pretty_err(tpath, fs::create_dir_all(tpath)); + // Initialize the global template if necessary. let mut lock = template_lock(); - let read = lock.read().unwrap(); + let mut _read = Some(lock.read().unwrap()); if fs::read_to_string(&*TEMPLATE_LOCK).unwrap() != "1" { - eprintln!("initializing template dir"); - - drop(read); + // We are the first to acquire the lock: + // - initialize a new empty temp project; + // - run `forge init`; + // - run `forge build`; + // - copy it over to the global template; + // Ideally we would be able to initialize a temp project directly in the global template, + // but `TempProject` does not currently allow this: https://github.com/foundry-rs/compilers/issues/22 + + // Release the read lock and acquire a write lock, initializing the lock file. + _read = None; let mut write = lock.write().unwrap(); write.write_all(b"1").unwrap(); + // Initialize and build. let (prj, mut cmd) = setup_forge("template", foundry_compilers::PathStyle::Dapptools); + eprintln!("- initializing template dir in {}", prj.root().display()); + cmd.args(["init", "--force"]).assert_success(); - pretty_err(tpath, fs::remove_dir_all(tpath)); + cmd.forge_fuse().args(["build", "--use", SOLC_VERSION]).assert_success(); + + // Remove the existing template, if any. + let _ = fs::remove_dir_all(tpath); + + // Copy the template to the global template path. pretty_err(tpath, copy_dir(prj.root(), tpath)); - } else { - pretty_err(target, fs::create_dir_all(target)); - pretty_err(target, copy_dir(tpath, target)); + + // Release the write lock and acquire a new read lock. + drop(write); + _read = Some(lock.read().unwrap()); } + + eprintln!("- copying template dir from {}", tpath.display()); + pretty_err(target, fs::create_dir_all(target)); + pretty_err(target, copy_dir(tpath, target)); } /// Clones a remote repository into the specified directory. Panics if the command fails. @@ -241,7 +284,7 @@ pub fn setup_cast_project(test: TestProject) -> (TestProject, TestCommand) { #[derive(Clone, Debug)] pub struct TestProject { /// The directory in which this test executable is running. - root: PathBuf, + exe_root: PathBuf, /// The project in which the test should run. inner: Arc>, } @@ -258,9 +301,9 @@ impl TestProject { pub fn with_project(project: TempProject) -> Self { init_tracing(); - let root = - env::current_exe().unwrap().parent().expect("executable's directory").to_path_buf(); - Self { root, inner: Arc::new(project) } + let this = env::current_exe().unwrap(); + let exe_root = this.parent().expect("executable's directory").to_path_buf(); + Self { exe_root, inner: Arc::new(project) } } /// Returns the root path of the project's workspace. @@ -268,46 +311,98 @@ impl TestProject { self.inner.root() } - pub fn inner(&self) -> &TempProject { - &self.inner - } - + /// Returns the paths config. pub fn paths(&self) -> &ProjectPathsConfig { - self.inner().paths() + self.inner.paths() } - /// Returns the path to the project's `foundry.toml` file - pub fn config_path(&self) -> PathBuf { + /// Returns the path to the project's `foundry.toml` file. + pub fn config(&self) -> PathBuf { self.root().join(Config::FILE_NAME) } - /// Returns the path to the project's cache file - pub fn cache_path(&self) -> &PathBuf { + /// Returns the path to the project's cache file. + pub fn cache(&self) -> &PathBuf { &self.paths().cache } - /// Writes the given config as toml to `foundry.toml` + /// Returns the path to the project's artifacts directory. + pub fn artifacts(&self) -> &PathBuf { + &self.paths().artifacts + } + + /// Removes this project's cache file. + pub fn clear_cache(&self) { + let _ = fs::remove_file(self.cache()); + } + + /// Removes this project's artifacts directory. + pub fn clear_artifacts(&self) { + let _ = fs::remove_dir_all(self.artifacts()); + } + + /// Writes the given config as toml to `foundry.toml`. pub fn write_config(&self, config: Config) { - let file = self.config_path(); + let file = self.config(); pretty_err(&file, fs::write(&file, config.to_string_pretty().unwrap())); } - /// Asserts that the `/foundry.toml` file exits + /// Adds a source file to the project. + pub fn add_source(&self, name: &str, contents: &str) -> SolcResult { + self.inner.add_source(name, Self::add_source_prelude(contents)) + } + + /// Adds a source file to the project. Prefer using `add_source` instead. + pub fn add_raw_source(&self, name: &str, contents: &str) -> SolcResult { + self.inner.add_source(name, contents) + } + + /// Adds a script file to the project. + pub fn add_script(&self, name: &str, contents: &str) -> SolcResult { + self.inner.add_script(name, Self::add_source_prelude(contents)) + } + + /// Adds a test file to the project. + pub fn add_test(&self, name: &str, contents: &str) -> SolcResult { + self.inner.add_test(name, Self::add_source_prelude(contents)) + } + + /// Adds a library file to the project. + pub fn add_lib(&self, name: &str, contents: &str) -> SolcResult { + self.inner.add_lib(name, Self::add_source_prelude(contents)) + } + + fn add_source_prelude(s: &str) -> String { + let mut s = s.to_string(); + if !s.contains("pragma solidity") { + s = format!("pragma solidity ={SOLC_VERSION};\n{s}"); + } + if !s.contains("// SPDX") { + s = format!("// SPDX-License-Identifier: MIT OR Apache-2.0\n{s}"); + } + s + } + + /// Asserts that the `/foundry.toml` file exists. + #[track_caller] pub fn assert_config_exists(&self) { - assert!(self.config_path().exists()); + assert!(self.config().exists()); } - /// Asserts that the `/cache/sol-files-cache.json` file exits + /// Asserts that the `/cache/sol-files-cache.json` file exists. + #[track_caller] pub fn assert_cache_exists(&self) { - assert!(self.cache_path().exists()); + assert!(self.cache().exists()); } - /// Asserts that the `/out` file exits + /// Asserts that the `/out` file exists. + #[track_caller] pub fn assert_artifacts_dir_exists(&self) { assert!(self.paths().artifacts.exists()); } /// Creates all project dirs and ensure they were created + #[track_caller] pub fn assert_create_dirs_exists(&self) { self.paths().create_all().unwrap_or_else(|_| panic!("Failed to create project paths")); SolFilesCache::default().write(&self.paths().cache).expect("Failed to create cache"); @@ -315,12 +410,14 @@ impl TestProject { } /// Ensures that the given layout exists + #[track_caller] pub fn assert_style_paths_exist(&self, style: PathStyle) { let paths = style.paths(&self.paths().root).unwrap(); - config_paths_exist(&paths, self.inner().project().cached); + config_paths_exist(&paths, self.inner.project().cached); } /// Copies the project's root directory to the given target + #[track_caller] pub fn copy_to(&self, target: impl AsRef) { let target = target.as_ref(); pretty_err(target, fs::create_dir_all(target)); @@ -347,24 +444,23 @@ impl TestProject { /// Adds DSTest as a source under "test.sol" pub fn insert_ds_test(&self) -> PathBuf { let s = include_str!("../../../testdata/lib/ds-test/src/test.sol"); - self.inner().add_source("test.sol", s).unwrap() + self.add_source("test.sol", s).unwrap() } /// Adds `console.sol` as a source under "console.sol" pub fn insert_console(&self) -> PathBuf { let s = include_str!("../../../testdata/logs/console.sol"); - self.inner().add_source("console.sol", s).unwrap() + self.add_source("console.sol", s).unwrap() } - /// Asserts all project paths exist - /// - /// - sources - /// - artifacts - /// - libs - /// - cache + /// Asserts all project paths exist. These are: + /// - sources + /// - artifacts + /// - libs + /// - cache pub fn assert_all_paths_exist(&self) { let paths = self.paths(); - config_paths_exist(paths, self.inner().project().cached); + config_paths_exist(paths, self.inner.project().cached); } /// Asserts that the artifacts dir and cache don't exist @@ -404,7 +500,7 @@ impl TestProject { /// Returns the path to the forge executable. pub fn forge_bin(&self) -> Command { - let forge = self.root.join(format!("../forge{}", env::consts::EXE_SUFFIX)); + let forge = self.exe_root.join(format!("../forge{}", env::consts::EXE_SUFFIX)); let mut cmd = Command::new(forge); cmd.current_dir(self.inner.root()); // disable color output for comparisons @@ -414,7 +510,7 @@ impl TestProject { /// Returns the path to the cast executable. pub fn cast_bin(&self) -> Command { - let cast = self.root.join(format!("../cast{}", env::consts::EXE_SUFFIX)); + let cast = self.exe_root.join(format!("../cast{}", env::consts::EXE_SUFFIX)); let mut cmd = Command::new(cast); // disable color output for comparisons cmd.env("NO_COLOR", "1"); @@ -788,41 +884,51 @@ stderr: /// terminal is tty, the path argument can be wrapped in [tty_fixture_path()] pub trait OutputExt { /// Ensure the command wrote the expected data to `stdout`. - fn stdout_matches_path(&self, expected_path: impl AsRef) -> &Self; + fn stdout_matches_path(&self, expected_path: impl AsRef); /// Ensure the command wrote the expected data to `stderr`. - fn stderr_matches_path(&self, expected_path: impl AsRef) -> &Self; + fn stderr_matches_path(&self, expected_path: impl AsRef); } /// Patterns to remove from fixtures before comparing output /// /// This should strip everything that can vary from run to run, like elapsed time, file paths static IGNORE_IN_FIXTURES: Lazy = Lazy::new(|| { - Regex::new(r"(\r|finished in (.*)?s|-->(.*).sol|Location(.|\n)*\.rs(.|\n)*Backtrace|Installing solc version(.*?)\n|Successfully installed solc(.*?)\n|runs: \d+, μ: \d+, ~: \d+)").unwrap() + let re = &[ + // solc version + r" ?Solc(?: version)? \d+.\d+.\d+", + r" with \d+.\d+.\d+", + // solc runs + r"runs: \d+, μ: \d+, ~: \d+", + // elapsed time + "finished in .*?s", + // file paths + r"-->.*\.sol", + r"Location(.|\n)*\.rs(.|\n)*Backtrace", + // other + r"Transaction hash: 0x[0-9A-Fa-f]{64}", + ]; + Regex::new(&format!("({})", re.join("|"))).unwrap() }); +fn normalize_output(s: &str) -> String { + let s = s.replace("\r\n", "\n").replace('\\', "/"); + IGNORE_IN_FIXTURES.replace_all(&s, "").into_owned() +} + impl OutputExt for Output { #[track_caller] - fn stdout_matches_path(&self, expected_path: impl AsRef) -> &Self { + fn stdout_matches_path(&self, expected_path: impl AsRef) { let expected = fs::read_to_string(expected_path).unwrap(); - let expected = IGNORE_IN_FIXTURES.replace_all(&expected, "").replace('\\', "/"); - let stdout = lossy_string(&self.stdout); - let out = IGNORE_IN_FIXTURES.replace_all(&stdout, "").replace('\\', "/"); - - pretty_assertions::assert_eq!(expected, out); - - self + let out = lossy_string(&self.stdout); + pretty_assertions::assert_eq!(normalize_output(&out), normalize_output(&expected)); } #[track_caller] - fn stderr_matches_path(&self, expected_path: impl AsRef) -> &Self { + fn stderr_matches_path(&self, expected_path: impl AsRef) { let expected = fs::read_to_string(expected_path).unwrap(); - let expected = IGNORE_IN_FIXTURES.replace_all(&expected, "").replace('\\', "/"); - let stderr = lossy_string(&self.stderr); - let out = IGNORE_IN_FIXTURES.replace_all(&stderr, "").replace('\\', "/"); - - pretty_assertions::assert_eq!(expected, out); - self + let err = lossy_string(&self.stderr); + pretty_assertions::assert_eq!(normalize_output(&err), normalize_output(&expected)); } }