Skip to content

Commit 266f26d

Browse files
author
Gilad Naaman
committed
libtest: Better JSON escaping
1 parent 47f7ff1 commit 266f26d

File tree

2 files changed

+83
-18
lines changed

2 files changed

+83
-18
lines changed

src/libtest/formatters.rs

+82-17
Original file line numberDiff line numberDiff line change
@@ -261,10 +261,6 @@ impl<T: Write> JsonFormatter<T> {
261261
}
262262
}
263263

264-
fn naive_json_escape(input: &str) -> String {
265-
input.replace("\\", "\\\\").replace("\"", "\\\"")
266-
}
267-
268264
impl<T: Write> OutputFormatter for JsonFormatter<T> {
269265
fn write_run_start(&mut self, len: usize) -> io::Result<()> {
270266
self.write_str(
@@ -294,7 +290,7 @@ impl<T: Write> OutputFormatter for JsonFormatter<T> {
294290
TrFailedMsg(ref m) => {
295291
format!(r#"{{ "type": "test", "event": "failed", "name": "{}", "message": "{}" }}"#,
296292
desc.name,
297-
naive_json_escape(m))
293+
EscapedString(m))
298294
},
299295

300296
TrIgnored => {
@@ -307,16 +303,22 @@ impl<T: Write> OutputFormatter for JsonFormatter<T> {
307303
desc.name)
308304
},
309305

310-
TrMetrics(ref mm) => {
311-
format!(r#"{{ "type": "metrics", "name": "{}", "metrics": "{}" }}"#,
312-
desc.name,
313-
mm.fmt_metrics())
314-
},
315-
316306
TrBench(ref bs) => {
317-
format!(r#"{{ "type": "bench", "name": "{}", "bench": "{}" }}"#,
307+
let median = bs.ns_iter_summ.median as usize;
308+
let deviation = (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as usize;
309+
310+
let mbps = if bs.mb_s == 0 {
311+
"".into()
312+
}
313+
else {
314+
format!(r#", "mib_per_second": {}"#, bs.mb_s)
315+
};
316+
317+
format!(r#"{{ "type": "bench", "name": "{}", "median": {}, "deviation": {}{} }}"#,
318318
desc.name,
319-
fmt_bench_samples(bs))
319+
median,
320+
deviation,
321+
mbps)
320322
},
321323
};
322324

@@ -330,7 +332,7 @@ impl<T: Write> OutputFormatter for JsonFormatter<T> {
330332

331333
fn write_run_finish(&mut self, state: &ConsoleTestState) -> io::Result<bool> {
332334

333-
self.write_str(&*format!("{{ \"type\": \"suite\",\
335+
self.write_str(&*format!("{{ \"type\": \"suite\", \
334336
\"event\": \"{}\", \
335337
\"passed\": {}, \
336338
\"failed\": {}, \
@@ -348,15 +350,78 @@ impl<T: Write> OutputFormatter for JsonFormatter<T> {
348350

349351
for &(ref f, ref stdout) in &state.failures {
350352
if !stdout.is_empty() {
351-
let output = naive_json_escape(&*String::from_utf8_lossy(stdout));
352-
353353
self.write_str(
354354
&*format!(r#"{{ "type": "test_output", "name": "{}", "output": "{}" }}"#,
355355
f.name,
356-
output))?;
356+
EscapedString(&*String::from_utf8_lossy(stdout))))?;
357357
}
358358
}
359359

360360
Ok(state.failed == 0)
361361
}
362362
}
363+
364+
/// A formatting utility used to print strings with characters in need of escaping.
365+
/// Base code taken form `libserialize::json::escape_str`
366+
struct EscapedString<'a>(&'a str);
367+
368+
impl<'a> ::std::fmt::Display for EscapedString<'a> {
369+
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
370+
let mut start = 0;
371+
372+
for (i, byte) in self.0.bytes().enumerate() {
373+
let escaped = match byte {
374+
b'"' => "\\\"",
375+
b'\\' => "\\\\",
376+
b'\x00' => "\\u0000",
377+
b'\x01' => "\\u0001",
378+
b'\x02' => "\\u0002",
379+
b'\x03' => "\\u0003",
380+
b'\x04' => "\\u0004",
381+
b'\x05' => "\\u0005",
382+
b'\x06' => "\\u0006",
383+
b'\x07' => "\\u0007",
384+
b'\x08' => "\\b",
385+
b'\t' => "\\t",
386+
b'\n' => "\\n",
387+
b'\x0b' => "\\u000b",
388+
b'\x0c' => "\\f",
389+
b'\r' => "\\r",
390+
b'\x0e' => "\\u000e",
391+
b'\x0f' => "\\u000f",
392+
b'\x10' => "\\u0010",
393+
b'\x11' => "\\u0011",
394+
b'\x12' => "\\u0012",
395+
b'\x13' => "\\u0013",
396+
b'\x14' => "\\u0014",
397+
b'\x15' => "\\u0015",
398+
b'\x16' => "\\u0016",
399+
b'\x17' => "\\u0017",
400+
b'\x18' => "\\u0018",
401+
b'\x19' => "\\u0019",
402+
b'\x1a' => "\\u001a",
403+
b'\x1b' => "\\u001b",
404+
b'\x1c' => "\\u001c",
405+
b'\x1d' => "\\u001d",
406+
b'\x1e' => "\\u001e",
407+
b'\x1f' => "\\u001f",
408+
b'\x7f' => "\\u007f",
409+
_ => { continue; }
410+
};
411+
412+
if start < i {
413+
f.write_str(&self.0[start..i])?;
414+
}
415+
416+
f.write_str(escaped)?;
417+
418+
start = i + 1;
419+
}
420+
421+
if start != self.0.len() {
422+
f.write_str(&self.0[start..])?;
423+
}
424+
425+
Ok(())
426+
}
427+
}

src/libtest/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,7 @@ pub fn list_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Res
713713
if ntest != 0 || nbench != 0 {
714714
out.write_plain("\n")?;
715715
}
716-
st.write_plain(format!("{}, {}\n",
716+
out.write_plain(format!("{}, {}\n",
717717
plural(ntest, "test"),
718718
plural(nbench, "benchmark")))?;
719719
}

0 commit comments

Comments
 (0)