diff --git a/crates/forge/tests/it/cheats.rs b/crates/forge/tests/it/cheats.rs index 11fcdbcfd5c1b..f0f3b5541e3c1 100644 --- a/crates/forge/tests/it/cheats.rs +++ b/crates/forge/tests/it/cheats.rs @@ -3,8 +3,9 @@ use crate::{ config::*, test_helpers::{ - ForgeTestData, RE_PATH_SEPARATOR, TEST_DATA_DEFAULT, TEST_DATA_MULTI_VERSION, - TEST_DATA_PARIS, + ForgeTestData, RE_PATH_SEPARATOR, TEST_DATA_DEFAULT, TEST_DATA_DEFAULT_RESOLC, + TEST_DATA_MULTI_VERSION, TEST_DATA_MULTI_VERSION_RESOLC, TEST_DATA_PARIS, + TEST_DATA_PARIS_RESOLC, }, }; use alloy_primitives::U256; @@ -80,3 +81,28 @@ async fn test_cheats_local_multi_version() { async fn test_cheats_local_paris() { test_cheats_local(&TEST_DATA_PARIS).await } + +#[tokio::test(flavor = "multi_thread")] +async fn test_resolc_cheats_local_default() { + test_cheats_local(&TEST_DATA_DEFAULT_RESOLC).await +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_resolc_cheats_local_default_isolated() { + test_cheats_local_isolated(&TEST_DATA_DEFAULT_RESOLC).await +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_resolc_cheats_local_default_with_seed() { + test_cheats_local_with_seed(&TEST_DATA_DEFAULT_RESOLC).await +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_resolc_cheats_local_multi_version() { + test_cheats_local(&TEST_DATA_MULTI_VERSION_RESOLC).await +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_resolc_cheats_local_paris() { + test_cheats_local(&TEST_DATA_PARIS_RESOLC).await +} diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 2b1073e9d0d79..be0e590aa7a5a 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -6,16 +6,18 @@ use forge::{ executors::ExecutorStrategy, revm::primitives::SpecId, MultiContractRunner, MultiContractRunnerBuilder, }; -use foundry_cli::utils::install_crypto_provider; +use foundry_cli::utils::{get_executor_strategy, install_crypto_provider}; +use foundry_common::{compile::ProjectCompiler, shell}; use foundry_compilers::{ artifacts::{EvmVersion, Libraries, Settings}, compilers::multi::MultiCompiler, + resolc::dual_compiled_contracts::DualCompiledContracts, utils::RuntimeOrHandle, Project, ProjectCompileOutput, SolcConfig, Vyper, }; use foundry_config::{ - fs_permissions::PathPermission, Config, FsPermissions, FuzzConfig, FuzzDictionaryConfig, - InvariantConfig, RpcEndpointUrl, RpcEndpoints, + fs_permissions::PathPermission, revive, Config, FsPermissions, FuzzConfig, + FuzzDictionaryConfig, InvariantConfig, RpcEndpointUrl, RpcEndpoints, }; use foundry_evm::{constants::CALLER, opts::EvmOpts}; use foundry_test_utils::{ @@ -23,6 +25,7 @@ use foundry_test_utils::{ rpc::{next_http_archive_rpc_url, next_rpc_endpoint}, }; use std::{ + collections::BTreeSet, env, fmt, io::Write, path::{Path, PathBuf}, @@ -35,17 +38,17 @@ static VYPER: LazyLock = LazyLock::new(|| std::env::temp_dir().join("vy /// Profile for the tests group. Used to configure separate configurations for test runs. pub enum ForgeTestProfile { - Default, - Paris, - MultiVersion, + Default { resolc: bool }, + Paris { resolc: bool }, + MultiVersion { resolc: bool }, } impl fmt::Display for ForgeTestProfile { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Default => write!(f, "default"), - Self::Paris => write!(f, "paris"), - Self::MultiVersion => write!(f, "multi-version"), + Self::Default { resolc: _ } => write!(f, "default"), + Self::Paris { resolc: _ } => write!(f, "paris"), + Self::MultiVersion { resolc: _ } => write!(f, "multi-version"), } } } @@ -53,7 +56,7 @@ impl fmt::Display for ForgeTestProfile { impl ForgeTestProfile { /// Returns true if the profile is Paris. pub fn is_paris(&self) -> bool { - matches!(self, Self::Paris) + matches!(self, Self::Paris { resolc: _ }) } pub fn root(&self) -> PathBuf { @@ -68,7 +71,7 @@ impl ForgeTestProfile { let mut settings = Settings { libraries: Libraries::parse(&libs).unwrap(), ..Default::default() }; - if matches!(self, Self::Paris) { + if matches!(self, Self::Paris { resolc: _ }) { settings.evm_version = Some(EvmVersion::Paris); } @@ -159,6 +162,33 @@ impl ForgeTestProfile { show_solidity: false, }; + if matches!( + self, + Self::Default { resolc: true } | + Self::Paris { resolc: true } | + Self::MultiVersion { resolc: true } + ) { + config.resolc.resolc_compile = true; + config.resolc.resolc_startup = true; + config.skip = vec![ + "*testdata/default/fork/*".parse().unwrap(), + "*testdata/default/repros/*".parse().unwrap(), + "*testdata/default/fuzz/*".parse().unwrap(), + "*testdata/default/linking/*".parse().unwrap(), + "*testdata/default/cheats/AttachDelegation.t.sol*".parse().unwrap(), + "*testdata/default/cheats/GetArtifactPath.t.sol*".parse().unwrap(), + "*testdata/default/cheats/GetDeployedCode.t.sol*".parse().unwrap(), + "*testdata/default/cheats/DeployCode.t.sol*".parse().unwrap(), + "*testdata/default/cheats/dumpState.t.sol*".parse().unwrap(), + "*testdata/default/cheats/Etch.t.sol*".parse().unwrap(), + "*testdata/default/cheats/GetCode.t.sol*".parse().unwrap(), + "*testdata/default/cheats/loadAllocs.t.sol*".parse().unwrap(), + "*testdata/default/cheats/RecordAccountAccesses.t.sol*".parse().unwrap(), + "*testdata/default/cheats/Broadcast.t.sol*".parse().unwrap(), + "*testdata/default/cheats/MemSafety.t.sol*".parse().unwrap(), + ]; + } + config.sanitized() } } @@ -169,6 +199,7 @@ pub struct ForgeTestData { pub output: ProjectCompileOutput, pub config: Arc, pub profile: ForgeTestProfile, + pub dual_compiled_contracts: Option, } impl ForgeTestData { @@ -180,8 +211,46 @@ impl ForgeTestData { init_tracing(); let config = Arc::new(profile.config()); let mut project = config.project().unwrap(); - let output = get_compiled(&mut project); - Self { project, output, config, profile } + + let sources_to_compile = project + .sources() + .expect("Could not read sources") + .keys() + .cloned() + .collect::>(); + + // Handle compilation based on whether dual compilation is enabled + let (output, dual_compiled_contracts) = if config.resolc.resolc_compile { + // Dual compilation mode: compile both solc and resolc + + // Compile with solc to a subdirectory + let mut solc_config = config.as_ref().clone(); + solc_config.out = solc_config.out.join(revive::SOLC_ARTIFACTS_SUBDIR); + solc_config.resolc = Default::default(); + solc_config.build_info_path = Some(solc_config.out.join("build-info")); + let mut solc_project = solc_config.project().expect("Could not create solc project"); + + let solc_output = get_compiled(&mut solc_project); + + // Compile with resolc to the main output directory + let mut resolc_project = + config.clone().project().expect("Could not create resolc project"); + + let resolc_output = get_compiled(&mut resolc_project); + + // Create dual compiled contracts + let dual_compiled_contracts = DualCompiledContracts::new( + &solc_output, + &resolc_output, + &solc_project.paths, + &resolc_project.paths, + ); + + (solc_output, Some(dual_compiled_contracts)) + } else { + (get_compiled(&mut project), None) + }; + Self { project, output, config, profile, dual_compiled_contracts } } /// Builds a base runner @@ -222,16 +291,17 @@ impl ForgeTestData { let config = Arc::new(config); let root = self.project.root(); builder.config = config.clone(); + let mut strategy = get_executor_strategy(&config); + + strategy.runner.revive_set_dual_compiled_contracts( + strategy.context.as_mut(), + self.dual_compiled_contracts.clone().unwrap_or_default(), + ); + builder .enable_isolation(opts.isolate) .sender(config.sender) - .build::( - ExecutorStrategy::new_evm(), - root, - &self.output, - opts.local_evm_env(), - opts, - ) + .build::(strategy, root, &self.output, opts.local_evm_env(), opts) .unwrap() } @@ -346,15 +416,27 @@ pub fn get_compiled(project: &mut Project) -> ProjectCompileOutput { /// Default data for the tests group. pub static TEST_DATA_DEFAULT: LazyLock = - LazyLock::new(|| ForgeTestData::new(ForgeTestProfile::Default)); + LazyLock::new(|| ForgeTestData::new(ForgeTestProfile::Default { resolc: false })); /// Data for tests requiring Paris support on Solc and EVM level. pub static TEST_DATA_PARIS: LazyLock = - LazyLock::new(|| ForgeTestData::new(ForgeTestProfile::Paris)); + LazyLock::new(|| ForgeTestData::new(ForgeTestProfile::Paris { resolc: false })); /// Data for tests requiring Cancun support on Solc and EVM level. pub static TEST_DATA_MULTI_VERSION: LazyLock = - LazyLock::new(|| ForgeTestData::new(ForgeTestProfile::MultiVersion)); + LazyLock::new(|| ForgeTestData::new(ForgeTestProfile::MultiVersion { resolc: false })); + +/// Default data for the tests group with resolc enabled. +pub static TEST_DATA_DEFAULT_RESOLC: LazyLock = + LazyLock::new(|| ForgeTestData::new(ForgeTestProfile::Default { resolc: true })); + +/// Data for tests requiring Paris support on Solc and EVM level with resolc enabled. +pub static TEST_DATA_PARIS_RESOLC: LazyLock = + LazyLock::new(|| ForgeTestData::new(ForgeTestProfile::Paris { resolc: true })); + +/// Data for tests requiring Cancun support on Solc and EVM level with resolc enabled. +pub static TEST_DATA_MULTI_VERSION_RESOLC: LazyLock = + LazyLock::new(|| ForgeTestData::new(ForgeTestProfile::MultiVersion { resolc: true })); pub fn manifest_root() -> &'static Path { let mut root = Path::new(env!("CARGO_MANIFEST_DIR"));