]> git.proxmox.com Git - rustc.git/blob - src/libtest/formatters/json.rs
New upstream version 1.39.0+dfsg1
[rustc.git] / src / libtest / formatters / json.rs
1 use super::*;
2
3 pub(crate) struct JsonFormatter<T> {
4 out: OutputLocation<T>,
5 }
6
7 impl<T: Write> JsonFormatter<T> {
8 pub fn new(out: OutputLocation<T>) -> Self {
9 Self { out }
10 }
11
12 fn writeln_message(&mut self, s: &str) -> io::Result<()> {
13 assert!(!s.contains('\n'));
14
15 self.out.write_all(s.as_ref())?;
16 self.out.write_all(b"\n")
17 }
18
19 fn write_message(&mut self, s: &str) -> io::Result<()> {
20 assert!(!s.contains('\n'));
21
22 self.out.write_all(s.as_ref())
23 }
24
25 fn write_event(
26 &mut self,
27 ty: &str,
28 name: &str,
29 evt: &str,
30 stdout: Option<Cow<'_, str>>,
31 extra: Option<&str>,
32 ) -> io::Result<()> {
33 self.write_message(&*format!(
34 r#"{{ "type": "{}", "name": "{}", "event": "{}""#,
35 ty, name, evt
36 ))?;
37 if let Some(stdout) = stdout {
38 self.write_message(&*format!(
39 r#", "stdout": "{}""#,
40 EscapedString(stdout)
41 ))?;
42 }
43 if let Some(extra) = extra {
44 self.write_message(&*format!(
45 r#", {}"#,
46 extra
47 ))?;
48 }
49 self.writeln_message(" }")
50 }
51 }
52
53 impl<T: Write> OutputFormatter for JsonFormatter<T> {
54 fn write_run_start(&mut self, test_count: usize) -> io::Result<()> {
55 self.writeln_message(&*format!(
56 r#"{{ "type": "suite", "event": "started", "test_count": {} }}"#,
57 test_count
58 ))
59 }
60
61 fn write_test_start(&mut self, desc: &TestDesc) -> io::Result<()> {
62 self.writeln_message(&*format!(
63 r#"{{ "type": "test", "event": "started", "name": "{}" }}"#,
64 desc.name
65 ))
66 }
67
68 fn write_result(
69 &mut self,
70 desc: &TestDesc,
71 result: &TestResult,
72 stdout: &[u8],
73 state: &ConsoleTestState,
74 ) -> io::Result<()> {
75 let stdout = if (state.options.display_output || *result != TrOk) && stdout.len() > 0 {
76 Some(String::from_utf8_lossy(stdout))
77 } else {
78 None
79 };
80 match *result {
81 TrOk => self.write_event("test", desc.name.as_slice(), "ok", stdout, None),
82
83 TrFailed => self.write_event("test", desc.name.as_slice(), "failed", stdout, None),
84
85 TrFailedMsg(ref m) => self.write_event(
86 "test",
87 desc.name.as_slice(),
88 "failed",
89 stdout,
90 Some(&*format!(r#""message": "{}""#, EscapedString(m))),
91 ),
92
93 TrIgnored => self.write_event("test", desc.name.as_slice(), "ignored", stdout, None),
94
95 TrAllowedFail => {
96 self.write_event("test", desc.name.as_slice(), "allowed_failure", stdout, None)
97 }
98
99 TrBench(ref bs) => {
100 let median = bs.ns_iter_summ.median as usize;
101 let deviation = (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as usize;
102
103 let mbps = if bs.mb_s == 0 {
104 String::new()
105 } else {
106 format!(r#", "mib_per_second": {}"#, bs.mb_s)
107 };
108
109 let line = format!(
110 "{{ \"type\": \"bench\", \
111 \"name\": \"{}\", \
112 \"median\": {}, \
113 \"deviation\": {}{} }}",
114 desc.name, median, deviation, mbps
115 );
116
117 self.writeln_message(&*line)
118 }
119 }
120 }
121
122 fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()> {
123 self.writeln_message(&*format!(
124 r#"{{ "type": "test", "event": "timeout", "name": "{}" }}"#,
125 desc.name
126 ))
127 }
128
129 fn write_run_finish(&mut self, state: &ConsoleTestState) -> io::Result<bool> {
130 self.writeln_message(&*format!(
131 "{{ \"type\": \"suite\", \
132 \"event\": \"{}\", \
133 \"passed\": {}, \
134 \"failed\": {}, \
135 \"allowed_fail\": {}, \
136 \"ignored\": {}, \
137 \"measured\": {}, \
138 \"filtered_out\": {} }}",
139 if state.failed == 0 { "ok" } else { "failed" },
140 state.passed,
141 state.failed + state.allowed_fail,
142 state.allowed_fail,
143 state.ignored,
144 state.measured,
145 state.filtered_out
146 ))?;
147
148 Ok(state.failed == 0)
149 }
150 }
151
152 /// A formatting utility used to print strings with characters in need of escaping.
153 /// Base code taken form `libserialize::json::escape_str`
154 struct EscapedString<S: AsRef<str>>(S);
155
156 impl<S: AsRef<str>> ::std::fmt::Display for EscapedString<S> {
157 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
158 let mut start = 0;
159
160 for (i, byte) in self.0.as_ref().bytes().enumerate() {
161 let escaped = match byte {
162 b'"' => "\\\"",
163 b'\\' => "\\\\",
164 b'\x00' => "\\u0000",
165 b'\x01' => "\\u0001",
166 b'\x02' => "\\u0002",
167 b'\x03' => "\\u0003",
168 b'\x04' => "\\u0004",
169 b'\x05' => "\\u0005",
170 b'\x06' => "\\u0006",
171 b'\x07' => "\\u0007",
172 b'\x08' => "\\b",
173 b'\t' => "\\t",
174 b'\n' => "\\n",
175 b'\x0b' => "\\u000b",
176 b'\x0c' => "\\f",
177 b'\r' => "\\r",
178 b'\x0e' => "\\u000e",
179 b'\x0f' => "\\u000f",
180 b'\x10' => "\\u0010",
181 b'\x11' => "\\u0011",
182 b'\x12' => "\\u0012",
183 b'\x13' => "\\u0013",
184 b'\x14' => "\\u0014",
185 b'\x15' => "\\u0015",
186 b'\x16' => "\\u0016",
187 b'\x17' => "\\u0017",
188 b'\x18' => "\\u0018",
189 b'\x19' => "\\u0019",
190 b'\x1a' => "\\u001a",
191 b'\x1b' => "\\u001b",
192 b'\x1c' => "\\u001c",
193 b'\x1d' => "\\u001d",
194 b'\x1e' => "\\u001e",
195 b'\x1f' => "\\u001f",
196 b'\x7f' => "\\u007f",
197 _ => {
198 continue;
199 }
200 };
201
202 if start < i {
203 f.write_str(&self.0.as_ref()[start..i])?;
204 }
205
206 f.write_str(escaped)?;
207
208 start = i + 1;
209 }
210
211 if start != self.0.as_ref().len() {
212 f.write_str(&self.0.as_ref()[start..])?;
213 }
214
215 Ok(())
216 }
217 }