Skip to main content

forge/
runner.rs

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