Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,9 @@ foundry-compilers = { version = "0.19.1", default-features = false, features = [
foundry-fork-db = "0.18"
solang-parser = { version = "=0.3.9", package = "foundry-solang-parser" }
solar = { package = "solar-compiler", version = "=0.1.7", default-features = false }
svm = { package = "svm-rs", version = "0.5", default-features = false, features = [
"rustls",
] }

## alloy
alloy-consensus = { version = "1.0.23", default-features = false }
Expand Down
4 changes: 1 addition & 3 deletions crates/forge/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,7 @@ mockall = "0.13"
globset = "0.4"
paste = "1.0"
similar-asserts.workspace = true
svm = { package = "svm-rs", version = "0.5", default-features = false, features = [
"rustls",
] }
svm.workspace = true
tempfile.workspace = true

alloy-signer-local.workspace = true
Expand Down
12 changes: 4 additions & 8 deletions crates/forge/tests/cli/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,12 @@ contract Verify is Unique {
);
}

#[expect(clippy::disallowed_macros)]
fn parse_verification_result(cmd: &mut TestCommand, retries: u32) -> eyre::Result<()> {
// Give Etherscan some time to verify the contract.
Retry::new(retries, Duration::from_secs(30)).run(|| -> eyre::Result<()> {
let output = cmd.execute();
let out = String::from_utf8_lossy(&output.stdout);
println!("{out}");
test_debug!("{out}");
if out.contains("Contract successfully verified") {
return Ok(());
}
Expand Down Expand Up @@ -155,11 +154,10 @@ fn deploy_contract(
.unwrap_or_else(|| panic!("Failed to parse deployer {output}"))
}

#[expect(clippy::disallowed_macros)]
fn verify_on_chain(info: Option<EnvExternalities>, prj: TestProject, mut cmd: TestCommand) {
// only execute if keys present
if let Some(info) = info {
println!("verifying on {}", info.chain);
test_debug!("verifying on {}", info.chain);

let contract_path = "src/Verify.sol:Verify";
let address = deploy_contract(&info, contract_path, prj, &mut cmd);
Expand All @@ -186,11 +184,10 @@ fn verify_on_chain(info: Option<EnvExternalities>, prj: TestProject, mut cmd: Te
}
}

#[expect(clippy::disallowed_macros)]
fn guess_constructor_args(info: Option<EnvExternalities>, prj: TestProject, mut cmd: TestCommand) {
// only execute if keys present
if let Some(info) = info {
println!("verifying on {}", info.chain);
test_debug!("verifying on {}", info.chain);
add_unique(&prj);
add_verify_target_with_constructor(&prj);

Expand Down Expand Up @@ -227,12 +224,11 @@ fn guess_constructor_args(info: Option<EnvExternalities>, prj: TestProject, mut
}
}

#[expect(clippy::disallowed_macros)]
/// Executes create --verify on the given chain
fn create_verify_on_chain(info: Option<EnvExternalities>, prj: TestProject, mut cmd: TestCommand) {
// only execute if keys present
if let Some(info) = info {
println!("verifying on {}", info.chain);
test_debug!("verifying on {}", info.chain);
add_single_verify_target_file(&prj);

let contract_path = "src/Verify.sol:Verify";
Expand Down
4 changes: 2 additions & 2 deletions crates/forge/tests/it/cheats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ use crate::{
config::*,
test_helpers::{
ForgeTestData, ForgeTestProfile, RE_PATH_SEPARATOR, TEST_DATA_DEFAULT,
TEST_DATA_MULTI_VERSION, TEST_DATA_PARIS, get_compiled,
TEST_DATA_MULTI_VERSION, TEST_DATA_PARIS,
},
};
use alloy_primitives::U256;
use foundry_cli::utils::install_crypto_provider;
use foundry_compilers::artifacts::output_selection::ContractOutputSelection;
use foundry_config::{FsPermissions, fs_permissions::PathPermission};
use foundry_test_utils::{Filter, init_tracing};
use foundry_test_utils::{Filter, init_tracing, util::get_compiled};

/// Executes all cheat code tests but not fork cheat codes or tests that require isolation mode or
/// specific seed.
Expand Down
87 changes: 3 additions & 84 deletions crates/forge/tests/it/test_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,29 @@ use alloy_primitives::U256;
use forge::{MultiContractRunner, MultiContractRunnerBuilder};
use foundry_cli::utils::install_crypto_provider;
use foundry_compilers::{
Project, ProjectCompileOutput, SolcConfig, Vyper,
Project, ProjectCompileOutput, SolcConfig,
artifacts::{EvmVersion, Libraries, Settings},
compilers::multi::MultiCompiler,
utils::RuntimeOrHandle,
};
use foundry_config::{
Config, FsPermissions, FuzzConfig, FuzzCorpusConfig, FuzzDictionaryConfig, InvariantConfig,
RpcEndpointUrl, RpcEndpoints, fs_permissions::PathPermission,
};
use foundry_evm::{constants::CALLER, opts::EvmOpts};
use foundry_test_utils::{
fd_lock, init_tracing,
init_tracing,
rpc::{next_http_archive_rpc_url, next_rpc_endpoint},
test_debug,
util::get_compiled,
};
use revm::primitives::hardfork::SpecId;
use std::{
env, fmt,
io::Write,
path::{Path, PathBuf},
sync::{Arc, LazyLock},
};

pub const RE_PATH_SEPARATOR: &str = "/";
const TESTDATA: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../testdata");
static VYPER: LazyLock<PathBuf> = LazyLock::new(|| std::env::temp_dir().join("vyper"));

/// Profile for the tests group. Used to configure separate configurations for test runs.
pub enum ForgeTestProfile {
Expand Down Expand Up @@ -256,84 +253,6 @@ impl ForgeTestData {
}
}

/// Installs Vyper if it's not already present.
pub fn get_vyper() -> Vyper {
if let Ok(vyper) = Vyper::new("vyper") {
return vyper;
}
if let Ok(vyper) = Vyper::new(&*VYPER) {
return vyper;
}
RuntimeOrHandle::new().block_on(async {
#[cfg(target_family = "unix")]
use std::{fs::Permissions, os::unix::fs::PermissionsExt};

let suffix = match svm::platform() {
svm::Platform::MacOsAarch64 => "darwin",
svm::Platform::LinuxAmd64 => "linux",
svm::Platform::WindowsAmd64 => "windows.exe",
platform => panic!(
"unsupported platform {platform:?} for installing vyper, \
install it manually and add it to $PATH"
),
};
let url = format!("https://github.com/vyperlang/vyper/releases/download/v0.4.3/vyper.0.4.3+commit.bff19ea2.{suffix}");

test_debug!("downloading vyper from {url}");
let res = reqwest::Client::builder().build().unwrap().get(url).send().await.unwrap();

assert!(res.status().is_success());

let bytes = res.bytes().await.unwrap();

std::fs::write(&*VYPER, bytes).unwrap();

#[cfg(target_family = "unix")]
std::fs::set_permissions(&*VYPER, Permissions::from_mode(0o755)).unwrap();

Vyper::new(&*VYPER).unwrap()
})
}

#[tracing::instrument]
pub fn get_compiled(project: &mut Project) -> ProjectCompileOutput {
let lock_file_path = project.sources_path().join(".lock");
// 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_file_path);
let read = lock.read().unwrap();
let out;

let mut write = None;
if !project.cache_path().exists() || std::fs::read(&lock_file_path).unwrap() != b"1" {
drop(read);
write = Some(lock.write().unwrap());
test_debug!("cache miss for {}", lock_file_path.display());
} else {
test_debug!("cache hit for {}", lock_file_path.display());
}

if project.compiler.vyper.is_none() {
project.compiler.vyper = Some(get_vyper());
}

test_debug!("compiling {}", lock_file_path.display());
out = project.compile().unwrap();
test_debug!("compiled {}", lock_file_path.display());

if out.has_compiler_errors() {
panic!("Compiled with errors:\n{out}");
}

if let Some(ref mut write) = write {
write.write_all(b"1").unwrap();
}

out
}

/// Default data for the tests group.
pub static TEST_DATA_DEFAULT: LazyLock<ForgeTestData> =
LazyLock::new(|| ForgeTestData::new(ForgeTestProfile::Default));
Expand Down
2 changes: 2 additions & 0 deletions crates/test-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ rand.workspace = true
snapbox = { version = "0.6", features = ["json", "regex", "term-svg"] }
tempfile.workspace = true
ui_test = "0.30.2"
reqwest.workspace = true
svm.workspace = true

# Pinned dependencies. See /Cargo.toml.
[target.'cfg(any())'.dependencies]
Expand Down
6 changes: 6 additions & 0 deletions crates/test-utils/src/fd_lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,9 @@ pub fn new_lock(lock_path: impl AsRef<Path>) -> RwLock<File> {
}
new_lock(lock_path.as_ref())
}

pub(crate) const LOCK_TOKEN: &[u8] = b"1";

pub(crate) fn lock_exists(lock_path: &Path) -> bool {
std::fs::read(lock_path).is_ok_and(|b| b == LOCK_TOKEN)
}
Loading
Loading