Skip to content

Commit d2f3806

Browse files
committed
render compiletest output with render_tests
1 parent d7049ca commit d2f3806

File tree

3 files changed

+224
-5
lines changed

3 files changed

+224
-5
lines changed

src/bootstrap/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ mod format;
5555
mod install;
5656
mod metadata;
5757
mod native;
58+
mod render_tests;
5859
mod run;
5960
mod sanity;
6061
mod setup;

src/bootstrap/render_tests.rs

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
//! This module renders the JSON output of libtest into a human-readable form, trying to be as
2+
//! similar to libtest's native output as possible.
3+
//!
4+
//! This is needed because we need to use libtest in JSON mode to extract granluar information
5+
//! about the executed tests. Doing so suppresses the human-readable output, and (compared to Cargo
6+
//! and rustc) libtest doesn't include the rendered human-readable output as a JSON field. We had
7+
//! to reimplement all the rendering logic in this module because of that.
8+
9+
use crate::builder::Builder;
10+
use std::io::{BufRead, BufReader};
11+
use std::process::{ChildStdout, Command, Stdio};
12+
use std::time::Duration;
13+
14+
pub(crate) fn try_run_tests(builder: &Builder<'_>, cmd: &mut Command) -> bool {
15+
if builder.config.dry_run() {
16+
return true;
17+
}
18+
19+
if !run_tests(builder, cmd) {
20+
if builder.fail_fast {
21+
crate::detail_exit(1);
22+
} else {
23+
let mut failures = builder.delayed_failures.borrow_mut();
24+
failures.push(format!("{cmd:?}"));
25+
false
26+
}
27+
} else {
28+
true
29+
}
30+
}
31+
32+
fn run_tests(builder: &Builder<'_>, cmd: &mut Command) -> bool {
33+
cmd.stdout(Stdio::piped());
34+
35+
builder.verbose(&format!("running: {cmd:?}"));
36+
37+
let mut process = cmd.spawn().unwrap();
38+
let stdout = process.stdout.take().unwrap();
39+
let handle = std::thread::spawn(move || Renderer::new(stdout).render_all());
40+
41+
let result = process.wait().unwrap();
42+
handle.join().expect("test formatter thread failed");
43+
44+
if !result.success() && builder.is_verbose() {
45+
println!(
46+
"\n\ncommand did not execute successfully: {cmd:?}\n\
47+
expected success, got: {result}"
48+
);
49+
}
50+
51+
result.success()
52+
}
53+
54+
struct Renderer {
55+
stdout: BufReader<ChildStdout>,
56+
failures: Vec<TestOutcome>,
57+
}
58+
59+
impl Renderer {
60+
fn new(stdout: ChildStdout) -> Self {
61+
Self { stdout: BufReader::new(stdout), failures: Vec::new() }
62+
}
63+
64+
fn render_all(mut self) {
65+
let mut line = String::new();
66+
loop {
67+
line.clear();
68+
match self.stdout.read_line(&mut line) {
69+
Ok(_) => {}
70+
Err(err) if err.kind() == std::io::ErrorKind::UnexpectedEof => break,
71+
Err(err) => panic!("failed to read output of test runner: {err}"),
72+
}
73+
if line.is_empty() {
74+
break;
75+
}
76+
77+
self.render_message(match serde_json::from_str(&line) {
78+
Ok(parsed) => parsed,
79+
Err(err) => {
80+
panic!("failed to parse libtest json output; error: {err}, line: {line:?}");
81+
}
82+
});
83+
}
84+
}
85+
86+
fn render_test_outcome(&self, outcome: Outcome<'_>, test: &TestOutcome) {
87+
// TODO: add support for terse output
88+
self.render_test_outcome_verbose(outcome, test);
89+
}
90+
91+
fn render_test_outcome_verbose(&self, outcome: Outcome<'_>, test: &TestOutcome) {
92+
if let Some(exec_time) = test.exec_time {
93+
println!(
94+
"test {} ... {outcome} (in {:.2?})",
95+
test.name,
96+
Duration::from_secs_f64(exec_time)
97+
);
98+
} else {
99+
println!("test {} ... {outcome}", test.name);
100+
}
101+
}
102+
103+
fn render_suite_outcome(&self, outcome: Outcome<'_>, suite: &SuiteOutcome) {
104+
if !self.failures.is_empty() {
105+
println!("\nfailures:\n");
106+
for failure in &self.failures {
107+
if let Some(stdout) = &failure.stdout {
108+
println!("---- {} stdout ----", failure.name);
109+
println!("{stdout}");
110+
}
111+
}
112+
113+
println!("\nfailures:");
114+
for failure in &self.failures {
115+
println!(" {}", failure.name);
116+
}
117+
}
118+
119+
println!(
120+
"\ntest result: {outcome}. {} passed; {} failed; {} ignored; {} measured; \
121+
{} filtered out; finished in {:.2?}\n",
122+
suite.passed,
123+
suite.failed,
124+
suite.ignored,
125+
suite.measured,
126+
suite.filtered_out,
127+
Duration::from_secs_f64(suite.exec_time)
128+
);
129+
}
130+
131+
fn render_message(&mut self, message: Message) {
132+
match message {
133+
Message::Suite(SuiteMessage::Started { test_count }) => {
134+
println!("\nrunning {test_count} tests");
135+
}
136+
Message::Suite(SuiteMessage::Ok(outcome)) => {
137+
self.render_suite_outcome(Outcome::Ok, &outcome);
138+
}
139+
Message::Suite(SuiteMessage::Failed(outcome)) => {
140+
self.render_suite_outcome(Outcome::Failed, &outcome);
141+
}
142+
Message::Test(TestMessage::Ok(outcome)) => {
143+
self.render_test_outcome(Outcome::Ok, &outcome);
144+
}
145+
Message::Test(TestMessage::Ignored(outcome)) => {
146+
self.render_test_outcome(
147+
Outcome::Ignored { reason: outcome.reason.as_deref() },
148+
&outcome,
149+
);
150+
}
151+
Message::Test(TestMessage::Failed(outcome)) => {
152+
self.render_test_outcome(Outcome::Failed, &outcome);
153+
self.failures.push(outcome);
154+
}
155+
Message::Test(TestMessage::Started) => {} // Not useful
156+
Message::Test(TestMessage::Bench) => todo!("benchmarks are not supported yet"),
157+
}
158+
}
159+
}
160+
161+
enum Outcome<'a> {
162+
Ok,
163+
Failed,
164+
Ignored { reason: Option<&'a str> },
165+
}
166+
167+
impl std::fmt::Display for Outcome<'_> {
168+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169+
match self {
170+
Outcome::Ok => f.write_str("ok"),
171+
Outcome::Failed => f.write_str("FAILED"),
172+
Outcome::Ignored { reason: None } => f.write_str("ignored"),
173+
Outcome::Ignored { reason: Some(reason) } => write!(f, "ignored, {reason}"),
174+
}
175+
}
176+
}
177+
178+
#[derive(serde_derive::Deserialize)]
179+
#[serde(tag = "type", rename_all = "snake_case")]
180+
enum Message {
181+
Suite(SuiteMessage),
182+
Test(TestMessage),
183+
}
184+
185+
#[derive(serde_derive::Deserialize)]
186+
#[serde(tag = "event", rename_all = "snake_case")]
187+
enum SuiteMessage {
188+
Ok(SuiteOutcome),
189+
Failed(SuiteOutcome),
190+
Started { test_count: usize },
191+
}
192+
193+
#[derive(serde_derive::Deserialize)]
194+
struct SuiteOutcome {
195+
passed: usize,
196+
failed: usize,
197+
ignored: usize,
198+
measured: usize,
199+
filtered_out: usize,
200+
exec_time: f64,
201+
}
202+
203+
#[derive(serde_derive::Deserialize)]
204+
#[serde(tag = "event", rename_all = "snake_case")]
205+
enum TestMessage {
206+
Ok(TestOutcome),
207+
Failed(TestOutcome),
208+
Ignored(TestOutcome),
209+
// Ignored messages:
210+
Bench,
211+
Started,
212+
}
213+
214+
#[derive(serde_derive::Deserialize)]
215+
struct TestOutcome {
216+
name: String,
217+
exec_time: Option<f64>,
218+
stdout: Option<String>,
219+
reason: Option<String>,
220+
}

src/bootstrap/test.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1616,9 +1616,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the
16161616
cmd.arg("--verbose");
16171617
}
16181618

1619-
if !builder.config.verbose_tests {
1620-
cmd.arg("--quiet");
1621-
}
1619+
cmd.arg("--json");
16221620

16231621
let mut llvm_components_passed = false;
16241622
let mut copts_passed = false;
@@ -1769,7 +1767,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the
17691767
suite, mode, &compiler.host, target
17701768
));
17711769
let _time = util::timeit(&builder);
1772-
try_run(builder, &mut cmd);
1770+
crate::render_tests::try_run_tests(builder, &mut cmd);
17731771

17741772
if let Some(compare_mode) = compare_mode {
17751773
cmd.arg("--compare-mode").arg(compare_mode);
@@ -1778,7 +1776,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the
17781776
suite, mode, compare_mode, &compiler.host, target
17791777
));
17801778
let _time = util::timeit(&builder);
1781-
try_run(builder, &mut cmd);
1779+
crate::render_tests::try_run_tests(builder, &mut cmd);
17821780
}
17831781
}
17841782
}

0 commit comments

Comments
 (0)