1use 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
49pub const LIBRARY_DEPLOYER: Address = address!("0x1F95D37F27EA0dEA9C252FC09D5A6eaA97647353");
55
56pub struct ContractRunner<'a> {
58 name: &'a str,
60 contract: &'a TestContract,
62 executor: Executor,
64 progress: Option<&'a TestsProgress>,
66 tokio_handle: &'a tokio::runtime::Handle,
68 span: tracing::Span,
70 tcfg: Cow<'a, TestRunnerConfig>,
72 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 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 self.executor.set_balance(self.sender, U256::MAX)?;
126 self.executor.set_balance(CALLER, U256::MAX)?;
127
128 self.executor.set_nonce(self.sender, 1)?;
130
131 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 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
156 let address = self.sender.create(self.executor.get_nonce(self.sender)?);
157 result.address = address;
158
159 self.executor.set_balance(address, self.initial_balance())?;
162
163 let deploy_result = self.executor.deploy(
165 self.sender,
166 self.contract.bytecode.clone(),
167 U256::ZERO,
168 Some(&self.mcr.revert_decoder),
169 );
170
171 result.deployment_failure = deploy_result.is_err();
172
173 if let Ok(dr) = &deploy_result {
174 debug_assert_eq!(dr.address, address);
175 }
176 let (raw, reason) = RawCallResult::from_evm_result(deploy_result.map(Into::into))?;
177 result.extend(raw, TraceKind::Deployment);
178 if reason.is_some() {
179 result.reason = reason;
180 return Ok(result);
181 }
182
183 self.executor.set_balance(self.sender, self.initial_balance())?;
185 self.executor.set_balance(CALLER, self.initial_balance())?;
186 self.executor.set_balance(LIBRARY_DEPLOYER, self.initial_balance())?;
187
188 self.executor.deploy_create2_deployer()?;
189
190 if call_setup {
192 trace!("calling setUp");
193 let res = self.executor.setup(None, address, Some(&self.mcr.revert_decoder));
194 let (raw, reason) = RawCallResult::from_evm_result(res)?;
195 result.extend(raw, TraceKind::Setup);
196 result.reason = reason;
197 }
198
199 result.fuzz_fixtures = self.fuzz_fixtures(address);
200
201 Ok(result)
202 }
203
204 fn initial_balance(&self) -> U256 {
205 self.evm_opts.initial_balance
206 }
207
208 fn apply_contract_inline_config(&mut self) -> Result<()> {
210 if self.inline_config.contains_contract(self.name) {
211 let new_config = Arc::new(self.inline_config(None)?);
212 self.tcfg.to_mut().reconfigure_with(new_config);
213 let prev_tracer = self.executor.inspector_mut().tracer.take();
214 self.tcfg.configure_executor(&mut self.executor);
215 self.executor.inspector_mut().tracer = prev_tracer;
217 }
218 Ok(())
219 }
220
221 fn inline_config(&self, func: Option<&Function>) -> Result<Config> {
223 let function = func.map(|f| f.name.as_str()).unwrap_or("");
224 let config =
225 self.mcr.inline_config.merge(self.name, function, &self.config).extract::<Config>()?;
226 Ok(config)
227 }
228
229 fn fuzz_fixtures(&mut self, address: Address) -> FuzzFixtures {
245 let mut fixtures = HashMap::default();
246 let fixture_functions = self.contract.abi.functions().filter(|func| func.is_fixture());
247 for func in fixture_functions {
248 if func.inputs.is_empty() {
249 if let Ok(CallResult { raw: _, decoded_result }) =
251 self.executor.call(CALLER, address, func, &[], U256::ZERO, None)
252 {
253 fixtures.insert(fixture_name(func.name.clone()), decoded_result);
254 }
255 } else {
256 let mut vals = Vec::new();
259 let mut index = 0;
260 loop {
261 if let Ok(CallResult { raw: _, decoded_result }) = self.executor.call(
262 CALLER,
263 address,
264 func,
265 &[DynSolValue::Uint(U256::from(index), 256)],
266 U256::ZERO,
267 None,
268 ) {
269 vals.push(decoded_result);
270 } else {
271 break;
274 }
275 index += 1;
276 }
277 fixtures.insert(fixture_name(func.name.clone()), DynSolValue::Array(vals));
278 };
279 }
280 FuzzFixtures::new(fixtures)
281 }
282
283 pub fn run_tests(mut self, filter: &dyn TestFilter) -> SuiteResult {
285 let start = Instant::now();
286 let mut warnings = Vec::new();
287
288 let setup_fns: Vec<_> =
290 self.contract.abi.functions().filter(|func| func.name.is_setup()).collect();
291 let call_setup = setup_fns.len() == 1 && setup_fns[0].name == "setUp";
292 for &setup_fn in &setup_fns {
294 if setup_fn.name != "setUp" {
295 warnings.push(format!(
296 "Found invalid setup function \"{}\" did you mean \"setUp()\"?",
297 setup_fn.signature()
298 ));
299 }
300 }
301
302 if setup_fns.len() > 1 {
304 return SuiteResult::new(
305 start.elapsed(),
306 [("setUp()".to_string(), TestResult::fail("multiple setUp functions".to_string()))]
307 .into(),
308 warnings,
309 );
310 }
311
312 let after_invariant_fns: Vec<_> =
314 self.contract.abi.functions().filter(|func| func.name.is_after_invariant()).collect();
315 if after_invariant_fns.len() > 1 {
316 return SuiteResult::new(
318 start.elapsed(),
319 [(
320 "afterInvariant()".to_string(),
321 TestResult::fail("multiple afterInvariant functions".to_string()),
322 )]
323 .into(),
324 warnings,
325 );
326 }
327 let call_after_invariant = after_invariant_fns.first().is_some_and(|after_invariant_fn| {
328 let match_sig = after_invariant_fn.name == "afterInvariant";
329 if !match_sig {
330 warnings.push(format!(
331 "Found invalid afterInvariant function \"{}\" did you mean \"afterInvariant()\"?",
332 after_invariant_fn.signature()
333 ));
334 }
335 match_sig
336 });
337
338 let has_invariants = self.contract.abi.functions().any(|func| func.is_invariant_test());
341
342 let prev_tracer = self.executor.inspector_mut().tracer.take();
343 if prev_tracer.is_some() || has_invariants {
344 self.executor.set_tracing(TraceMode::Call);
345 }
346
347 let setup_time = Instant::now();
348 let setup = self.setup(call_setup);
349 debug!("finished setting up in {:?}", setup_time.elapsed());
350
351 self.executor.inspector_mut().tracer = prev_tracer;
352
353 if setup.reason.is_some() {
354 let fail_msg = if !setup.deployment_failure {
356 "setUp()".to_string()
357 } else {
358 "constructor()".to_string()
359 };
360 return SuiteResult::new(
361 start.elapsed(),
362 [(fail_msg, TestResult::setup_result(setup))].into(),
363 warnings,
364 );
365 }
366
367 let find_timer = Instant::now();
370 let functions = self
371 .contract
372 .abi
373 .functions()
374 .filter(|func| is_matching_test(func, filter))
375 .collect::<Vec<_>>();
376 debug!(
377 "Found {} test functions out of {} in {:?}",
378 functions.len(),
379 self.contract.abi.functions().count(),
380 find_timer.elapsed(),
381 );
382
383 let identified_contracts = has_invariants.then(|| {
384 load_contracts(setup.traces.iter().map(|(_, t)| &t.arena), &self.mcr.known_contracts)
385 });
386
387 let test_fail_instances = functions
388 .iter()
389 .filter_map(|func| {
390 TestFunctionKind::classify(&func.name, !func.inputs.is_empty())
391 .is_any_test_fail()
392 .then_some(func.name.clone())
393 })
394 .collect::<Vec<_>>();
395
396 if !test_fail_instances.is_empty() {
397 let instances = format!(
398 "Found {} instances: {}",
399 test_fail_instances.len(),
400 test_fail_instances.join(", ")
401 );
402 let fail = TestResult::fail("`testFail*` has been removed. Consider changing to test_Revert[If|When]_Condition and expecting a revert".to_string());
403 return SuiteResult::new(start.elapsed(), [(instances, fail)].into(), warnings);
404 }
405
406 let test_results = functions
407 .par_iter()
408 .map(|&func| {
409 let start = Instant::now();
410
411 let _guard = self.tokio_handle.enter();
412
413 let _guard;
414 let current_span = tracing::Span::current();
415 if current_span.is_none() || current_span.id() != self.span.id() {
416 _guard = self.span.enter();
417 }
418
419 let sig = func.signature();
420 let kind = func.test_function_kind();
421
422 let _guard = debug_span!(
423 "test",
424 %kind,
425 name = %if enabled!(tracing::Level::TRACE) { &sig } else { &func.name },
426 )
427 .entered();
428
429 let mut res = FunctionRunner::new(&self, &setup).run(
430 func,
431 kind,
432 call_after_invariant,
433 identified_contracts.as_ref(),
434 );
435 res.duration = start.elapsed();
436
437 (sig, res)
438 })
439 .collect::<BTreeMap<_, _>>();
440
441 let duration = start.elapsed();
442 SuiteResult::new(duration, test_results, warnings)
443 }
444}
445
446struct FunctionRunner<'a> {
448 tcfg: Cow<'a, TestRunnerConfig>,
450 executor: Cow<'a, Executor>,
452 cr: &'a ContractRunner<'a>,
454 address: Address,
456 setup: &'a TestSetup,
458 result: TestResult,
460}
461
462impl<'a> std::ops::Deref for FunctionRunner<'a> {
463 type Target = Cow<'a, TestRunnerConfig>;
464
465 #[inline(always)]
466 fn deref(&self) -> &Self::Target {
467 &self.tcfg
468 }
469}
470
471impl<'a> FunctionRunner<'a> {
472 fn new(cr: &'a ContractRunner<'a>, setup: &'a TestSetup) -> Self {
473 Self {
474 tcfg: match &cr.tcfg {
475 Cow::Borrowed(tcfg) => Cow::Borrowed(tcfg),
476 Cow::Owned(tcfg) => Cow::Owned(tcfg.clone()),
477 },
478 executor: Cow::Borrowed(&cr.executor),
479 cr,
480 address: setup.address,
481 setup,
482 result: TestResult::new(setup),
483 }
484 }
485
486 fn revert_decoder(&self) -> &'a RevertDecoder {
487 &self.cr.mcr.revert_decoder
488 }
489
490 fn apply_function_inline_config(&mut self, func: &Function) -> Result<()> {
492 if self.inline_config.contains_function(self.cr.name, &func.name) {
493 let new_config = Arc::new(self.cr.inline_config(Some(func))?);
494 self.tcfg.to_mut().reconfigure_with(new_config);
495 self.tcfg.configure_executor(self.executor.to_mut());
496 }
497 Ok(())
498 }
499
500 fn run(
501 mut self,
502 func: &Function,
503 kind: TestFunctionKind,
504 call_after_invariant: bool,
505 identified_contracts: Option<&ContractsByAddress>,
506 ) -> TestResult {
507 if let Err(e) = self.apply_function_inline_config(func) {
508 self.result.single_fail(Some(e.to_string()));
509 return self.result;
510 }
511
512 match kind {
513 TestFunctionKind::UnitTest { .. } => self.run_unit_test(func),
514 TestFunctionKind::FuzzTest { .. } => self.run_fuzz_test(func),
515 TestFunctionKind::TableTest => self.run_table_test(func),
516 TestFunctionKind::InvariantTest => {
517 let test_bytecode = &self.cr.contract.bytecode;
518 self.run_invariant_test(
519 func,
520 call_after_invariant,
521 identified_contracts.unwrap(),
522 test_bytecode,
523 )
524 }
525 _ => unreachable!(),
526 }
527 }
528
529 fn run_unit_test(mut self, func: &Function) -> TestResult {
538 if self.prepare_test(func).is_err() {
540 return self.result;
541 }
542
543 let (mut raw_call_result, reason) = match self.executor.call(
545 self.sender,
546 self.address,
547 func,
548 &[],
549 U256::ZERO,
550 Some(self.revert_decoder()),
551 ) {
552 Ok(res) => (res.raw, None),
553 Err(EvmError::Execution(err)) => (err.raw, Some(err.reason)),
554 Err(EvmError::Skip(reason)) => {
555 self.result.single_skip(reason);
556 return self.result;
557 }
558 Err(err) => {
559 self.result.single_fail(Some(err.to_string()));
560 return self.result;
561 }
562 };
563
564 let success =
565 self.executor.is_raw_call_mut_success(self.address, &mut raw_call_result, false);
566 self.result.single_result(success, reason, raw_call_result);
567 self.result
568 }
569
570 fn run_table_test(mut self, func: &Function) -> TestResult {
579 if self.prepare_test(func).is_err() {
581 return self.result;
582 }
583
584 let Some(first_param) = func.inputs.first() else {
586 self.result.single_fail(Some("Table test should have at least one parameter".into()));
587 return self.result;
588 };
589
590 let Some(first_param_fixtures) =
591 &self.setup.fuzz_fixtures.param_fixtures(first_param.name())
592 else {
593 self.result.single_fail(Some("Table test should have fixtures defined".into()));
594 return self.result;
595 };
596
597 if first_param_fixtures.is_empty() {
598 self.result.single_fail(Some("Table test should have at least one fixture".into()));
599 return self.result;
600 }
601
602 let fixtures_len = first_param_fixtures.len();
603 let mut table_fixtures = vec![&first_param_fixtures[..]];
604
605 for param in &func.inputs[1..] {
607 let param_name = param.name();
608 let Some(fixtures) = &self.setup.fuzz_fixtures.param_fixtures(param.name()) else {
609 self.result.single_fail(Some(format!("No fixture defined for param {param_name}")));
610 return self.result;
611 };
612
613 if fixtures.len() != fixtures_len {
614 self.result.single_fail(Some(format!(
615 "{} fixtures defined for {param_name} (expected {})",
616 fixtures.len(),
617 fixtures_len
618 )));
619 return self.result;
620 }
621
622 table_fixtures.push(&fixtures[..]);
623 }
624
625 let progress = start_fuzz_progress(
626 self.cr.progress,
627 self.cr.name,
628 &func.name,
629 None,
630 fixtures_len as u32,
631 );
632
633 for i in 0..fixtures_len {
634 if let Some(progress) = progress.as_ref() {
636 progress.inc(1);
637 }
638
639 let args = table_fixtures.iter().map(|row| row[i].clone()).collect_vec();
640 let (mut raw_call_result, reason) = match self.executor.call(
641 self.sender,
642 self.address,
643 func,
644 &args,
645 U256::ZERO,
646 Some(self.revert_decoder()),
647 ) {
648 Ok(res) => (res.raw, None),
649 Err(EvmError::Execution(err)) => (err.raw, Some(err.reason)),
650 Err(EvmError::Skip(reason)) => {
651 self.result.single_skip(reason);
652 return self.result;
653 }
654 Err(err) => {
655 self.result.single_fail(Some(err.to_string()));
656 return self.result;
657 }
658 };
659
660 let is_success =
661 self.executor.is_raw_call_mut_success(self.address, &mut raw_call_result, false);
662 if !is_success {
664 self.result.counterexample =
665 Some(CounterExample::Single(BaseCounterExample::from_fuzz_call(
666 Bytes::from(func.abi_encode_input(&args).unwrap()),
667 args,
668 raw_call_result.traces.clone(),
669 )));
670 self.result.single_result(false, reason, raw_call_result);
671 return self.result;
672 }
673
674 if i == fixtures_len - 1 {
677 self.result.single_result(true, None, raw_call_result);
678 return self.result;
679 }
680 }
681
682 self.result
683 }
684
685 fn run_invariant_test(
686 mut self,
687 func: &Function,
688 call_after_invariant: bool,
689 identified_contracts: &ContractsByAddress,
690 test_bytecode: &Bytes,
691 ) -> TestResult {
692 if let Err(EvmError::Skip(reason)) = self.executor.call(
694 self.sender,
695 self.address,
696 func,
697 &[],
698 U256::ZERO,
699 Some(self.revert_decoder()),
700 ) {
701 self.result.invariant_skip(reason);
702 return self.result;
703 };
704
705 let runner = self.invariant_runner();
706 let invariant_config = &self.config.invariant;
707
708 let mut evm = InvariantExecutor::new(
709 self.clone_executor(),
710 runner,
711 invariant_config.clone(),
712 identified_contracts,
713 &self.cr.mcr.known_contracts,
714 );
715 let invariant_contract = InvariantContract {
716 address: self.address,
717 invariant_function: func,
718 call_after_invariant,
719 abi: &self.cr.contract.abi,
720 };
721
722 let (failure_dir, failure_file) = invariant_failure_paths(
723 invariant_config,
724 self.cr.name,
725 &invariant_contract.invariant_function.name,
726 );
727 let show_solidity = invariant_config.show_solidity;
728
729 if let Some(mut call_sequence) =
731 persisted_call_sequence(failure_file.as_path(), test_bytecode)
732 {
733 let txes = call_sequence
735 .iter_mut()
736 .map(|seq| {
737 seq.show_solidity = show_solidity;
738 BasicTxDetails {
739 sender: seq.sender.unwrap_or_default(),
740 call_details: CallDetails {
741 target: seq.addr.unwrap_or_default(),
742 calldata: seq.calldata.clone(),
743 },
744 }
745 })
746 .collect::<Vec<BasicTxDetails>>();
747 if let Ok((success, replayed_entirely)) = check_sequence(
748 self.clone_executor(),
749 &txes,
750 (0..min(txes.len(), invariant_config.depth as usize)).collect(),
751 invariant_contract.address,
752 invariant_contract.invariant_function.selector().to_vec().into(),
753 invariant_config.fail_on_revert,
754 invariant_contract.call_after_invariant,
755 ) && !success
756 {
757 let _ = sh_warn!(
758 "\
759 Replayed invariant failure from {:?} file. \
760 Run `forge clean` or remove file to ignore failure and to continue invariant test campaign.",
761 failure_file.as_path()
762 );
763 let _ = replay_run(
766 &invariant_contract,
767 self.clone_executor(),
768 &self.cr.mcr.known_contracts,
769 identified_contracts.clone(),
770 &mut self.result.logs,
771 &mut self.result.traces,
772 &mut self.result.line_coverage,
773 &mut self.result.deprecated_cheatcodes,
774 &txes,
775 show_solidity,
776 );
777 self.result.invariant_replay_fail(
778 replayed_entirely,
779 &invariant_contract.invariant_function.name,
780 call_sequence,
781 );
782 return self.result;
783 }
784 }
785
786 let progress = start_fuzz_progress(
787 self.cr.progress,
788 self.cr.name,
789 &func.name,
790 invariant_config.timeout,
791 invariant_config.runs,
792 );
793 let invariant_result = match evm.invariant_fuzz(
794 invariant_contract.clone(),
795 &self.setup.fuzz_fixtures,
796 &self.setup.deployed_libs,
797 progress.as_ref(),
798 ) {
799 Ok(x) => x,
800 Err(e) => {
801 self.result.invariant_setup_fail(e);
802 return self.result;
803 }
804 };
805 self.result.merge_coverages(invariant_result.line_coverage);
807
808 let mut counterexample = None;
809 let success = invariant_result.error.is_none();
810 let reason = invariant_result.error.as_ref().and_then(|err| err.revert_reason());
811
812 match invariant_result.error {
813 Some(error) => match error {
815 InvariantFuzzError::BrokenInvariant(case_data)
816 | InvariantFuzzError::Revert(case_data) => {
817 match replay_error(
820 &case_data,
821 &invariant_contract,
822 self.clone_executor(),
823 &self.cr.mcr.known_contracts,
824 identified_contracts.clone(),
825 &mut self.result.logs,
826 &mut self.result.traces,
827 &mut self.result.line_coverage,
828 &mut self.result.deprecated_cheatcodes,
829 progress.as_ref(),
830 show_solidity,
831 ) {
832 Ok(call_sequence) => {
833 if !call_sequence.is_empty() {
834 if let Err(err) = foundry_common::fs::create_dir_all(failure_dir) {
836 error!(%err, "Failed to create invariant failure dir");
837 } else if let Err(err) = foundry_common::fs::write_json_file(
838 failure_file.as_path(),
839 &InvariantPersistedFailure {
840 call_sequence: call_sequence.clone(),
841 driver_bytecode: Some(test_bytecode.clone()),
842 },
843 ) {
844 error!(%err, "Failed to record call sequence");
845 }
846
847 let original_seq_len =
848 if let TestError::Fail(_, calls) = &case_data.test_error {
849 calls.len()
850 } else {
851 call_sequence.len()
852 };
853
854 counterexample =
855 Some(CounterExample::Sequence(original_seq_len, call_sequence))
856 }
857 }
858 Err(err) => {
859 error!(%err, "Failed to replay invariant error");
860 }
861 };
862 }
863 InvariantFuzzError::MaxAssumeRejects(_) => {}
864 },
865
866 _ => {
869 if let Err(err) = replay_run(
870 &invariant_contract,
871 self.clone_executor(),
872 &self.cr.mcr.known_contracts,
873 identified_contracts.clone(),
874 &mut self.result.logs,
875 &mut self.result.traces,
876 &mut self.result.line_coverage,
877 &mut self.result.deprecated_cheatcodes,
878 &invariant_result.last_run_inputs,
879 show_solidity,
880 ) {
881 error!(%err, "Failed to replay last invariant run");
882 }
883 }
884 }
885
886 self.result.invariant_result(
887 invariant_result.gas_report_traces,
888 success,
889 reason,
890 counterexample,
891 invariant_result.cases,
892 invariant_result.reverts,
893 invariant_result.metrics,
894 invariant_result.failed_corpus_replays,
895 );
896 self.result
897 }
898
899 fn run_fuzz_test(mut self, func: &Function) -> TestResult {
909 if self.prepare_test(func).is_err() {
911 return self.result;
912 }
913
914 let runner = self.fuzz_runner();
915 let fuzz_config = self.config.fuzz.clone();
916
917 let progress = start_fuzz_progress(
918 self.cr.progress,
919 self.cr.name,
920 &func.name,
921 fuzz_config.timeout,
922 fuzz_config.runs,
923 );
924
925 let fuzzed_executor =
927 FuzzedExecutor::new(self.executor.into_owned(), runner, self.tcfg.sender, fuzz_config);
928 let result = fuzzed_executor.fuzz(
929 func,
930 &self.setup.fuzz_fixtures,
931 &self.setup.deployed_libs,
932 self.address,
933 &self.cr.mcr.revert_decoder,
934 progress.as_ref(),
935 );
936 self.result.fuzz_result(result);
937 self.result
938 }
939
940 fn prepare_test(&mut self, func: &Function) -> Result<(), ()> {
950 let address = self.setup.address;
951
952 if self.cr.contract.abi.functions().filter(|func| func.name.is_before_test_setup()).count()
954 == 1
955 {
956 for calldata in self.executor.call_sol_default(
957 address,
958 &ITest::beforeTestSetupCall { testSelector: func.selector() },
959 ) {
960 match self.executor.to_mut().transact_raw(
962 self.tcfg.sender,
963 address,
964 calldata,
965 U256::ZERO,
966 ) {
967 Ok(call_result) => {
968 let reverted = call_result.reverted;
969
970 self.result.extend(call_result);
972
973 if reverted {
975 self.result.single_fail(None);
976 return Err(());
977 }
978 }
979 Err(_) => {
980 self.result.single_fail(None);
981 return Err(());
982 }
983 }
984 }
985 }
986 Ok(())
987 }
988
989 fn fuzz_runner(&self) -> TestRunner {
990 let config = &self.config.fuzz;
991 let failure_persist_path = config
992 .failure_persist_dir
993 .as_ref()
994 .unwrap()
995 .join(config.failure_persist_file.as_ref().unwrap())
996 .into_os_string()
997 .into_string()
998 .unwrap();
999 fuzzer_with_cases(
1000 config.seed,
1001 config.runs,
1002 config.max_test_rejects,
1003 Some(Box::new(FileFailurePersistence::Direct(failure_persist_path.leak()))),
1004 )
1005 }
1006
1007 fn invariant_runner(&self) -> TestRunner {
1008 let config = &self.config.invariant;
1009 fuzzer_with_cases(self.config.fuzz.seed, config.runs, config.max_assume_rejects, None)
1010 }
1011
1012 fn clone_executor(&self) -> Executor {
1013 self.executor.clone().into_owned()
1014 }
1015}
1016
1017fn fuzzer_with_cases(
1018 seed: Option<U256>,
1019 cases: u32,
1020 max_global_rejects: u32,
1021 file_failure_persistence: Option<Box<dyn FailurePersistence>>,
1022) -> TestRunner {
1023 let config = proptest::test_runner::Config {
1024 failure_persistence: file_failure_persistence,
1025 cases,
1026 max_global_rejects,
1027 max_shrink_iters: 0,
1030 ..Default::default()
1031 };
1032
1033 if let Some(seed) = seed {
1034 trace!(target: "forge::test", %seed, "building deterministic fuzzer");
1035 let rng = TestRng::from_seed(RngAlgorithm::ChaCha, &seed.to_be_bytes::<32>());
1036 TestRunner::new_with_rng(config, rng)
1037 } else {
1038 trace!(target: "forge::test", "building stochastic fuzzer");
1039 TestRunner::new(config)
1040 }
1041}
1042
1043#[derive(Serialize, Deserialize)]
1045struct InvariantPersistedFailure {
1046 call_sequence: Vec<BaseCounterExample>,
1048 #[serde(skip_serializing_if = "Option::is_none")]
1050 driver_bytecode: Option<Bytes>,
1051}
1052
1053fn persisted_call_sequence(path: &Path, bytecode: &Bytes) -> Option<Vec<BaseCounterExample>> {
1056 foundry_common::fs::read_json_file::<InvariantPersistedFailure>(path).ok().and_then(
1057 |persisted_failure| {
1058 if let Some(persisted_bytecode) = &persisted_failure.driver_bytecode {
1059 if !bytecode.eq(persisted_bytecode) {
1061 let _= sh_warn!("\
1062 Failure from {:?} file was ignored because test contract bytecode has changed.",
1063 path
1064 );
1065 return None;
1066 }
1067 };
1068 Some(persisted_failure.call_sequence)
1069 },
1070 )
1071}
1072
1073fn invariant_failure_paths(
1075 config: &InvariantConfig,
1076 contract_name: &str,
1077 invariant_name: &str,
1078) -> (PathBuf, PathBuf) {
1079 let dir = config
1080 .failure_persist_dir
1081 .clone()
1082 .unwrap()
1083 .join("failures")
1084 .join(contract_name.split(':').next_back().unwrap());
1085 let dir = canonicalized(dir);
1086 let file = canonicalized(dir.join(invariant_name));
1087 (dir, file)
1088}