@@ -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 ;
@@ -629,6 +642,10 @@ impl<'a> FunctionRunner<'a> {
629642 ) ;
630643
631644 for i in 0 ..fixtures_len {
645+ if fail_fast. should_stop ( ) {
646+ return self . result ;
647+ }
648+
632649 // Increment progress bar.
633650 if let Some ( progress) = progress. as_ref ( ) {
634651 progress. inc ( 1 ) ;
@@ -686,6 +703,7 @@ impl<'a> FunctionRunner<'a> {
686703 call_after_invariant : bool ,
687704 identified_contracts : & ContractsByAddress ,
688705 test_bytecode : & Bytes ,
706+ fail_fast : & FailFast ,
689707 ) -> TestResult {
690708 // First, run the test normally to see if it needs to be skipped.
691709 if let Err ( EvmError :: Skip ( reason) ) = self . executor . call (
@@ -800,6 +818,7 @@ impl<'a> FunctionRunner<'a> {
800818 & self . setup . fuzz_fixtures ,
801819 & self . setup . deployed_libs ,
802820 progress. as_ref ( ) ,
821+ fail_fast,
803822 ) {
804823 Ok ( x) => x,
805824 Err ( e) => {
@@ -910,7 +929,7 @@ impl<'a> FunctionRunner<'a> {
910929 /// (therefore the fuzz test will use the modified state).
911930 /// State modifications of before test txes and fuzz test are discarded after test ends,
912931 /// similar to `eth_call`.
913- fn run_fuzz_test ( mut self , func : & Function ) -> TestResult {
932+ fn run_fuzz_test ( mut self , func : & Function , fail_fast : & FailFast ) -> TestResult {
914933 // Prepare fuzz test execution.
915934 if self . prepare_test ( func) . is_err ( ) {
916935 return self . result ;
@@ -950,6 +969,7 @@ impl<'a> FunctionRunner<'a> {
950969 self . address ,
951970 & self . cr . mcr . revert_decoder ,
952971 progress. as_ref ( ) ,
972+ fail_fast,
953973 ) {
954974 Ok ( x) => x,
955975 Err ( e) => {
0 commit comments