diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index ac2fac8d5133..0d79f8fa4961 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -1,4 +1,4 @@ -use crate::executors::{Executor, RawCallResult}; +use crate::executors::{Executor, FuzzTestTimer, RawCallResult}; use alloy_dyn_abi::JsonAbiExt; use alloy_json_abi::Function; use alloy_primitives::{map::HashMap, Address, Bytes, Log, U256}; @@ -17,11 +17,7 @@ use foundry_evm_fuzz::{ use foundry_evm_traces::SparsedTraceArena; use indicatif::ProgressBar; use proptest::test_runner::{TestCaseError, TestError, TestRunner}; -use std::{ - cell::RefCell, - collections::BTreeMap, - time::{Duration, Instant}, -}; +use std::{cell::RefCell, collections::BTreeMap}; mod types; pub use types::{CaseOutcome, CounterExampleOutcome, FuzzOutcome}; @@ -102,15 +98,13 @@ impl FuzzedExecutor { let max_traces_to_collect = std::cmp::max(1, self.config.gas_report_samples) as usize; let show_logs = self.config.show_logs; - // Start a timer if timeout is set. - let start_time = start_timer(self.config.timeout); + // Start timer for this fuzz test. + let timer = FuzzTestTimer::new(self.config.timeout); let run_result = self.runner.clone().run(&strategy, |calldata| { // Check if the timeout has been reached. - if let Some((start_time, timeout)) = start_time { - if start_time.elapsed() > timeout { - return Err(TestCaseError::fail(TEST_TIMEOUT)); - } + if timer.is_timed_out() { + return Err(TestCaseError::fail(TEST_TIMEOUT)); } let fuzz_res = self.single_fuzz(address, should_fail, calldata)?; @@ -288,8 +282,3 @@ impl FuzzedExecutor { } } } - -/// Starts timer for fuzz test, if any timeout configured. -pub(crate) fn start_timer(timeout: Option) -> Option<(Instant, Duration)> { - timeout.map(|timeout| (Instant::now(), Duration::from_secs(timeout.into()))) -} diff --git a/crates/evm/evm/src/executors/invariant/mod.rs b/crates/evm/evm/src/executors/invariant/mod.rs index bb5874f79010..d5fdb5668f5e 100644 --- a/crates/evm/evm/src/executors/invariant/mod.rs +++ b/crates/evm/evm/src/executors/invariant/mod.rs @@ -50,7 +50,7 @@ pub use result::InvariantFuzzTestResult; use serde::{Deserialize, Serialize}; mod shrink; -use crate::executors::{fuzz::start_timer, EvmError}; +use crate::executors::{EvmError, FuzzTestTimer}; pub use shrink::check_sequence; sol! { @@ -333,8 +333,8 @@ impl<'a> InvariantExecutor<'a> { let (invariant_test, invariant_strategy) = self.prepare_test(&invariant_contract, fuzz_fixtures)?; - // Start a timer if timeout is set. - let start_time = start_timer(self.config.timeout); + // Start timer for this invariant test. + let timer = FuzzTestTimer::new(self.config.timeout); let _ = self.runner.run(&invariant_strategy, |first_input| { // Create current invariant run data. @@ -352,14 +352,12 @@ impl<'a> InvariantExecutor<'a> { while current_run.depth < self.config.depth { // Check if the timeout has been reached. - if let Some((start_time, timeout)) = start_time { - if start_time.elapsed() > timeout { - // Since we never record a revert here the test is still considered - // successful even though it timed out. We *want* - // this behavior for now, so that's ok, but - // future developers should be aware of this. - return Err(TestCaseError::fail(TEST_TIMEOUT)); - } + if timer.is_timed_out() { + // Since we never record a revert here the test is still considered + // successful even though it timed out. We *want* + // this behavior for now, so that's ok, but + // future developers should be aware of this. + return Err(TestCaseError::fail(TEST_TIMEOUT)); } let tx = current_run.inputs.last().ok_or_else(|| { diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index b5b31b812298..2ccfad9e2a58 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -35,7 +35,10 @@ use revm::{ ResultAndState, SignedAuthorization, SpecId, TxEnv, TxKind, }, }; -use std::borrow::Cow; +use std::{ + borrow::Cow, + time::{Duration, Instant}, +}; mod builder; pub use builder::ExecutorBuilder; @@ -952,3 +955,20 @@ fn convert_executed_result( chisel_state, }) } + +/// Timer for a fuzz test. +pub struct FuzzTestTimer { + /// Inner fuzz test timer - (test start time, test duration). + inner: Option<(Instant, Duration)>, +} + +impl FuzzTestTimer { + pub fn new(timeout: Option) -> Self { + Self { inner: timeout.map(|timeout| (Instant::now(), Duration::from_secs(timeout.into()))) } + } + + /// Whether the current fuzz test timed out and should be stopped. + pub fn is_timed_out(&self) -> bool { + self.inner.is_some_and(|(start, duration)| start.elapsed() > duration) + } +}