@@ -5,7 +5,7 @@ use crate::{
55 fuzz:: BaseCounterExample ,
66 multi_runner:: { TestContract , TestRunnerConfig , is_matching_test} ,
77 progress:: { TestsProgress , start_fuzz_progress} ,
8- result:: { SuiteResult , TestResult , TestSetup } ,
8+ result:: { SuiteResult , TestResult , TestSetup , TestStatus } ,
99} ;
1010use alloy_dyn_abi:: { DynSolValue , JsonAbiExt } ;
1111use alloy_json_abi:: Function ;
@@ -18,7 +18,7 @@ use foundry_evm::{
1818 constants:: CALLER ,
1919 decode:: RevertDecoder ,
2020 executors:: {
21- CallResult , EvmError , Executor , ITest , RawCallResult ,
21+ CallResult , EvmError , Executor , FailFast , ITest , RawCallResult ,
2222 fuzz:: FuzzedExecutor ,
2323 invariant:: {
2424 InvariantExecutor , InvariantFuzzError , check_sequence, replay_error, replay_run,
@@ -279,7 +279,7 @@ impl<'a> ContractRunner<'a> {
279279 }
280280
281281 /// Runs all tests for a contract whose names match the provided regular expression
282- pub fn run_tests ( mut self , filter : & dyn TestFilter ) -> SuiteResult {
282+ pub fn run_tests ( mut self , filter : & dyn TestFilter , fail_fast : & FailFast ) -> SuiteResult {
283283 let start = Instant :: now ( ) ;
284284 let mut warnings = Vec :: new ( ) ;
285285
@@ -404,6 +404,11 @@ impl<'a> ContractRunner<'a> {
404404 let test_results = functions
405405 . par_iter ( )
406406 . map ( |& func| {
407+ // Early exit if we're running with fail-fast and a test already failed.
408+ if fail_fast. should_stop ( ) {
409+ return ( func. signature ( ) , TestResult :: setup_result ( setup. clone ( ) ) ) ;
410+ }
411+
407412 let start = Instant :: now ( ) ;
408413
409414 let _guard = self . tokio_handle . enter ( ) ;
@@ -429,9 +434,15 @@ impl<'a> ContractRunner<'a> {
429434 kind,
430435 call_after_invariant,
431436 identified_contracts. as_ref ( ) ,
437+ fail_fast,
432438 ) ;
433439 res. duration = start. elapsed ( ) ;
434440
441+ // Set fail fast flag if current test failed.
442+ if res. status . is_failure ( ) {
443+ fail_fast. record_fail ( ) ;
444+ }
445+
435446 ( sig, res)
436447 } )
437448 . collect :: < BTreeMap < _ , _ > > ( ) ;
@@ -501,6 +512,7 @@ impl<'a> FunctionRunner<'a> {
501512 kind : TestFunctionKind ,
502513 call_after_invariant : bool ,
503514 identified_contracts : Option < & ContractsByAddress > ,
515+ fail_fast : & FailFast ,
504516 ) -> TestResult {
505517 if let Err ( e) = self . apply_function_inline_config ( func) {
506518 self . result . single_fail ( Some ( e. to_string ( ) ) ) ;
@@ -509,15 +521,16 @@ impl<'a> FunctionRunner<'a> {
509521
510522 match kind {
511523 TestFunctionKind :: UnitTest { .. } => self . run_unit_test ( func) ,
512- TestFunctionKind :: FuzzTest { .. } => self . run_fuzz_test ( func) ,
513- TestFunctionKind :: TableTest => self . run_table_test ( func) ,
524+ TestFunctionKind :: FuzzTest { .. } => self . run_fuzz_test ( func, fail_fast ) ,
525+ TestFunctionKind :: TableTest => self . run_table_test ( func, fail_fast ) ,
514526 TestFunctionKind :: InvariantTest => {
515527 let test_bytecode = & self . cr . contract . bytecode ;
516528 self . run_invariant_test (
517529 func,
518530 call_after_invariant,
519531 identified_contracts. unwrap ( ) ,
520532 test_bytecode,
533+ fail_fast,
521534 )
522535 }
523536 _ => unreachable ! ( ) ,
@@ -573,7 +586,7 @@ impl<'a> FunctionRunner<'a> {
573586 /// - `uint256[] public fixtureAmount = [2, 5]`
574587 /// - `bool[] public fixtureSwap = [true, false]` The `table_test` is then called with the pair
575588 /// of args `(2, true)` and `(5, false)`.
576- fn run_table_test ( mut self , func : & Function ) -> TestResult {
589+ fn run_table_test ( mut self , func : & Function , fail_fast : & FailFast ) -> TestResult {
577590 // Prepare unit test execution.
578591 if self . prepare_test ( func) . is_err ( ) {
579592 return self . result ;
@@ -628,7 +641,12 @@ impl<'a> FunctionRunner<'a> {
628641 fixtures_len as u32 ,
629642 ) ;
630643
644+ self . result . status = TestStatus :: Success ;
631645 for i in 0 ..fixtures_len {
646+ if fail_fast. should_stop ( ) {
647+ return self . result ;
648+ }
649+
632650 // Increment progress bar.
633651 if let Some ( progress) = progress. as_ref ( ) {
634652 progress. inc ( 1 ) ;
@@ -686,6 +704,7 @@ impl<'a> FunctionRunner<'a> {
686704 call_after_invariant : bool ,
687705 identified_contracts : & ContractsByAddress ,
688706 test_bytecode : & Bytes ,
707+ fail_fast : & FailFast ,
689708 ) -> TestResult {
690709 // First, run the test normally to see if it needs to be skipped.
691710 if let Err ( EvmError :: Skip ( reason) ) = self . executor . call (
@@ -800,6 +819,7 @@ impl<'a> FunctionRunner<'a> {
800819 & self . setup . fuzz_fixtures ,
801820 & self . setup . deployed_libs ,
802821 progress. as_ref ( ) ,
822+ fail_fast,
803823 ) {
804824 Ok ( x) => x,
805825 Err ( e) => {
@@ -910,7 +930,7 @@ impl<'a> FunctionRunner<'a> {
910930 /// (therefore the fuzz test will use the modified state).
911931 /// State modifications of before test txes and fuzz test are discarded after test ends,
912932 /// similar to `eth_call`.
913- fn run_fuzz_test ( mut self , func : & Function ) -> TestResult {
933+ fn run_fuzz_test ( mut self , func : & Function , fail_fast : & FailFast ) -> TestResult {
914934 // Prepare fuzz test execution.
915935 if self . prepare_test ( func) . is_err ( ) {
916936 return self . result ;
@@ -950,6 +970,7 @@ impl<'a> FunctionRunner<'a> {
950970 self . address ,
951971 & self . cr . mcr . revert_decoder ,
952972 progress. as_ref ( ) ,
973+ fail_fast,
953974 ) {
954975 Ok ( x) => x,
955976 Err ( e) => {
0 commit comments