1
1
use std:: {
2
2
collections:: { BTreeMap , HashMap } ,
3
- io :: Write ,
3
+ fmt :: Display ,
4
4
panic:: { catch_unwind, UnwindSafe } ,
5
5
path:: PathBuf ,
6
6
sync:: { mpsc, Mutex } ,
@@ -12,19 +12,21 @@ use acvm::{BlackBoxFunctionSolver, FieldElement};
12
12
use bn254_blackbox_solver:: Bn254BlackBoxSolver ;
13
13
use clap:: Args ;
14
14
use fm:: FileManager ;
15
+ use formatters:: { Formatter , PrettyFormatter , TerseFormatter } ;
15
16
use nargo:: {
16
17
insert_all_files_for_workspace_into_file_manager, ops:: TestStatus , package:: Package , parse_all,
17
18
prepare_package, workspace:: Workspace , PrintOutput ,
18
19
} ;
19
20
use nargo_toml:: { get_package_manifest, resolve_workspace_from_toml} ;
20
21
use noirc_driver:: { check_crate, CompileOptions , NOIR_ARTIFACT_VERSION_STRING } ;
21
22
use noirc_frontend:: hir:: { FunctionNameMatch , ParsedFiles } ;
22
- use termcolor:: { Color , ColorChoice , ColorSpec , StandardStream , StandardStreamLock , WriteColor } ;
23
23
24
24
use crate :: { cli:: check_cmd:: check_crate_and_report_errors, errors:: CliError } ;
25
25
26
26
use super :: { NargoConfig , PackageOptions } ;
27
27
28
+ mod formatters;
29
+
28
30
/// Run the tests for this program
29
31
#[ derive( Debug , Clone , Args ) ]
30
32
#[ clap( visible_alias = "t" ) ]
@@ -53,6 +55,40 @@ pub(crate) struct TestCommand {
53
55
/// Number of threads used for running tests in parallel
54
56
#[ clap( long, default_value_t = rayon:: current_num_threads( ) ) ]
55
57
test_threads : usize ,
58
+
59
+ /// Configure formatting of output
60
+ #[ clap( long) ]
61
+ format : Option < Format > ,
62
+
63
+ /// Display one character per test instead of one line
64
+ #[ clap( short = 'q' , long = "quiet" ) ]
65
+ quiet : bool ,
66
+ }
67
+
68
+ #[ derive( Debug , Copy , Clone , clap:: ValueEnum ) ]
69
+ enum Format {
70
+ /// Print verbose output
71
+ Pretty ,
72
+ /// Display one character per test
73
+ Terse ,
74
+ }
75
+
76
+ impl Format {
77
+ fn formatter ( & self ) -> Box < dyn Formatter > {
78
+ match self {
79
+ Format :: Pretty => Box :: new ( PrettyFormatter ) ,
80
+ Format :: Terse => Box :: new ( TerseFormatter ) ,
81
+ }
82
+ }
83
+ }
84
+
85
+ impl Display for Format {
86
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
87
+ match self {
88
+ Format :: Pretty => write ! ( f, "pretty" ) ,
89
+ Format :: Terse => write ! ( f, "terse" ) ,
90
+ }
91
+ }
56
92
}
57
93
58
94
struct Test < ' a > {
@@ -95,13 +131,22 @@ pub(crate) fn run(args: TestCommand, config: NargoConfig) -> Result<(), CliError
95
131
None => FunctionNameMatch :: Anything ,
96
132
} ;
97
133
134
+ let formatter: Box < dyn Formatter > = if let Some ( format) = args. format {
135
+ format. formatter ( )
136
+ } else if args. quiet {
137
+ Box :: new ( TerseFormatter )
138
+ } else {
139
+ Box :: new ( PrettyFormatter )
140
+ } ;
141
+
98
142
let runner = TestRunner {
99
143
file_manager : & file_manager,
100
144
parsed_files : & parsed_files,
101
145
workspace,
102
146
args : & args,
103
147
pattern,
104
148
num_threads : args. test_threads ,
149
+ formatter,
105
150
} ;
106
151
runner. run ( )
107
152
}
@@ -113,6 +158,7 @@ struct TestRunner<'a> {
113
158
args : & ' a TestCommand ,
114
159
pattern : FunctionNameMatch < ' a > ,
115
160
num_threads : usize ,
161
+ formatter : Box < dyn Formatter > ,
116
162
}
117
163
118
164
impl < ' a > TestRunner < ' a > {
@@ -222,27 +268,31 @@ impl<'a> TestRunner<'a> {
222
268
// We'll go package by package, but we might get test results from packages ahead of us.
223
269
// We'll buffer those here and show them all at once when we get to those packages.
224
270
let mut buffer: HashMap < String , Vec < TestResult > > = HashMap :: new ( ) ;
225
- for ( package_name, test_count) in test_count_per_package {
226
- let plural = if * test_count == 1 { "" } else { "s" } ;
227
- println ! ( "[{package_name}] Running {test_count} test function{plural}" ) ;
228
-
271
+ for ( package_name, total_test_count) in test_count_per_package {
229
272
let mut test_report = Vec :: new ( ) ;
230
273
231
- // How many tests are left to receive for this package
232
- let mut remaining_test_count = * test_count;
274
+ let mut current_test_count = 0 ;
275
+ let total_test_count = * total_test_count;
276
+
277
+ self . formatter
278
+ . package_start ( package_name, total_test_count)
279
+ . expect ( "Could not display package start" ) ;
233
280
234
281
// Check if we have buffered test results for this package
235
282
if let Some ( buffered_tests) = buffer. remove ( package_name) {
236
- remaining_test_count -= buffered_tests. len ( ) ;
237
-
238
283
for test_result in buffered_tests {
239
- self . display_test_result ( & test_result)
240
- . expect ( "Could not display test status" ) ;
284
+ self . display_test_result (
285
+ & test_result,
286
+ current_test_count + 1 ,
287
+ total_test_count,
288
+ )
289
+ . expect ( "Could not display test status" ) ;
241
290
test_report. push ( test_result) ;
291
+ current_test_count += 1 ;
242
292
}
243
293
}
244
294
245
- if remaining_test_count > 0 {
295
+ if current_test_count < total_test_count {
246
296
while let Ok ( test_result) = receiver. recv ( ) {
247
297
if test_result. status . failed ( ) {
248
298
all_passed = false ;
@@ -257,17 +307,29 @@ impl<'a> TestRunner<'a> {
257
307
continue ;
258
308
}
259
309
260
- self . display_test_result ( & test_result)
261
- . expect ( "Could not display test status" ) ;
310
+ self . display_test_result (
311
+ & test_result,
312
+ current_test_count + 1 ,
313
+ total_test_count,
314
+ )
315
+ . expect ( "Could not display test status" ) ;
262
316
test_report. push ( test_result) ;
263
- remaining_test_count - = 1 ;
264
- if remaining_test_count == 0 {
317
+ current_test_count + = 1 ;
318
+ if current_test_count == total_test_count {
265
319
break ;
266
320
}
267
321
}
268
322
}
269
323
270
- display_test_report ( package_name, & test_report)
324
+ self . formatter
325
+ . package_end (
326
+ package_name,
327
+ & test_report,
328
+ self . file_manager ,
329
+ self . args . show_output ,
330
+ self . args . compile_options . deny_warnings ,
331
+ self . args . compile_options . silence_warnings ,
332
+ )
271
333
. expect ( "Could not display test report" ) ;
272
334
}
273
335
} ) ;
@@ -417,116 +479,20 @@ impl<'a> TestRunner<'a> {
417
479
}
418
480
419
481
/// Display the status of a single test
420
- fn display_test_result ( & ' a self , test_result : & ' a TestResult ) -> std:: io:: Result < ( ) > {
421
- let writer = StandardStream :: stderr ( ColorChoice :: Always ) ;
422
- let mut writer = writer. lock ( ) ;
423
-
424
- let is_slow = test_result. time_to_run >= Duration :: from_secs ( 30 ) ;
425
- let show_time = |writer : & mut StandardStreamLock < ' _ > | {
426
- if is_slow {
427
- write ! ( writer, " <{:.3}s>" , test_result. time_to_run. as_secs_f64( ) )
428
- } else {
429
- Ok ( ( ) )
430
- }
431
- } ;
432
-
433
- write ! ( writer, "[{}] Testing {}... " , & test_result. package_name, & test_result. name) ?;
434
- writer. flush ( ) ?;
435
-
436
- match & test_result. status {
437
- TestStatus :: Pass { .. } => {
438
- writer. set_color ( ColorSpec :: new ( ) . set_fg ( Some ( Color :: Green ) ) ) ?;
439
- write ! ( writer, "ok" ) ?;
440
- writer. reset ( ) ?;
441
- show_time ( & mut writer) ?;
442
- writeln ! ( writer) ?;
443
- }
444
- TestStatus :: Fail { message, error_diagnostic } => {
445
- writer. set_color ( ColorSpec :: new ( ) . set_fg ( Some ( Color :: Red ) ) ) ?;
446
- write ! ( writer, "FAIL\n {message}\n " ) ?;
447
- writer. reset ( ) ?;
448
- show_time ( & mut writer) ?;
449
- writeln ! ( writer) ?;
450
- if let Some ( diag) = error_diagnostic {
451
- noirc_errors:: reporter:: report_all (
452
- self . file_manager . as_file_map ( ) ,
453
- & [ diag. clone ( ) ] ,
454
- self . args . compile_options . deny_warnings ,
455
- self . args . compile_options . silence_warnings ,
456
- ) ;
457
- }
458
- }
459
- TestStatus :: Skipped { .. } => {
460
- writer. set_color ( ColorSpec :: new ( ) . set_fg ( Some ( Color :: Yellow ) ) ) ?;
461
- write ! ( writer, "skipped" ) ?;
462
- writer. reset ( ) ?;
463
- show_time ( & mut writer) ?;
464
- writeln ! ( writer) ?;
465
- }
466
- TestStatus :: CompileError ( err) => {
467
- noirc_errors:: reporter:: report_all (
468
- self . file_manager . as_file_map ( ) ,
469
- & [ err. clone ( ) ] ,
470
- self . args . compile_options . deny_warnings ,
471
- self . args . compile_options . silence_warnings ,
472
- ) ;
473
- }
474
- }
475
-
476
- if self . args . show_output && !test_result. output . is_empty ( ) {
477
- writeln ! ( writer, "--- {} stdout ---" , test_result. name) ?;
478
- write ! ( writer, "{}" , test_result. output) ?;
479
- let name_len = test_result. name . len ( ) ;
480
- writeln ! ( writer, "{}" , "-" . repeat( name_len + "--- stdout ---" . len( ) ) )
481
- } else {
482
- Ok ( ( ) )
483
- }
484
- }
485
- }
486
-
487
- /// Display a report for all tests in a package
488
- fn display_test_report ( package_name : & String , test_report : & [ TestResult ] ) -> std:: io:: Result < ( ) > {
489
- let writer = StandardStream :: stderr ( ColorChoice :: Always ) ;
490
- let mut writer = writer. lock ( ) ;
491
-
492
- let failed_tests: Vec < _ > = test_report
493
- . iter ( )
494
- . filter_map ( |test_result| test_result. status . failed ( ) . then_some ( & test_result. name ) )
495
- . collect ( ) ;
496
-
497
- if !failed_tests. is_empty ( ) {
498
- writeln ! ( writer) ?;
499
- writeln ! ( writer, "[{}] Failures:" , package_name) ?;
500
- for failed_test in failed_tests {
501
- writeln ! ( writer, " {}" , failed_test) ?;
502
- }
503
- writeln ! ( writer) ?;
504
- }
505
-
506
- write ! ( writer, "[{}] " , package_name) ?;
507
-
508
- let count_all = test_report. len ( ) ;
509
- let count_failed = test_report. iter ( ) . filter ( |test_result| test_result. status . failed ( ) ) . count ( ) ;
510
- let plural = if count_all == 1 { "" } else { "s" } ;
511
- if count_failed == 0 {
512
- writer. set_color ( ColorSpec :: new ( ) . set_fg ( Some ( Color :: Green ) ) ) ?;
513
- write ! ( writer, "{count_all} test{plural} passed" ) ?;
514
- writer. reset ( ) ?;
515
- writeln ! ( writer) ?;
516
- } else {
517
- let count_passed = count_all - count_failed;
518
- let plural_failed = if count_failed == 1 { "" } else { "s" } ;
519
- let plural_passed = if count_passed == 1 { "" } else { "s" } ;
520
-
521
- if count_passed != 0 {
522
- writer. set_color ( ColorSpec :: new ( ) . set_fg ( Some ( Color :: Green ) ) ) ?;
523
- write ! ( writer, "{count_passed} test{plural_passed} passed, " ) ?;
524
- }
525
-
526
- writer. set_color ( ColorSpec :: new ( ) . set_fg ( Some ( Color :: Red ) ) ) ?;
527
- writeln ! ( writer, "{count_failed} test{plural_failed} failed" ) ?;
528
- writer. reset ( ) ?;
482
+ fn display_test_result (
483
+ & ' a self ,
484
+ test_result : & ' a TestResult ,
485
+ current_test_count : usize ,
486
+ total_test_count : usize ,
487
+ ) -> std:: io:: Result < ( ) > {
488
+ self . formatter . test_end (
489
+ test_result,
490
+ current_test_count,
491
+ total_test_count,
492
+ self . file_manager ,
493
+ self . args . show_output ,
494
+ self . args . compile_options . deny_warnings ,
495
+ self . args . compile_options . silence_warnings ,
496
+ )
529
497
}
530
-
531
- Ok ( ( ) )
532
498
}
0 commit comments