forge/
runner.rs

1//! The Forge test runner.
2
3use crate::{
4    MultiContractRunner, TestFilter,
5    coverage::HitMaps,
6    fuzz::{BaseCounterExample, FuzzTestResult},
7    multi_runner::{TestContract, TestRunnerConfig},
8    progress::{TestsProgress, start_fuzz_progress},
9    result::{SuiteResult, TestResult, TestSetup},
10};
11use alloy_dyn_abi::{DynSolValue, JsonAbiExt};
12use alloy_json_abi::Function;
13use alloy_primitives::{Address, Bytes, U256, address, map::HashMap};
14use eyre::Result;
15use foundry_common::{TestFunctionExt, TestFunctionKind, contracts::ContractsByAddress};
16use foundry_compilers::utils::canonicalized;
17use foundry_config::{Config, FuzzCorpusConfig};
18use foundry_evm::{
19    constants::CALLER,
20    decode::RevertDecoder,
21    executors::{
22        CallResult, EvmError, Executor, ITest, RawCallResult,
23        fuzz::FuzzedExecutor,
24        invariant::{
25            InvariantExecutor, InvariantFuzzError, check_sequence, replay_error, replay_run,
26        },
27    },
28    fuzz::{
29        BasicTxDetails, CallDetails, CounterExample, FuzzFixtures, fixture_name,
30        invariant::InvariantContract,
31    },
32    traces::{TraceKind, TraceMode, load_contracts},
33};
34use itertools::Itertools;
35use proptest::test_runner::{RngAlgorithm, TestError, TestRng, TestRunner};
36use rayon::prelude::*;
37use serde::{Deserialize, Serialize};
38use std::{
39    borrow::Cow,
40    cmp::min,
41    collections::BTreeMap,
42    path::{Path, PathBuf},
43    sync::Arc,
44    time::Instant,
45};
46use tracing::Span;
47
48/// When running tests, we deploy all external libraries present in the project. To avoid additional
49/// libraries affecting nonces of senders used in tests, we are using separate address to
50/// predeploy libraries.
51///
52/// `address(uint160(uint256(keccak256("foundry library deployer"))))`
53pub const LIBRARY_DEPLOYER: Address = address!("0x1F95D37F27EA0dEA9C252FC09D5A6eaA97647353");
54
55/// A type that executes all tests of a contract
56pub struct ContractRunner<'a> {
57    /// The name of the contract.
58    name: &'a str,
59    /// The data of the contract.
60    contract: &'a TestContract,
61    /// The EVM executor.
62    executor: Executor,
63    /// Overall test run progress.
64    progress: Option<&'a TestsProgress>,
65    /// The handle to the tokio runtime.
66    tokio_handle: &'a tokio::runtime::Handle,
67    /// The span of the contract.
68    span: tracing::Span,
69    /// The contract-level configuration.
70    tcfg: Cow<'a, TestRunnerConfig>,
71    /// The parent runner.
72    mcr: &'a MultiContractRunner,
73}
74
75impl<'a> std::ops::Deref for ContractRunner<'a> {
76    type Target = Cow<'a, TestRunnerConfig>;
77
78    #[inline(always)]
79    fn deref(&self) -> &Self::Target {
80        &self.tcfg
81    }
82}
83
84impl<'a> ContractRunner<'a> {
85    pub fn new(
86        name: &'a str,
87        contract: &'a TestContract,
88        executor: Executor,
89        progress: Option<&'a TestsProgress>,
90        tokio_handle: &'a tokio::runtime::Handle,
91        span: Span,
92        mcr: &'a MultiContractRunner,
93    ) -> Self {
94        Self {
95            name,
96            contract,
97            executor,
98            progress,
99            tokio_handle,
100            span,
101            tcfg: Cow::Borrowed(&mcr.tcfg),
102            mcr,
103        }
104    }
105
106    /// Deploys the test contract inside the runner from the sending account, and optionally runs
107    /// the `setUp` function on the test contract.
108    pub fn setup(&mut self, call_setup: bool) -> TestSetup {
109        self._setup(call_setup).unwrap_or_else(|err| {
110            if err.to_string().contains("skipped") {
111                TestSetup::skipped(err.to_string())
112            } else {
113                TestSetup::failed(err.to_string())
114            }
115        })
116    }
117
118    fn _setup(&mut self, call_setup: bool) -> Result<TestSetup> {
119        trace!(call_setup, "setting up");
120
121        self.apply_contract_inline_config()?;
122
123        // We max out their balance so that they can deploy and make calls.
124        self.executor.set_balance(self.sender, U256::MAX)?;
125        self.executor.set_balance(CALLER, U256::MAX)?;
126
127        // We set the nonce of the deployer accounts to 1 to get the same addresses as DappTools.
128        self.executor.set_nonce(self.sender, 1)?;
129
130        // Deploy libraries.
131        self.executor.set_balance(LIBRARY_DEPLOYER, U256::MAX)?;
132
133        let mut result = TestSetup::default();
134        for code in &self.mcr.libs_to_deploy {
135            let deploy_result = self.executor.deploy(
136                LIBRARY_DEPLOYER,
137                code.clone(),
138                U256::ZERO,
139                Some(&self.mcr.revert_decoder),
140            );
141
142            // Record deployed library address.
143            if let Ok(deployed) = &deploy_result {
144                result.deployed_libs.push(deployed.address);
145            }
146
147            let (raw, reason) = RawCallResult::from_evm_result(deploy_result.map(Into::into))?;
148            result.extend(raw, TraceKind::Deployment);
149            if reason.is_some() {
150                result.reason = reason;
151                return Ok(result);
152            }
153        }
154
155        let address = self.sender.create(self.executor.get_nonce(self.sender)?);
156        result.address = address;
157
158        // Set the contracts initial balance before deployment, so it is available during
159        // construction
160        self.executor.set_balance(address, self.initial_balance())?;
161
162        // Deploy the test contract
163        let deploy_result = self.executor.deploy(
164            self.sender,
165            self.contract.bytecode.clone(),
166            U256::ZERO,
167            Some(&self.mcr.revert_decoder),
168        );
169
170        result.deployment_failure = deploy_result.is_err();
171
172        if let Ok(dr) = &deploy_result {
173            debug_assert_eq!(dr.address, address);
174        }
175        let (raw, reason) = RawCallResult::from_evm_result(deploy_result.map(Into::into))?;
176        result.extend(raw, TraceKind::Deployment);
177        if reason.is_some() {
178            result.reason = reason;
179            return Ok(result);
180        }
181
182        // Reset `self.sender`s, `CALLER`s and `LIBRARY_DEPLOYER`'s balance to the initial balance.
183        self.executor.set_balance(self.sender, self.initial_balance())?;
184        self.executor.set_balance(CALLER, self.initial_balance())?;
185        self.executor.set_balance(LIBRARY_DEPLOYER, self.initial_balance())?;
186
187        self.executor.deploy_create2_deployer()?;
188
189        // Optionally call the `setUp` function
190        if call_setup {
191            trace!("calling setUp");
192            let res = self.executor.setup(None, address, Some(&self.mcr.revert_decoder));
193            let (raw, reason) = RawCallResult::from_evm_result(res)?;
194            result.extend(raw, TraceKind::Setup);
195            result.reason = reason;
196        }
197
198        result.fuzz_fixtures = self.fuzz_fixtures(address);
199
200        Ok(result)
201    }
202
203    fn initial_balance(&self) -> U256 {
204        self.evm_opts.initial_balance
205    }
206
207    /// Configures this runner with the inline configuration for the contract.
208    fn apply_contract_inline_config(&mut self) -> Result<()> {
209        if self.inline_config.contains_contract(self.name) {
210            let new_config = Arc::new(self.inline_config(None)?);
211            self.tcfg.to_mut().reconfigure_with(new_config);
212            let prev_tracer = self.executor.inspector_mut().tracer.take();
213            self.tcfg.configure_executor(&mut self.executor);
214            // Don't set tracer here.
215            self.executor.inspector_mut().tracer = prev_tracer;
216        }
217        Ok(())
218    }
219
220    /// Returns the configuration for a contract or function.
221    fn inline_config(&self, func: Option<&Function>) -> Result<Config> {
222        let function = func.map(|f| f.name.as_str()).unwrap_or("");
223        let config =
224            self.mcr.inline_config.merge(self.name, function, &self.config).extract::<Config>()?;
225        Ok(config)
226    }
227
228    /// Collect fixtures from test contract.
229    ///
230    /// Fixtures can be defined:
231    /// - as storage arrays in test contract, prefixed with `fixture`
232    /// - as functions prefixed with `fixture` and followed by parameter name to be fuzzed
233    ///
234    /// Storage array fixtures:
235    /// `uint256[] public fixture_amount = [1, 2, 3];`
236    /// define an array of uint256 values to be used for fuzzing `amount` named parameter in scope
237    /// of the current test.
238    ///
239    /// Function fixtures:
240    /// `function fixture_owner() public returns (address[] memory){}`
241    /// returns an array of addresses to be used for fuzzing `owner` named parameter in scope of the
242    /// current test.
243    fn fuzz_fixtures(&mut self, address: Address) -> FuzzFixtures {
244        let mut fixtures = HashMap::default();
245        let fixture_functions = self.contract.abi.functions().filter(|func| func.is_fixture());
246        for func in fixture_functions {
247            if func.inputs.is_empty() {
248                // Read fixtures declared as functions.
249                if let Ok(CallResult { raw: _, decoded_result }) =
250                    self.executor.call(CALLER, address, func, &[], U256::ZERO, None)
251                {
252                    fixtures.insert(fixture_name(func.name.clone()), decoded_result);
253                }
254            } else {
255                // For reading fixtures from storage arrays we collect values by calling the
256                // function with incremented indexes until there's an error.
257                let mut vals = Vec::new();
258                let mut index = 0;
259                loop {
260                    if let Ok(CallResult { raw: _, decoded_result }) = self.executor.call(
261                        CALLER,
262                        address,
263                        func,
264                        &[DynSolValue::Uint(U256::from(index), 256)],
265                        U256::ZERO,
266                        None,
267                    ) {
268                        vals.push(decoded_result);
269                    } else {
270                        // No result returned for this index, we reached the end of storage
271                        // array or the function is not a valid fixture.
272                        break;
273                    }
274                    index += 1;
275                }
276                fixtures.insert(fixture_name(func.name.clone()), DynSolValue::Array(vals));
277            };
278        }
279        FuzzFixtures::new(fixtures)
280    }
281
282    /// Runs all tests for a contract whose names match the provided regular expression
283    pub fn run_tests(mut self, filter: &dyn TestFilter) -> SuiteResult {
284        let start = Instant::now();
285        let mut warnings = Vec::new();
286
287        // Check if `setUp` function with valid signature declared.
288        let setup_fns: Vec<_> =
289            self.contract.abi.functions().filter(|func| func.name.is_setup()).collect();
290        let call_setup = setup_fns.len() == 1 && setup_fns[0].name == "setUp";
291        // There is a single miss-cased `setUp` function, so we add a warning
292        for &setup_fn in &setup_fns {
293            if setup_fn.name != "setUp" {
294                warnings.push(format!(
295                    "Found invalid setup function \"{}\" did you mean \"setUp()\"?",
296                    setup_fn.signature()
297                ));
298            }
299        }
300
301        // There are multiple setUp function, so we return a single test result for `setUp`
302        if setup_fns.len() > 1 {
303            return SuiteResult::new(
304                start.elapsed(),
305                [("setUp()".to_string(), TestResult::fail("multiple setUp functions".to_string()))]
306                    .into(),
307                warnings,
308            );
309        }
310
311        // Check if `afterInvariant` function with valid signature declared.
312        let after_invariant_fns: Vec<_> =
313            self.contract.abi.functions().filter(|func| func.name.is_after_invariant()).collect();
314        if after_invariant_fns.len() > 1 {
315            // Return a single test result failure if multiple functions declared.
316            return SuiteResult::new(
317                start.elapsed(),
318                [(
319                    "afterInvariant()".to_string(),
320                    TestResult::fail("multiple afterInvariant functions".to_string()),
321                )]
322                .into(),
323                warnings,
324            );
325        }
326        let call_after_invariant = after_invariant_fns.first().is_some_and(|after_invariant_fn| {
327            let match_sig = after_invariant_fn.name == "afterInvariant";
328            if !match_sig {
329                warnings.push(format!(
330                    "Found invalid afterInvariant function \"{}\" did you mean \"afterInvariant()\"?",
331                    after_invariant_fn.signature()
332                ));
333            }
334            match_sig
335        });
336
337        // Invariant testing requires tracing to figure out what contracts were created.
338        // We also want to disable `debug` for setup since we won't be using those traces.
339        let has_invariants = self.contract.abi.functions().any(|func| func.is_invariant_test());
340
341        let prev_tracer = self.executor.inspector_mut().tracer.take();
342        if prev_tracer.is_some() || has_invariants {
343            self.executor.set_tracing(TraceMode::Call);
344        }
345
346        let setup_time = Instant::now();
347        let setup = self.setup(call_setup);
348        debug!("finished setting up in {:?}", setup_time.elapsed());
349
350        self.executor.inspector_mut().tracer = prev_tracer;
351
352        if setup.reason.is_some() {
353            // The setup failed, so we return a single test result for `setUp`
354            let fail_msg = if !setup.deployment_failure {
355                "setUp()".to_string()
356            } else {
357                "constructor()".to_string()
358            };
359            return SuiteResult::new(
360                start.elapsed(),
361                [(fail_msg, TestResult::setup_result(setup))].into(),
362                warnings,
363            );
364        }
365
366        // Filter out functions sequentially since it's very fast and there is no need to do it
367        // in parallel.
368        let find_timer = Instant::now();
369        let functions = self
370            .contract
371            .abi
372            .functions()
373            .filter(|func| filter.matches_test_function(func))
374            .collect::<Vec<_>>();
375        debug!(
376            "Found {} test functions out of {} in {:?}",
377            functions.len(),
378            self.contract.abi.functions().count(),
379            find_timer.elapsed(),
380        );
381
382        let identified_contracts = has_invariants.then(|| {
383            load_contracts(setup.traces.iter().map(|(_, t)| &t.arena), &self.mcr.known_contracts)
384        });
385
386        let test_fail_instances = functions
387            .iter()
388            .filter_map(|func| {
389                TestFunctionKind::classify(&func.name, !func.inputs.is_empty())
390                    .is_any_test_fail()
391                    .then_some(func.name.clone())
392            })
393            .collect::<Vec<_>>();
394
395        if !test_fail_instances.is_empty() {
396            let instances = format!(
397                "Found {} instances: {}",
398                test_fail_instances.len(),
399                test_fail_instances.join(", ")
400            );
401            let fail =  TestResult::fail("`testFail*` has been removed. Consider changing to test_Revert[If|When]_Condition and expecting a revert".to_string());
402            return SuiteResult::new(start.elapsed(), [(instances, fail)].into(), warnings);
403        }
404
405        let fail_fast = &self.tcfg.fail_fast;
406
407        let test_results = functions
408            .par_iter()
409            .map(|&func| {
410                // Early exit if we're running with fail-fast and a test already failed.
411                if fail_fast.should_stop() {
412                    return (func.signature(), TestResult::setup_result(setup.clone()));
413                }
414
415                let start = Instant::now();
416
417                let _guard = self.tokio_handle.enter();
418
419                let _guard;
420                let current_span = tracing::Span::current();
421                if current_span.is_none() || current_span.id() != self.span.id() {
422                    _guard = self.span.enter();
423                }
424
425                let sig = func.signature();
426                let kind = func.test_function_kind();
427
428                let _guard = debug_span!(
429                    "test",
430                    %kind,
431                    name = %if enabled!(tracing::Level::TRACE) { &sig } else { &func.name },
432                )
433                .entered();
434
435                let mut res = FunctionRunner::new(&self, &setup).run(
436                    func,
437                    kind,
438                    call_after_invariant,
439                    identified_contracts.as_ref(),
440                );
441                res.duration = start.elapsed();
442
443                // Set fail fast flag if current test failed.
444                if res.status.is_failure() {
445                    fail_fast.record_fail();
446                }
447
448                (sig, res)
449            })
450            .collect::<BTreeMap<_, _>>();
451
452        let duration = start.elapsed();
453        SuiteResult::new(duration, test_results, warnings)
454    }
455}
456
457/// Executes a single test function, returning a [`TestResult`].
458struct FunctionRunner<'a> {
459    /// The function-level configuration.
460    tcfg: Cow<'a, TestRunnerConfig>,
461    /// The EVM executor.
462    executor: Cow<'a, Executor>,
463    /// The parent runner.
464    cr: &'a ContractRunner<'a>,
465    /// The address of the test contract.
466    address: Address,
467    /// The test setup result.
468    setup: &'a TestSetup,
469    /// The test result. Returned after running the test.
470    result: TestResult,
471}
472
473impl<'a> std::ops::Deref for FunctionRunner<'a> {
474    type Target = Cow<'a, TestRunnerConfig>;
475
476    #[inline(always)]
477    fn deref(&self) -> &Self::Target {
478        &self.tcfg
479    }
480}
481
482impl<'a> FunctionRunner<'a> {
483    fn new(cr: &'a ContractRunner<'a>, setup: &'a TestSetup) -> Self {
484        Self {
485            tcfg: match &cr.tcfg {
486                Cow::Borrowed(tcfg) => Cow::Borrowed(tcfg),
487                Cow::Owned(tcfg) => Cow::Owned(tcfg.clone()),
488            },
489            executor: Cow::Borrowed(&cr.executor),
490            cr,
491            address: setup.address,
492            setup,
493            result: TestResult::new(setup),
494        }
495    }
496
497    fn revert_decoder(&self) -> &'a RevertDecoder {
498        &self.cr.mcr.revert_decoder
499    }
500
501    /// Configures this runner with the inline configuration for the contract.
502    fn apply_function_inline_config(&mut self, func: &Function) -> Result<()> {
503        if self.inline_config.contains_function(self.cr.name, &func.name) {
504            let new_config = Arc::new(self.cr.inline_config(Some(func))?);
505            self.tcfg.to_mut().reconfigure_with(new_config);
506            self.tcfg.configure_executor(self.executor.to_mut());
507        }
508        Ok(())
509    }
510
511    fn run(
512        mut self,
513        func: &Function,
514        kind: TestFunctionKind,
515        call_after_invariant: bool,
516        identified_contracts: Option<&ContractsByAddress>,
517    ) -> TestResult {
518        if let Err(e) = self.apply_function_inline_config(func) {
519            self.result.single_fail(Some(e.to_string()));
520            return self.result;
521        }
522
523        match kind {
524            TestFunctionKind::UnitTest { .. } => self.run_unit_test(func),
525            TestFunctionKind::FuzzTest { .. } => self.run_fuzz_test(func),
526            TestFunctionKind::TableTest => self.run_table_test(func),
527            TestFunctionKind::InvariantTest => {
528                let test_bytecode = &self.cr.contract.bytecode;
529                self.run_invariant_test(
530                    func,
531                    call_after_invariant,
532                    identified_contracts.unwrap(),
533                    test_bytecode,
534                )
535            }
536            _ => unreachable!(),
537        }
538    }
539
540    /// Runs a single unit test.
541    ///
542    /// Applies before test txes (if any), runs current test and returns the `TestResult`.
543    ///
544    /// Before test txes are applied in order and state modifications committed to the EVM database
545    /// (therefore the unit test call will be made on modified state).
546    /// State modifications of before test txes and unit test function call are discarded after
547    /// test ends, similar to `eth_call`.
548    fn run_unit_test(mut self, func: &Function) -> TestResult {
549        // Prepare unit test execution.
550        if self.prepare_test(func).is_err() {
551            return self.result;
552        }
553
554        // Run current unit test.
555        let (mut raw_call_result, reason) = match self.executor.call(
556            self.sender,
557            self.address,
558            func,
559            &[],
560            U256::ZERO,
561            Some(self.revert_decoder()),
562        ) {
563            Ok(res) => (res.raw, None),
564            Err(EvmError::Execution(err)) => (err.raw, Some(err.reason)),
565            Err(EvmError::Skip(reason)) => {
566                self.result.single_skip(reason);
567                return self.result;
568            }
569            Err(err) => {
570                self.result.single_fail(Some(err.to_string()));
571                return self.result;
572            }
573        };
574
575        let success =
576            self.executor.is_raw_call_mut_success(self.address, &mut raw_call_result, false);
577        self.result.single_result(success, reason, raw_call_result);
578        self.result
579    }
580
581    /// Runs a table test.
582    /// The parameters dataset (table) is created from defined parameter fixtures, therefore each
583    /// test table parameter should have the same number of fixtures defined.
584    /// E.g. for table test
585    /// - `table_test(uint256 amount, bool swap)` fixtures are defined as
586    /// - `uint256[] public fixtureAmount = [2, 5]`
587    /// - `bool[] public fixtureSwap = [true, false]` The `table_test` is then called with the pair
588    ///   of args `(2, true)` and `(5, false)`.
589    fn run_table_test(mut self, func: &Function) -> TestResult {
590        // Prepare unit test execution.
591        if self.prepare_test(func).is_err() {
592            return self.result;
593        }
594
595        // Extract and validate fixtures for the first table test parameter.
596        let Some(first_param) = func.inputs.first() else {
597            self.result.single_fail(Some("Table test should have at least one parameter".into()));
598            return self.result;
599        };
600
601        let Some(first_param_fixtures) =
602            &self.setup.fuzz_fixtures.param_fixtures(first_param.name())
603        else {
604            self.result.single_fail(Some("Table test should have fixtures defined".into()));
605            return self.result;
606        };
607
608        if first_param_fixtures.is_empty() {
609            self.result.single_fail(Some("Table test should have at least one fixture".into()));
610            return self.result;
611        }
612
613        let fixtures_len = first_param_fixtures.len();
614        let mut table_fixtures = vec![&first_param_fixtures[..]];
615
616        // Collect fixtures for remaining parameters.
617        for param in &func.inputs[1..] {
618            let param_name = param.name();
619            let Some(fixtures) = &self.setup.fuzz_fixtures.param_fixtures(param.name()) else {
620                self.result.single_fail(Some(format!("No fixture defined for param {param_name}")));
621                return self.result;
622            };
623
624            if fixtures.len() != fixtures_len {
625                self.result.single_fail(Some(format!(
626                    "{} fixtures defined for {param_name} (expected {})",
627                    fixtures.len(),
628                    fixtures_len
629                )));
630                return self.result;
631            }
632
633            table_fixtures.push(&fixtures[..]);
634        }
635
636        let progress = start_fuzz_progress(
637            self.cr.progress,
638            self.cr.name,
639            &func.name,
640            None,
641            fixtures_len as u32,
642        );
643
644        let mut result = FuzzTestResult::default();
645
646        for i in 0..fixtures_len {
647            if self.tcfg.fail_fast.should_stop() {
648                return self.result;
649            }
650
651            // Increment progress bar.
652            if let Some(progress) = progress.as_ref() {
653                progress.inc(1);
654            }
655
656            let args = table_fixtures.iter().map(|row| row[i].clone()).collect_vec();
657            let (mut raw_call_result, reason) = match self.executor.call(
658                self.sender,
659                self.address,
660                func,
661                &args,
662                U256::ZERO,
663                Some(self.revert_decoder()),
664            ) {
665                Ok(res) => (res.raw, None),
666                Err(EvmError::Execution(err)) => (err.raw, Some(err.reason)),
667                Err(EvmError::Skip(reason)) => {
668                    self.result.single_skip(reason);
669                    return self.result;
670                }
671                Err(err) => {
672                    self.result.single_fail(Some(err.to_string()));
673                    return self.result;
674                }
675            };
676
677            result.gas_by_case.push((raw_call_result.gas_used, raw_call_result.stipend));
678            result.logs.extend(raw_call_result.logs.clone());
679            result.labels.extend(raw_call_result.labels.clone());
680            HitMaps::merge_opt(&mut result.line_coverage, raw_call_result.line_coverage.clone());
681
682            let is_success =
683                self.executor.is_raw_call_mut_success(self.address, &mut raw_call_result, false);
684            // Record counterexample if test fails.
685            if !is_success {
686                result.counterexample =
687                    Some(CounterExample::Single(BaseCounterExample::from_fuzz_call(
688                        Bytes::from(func.abi_encode_input(&args).unwrap()),
689                        args,
690                        raw_call_result.traces.clone(),
691                    )));
692                result.reason = reason;
693                result.traces = raw_call_result.traces;
694                self.result.table_result(result);
695                return self.result;
696            }
697
698            // If it's the last iteration and all other runs succeeded, then use last call result
699            // for logs and traces.
700            if i == fixtures_len - 1 {
701                result.success = true;
702                result.traces = raw_call_result.traces;
703                self.result.table_result(result);
704                return self.result;
705            }
706        }
707
708        self.result
709    }
710
711    fn run_invariant_test(
712        mut self,
713        func: &Function,
714        call_after_invariant: bool,
715        identified_contracts: &ContractsByAddress,
716        test_bytecode: &Bytes,
717    ) -> TestResult {
718        // First, run the test normally to see if it needs to be skipped.
719        if let Err(EvmError::Skip(reason)) = self.executor.call(
720            self.sender,
721            self.address,
722            func,
723            &[],
724            U256::ZERO,
725            Some(self.revert_decoder()),
726        ) {
727            self.result.invariant_skip(reason);
728            return self.result;
729        };
730
731        let runner = self.invariant_runner();
732        let invariant_config = &self.config.invariant;
733
734        let mut executor = self.clone_executor();
735        // Enable edge coverage if running with coverage guided fuzzing or with edge coverage
736        // metrics (useful for benchmarking the fuzzer).
737        executor
738            .inspector_mut()
739            .collect_edge_coverage(invariant_config.corpus.collect_edge_coverage());
740        let mut config = invariant_config.clone();
741        let (failure_dir, failure_file) = test_paths(
742            &mut config.corpus,
743            invariant_config.failure_persist_dir.clone().unwrap(),
744            self.cr.name,
745            &func.name,
746        );
747
748        let mut evm = InvariantExecutor::new(
749            executor,
750            runner,
751            config,
752            identified_contracts,
753            &self.cr.mcr.known_contracts,
754        );
755        let invariant_contract = InvariantContract {
756            address: self.address,
757            invariant_function: func,
758            call_after_invariant,
759            abi: &self.cr.contract.abi,
760        };
761        let show_solidity = invariant_config.show_solidity;
762
763        // Try to replay recorded failure if any.
764        if let Some(mut call_sequence) =
765            persisted_call_sequence(failure_file.as_path(), test_bytecode)
766        {
767            // Create calls from failed sequence and check if invariant still broken.
768            let txes = call_sequence
769                .iter_mut()
770                .map(|seq| {
771                    seq.show_solidity = show_solidity;
772                    BasicTxDetails {
773                        sender: seq.sender.unwrap_or_default(),
774                        call_details: CallDetails {
775                            target: seq.addr.unwrap_or_default(),
776                            calldata: seq.calldata.clone(),
777                        },
778                    }
779                })
780                .collect::<Vec<BasicTxDetails>>();
781            if let Ok((success, replayed_entirely)) = check_sequence(
782                self.clone_executor(),
783                &txes,
784                (0..min(txes.len(), invariant_config.depth as usize)).collect(),
785                invariant_contract.address,
786                invariant_contract.invariant_function.selector().to_vec().into(),
787                invariant_config.fail_on_revert,
788                invariant_contract.call_after_invariant,
789            ) && !success
790            {
791                let _ = sh_warn!(
792                    "\
793                            Replayed invariant failure from {:?} file. \
794                            Run `forge clean` or remove file to ignore failure and to continue invariant test campaign.",
795                    failure_file.as_path()
796                );
797                // If sequence still fails then replay error to collect traces and
798                // exit without executing new runs.
799                let _ = replay_run(
800                    &invariant_contract,
801                    self.clone_executor(),
802                    &self.cr.mcr.known_contracts,
803                    identified_contracts.clone(),
804                    &mut self.result.logs,
805                    &mut self.result.traces,
806                    &mut self.result.line_coverage,
807                    &mut self.result.deprecated_cheatcodes,
808                    &txes,
809                    show_solidity,
810                );
811                self.result.invariant_replay_fail(
812                    replayed_entirely,
813                    &invariant_contract.invariant_function.name,
814                    call_sequence,
815                );
816                return self.result;
817            }
818        }
819
820        let progress = start_fuzz_progress(
821            self.cr.progress,
822            self.cr.name,
823            &func.name,
824            invariant_config.timeout,
825            invariant_config.runs,
826        );
827        let invariant_result = match evm.invariant_fuzz(
828            invariant_contract.clone(),
829            &self.setup.fuzz_fixtures,
830            &self.setup.deployed_libs,
831            progress.as_ref(),
832            &self.tcfg.fail_fast,
833        ) {
834            Ok(x) => x,
835            Err(e) => {
836                self.result.invariant_setup_fail(e);
837                return self.result;
838            }
839        };
840        // Merge coverage collected during invariant run with test setup coverage.
841        self.result.merge_coverages(invariant_result.line_coverage);
842
843        let mut counterexample = None;
844        let success = invariant_result.error.is_none();
845        let reason = invariant_result.error.as_ref().and_then(|err| err.revert_reason());
846
847        match invariant_result.error {
848            // If invariants were broken, replay the error to collect logs and traces
849            Some(error) => match error {
850                InvariantFuzzError::BrokenInvariant(case_data)
851                | InvariantFuzzError::Revert(case_data) => {
852                    // Replay error to create counterexample and to collect logs, traces and
853                    // coverage.
854                    match replay_error(
855                        &case_data,
856                        &invariant_contract,
857                        self.clone_executor(),
858                        &self.cr.mcr.known_contracts,
859                        identified_contracts.clone(),
860                        &mut self.result.logs,
861                        &mut self.result.traces,
862                        &mut self.result.line_coverage,
863                        &mut self.result.deprecated_cheatcodes,
864                        progress.as_ref(),
865                        show_solidity,
866                    ) {
867                        Ok(call_sequence) => {
868                            if !call_sequence.is_empty() {
869                                // Persist error in invariant failure dir.
870                                if let Err(err) = foundry_common::fs::create_dir_all(failure_dir) {
871                                    error!(%err, "Failed to create invariant failure dir");
872                                } else if let Err(err) = foundry_common::fs::write_json_file(
873                                    failure_file.as_path(),
874                                    &InvariantPersistedFailure {
875                                        call_sequence: call_sequence.clone(),
876                                        driver_bytecode: Some(test_bytecode.clone()),
877                                    },
878                                ) {
879                                    error!(%err, "Failed to record call sequence");
880                                }
881
882                                let original_seq_len =
883                                    if let TestError::Fail(_, calls) = &case_data.test_error {
884                                        calls.len()
885                                    } else {
886                                        call_sequence.len()
887                                    };
888
889                                counterexample =
890                                    Some(CounterExample::Sequence(original_seq_len, call_sequence))
891                            }
892                        }
893                        Err(err) => {
894                            error!(%err, "Failed to replay invariant error");
895                        }
896                    };
897                }
898                InvariantFuzzError::MaxAssumeRejects(_) => {}
899            },
900
901            // If invariants ran successfully, replay the last run to collect logs and
902            // traces.
903            _ => {
904                if let Err(err) = replay_run(
905                    &invariant_contract,
906                    self.clone_executor(),
907                    &self.cr.mcr.known_contracts,
908                    identified_contracts.clone(),
909                    &mut self.result.logs,
910                    &mut self.result.traces,
911                    &mut self.result.line_coverage,
912                    &mut self.result.deprecated_cheatcodes,
913                    &invariant_result.last_run_inputs,
914                    show_solidity,
915                ) {
916                    error!(%err, "Failed to replay last invariant run");
917                }
918            }
919        }
920
921        self.result.invariant_result(
922            invariant_result.gas_report_traces,
923            success,
924            reason,
925            counterexample,
926            invariant_result.cases,
927            invariant_result.reverts,
928            invariant_result.metrics,
929            invariant_result.failed_corpus_replays,
930        );
931        self.result
932    }
933
934    /// Runs a fuzzed test.
935    ///
936    /// Applies the before test txes (if any), fuzzes the current function and returns the
937    /// `TestResult`.
938    ///
939    /// Before test txes are applied in order and state modifications committed to the EVM database
940    /// (therefore the fuzz test will use the modified state).
941    /// State modifications of before test txes and fuzz test are discarded after test ends,
942    /// similar to `eth_call`.
943    fn run_fuzz_test(mut self, func: &Function) -> TestResult {
944        // Prepare fuzz test execution.
945        if self.prepare_test(func).is_err() {
946            return self.result;
947        }
948
949        let runner = self.fuzz_runner();
950        let mut fuzz_config = self.config.fuzz.clone();
951        let (failure_dir, failure_file) = test_paths(
952            &mut fuzz_config.corpus,
953            fuzz_config.failure_persist_dir.clone().unwrap(),
954            self.cr.name,
955            &func.name,
956        );
957
958        let progress = start_fuzz_progress(
959            self.cr.progress,
960            self.cr.name,
961            &func.name,
962            fuzz_config.timeout,
963            fuzz_config.runs,
964        );
965
966        let mut executor = self.executor.into_owned();
967        // Enable edge coverage if running with coverage guided fuzzing or with edge coverage
968        // metrics (useful for benchmarking the fuzzer).
969        executor.inspector_mut().collect_edge_coverage(fuzz_config.corpus.collect_edge_coverage());
970        // Load persisted counterexample, if any.
971        let persisted_failure =
972            foundry_common::fs::read_json_file::<BaseCounterExample>(failure_file.as_path()).ok();
973        // Run fuzz test.
974        let mut fuzzed_executor =
975            FuzzedExecutor::new(executor, runner, self.tcfg.sender, fuzz_config, persisted_failure);
976        let result = match fuzzed_executor.fuzz(
977            func,
978            &self.setup.fuzz_fixtures,
979            &self.setup.deployed_libs,
980            self.address,
981            &self.cr.mcr.revert_decoder,
982            progress.as_ref(),
983            &self.tcfg.fail_fast,
984        ) {
985            Ok(x) => x,
986            Err(e) => {
987                self.result.fuzz_setup_fail(e);
988                return self.result;
989            }
990        };
991
992        // Record counterexample.
993        if let Some(CounterExample::Single(counterexample)) = &result.counterexample {
994            if let Err(err) = foundry_common::fs::create_dir_all(failure_dir) {
995                error!(%err, "Failed to create fuzz failure dir");
996            } else if let Err(err) =
997                foundry_common::fs::write_json_file(failure_file.as_path(), counterexample)
998            {
999                error!(%err, "Failed to record call sequence");
1000            }
1001        }
1002
1003        self.result.fuzz_result(result);
1004        self.result
1005    }
1006
1007    /// Prepares single unit test and fuzz test execution:
1008    /// - set up the test result and executor
1009    /// - check if before test txes are configured and apply them in order
1010    ///
1011    /// Before test txes are arrays of arbitrary calldata obtained by calling the `beforeTest`
1012    /// function with test selector as a parameter.
1013    ///
1014    /// Unit tests within same contract (or even current test) are valid options for before test tx
1015    /// configuration. Test execution stops if any of before test txes fails.
1016    fn prepare_test(&mut self, func: &Function) -> Result<(), ()> {
1017        let address = self.setup.address;
1018
1019        // Apply before test configured functions (if any).
1020        if self.cr.contract.abi.functions().filter(|func| func.name.is_before_test_setup()).count()
1021            == 1
1022        {
1023            for calldata in self.executor.call_sol_default(
1024                address,
1025                &ITest::beforeTestSetupCall { testSelector: func.selector() },
1026            ) {
1027                // Apply before test configured calldata.
1028                match self.executor.to_mut().transact_raw(
1029                    self.tcfg.sender,
1030                    address,
1031                    calldata,
1032                    U256::ZERO,
1033                ) {
1034                    Ok(call_result) => {
1035                        let reverted = call_result.reverted;
1036
1037                        // Merge tx result traces in unit test result.
1038                        self.result.extend(call_result);
1039
1040                        // To continue unit test execution the call should not revert.
1041                        if reverted {
1042                            self.result.single_fail(None);
1043                            return Err(());
1044                        }
1045                    }
1046                    Err(_) => {
1047                        self.result.single_fail(None);
1048                        return Err(());
1049                    }
1050                }
1051            }
1052        }
1053        Ok(())
1054    }
1055
1056    fn fuzz_runner(&self) -> TestRunner {
1057        let config = &self.config.fuzz;
1058        fuzzer_with_cases(config.seed, config.runs, config.max_test_rejects)
1059    }
1060
1061    fn invariant_runner(&self) -> TestRunner {
1062        let config = &self.config.invariant;
1063        fuzzer_with_cases(self.config.fuzz.seed, config.runs, config.max_assume_rejects)
1064    }
1065
1066    fn clone_executor(&self) -> Executor {
1067        self.executor.clone().into_owned()
1068    }
1069}
1070
1071fn fuzzer_with_cases(seed: Option<U256>, cases: u32, max_global_rejects: u32) -> TestRunner {
1072    let config = proptest::test_runner::Config {
1073        cases,
1074        max_global_rejects,
1075        // Disable proptest shrink: for fuzz tests we provide single counterexample,
1076        // for invariant tests we shrink outside proptest.
1077        max_shrink_iters: 0,
1078        ..Default::default()
1079    };
1080
1081    if let Some(seed) = seed {
1082        trace!(target: "forge::test", %seed, "building deterministic fuzzer");
1083        let rng = TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>());
1084        TestRunner::new_with_rng(config, rng)
1085    } else {
1086        trace!(target: "forge::test", "building stochastic fuzzer");
1087        TestRunner::new(config)
1088    }
1089}
1090
1091/// Holds data about a persisted invariant failure.
1092#[derive(Serialize, Deserialize)]
1093struct InvariantPersistedFailure {
1094    /// Recorded counterexample.
1095    call_sequence: Vec<BaseCounterExample>,
1096    /// Bytecode of the test contract that generated the counterexample.
1097    #[serde(skip_serializing_if = "Option::is_none")]
1098    driver_bytecode: Option<Bytes>,
1099}
1100
1101/// Helper function to load failed call sequence from file.
1102/// Ignores failure if generated with different test contract than the current one.
1103fn persisted_call_sequence(path: &Path, bytecode: &Bytes) -> Option<Vec<BaseCounterExample>> {
1104    foundry_common::fs::read_json_file::<InvariantPersistedFailure>(path).ok().and_then(
1105        |persisted_failure| {
1106            if let Some(persisted_bytecode) = &persisted_failure.driver_bytecode {
1107                // Ignore persisted sequence if test bytecode doesn't match.
1108                if !bytecode.eq(persisted_bytecode) {
1109                    let _= sh_warn!("\
1110                            Failure from {:?} file was ignored because test contract bytecode has changed.",
1111                        path
1112                    );
1113                    return None;
1114                }
1115            };
1116            Some(persisted_failure.call_sequence)
1117        },
1118    )
1119}
1120
1121/// Helper function to set test corpus dir and to compose persisted failure paths.
1122fn test_paths(
1123    corpus_config: &mut FuzzCorpusConfig,
1124    persist_dir: PathBuf,
1125    contract_name: &str,
1126    test_name: &str,
1127) -> (PathBuf, PathBuf) {
1128    let contract = contract_name.split(':').next_back().unwrap();
1129    // Update config with corpus dir for current test.
1130    corpus_config.with_test(contract, test_name);
1131
1132    let failures_dir = canonicalized(persist_dir.join("failures").join(contract));
1133    let failure_file = canonicalized(failures_dir.join(test_name));
1134    (failures_dir, failure_file)
1135}