Skip to content

Commit d57b2da

Browse files
de-vri-esjakoschiko
authored andcommitted
Pretty-print assertion failures in test with unstable flag.
1 parent 0994b8a commit d57b2da

File tree

6 files changed

+150
-0
lines changed

6 files changed

+150
-0
lines changed

library/test/src/cli.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub struct TestOpts {
2323
pub format: OutputFormat,
2424
pub shuffle: bool,
2525
pub shuffle_seed: Option<u64>,
26+
pub pretty_print_assertions: bool,
2627
pub test_threads: Option<usize>,
2728
pub skip: Vec<String>,
2829
pub time_options: Option<TestTimeOptions>,
@@ -147,6 +148,11 @@ fn optgroups() -> getopts::Options {
147148
"shuffle-seed",
148149
"Run tests in random order; seed the random number generator with SEED",
149150
"SEED",
151+
)
152+
.optflag(
153+
"",
154+
"pretty-print-assertions",
155+
"Pretty-print assertion failures using experimental formatting.",
150156
);
151157
opts
152158
}
@@ -276,6 +282,8 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes {
276282
let test_threads = get_test_threads(&matches)?;
277283
let color = get_color_config(&matches)?;
278284
let format = get_format(&matches, quiet, allow_unstable)?;
285+
let pretty_print_assertions =
286+
unstable_optflag!(matches, allow_unstable, "pretty-print-assertions");
279287

280288
let options = Options::new().display_output(matches.opt_present("show-output"));
281289

@@ -294,6 +302,7 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes {
294302
format,
295303
shuffle,
296304
shuffle_seed,
305+
pretty_print_assertions,
297306
test_threads,
298307
skip,
299308
time_options,

library/test/src/console.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use super::{
1313
formatters::{JsonFormatter, JunitFormatter, OutputFormatter, PrettyFormatter, TerseFormatter},
1414
helpers::{concurrency::get_concurrency, metrics::MetricMap},
1515
options::{Options, OutputFormat},
16+
pretty_print_assertion::pretty_print_assertion,
1617
run_tests, term,
1718
test_result::TestResult,
1819
time::{TestExecTime, TestSuiteExecTime},
@@ -25,6 +26,16 @@ pub enum OutputLocation<T> {
2526
Raw(T),
2627
}
2728

29+
impl<T> OutputLocation<T> {
30+
/// Check if the output location supports ANSI color codes.
31+
pub fn supports_ansi_colors(&self) -> bool {
32+
match self {
33+
OutputLocation::Pretty(term) => term.supports_ansi_colors(),
34+
OutputLocation::Raw(_) => false,
35+
}
36+
}
37+
}
38+
2839
impl<T: Write> Write for OutputLocation<T> {
2940
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
3041
match *self {
@@ -251,12 +262,27 @@ fn on_test_event(
251262

252263
/// A simple console test runner.
253264
/// Runs provided tests reporting process and results to the stdout.
265+
///
266+
/// If pretty-printing of assertion failures is enabled, this installs a panic hook.
254267
pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Result<bool> {
255268
let output = match term::stdout() {
256269
None => OutputLocation::Raw(io::stdout()),
257270
Some(t) => OutputLocation::Pretty(t),
258271
};
259272

273+
if opts.pretty_print_assertions {
274+
let panic_hook = std::panic::take_hook();
275+
let use_color = opts.use_color() && output.supports_ansi_colors();
276+
std::panic::set_hook(Box::new(move |info| {
277+
if let Some(assert) = info.assert_info() {
278+
let loc = *info.location().unwrap();
279+
let _ = pretty_print_assertion(assert, loc, use_color);
280+
} else {
281+
panic_hook(info)
282+
}
283+
}));
284+
}
285+
260286
let max_name_len = tests
261287
.iter()
262288
.max_by_key(|t| len_if_padded(*t))

library/test/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@
2323
#![feature(libc)]
2424
#![feature(rustc_private)]
2525
#![feature(nll)]
26+
#![feature(core_panic)]
2627
#![feature(available_parallelism)]
2728
#![feature(bench_black_box)]
2829
#![feature(internal_output_capture)]
2930
#![feature(panic_unwind)]
31+
#![feature(panic_internals)]
3032
#![feature(staged_api)]
3133
#![feature(termination_trait_lib)]
3234
#![feature(test)]
@@ -79,6 +81,7 @@ mod event;
7981
mod formatters;
8082
mod helpers;
8183
mod options;
84+
mod pretty_print_assertion;
8285
pub mod stats;
8386
mod term;
8487
mod test_result;
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
use core::panic::assert_info::{AssertInfo, Assertion, BinaryAssertion, BoolAssertion};
2+
use core::panic::Location;
3+
4+
const RED: &str = "\x1b[31m";
5+
const YELLOW: &str = "\x1b[33m";
6+
const BLUE: &str = "\x1b[34m";
7+
const CYAN: &str = "\x1b[36m";
8+
const BOLD: &str = "\x1b[1m";
9+
const RESET: &str = "\x1b[0m";
10+
11+
/// Print an assertion to standard error.
12+
///
13+
/// If `ansi_colors` is true, this function unconditionally prints ANSI color codes.
14+
/// It should only be set to true only if it is known that the terminal supports it.
15+
pub fn pretty_print_assertion(assert: &AssertInfo<'_>, loc: Location<'_>, ansi_colors: bool) {
16+
if ansi_colors {
17+
print_pretty_header(loc);
18+
match &assert.assertion {
19+
Assertion::Bool(assert) => print_pretty_bool_assertion(assert),
20+
Assertion::Binary(assert) => print_pretty_binary_assertion(assert),
21+
}
22+
if let Some(msg) = &assert.message {
23+
print_pretty_message(msg);
24+
}
25+
} else {
26+
print_plain_header(loc);
27+
match &assert.assertion {
28+
Assertion::Bool(assert) => print_plain_bool_assertion(assert),
29+
Assertion::Binary(assert) => print_plain_binary_assertion(assert),
30+
}
31+
if let Some(msg) = &assert.message {
32+
print_plain_message(msg);
33+
}
34+
}
35+
}
36+
37+
fn print_plain_header(loc: Location<'_>) {
38+
eprintln!("Assertion failed at {}:{}:{}:", loc.file(), loc.line(), loc.column())
39+
}
40+
41+
fn print_pretty_header(loc: Location<'_>) {
42+
eprintln!(
43+
"{bold}{red}Assertion failed{reset} at {bold}{file}{reset}:{line}:{col}:",
44+
red = RED,
45+
bold = BOLD,
46+
reset = RESET,
47+
file = loc.file(),
48+
line = loc.line(),
49+
col = loc.column(),
50+
);
51+
}
52+
53+
fn print_plain_bool_assertion(assert: &BoolAssertion) {
54+
eprintln!("Assertion:\n {}", assert.expr);
55+
eprintln!("Expansion:\n false");
56+
}
57+
58+
fn print_pretty_bool_assertion(assert: &BoolAssertion) {
59+
eprintln!(
60+
"{bold}Assertion:{reset}\n {cyan}{expr}{reset}",
61+
cyan = CYAN,
62+
reset = RESET,
63+
bold = BOLD,
64+
expr = assert.expr,
65+
);
66+
eprintln!(
67+
"{bold}Expansion:{reset}\n {cyan}false{reset}",
68+
cyan = CYAN,
69+
bold = BOLD,
70+
reset = RESET,
71+
);
72+
}
73+
74+
fn print_plain_binary_assertion(assert: &BinaryAssertion<'_>) {
75+
eprintln!("Assertion:\n {} {} {}", assert.left_expr, assert.op, assert.right_expr);
76+
eprintln!("Expansion:\n {:?} {} {:?}", assert.left_val, assert.op, assert.right_val);
77+
}
78+
79+
fn print_pretty_binary_assertion(assert: &BinaryAssertion<'_>) {
80+
eprintln!(
81+
"{bold}Assertion:{reset}\n {cyan}{left} {bold}{blue}{op}{reset} {yellow}{right}{reset}",
82+
cyan = CYAN,
83+
blue = BLUE,
84+
yellow = YELLOW,
85+
bold = BOLD,
86+
reset = RESET,
87+
left = assert.left_expr,
88+
op = assert.op,
89+
right = assert.right_expr,
90+
);
91+
eprintln!(
92+
"{bold}Expansion:{reset}\n {cyan}{left:?} {bold}{blue}{op}{reset} {yellow}{right:?}{reset}",
93+
cyan = CYAN,
94+
blue = BLUE,
95+
yellow = YELLOW,
96+
bold = BOLD,
97+
reset = RESET,
98+
left = assert.left_val,
99+
op = assert.op,
100+
right = assert.right_val,
101+
);
102+
}
103+
104+
fn print_plain_message(message: &std::fmt::Arguments<'_>) {
105+
eprintln!("Message:\n {}", message);
106+
}
107+
108+
fn print_pretty_message(message: &std::fmt::Arguments<'_>) {
109+
eprintln!("{bold}Message:{reset}\n {msg}", bold = BOLD, reset = RESET, msg = message,);
110+
}

library/test/src/tests.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ impl TestOpts {
4747
format: OutputFormat::Pretty,
4848
shuffle: false,
4949
shuffle_seed: None,
50+
pretty_print_assertions: false,
5051
test_threads: None,
5152
skip: vec![],
5253
time_options: None,

src/tools/compiletest/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,7 @@ pub fn test_opts(config: &Config) -> test::TestOpts {
508508
color: config.color,
509509
shuffle: false,
510510
shuffle_seed: None,
511+
pretty_print_assertions: false,
511512
test_threads: None,
512513
skip: vec![],
513514
list: false,

0 commit comments

Comments
 (0)