forge/
runner.rs

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