@@ -63,9 +63,13 @@ struct Opt {
63
63
#[ clap( long, short) ]
64
64
jobs : Option < usize > ,
65
65
/// 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" ) ]
67
67
keep_db_on_failure : bool ,
68
68
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
+
69
73
/// Report to junit XML.
70
74
#[ clap( long) ]
71
75
junit : Option < String > ,
@@ -150,6 +154,7 @@ pub async fn main() -> Result<()> {
150
154
color,
151
155
jobs,
152
156
keep_db_on_failure,
157
+ fail_fast,
153
158
junit,
154
159
host,
155
160
port,
@@ -239,6 +244,7 @@ pub async fn main() -> Result<()> {
239
244
config,
240
245
& labels,
241
246
junit. clone ( ) ,
247
+ fail_fast,
242
248
)
243
249
. await
244
250
} else {
@@ -249,6 +255,7 @@ pub async fn main() -> Result<()> {
249
255
config,
250
256
& labels,
251
257
junit. clone ( ) ,
258
+ fail_fast,
252
259
)
253
260
. await
254
261
} ;
@@ -272,6 +279,7 @@ async fn run_parallel(
272
279
config : DBConfig ,
273
280
labels : & [ String ] ,
274
281
junit : Option < String > ,
282
+ fail_fast : bool ,
275
283
) -> Result < ( ) > {
276
284
let mut create_databases = BTreeMap :: new ( ) ;
277
285
let mut filenames = BTreeSet :: new ( ) ;
@@ -332,11 +340,14 @@ async fn run_parallel(
332
340
333
341
let mut failed_case = vec ! [ ] ;
334
342
let mut failed_db: HashSet < String > = HashSet :: new ( ) ;
343
+ let mut remaining_files: HashSet < String > = HashSet :: from_iter ( filenames. clone ( ) ) ;
335
344
336
345
let start = Instant :: now ( ) ;
337
346
338
347
while let Some ( ( db_name, file, res, mut buf) ) = stream. next ( ) . await {
348
+ remaining_files. remove ( & file) ;
339
349
let test_case_name = file. replace ( [ '/' , ' ' , '.' , '-' ] , "_" ) ;
350
+ let mut failed = false ;
340
351
let case = match res {
341
352
Ok ( duration) => {
342
353
let mut case = TestCase :: new ( test_case_name, TestCaseStatus :: success ( ) ) ;
@@ -346,6 +357,7 @@ async fn run_parallel(
346
357
case
347
358
}
348
359
Err ( e) => {
360
+ failed = true ;
349
361
writeln ! ( buf, "{}\n \n {:?}" , style( "[FAILED]" ) . red( ) . bold( ) , e) ?;
350
362
writeln ! ( buf) ?;
351
363
failed_case. push ( file. clone ( ) ) ;
@@ -363,6 +375,20 @@ async fn run_parallel(
363
375
} ;
364
376
test_suite. add_test_case ( case) ;
365
377
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) ;
366
392
}
367
393
368
394
eprintln ! (
@@ -404,17 +430,20 @@ async fn run_serial(
404
430
config : DBConfig ,
405
431
labels : & [ String ] ,
406
432
junit : Option < String > ,
433
+ fail_fast : bool ,
407
434
) -> Result < ( ) > {
408
435
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 {
411
439
let mut runner = Runner :: new ( || engines:: connect ( engine, & config) ) ;
412
440
for label in labels {
413
441
runner. add_label ( label) ;
414
442
}
415
443
416
444
let filename = file. to_string_lossy ( ) . to_string ( ) ;
417
445
let test_case_name = filename. replace ( [ '/' , ' ' , '.' , '-' ] , "_" ) ;
446
+ let mut failed = false ;
418
447
let case = match run_test_file ( & mut std:: io:: stdout ( ) , runner, & file) . await {
419
448
Ok ( duration) => {
420
449
let mut case = TestCase :: new ( test_case_name, TestCaseStatus :: success ( ) ) ;
@@ -424,6 +453,7 @@ async fn run_serial(
424
453
case
425
454
}
426
455
Err ( e) => {
456
+ failed = true ;
427
457
println ! ( "{}\n \n {:?}" , style( "[FAILED]" ) . red( ) . bold( ) , e) ;
428
458
println ! ( ) ;
429
459
failed_case. push ( filename. clone ( ) ) ;
@@ -439,6 +469,23 @@ async fn run_serial(
439
469
}
440
470
} ;
441
471
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) ;
442
489
}
443
490
444
491
if !failed_case. is_empty ( ) {
0 commit comments