@@ -18,14 +18,10 @@ impl<T: Write> JsonFormatter<T> {
18
18
}
19
19
20
20
fn writeln_message ( & mut self , s : & str ) -> io:: Result < ( ) > {
21
- assert ! ( !s. contains( '\n' ) ) ;
22
-
23
- self . out . write_all ( s. as_ref ( ) ) ?;
24
- self . out . write_all ( b"\n " )
25
- }
26
-
27
- fn write_message ( & mut self , s : & str ) -> io:: Result < ( ) > {
28
- assert ! ( !s. contains( '\n' ) ) ;
21
+ // self.out will take a lock, but that lock is released when write_all returns. This
22
+ // results in a race condition and json output may not end with a new line. We avoid this
23
+ // by issuing `write_all` calls line-by-line.
24
+ assert_eq ! ( s. chars( ) . last( ) , Some ( '\n' ) ) ;
29
25
30
26
self . out . write_all ( s. as_ref ( ) )
31
27
}
@@ -34,34 +30,35 @@ impl<T: Write> JsonFormatter<T> {
34
30
& mut self ,
35
31
ty : & str ,
36
32
name : & str ,
37
- evt : & str ,
33
+ event : & str ,
38
34
exec_time : Option < & time:: TestExecTime > ,
39
35
stdout : Option < Cow < ' _ , str > > ,
40
36
extra : Option < & str > ,
41
37
) -> io:: Result < ( ) > {
42
38
// A doc test's name includes a filename which must be escaped for correct json.
43
- self . write_message ( & format ! (
44
- r#"{{ "type": "{}", "name": "{}", "event": "{}""# ,
45
- ty,
46
- EscapedString ( name) ,
47
- evt
48
- ) ) ?;
49
- if let Some ( exec_time) = exec_time {
50
- self . write_message ( & format ! ( r#", "exec_time": {}"# , exec_time. 0 . as_secs_f64( ) ) ) ?;
51
- }
52
- if let Some ( stdout) = stdout {
53
- self . write_message ( & format ! ( r#", "stdout": "{}""# , EscapedString ( stdout) ) ) ?;
54
- }
55
- if let Some ( extra) = extra {
56
- self . write_message ( & format ! ( r#", {extra}"# ) ) ?;
57
- }
58
- self . writeln_message ( " }" )
39
+ let name = EscapedString ( name) ;
40
+ let exec_time_json = if let Some ( exec_time) = exec_time {
41
+ format ! ( r#", "exec_time": {}"# , exec_time. 0 . as_secs_f64( ) )
42
+ } else {
43
+ String :: from ( "" )
44
+ } ;
45
+ let stdout_json = if let Some ( stdout) = stdout {
46
+ format ! ( r#", "stdout": "{}""# , EscapedString ( stdout) )
47
+ } else {
48
+ String :: from ( "" )
49
+ } ;
50
+ let extra_json =
51
+ if let Some ( extra) = extra { format ! ( r#", {extra}"# ) } else { String :: from ( "" ) } ;
52
+ let newline = "\n " ;
53
+
54
+ self . writeln_message ( & format ! (
55
+ r#"{{ "type": "{ty}", "name": "{name}", "event": "{event}"{exec_time_json}{stdout_json}{extra_json} }}{newline}"# ) )
59
56
}
60
57
}
61
58
62
59
impl < T : Write > OutputFormatter for JsonFormatter < T > {
63
60
fn write_discovery_start ( & mut self ) -> io:: Result < ( ) > {
64
- self . writeln_message ( & format ! ( r#"{{ "type": "suite", "event": "discovery" }}"# ) )
61
+ self . writeln_message ( concat ! ( r#"{ "type": "suite", "event": "discovery" }"# , " \n " ) )
65
62
}
66
63
67
64
fn write_test_discovered ( & mut self , desc : & TestDesc , test_type : & str ) -> io:: Result < ( ) > {
@@ -77,21 +74,24 @@ impl<T: Write> OutputFormatter for JsonFormatter<T> {
77
74
..
78
75
} = desc;
79
76
77
+ let name = EscapedString ( name. as_slice ( ) ) ;
78
+ let ignore_message = ignore_message. unwrap_or ( "" ) ;
79
+ let source_path = EscapedString ( source_file) ;
80
+ let newline = "\n " ;
81
+
80
82
self . writeln_message ( & format ! (
81
- r#"{{ "type": "{test_type}", "event": "discovered", "name": "{}", "ignore": {ignore}, "ignore_message": "{}", "source_path": "{}", "start_line": {start_line}, "start_col": {start_col}, "end_line": {end_line}, "end_col": {end_col} }}"# ,
82
- EscapedString ( name. as_slice( ) ) ,
83
- ignore_message. unwrap_or( "" ) ,
84
- EscapedString ( source_file) ,
83
+ r#"{{ "type": "{test_type}", "event": "discovered", "name": "{name}", "ignore": {ignore}, "ignore_message": "{ignore_message}", "source_path": "{source_path}", "start_line": {start_line}, "start_col": {start_col}, "end_line": {end_line}, "end_col": {end_col} }}{newline}"#
85
84
) )
86
85
}
87
86
88
87
fn write_discovery_finish ( & mut self , state : & ConsoleTestDiscoveryState ) -> io:: Result < ( ) > {
89
88
let ConsoleTestDiscoveryState { tests, benchmarks, ignored, .. } = state;
90
89
91
90
let total = tests + benchmarks;
91
+ let newline = "\n " ;
92
92
self . writeln_message ( & format ! (
93
- r#"{{ "type": "suite", "event": "completed", "tests": {tests}, "benchmarks": {benchmarks}, "total": {total}, "ignored": {ignored} }}"#
94
- ) )
93
+ r#"{{ "type": "suite", "event": "completed", "tests": {tests}, "benchmarks": {benchmarks}, "total": {total}, "ignored": {ignored} }}{newline} "#
94
+ ) )
95
95
}
96
96
97
97
fn write_run_start ( & mut self , test_count : usize , shuffle_seed : Option < u64 > ) -> io:: Result < ( ) > {
@@ -100,15 +100,17 @@ impl<T: Write> OutputFormatter for JsonFormatter<T> {
100
100
} else {
101
101
String :: new ( )
102
102
} ;
103
+ let newline = "\n " ;
103
104
self . writeln_message ( & format ! (
104
- r#"{{ "type": "suite", "event": "started", "test_count": {test_count}{shuffle_seed_json} }}"#
105
- ) )
105
+ r#"{{ "type": "suite", "event": "started", "test_count": {test_count}{shuffle_seed_json} }}{newline} "#
106
+ ) )
106
107
}
107
108
108
109
fn write_test_start ( & mut self , desc : & TestDesc ) -> io:: Result < ( ) > {
110
+ let name = EscapedString ( desc. name . as_slice ( ) ) ;
111
+ let newline = "\n " ;
109
112
self . writeln_message ( & format ! (
110
- r#"{{ "type": "test", "event": "started", "name": "{}" }}"# ,
111
- EscapedString ( desc. name. as_slice( ) )
113
+ r#"{{ "type": "test", "event": "started", "name": "{name}" }}{newline}"#
112
114
) )
113
115
}
114
116
@@ -173,53 +175,43 @@ impl<T: Write> OutputFormatter for JsonFormatter<T> {
173
175
} else {
174
176
format ! ( r#", "mib_per_second": {}"# , bs. mb_s)
175
177
} ;
178
+ let name = EscapedString ( desc. name . as_slice ( ) ) ;
176
179
177
- let line = format ! (
180
+ self . writeln_message ( & format ! (
178
181
"{{ \" type\" : \" bench\" , \
179
- \" name\" : \" {}\" , \
180
- \" median\" : {}, \
181
- \" deviation\" : {}{} }}",
182
- EscapedString ( desc. name. as_slice( ) ) ,
183
- median,
184
- deviation,
185
- mbps
186
- ) ;
187
-
188
- self . writeln_message ( & line)
182
+ \" name\" : \" {name}\" , \
183
+ \" median\" : {median}, \
184
+ \" deviation\" : {deviation}{mbps} }}\n ",
185
+ ) )
189
186
}
190
187
}
191
188
}
192
189
193
190
fn write_timeout ( & mut self , desc : & TestDesc ) -> io:: Result < ( ) > {
191
+ let name = EscapedString ( desc. name . as_slice ( ) ) ;
192
+ let newline = "\n " ;
194
193
self . writeln_message ( & format ! (
195
- r#"{{ "type": "test", "event": "timeout", "name": "{}" }}"# ,
196
- EscapedString ( desc. name. as_slice( ) )
194
+ r#"{{ "type": "test", "event": "timeout", "name": "{name}" }}{newline}"# ,
197
195
) )
198
196
}
199
197
200
198
fn write_run_finish ( & mut self , state : & ConsoleTestState ) -> io:: Result < bool > {
201
- self . write_message ( & format ! (
202
- "{{ \" type\" : \" suite\" , \
203
- \" event\" : \" {}\" , \
204
- \" passed\" : {}, \
205
- \" failed\" : {}, \
206
- \" ignored\" : {}, \
207
- \" measured\" : {}, \
208
- \" filtered_out\" : {}",
209
- if state. failed == 0 { "ok" } else { "failed" } ,
210
- state. passed,
211
- state. failed,
212
- state. ignored,
213
- state. measured,
214
- state. filtered_out,
215
- ) ) ?;
216
-
217
- if let Some ( ref exec_time) = state. exec_time {
218
- let time_str = format ! ( ", \" exec_time\" : {}" , exec_time. 0 . as_secs_f64( ) ) ;
219
- self . write_message ( & time_str) ?;
220
- }
199
+ let event = if state. failed == 0 { "ok" } else { "failed" } ;
200
+ let passed = state. passed ;
201
+ let failed = state. failed ;
202
+ let ignored = state. ignored ;
203
+ let measured = state. measured ;
204
+ let filtered_out = state. filtered_out ;
205
+ let exec_time_json = if let Some ( ref exec_time) = state. exec_time {
206
+ format ! ( r#", "exec_time": {}"# , exec_time. 0 . as_secs_f64( ) )
207
+ } else {
208
+ String :: from ( "" )
209
+ } ;
210
+ let newline = "\n " ;
221
211
222
- self . writeln_message ( " }" ) ?;
212
+ self . writeln_message ( & format ! (
213
+ r#"{{ "type": "suite", "event": "{event}", "passed": {passed}, "failed": {failed}, "ignored": {ignored}, "measured": {measured}, "filtered_out": {filtered_out}{exec_time_json} }}{newline}"#
214
+ ) ) ?;
223
215
224
216
Ok ( state. failed == 0 )
225
217
}
0 commit comments