@@ -63,9 +63,13 @@ struct Opt {
6363 #[ clap( long, short) ]
6464 jobs : Option < usize > ,
6565 /// When using `-j`, whether to keep the temporary database when a test case fails.
66- #[ clap( long, default_value = "false" ) ]
66+ #[ clap( long, default_value = "false" , env = "SLT_KEEP_DB_ON_FAILURE" ) ]
6767 keep_db_on_failure : bool ,
6868
69+ /// Whether to exit immediately when a test case fails.
70+ #[ clap( long, default_value = "false" , env = "SLT_FAIL_FAST" ) ]
71+ fail_fast : bool ,
72+
6973 /// Report to junit XML.
7074 #[ clap( long) ]
7175 junit : Option < String > ,
@@ -150,6 +154,7 @@ pub async fn main() -> Result<()> {
150154 color,
151155 jobs,
152156 keep_db_on_failure,
157+ fail_fast,
153158 junit,
154159 host,
155160 port,
@@ -239,6 +244,7 @@ pub async fn main() -> Result<()> {
239244 config,
240245 & labels,
241246 junit. clone ( ) ,
247+ fail_fast,
242248 )
243249 . await
244250 } else {
@@ -249,6 +255,7 @@ pub async fn main() -> Result<()> {
249255 config,
250256 & labels,
251257 junit. clone ( ) ,
258+ fail_fast,
252259 )
253260 . await
254261 } ;
@@ -272,6 +279,7 @@ async fn run_parallel(
272279 config : DBConfig ,
273280 labels : & [ String ] ,
274281 junit : Option < String > ,
282+ fail_fast : bool ,
275283) -> Result < ( ) > {
276284 let mut create_databases = BTreeMap :: new ( ) ;
277285 let mut filenames = BTreeSet :: new ( ) ;
@@ -332,11 +340,14 @@ async fn run_parallel(
332340
333341 let mut failed_case = vec ! [ ] ;
334342 let mut failed_db: HashSet < String > = HashSet :: new ( ) ;
343+ let mut remaining_files: HashSet < String > = HashSet :: from_iter ( filenames. clone ( ) ) ;
335344
336345 let start = Instant :: now ( ) ;
337346
338347 while let Some ( ( db_name, file, res, mut buf) ) = stream. next ( ) . await {
348+ remaining_files. remove ( & file) ;
339349 let test_case_name = file. replace ( [ '/' , ' ' , '.' , '-' ] , "_" ) ;
350+ let mut failed = false ;
340351 let case = match res {
341352 Ok ( duration) => {
342353 let mut case = TestCase :: new ( test_case_name, TestCaseStatus :: success ( ) ) ;
@@ -346,6 +357,7 @@ async fn run_parallel(
346357 case
347358 }
348359 Err ( e) => {
360+ failed = true ;
349361 writeln ! ( buf, "{}\n \n {:?}" , style( "[FAILED]" ) . red( ) . bold( ) , e) ?;
350362 writeln ! ( buf) ?;
351363 failed_case. push ( file. clone ( ) ) ;
@@ -363,6 +375,20 @@ async fn run_parallel(
363375 } ;
364376 test_suite. add_test_case ( case) ;
365377 tokio:: task:: block_in_place ( || stdout ( ) . write_all ( & buf) ) ?;
378+ if fail_fast && failed {
379+ println ! ( "early exit after failure..." ) ;
380+ break ;
381+ }
382+ }
383+
384+ for file in remaining_files {
385+ println ! ( "{file} is not finished, skipping" ) ;
386+ let test_case_name = file. replace ( [ '/' , ' ' , '.' , '-' ] , "_" ) ;
387+ let mut case = TestCase :: new ( test_case_name, TestCaseStatus :: skipped ( ) ) ;
388+ case. set_time ( Duration :: from_millis ( 0 ) ) ;
389+ case. set_timestamp ( Local :: now ( ) ) ;
390+ case. set_classname ( junit. as_deref ( ) . unwrap_or_default ( ) ) ;
391+ test_suite. add_test_case ( case) ;
366392 }
367393
368394 eprintln ! (
@@ -404,17 +430,20 @@ async fn run_serial(
404430 config : DBConfig ,
405431 labels : & [ String ] ,
406432 junit : Option < String > ,
433+ fail_fast : bool ,
407434) -> Result < ( ) > {
408435 let mut failed_case = vec ! [ ] ;
409-
410- for file in files {
436+ let mut skipped_case = vec ! [ ] ;
437+ let mut files = files. into_iter ( ) ;
438+ for file in & mut files {
411439 let mut runner = Runner :: new ( || engines:: connect ( engine, & config) ) ;
412440 for label in labels {
413441 runner. add_label ( label) ;
414442 }
415443
416444 let filename = file. to_string_lossy ( ) . to_string ( ) ;
417445 let test_case_name = filename. replace ( [ '/' , ' ' , '.' , '-' ] , "_" ) ;
446+ let mut failed = false ;
418447 let case = match run_test_file ( & mut std:: io:: stdout ( ) , runner, & file) . await {
419448 Ok ( duration) => {
420449 let mut case = TestCase :: new ( test_case_name, TestCaseStatus :: success ( ) ) ;
@@ -424,6 +453,7 @@ async fn run_serial(
424453 case
425454 }
426455 Err ( e) => {
456+ failed = true ;
427457 println ! ( "{}\n \n {:?}" , style( "[FAILED]" ) . red( ) . bold( ) , e) ;
428458 println ! ( ) ;
429459 failed_case. push ( filename. clone ( ) ) ;
@@ -439,6 +469,23 @@ async fn run_serial(
439469 }
440470 } ;
441471 test_suite. add_test_case ( case) ;
472+ if fail_fast && failed {
473+ println ! ( "early exit after failure..." ) ;
474+ break ;
475+ }
476+ }
477+ for file in files {
478+ let filename = file. to_string_lossy ( ) . to_string ( ) ;
479+ let test_case_name = filename. replace ( [ '/' , ' ' , '.' , '-' ] , "_" ) ;
480+ let mut case = TestCase :: new ( test_case_name, TestCaseStatus :: skipped ( ) ) ;
481+ case. set_time ( Duration :: from_millis ( 0 ) ) ;
482+ case. set_timestamp ( Local :: now ( ) ) ;
483+ case. set_classname ( junit. as_deref ( ) . unwrap_or_default ( ) ) ;
484+ test_suite. add_test_case ( case) ;
485+ skipped_case. push ( filename. clone ( ) ) ;
486+ }
487+ if !skipped_case. is_empty ( ) {
488+ println ! ( "some test case skipped:\n {:#?}" , skipped_case) ;
442489 }
443490
444491 if !failed_case. is_empty ( ) {
0 commit comments