@@ -12,12 +12,12 @@ use super::*;
12
12
13
13
pub ( crate ) trait OutputFormatter {
14
14
fn write_run_start ( & mut self , len : usize ) -> io:: Result < ( ) > ;
15
- fn write_test_start ( & mut self ,
16
- test : & TestDesc ,
17
- align : NamePadding ,
18
- max_name_len : usize ) -> io:: Result < ( ) > ;
15
+ fn write_test_start ( & mut self , test : & TestDesc ) -> io:: Result < ( ) > ;
19
16
fn write_timeout ( & mut self , desc : & TestDesc ) -> io:: Result < ( ) > ;
20
- fn write_result ( & mut self , result : & TestResult ) -> io:: Result < ( ) > ;
17
+ fn write_result ( & mut self ,
18
+ desc : & TestDesc ,
19
+ result : & TestResult ,
20
+ stdout : & [ u8 ] ) -> io:: Result < ( ) > ;
21
21
fn write_run_finish ( & mut self , state : & ConsoleTestState ) -> io:: Result < bool > ;
22
22
}
23
23
@@ -26,15 +26,17 @@ pub(crate) struct HumanFormatter<T> {
26
26
terse : bool ,
27
27
use_color : bool ,
28
28
test_count : usize ,
29
+ max_name_len : usize , // number of columns to fill when aligning names
29
30
}
30
31
31
32
impl < T : Write > HumanFormatter < T > {
32
- pub fn new ( out : OutputLocation < T > , use_color : bool , terse : bool ) -> Self {
33
+ pub fn new ( out : OutputLocation < T > , use_color : bool , terse : bool , max_name_len : usize ) -> Self {
33
34
HumanFormatter {
34
35
out,
35
36
terse,
36
37
use_color,
37
38
test_count : 0 ,
39
+ max_name_len,
38
40
}
39
41
}
40
42
@@ -73,7 +75,7 @@ impl<T: Write> HumanFormatter<T> {
73
75
// `stamp` in the rust CI).
74
76
self . write_plain ( "\n " ) ?;
75
77
}
76
-
78
+
77
79
self . test_count += 1 ;
78
80
Ok ( ( ) )
79
81
} else {
@@ -170,20 +172,18 @@ impl<T: Write> OutputFormatter for HumanFormatter<T> {
170
172
self . write_plain ( & format ! ( "\n running {} {}\n " , len, noun) )
171
173
}
172
174
173
- fn write_test_start ( & mut self ,
174
- test : & TestDesc ,
175
- align : NamePadding ,
176
- max_name_len : usize ) -> io:: Result < ( ) > {
177
- if self . terse && align != PadOnRight {
178
- Ok ( ( ) )
179
- }
180
- else {
181
- let name = test. padded_name ( max_name_len, align) ;
182
- self . write_plain ( & format ! ( "test {} ... " , name) )
183
- }
175
+ fn write_test_start ( & mut self , _desc : & TestDesc ) -> io:: Result < ( ) > {
176
+ // Do not print header, as priting it at this point will result in
177
+ // an unreadable output when running tests concurrently.
178
+ Ok ( ( ) )
184
179
}
185
180
186
- fn write_result ( & mut self , result : & TestResult ) -> io:: Result < ( ) > {
181
+ fn write_result ( & mut self , desc : & TestDesc , result : & TestResult , _: & [ u8 ] ) -> io:: Result < ( ) > {
182
+ if !( self . terse && desc. name . padding ( ) != PadOnRight ) {
183
+ let name = desc. padded_name ( self . max_name_len , desc. name . padding ( ) ) ;
184
+ self . write_plain ( & format ! ( "test {} ... " , name) ) ?;
185
+ }
186
+
187
187
match * result {
188
188
TrOk => self . write_ok ( ) ,
189
189
TrFailed | TrFailedMsg ( _) => self . write_failed ( ) ,
@@ -244,3 +244,203 @@ impl<T: Write> OutputFormatter for HumanFormatter<T> {
244
244
Ok ( success)
245
245
}
246
246
}
247
+
248
+ pub ( crate ) struct JsonFormatter < T > {
249
+ out : OutputLocation < T >
250
+ }
251
+
252
+ impl < T : Write > JsonFormatter < T > {
253
+ pub fn new ( out : OutputLocation < T > ) -> Self {
254
+ Self {
255
+ out, }
256
+ }
257
+
258
+ fn write_str < S : AsRef < str > > ( & mut self , s : S ) -> io:: Result < ( ) > {
259
+ self . out . write_all ( s. as_ref ( ) . as_ref ( ) ) ?;
260
+ self . out . write_all ( "\n " . as_ref ( ) )
261
+ }
262
+
263
+ fn write_event ( & mut self ,
264
+ ty : & str ,
265
+ name : & str ,
266
+ evt : & str ,
267
+ extra : Option < String > ) -> io:: Result < ( ) > {
268
+ if let Some ( extras) = extra {
269
+ self . write_str ( & * format ! ( r#"{{ "type": "{}", "name": "{}", "event": "{}", {} }}"# ,
270
+ ty,
271
+ name,
272
+ evt,
273
+ extras) )
274
+ }
275
+ else {
276
+ self . write_str ( & * format ! ( r#"{{ "type": "{}", "name": "{}", "event": "{}" }}"# ,
277
+ ty,
278
+ name,
279
+ evt) )
280
+ }
281
+ }
282
+ }
283
+
284
+ impl < T : Write > OutputFormatter for JsonFormatter < T > {
285
+ fn write_run_start ( & mut self , len : usize ) -> io:: Result < ( ) > {
286
+ self . write_str (
287
+ & * format ! ( r#"{{ "type": "suite", "event": "started", "test_count": "{}" }}"# , len) )
288
+ }
289
+
290
+ fn write_test_start ( & mut self , desc : & TestDesc ) -> io:: Result < ( ) > {
291
+ self . write_str ( & * format ! ( r#"{{ "type": "test", "event": "started", "name": "{}" }}"# ,
292
+ desc. name) )
293
+ }
294
+
295
+ fn write_result ( & mut self ,
296
+ desc : & TestDesc ,
297
+ result : & TestResult ,
298
+ stdout : & [ u8 ] ) -> io:: Result < ( ) > {
299
+ match * result {
300
+ TrOk => {
301
+ self . write_event ( "test" , desc. name . as_slice ( ) , "ok" , None )
302
+ } ,
303
+
304
+ TrFailed => {
305
+ let extra_data = if stdout. len ( ) > 0 {
306
+ Some ( format ! ( r#""stdout": "{}""# ,
307
+ EscapedString ( String :: from_utf8_lossy( stdout) ) ) )
308
+ }
309
+ else {
310
+ None
311
+ } ;
312
+
313
+ self . write_event ( "test" , desc. name . as_slice ( ) , "failed" , extra_data)
314
+ } ,
315
+
316
+ TrFailedMsg ( ref m) => {
317
+ self . write_event ( "test" ,
318
+ desc. name . as_slice ( ) ,
319
+ "failed" ,
320
+ Some ( format ! ( r#""message": "{}""# , EscapedString ( m) ) ) )
321
+ } ,
322
+
323
+ TrIgnored => {
324
+ self . write_event ( "test" , desc. name . as_slice ( ) , "ignored" , None )
325
+ } ,
326
+
327
+ TrAllowedFail => {
328
+ self . write_event ( "test" , desc. name . as_slice ( ) , "allowed_failure" , None )
329
+ } ,
330
+
331
+ TrBench ( ref bs) => {
332
+ let median = bs. ns_iter_summ . median as usize ;
333
+ let deviation = ( bs. ns_iter_summ . max - bs. ns_iter_summ . min ) as usize ;
334
+
335
+ let mbps = if bs. mb_s == 0 {
336
+ "" . into ( )
337
+ }
338
+ else {
339
+ format ! ( r#", "mib_per_second": {}"# , bs. mb_s)
340
+ } ;
341
+
342
+ let line = format ! ( "{{ \" type\" : \" bench\" , \
343
+ \" name\" : \" {}\" , \
344
+ \" median\" : {}, \
345
+ \" deviation\" : {}{} }}",
346
+ desc. name,
347
+ median,
348
+ deviation,
349
+ mbps) ;
350
+
351
+ self . write_str ( & * line)
352
+ } ,
353
+ }
354
+ }
355
+
356
+ fn write_timeout ( & mut self , desc : & TestDesc ) -> io:: Result < ( ) > {
357
+ self . write_str ( & * format ! ( r#"{{ "type": "test", "event": "timeout", "name": "{}" }}"# ,
358
+ desc. name) )
359
+ }
360
+
361
+ fn write_run_finish ( & mut self , state : & ConsoleTestState ) -> io:: Result < bool > {
362
+
363
+ self . write_str ( & * format ! ( "{{ \" type\" : \" suite\" , \
364
+ \" event\" : \" {}\" , \
365
+ \" passed\" : {}, \
366
+ \" failed\" : {}, \
367
+ \" allowed_fail\" : {}, \
368
+ \" ignored\" : {}, \
369
+ \" measured\" : {}, \
370
+ \" filtered_out\" : \" {}\" }}",
371
+ if state. failed == 0 { "ok" } else { "failed" } ,
372
+ state. passed,
373
+ state. failed + state. allowed_fail,
374
+ state. allowed_fail,
375
+ state. ignored,
376
+ state. measured,
377
+ state. filtered_out) ) ?;
378
+
379
+ Ok ( state. failed == 0 )
380
+ }
381
+ }
382
+
383
+ /// A formatting utility used to print strings with characters in need of escaping.
384
+ /// Base code taken form `libserialize::json::escape_str`
385
+ struct EscapedString < S : AsRef < str > > ( S ) ;
386
+
387
+ impl < S : AsRef < str > > :: std:: fmt:: Display for EscapedString < S > {
388
+ fn fmt ( & self , f : & mut :: std:: fmt:: Formatter ) -> :: std:: fmt:: Result {
389
+ let mut start = 0 ;
390
+
391
+ for ( i, byte) in self . 0 . as_ref ( ) . bytes ( ) . enumerate ( ) {
392
+ let escaped = match byte {
393
+ b'"' => "\\ \" " ,
394
+ b'\\' => "\\ \\ " ,
395
+ b'\x00' => "\\ u0000" ,
396
+ b'\x01' => "\\ u0001" ,
397
+ b'\x02' => "\\ u0002" ,
398
+ b'\x03' => "\\ u0003" ,
399
+ b'\x04' => "\\ u0004" ,
400
+ b'\x05' => "\\ u0005" ,
401
+ b'\x06' => "\\ u0006" ,
402
+ b'\x07' => "\\ u0007" ,
403
+ b'\x08' => "\\ b" ,
404
+ b'\t' => "\\ t" ,
405
+ b'\n' => "\\ n" ,
406
+ b'\x0b' => "\\ u000b" ,
407
+ b'\x0c' => "\\ f" ,
408
+ b'\r' => "\\ r" ,
409
+ b'\x0e' => "\\ u000e" ,
410
+ b'\x0f' => "\\ u000f" ,
411
+ b'\x10' => "\\ u0010" ,
412
+ b'\x11' => "\\ u0011" ,
413
+ b'\x12' => "\\ u0012" ,
414
+ b'\x13' => "\\ u0013" ,
415
+ b'\x14' => "\\ u0014" ,
416
+ b'\x15' => "\\ u0015" ,
417
+ b'\x16' => "\\ u0016" ,
418
+ b'\x17' => "\\ u0017" ,
419
+ b'\x18' => "\\ u0018" ,
420
+ b'\x19' => "\\ u0019" ,
421
+ b'\x1a' => "\\ u001a" ,
422
+ b'\x1b' => "\\ u001b" ,
423
+ b'\x1c' => "\\ u001c" ,
424
+ b'\x1d' => "\\ u001d" ,
425
+ b'\x1e' => "\\ u001e" ,
426
+ b'\x1f' => "\\ u001f" ,
427
+ b'\x7f' => "\\ u007f" ,
428
+ _ => { continue ; }
429
+ } ;
430
+
431
+ if start < i {
432
+ f. write_str ( & self . 0 . as_ref ( ) [ start..i] ) ?;
433
+ }
434
+
435
+ f. write_str ( escaped) ?;
436
+
437
+ start = i + 1 ;
438
+ }
439
+
440
+ if start != self . 0 . as_ref ( ) . len ( ) {
441
+ f. write_str ( & self . 0 . as_ref ( ) [ start..] ) ?;
442
+ }
443
+
444
+ Ok ( ( ) )
445
+ }
446
+ }
0 commit comments