Skip to content

Commit 570dced

Browse files
authored
feat(forge): forge test/coverage Tempo support (foundry-rs#14165)
* feat(forge): `forge test/coverage` Tempo support Introduce generic `FoundryEvmNetwork` dispatch in forge test/coverage based on `EvmOpts` network configuration Key changes: - `MultiContractRunner, TestRunnerConfig, ContractRunner, FunctionRunner` are now generic over `FoundryEvmNetwork`, replacing hardcoded `EthEvmNetwork` - `MultiContractRunnerBuilder::build()` is now generic over `FoundryEvmNetwork` - `run_tests()` dispatches between temo or eth build/run based on `evm_opts.networks.is_tempo()` - `evm_spec` builder field removed from MultiContractRunnerBuilder; spec_id now always derived from `config.evm_spec_id()` - `TestOutcome.runner` replaced with `known_contracts: Option<ContractsByArtifact>` to break the generic dependency on `MultiContractRunner` in result types - added `Display` bound on `FoundryEvmFactory::Spec` * chore: uniform imports
1 parent 0047f0d commit 570dced

6 files changed

Lines changed: 149 additions & 97 deletions

File tree

crates/evm/core/src/evm.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use std::{
2-
fmt::Debug,
2+
fmt::{Debug, Display},
33
marker::PhantomData,
44
ops::{Deref, DerefMut},
55
};
@@ -132,7 +132,7 @@ pub type BlockResponseFor<FEN> = <NetworkFor<FEN> as Network>::BlockResponse;
132132

133133
pub trait FoundryEvmFactory:
134134
EvmFactory<
135-
Spec: Into<SpecId> + FromEvmVersion + Default + Copy + Unpin + Send + 'static,
135+
Spec: Into<SpecId> + FromEvmVersion + Default + Display + Copy + Unpin + Send + 'static,
136136
BlockEnv: FoundryBlock + ForkBlockEnv + Default + Unpin,
137137
Tx: Clone + Debug + FoundryTransaction + Default + Send + Sync,
138138
HaltReason: IntoInstructionResult,

crates/forge/src/cmd/coverage.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ impl CoverageArgs {
252252
let outcome =
253253
self.test.run_tests(project_root, config, evm_opts, output, &filter, true).await?;
254254

255-
let known_contracts = outcome.runner.as_ref().unwrap().known_contracts.clone();
255+
let known_contracts = outcome.known_contracts.as_ref().unwrap().clone();
256256

257257
// Add hit data to the coverage report
258258
let data = outcome.results.values().flat_map(|suite| {

crates/forge/src/cmd/test/mod.rs

Lines changed: 81 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use foundry_cli::{
2323
use foundry_common::{EmptyTestFilter, TestFunctionExt, compile::ProjectCompiler, fs, shell};
2424
use foundry_compilers::{
2525
ProjectCompileOutput,
26-
artifacts::output_selection::OutputSelection,
26+
artifacts::{Libraries, output_selection::OutputSelection},
2727
compilers::{
2828
Language,
2929
multi::{MultiCompiler, MultiCompilerLanguage},
@@ -40,11 +40,15 @@ use foundry_config::{
4040
};
4141
use foundry_debugger::Debugger;
4242
use foundry_evm::{
43+
core::evm::{
44+
BlockEnvFor, EthEvmNetwork, FoundryEvmNetwork, SpecFor, TempoEvmNetwork, TxEnvFor,
45+
},
4346
opts::EvmOpts,
4447
traces::{backtrace::BacktraceBuilder, identifier::TraceIdentifiers, prune_trace_depth},
4548
};
4649
use rand::Rng;
4750
use regex::Regex;
51+
use revm::context::Transaction;
4852
use std::{
4953
collections::{BTreeMap, BTreeSet},
5054
fmt::Write,
@@ -315,14 +319,11 @@ impl TestArgs {
315319
let should_debug = self.debug;
316320
let should_draw = self.flamegraph || self.flamechart;
317321

318-
// Determine print verbosity and executor verbosity.
319-
let verbosity = evm_opts.verbosity;
322+
// Determine executor verbosity.
320323
if (self.gas_report && evm_opts.verbosity < 3) || self.flamegraph || self.flamechart {
321324
evm_opts.verbosity = 3;
322325
}
323326

324-
let (evm_env, tx_env, fork_block) = evm_opts.env().await?;
325-
326327
// Enable internal tracing for more informative flamegraph.
327328
if should_draw && !self.decode_internal {
328329
self.decode_internal = true;
@@ -337,23 +338,30 @@ impl TestArgs {
337338
InternalTraceMode::None
338339
};
339340

340-
// Prepare the test builder.
341-
let config = Arc::new(config);
342-
let runner = MultiContractRunnerBuilder::new(config.clone())
343-
.set_debug(should_debug)
344-
.set_decode_internal(decode_internal)
345-
.initial_balance(evm_opts.initial_balance)
346-
.evm_spec(config.evm_spec_id())
347-
.sender(evm_opts.sender)
348-
.with_fork(evm_opts.get_fork(&config, evm_env.cfg_env.chain_id, fork_block))
349-
.enable_isolation(evm_opts.isolate)
350-
.networks(evm_opts.networks)
351-
.fail_fast(self.fail_fast)
352-
.set_coverage(coverage)
353-
.build::<MultiCompiler>(output, evm_env, tx_env, evm_opts)?;
354-
355-
let libraries = runner.libraries.clone();
356-
let mut outcome = self.run_tests_inner(runner, config, verbosity, filter, output).await?;
341+
// Dispatch based on network type.
342+
let (libraries, mut outcome) = if evm_opts.networks.is_tempo() {
343+
self.build_and_run_tests::<TempoEvmNetwork>(
344+
config,
345+
evm_opts,
346+
output,
347+
filter,
348+
coverage,
349+
should_debug,
350+
decode_internal,
351+
)
352+
.await?
353+
} else {
354+
self.build_and_run_tests::<EthEvmNetwork>(
355+
config,
356+
evm_opts,
357+
output,
358+
filter,
359+
coverage,
360+
should_debug,
361+
decode_internal,
362+
)
363+
.await?
364+
};
357365

358366
if should_draw {
359367
let (suite_name, test_name, mut test_result) =
@@ -427,10 +435,44 @@ impl TestArgs {
427435
Ok(outcome)
428436
}
429437

438+
/// Build the test runner and execute tests for a specific network type.
439+
#[allow(clippy::too_many_arguments)]
440+
async fn build_and_run_tests<FEN: FoundryEvmNetwork>(
441+
&self,
442+
config: Config,
443+
evm_opts: EvmOpts,
444+
output: &ProjectCompileOutput,
445+
filter: &ProjectPathsAwareFilter,
446+
coverage: bool,
447+
should_debug: bool,
448+
decode_internal: InternalTraceMode,
449+
) -> eyre::Result<(Libraries, TestOutcome)> {
450+
let verbosity = evm_opts.verbosity;
451+
let (evm_env, tx_env, fork_block) =
452+
evm_opts.env::<SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>().await?;
453+
454+
let config = Arc::new(config);
455+
let runner = MultiContractRunnerBuilder::new(config.clone())
456+
.set_debug(should_debug)
457+
.set_decode_internal(decode_internal)
458+
.initial_balance(evm_opts.initial_balance)
459+
.sender(evm_opts.sender)
460+
.with_fork(evm_opts.get_fork(&config, evm_env.cfg_env.chain_id, fork_block))
461+
.enable_isolation(evm_opts.isolate)
462+
.networks(evm_opts.networks)
463+
.fail_fast(self.fail_fast)
464+
.set_coverage(coverage)
465+
.build::<FEN, MultiCompiler>(output, evm_env, tx_env, evm_opts)?;
466+
467+
let libraries = runner.libraries.clone();
468+
let outcome = self.run_tests_inner(runner, config, verbosity, filter, output).await?;
469+
Ok((libraries, outcome))
470+
}
471+
430472
/// Run all tests that matches the filter predicate from a test runner
431-
async fn run_tests_inner(
473+
async fn run_tests_inner<FEN: FoundryEvmNetwork>(
432474
&self,
433-
mut runner: MultiContractRunner,
475+
mut runner: MultiContractRunner<FEN>,
434476
config: Arc<Config>,
435477
verbosity: u8,
436478
filter: &ProjectPathsAwareFilter,
@@ -470,7 +512,7 @@ impl TestArgs {
470512
}
471513
sh_warn!("{msg}")?;
472514
}
473-
return Ok(TestOutcome::empty(Some(runner), false));
515+
return Ok(TestOutcome::empty(Some(runner.known_contracts.clone()), false));
474516
}
475517

476518
if num_filtered != 1 && (self.debug || self.flamegraph || self.flamechart) {
@@ -512,17 +554,19 @@ impl TestArgs {
512554
}
513555
});
514556
sh_println!("{}", serde_json::to_string(&results)?)?;
515-
return Ok(TestOutcome::new(Some(runner), results, self.allow_failure, fuzz_seed));
557+
let kc = runner.known_contracts.clone();
558+
return Ok(TestOutcome::new(Some(kc), results, self.allow_failure, fuzz_seed));
516559
}
517560

518561
if self.junit {
519562
let results = runner.test_collect(filter)?;
520563
sh_println!("{}", junit_xml_report(&results, verbosity).to_string()?)?;
521-
return Ok(TestOutcome::new(Some(runner), results, self.allow_failure, fuzz_seed));
564+
let kc = runner.known_contracts.clone();
565+
return Ok(TestOutcome::new(Some(kc), results, self.allow_failure, fuzz_seed));
522566
}
523567

524568
let remote_chain =
525-
if runner.fork.is_some() { runner.tx_env.chain_id.map(Into::into) } else { None };
569+
if runner.fork.is_some() { runner.tx_env.chain_id().map(Into::into) } else { None };
526570
let known_contracts = runner.known_contracts.clone();
527571

528572
let libraries = runner.libraries.clone();
@@ -865,7 +909,10 @@ impl TestArgs {
865909

866910
// Reattach the task.
867911
match handle.await {
868-
Ok(result) => outcome.runner = Some(result?),
912+
Ok(result) => {
913+
let runner = result?;
914+
outcome.known_contracts = Some(runner.known_contracts);
915+
}
869916
Err(e) => match e.try_into_panic() {
870917
Ok(payload) => std::panic::resume_unwind(payload),
871918
Err(e) => return Err(e.into()),
@@ -947,7 +994,10 @@ impl Provider for TestArgs {
947994
}
948995

949996
/// Lists all matching tests
950-
fn list(runner: MultiContractRunner, filter: &ProjectPathsAwareFilter) -> Result<TestOutcome> {
997+
fn list<FEN: FoundryEvmNetwork>(
998+
runner: MultiContractRunner<FEN>,
999+
filter: &ProjectPathsAwareFilter,
1000+
) -> Result<TestOutcome> {
9511001
let results = runner.list(filter);
9521002

9531003
if shell::is_json() {
@@ -961,7 +1011,7 @@ fn list(runner: MultiContractRunner, filter: &ProjectPathsAwareFilter) -> Result
9611011
}
9621012
}
9631013
}
964-
Ok(TestOutcome::empty(Some(runner), false))
1014+
Ok(TestOutcome::empty(Some(runner.known_contracts), false))
9651015
}
9661016

9671017
/// Load persisted filter (with last test run failures) from file.

0 commit comments

Comments
 (0)