]> git.lizzy.rs Git - rust.git/blob - library/test/src/formatters/terse.rs
Rollup merge of #92383 - lancethepants:armv7-unknown-linux-uclibceabi, r=nagisa
[rust.git] / library / test / src / formatters / terse.rs
1 use std::{io, io::prelude::Write};
2
3 use super::OutputFormatter;
4 use crate::{
5     bench::fmt_bench_samples,
6     console::{ConsoleTestState, OutputLocation},
7     term,
8     test_result::TestResult,
9     time,
10     types::NamePadding,
11     types::TestDesc,
12 };
13
14 // insert a '\n' after 100 tests in quiet mode
15 const QUIET_MODE_MAX_COLUMN: usize = 100;
16
17 pub(crate) struct TerseFormatter<T> {
18     out: OutputLocation<T>,
19     use_color: bool,
20     is_multithreaded: bool,
21     /// Number of columns to fill when aligning names
22     max_name_len: usize,
23
24     test_count: usize,
25     total_test_count: usize,
26 }
27
28 impl<T: Write> TerseFormatter<T> {
29     pub fn new(
30         out: OutputLocation<T>,
31         use_color: bool,
32         max_name_len: usize,
33         is_multithreaded: bool,
34     ) -> Self {
35         TerseFormatter {
36             out,
37             use_color,
38             max_name_len,
39             is_multithreaded,
40             test_count: 0,
41             total_test_count: 0, // initialized later, when write_run_start is called
42         }
43     }
44
45     pub fn write_ok(&mut self) -> io::Result<()> {
46         self.write_short_result(".", term::color::GREEN)
47     }
48
49     pub fn write_failed(&mut self) -> io::Result<()> {
50         self.write_short_result("F", term::color::RED)
51     }
52
53     pub fn write_ignored(&mut self) -> io::Result<()> {
54         self.write_short_result("i", term::color::YELLOW)
55     }
56
57     pub fn write_allowed_fail(&mut self) -> io::Result<()> {
58         self.write_short_result("a", term::color::YELLOW)
59     }
60
61     pub fn write_bench(&mut self) -> io::Result<()> {
62         self.write_pretty("bench", term::color::CYAN)
63     }
64
65     pub fn write_short_result(
66         &mut self,
67         result: &str,
68         color: term::color::Color,
69     ) -> io::Result<()> {
70         self.write_pretty(result, color)?;
71         if self.test_count % QUIET_MODE_MAX_COLUMN == QUIET_MODE_MAX_COLUMN - 1 {
72             // we insert a new line every 100 dots in order to flush the
73             // screen when dealing with line-buffered output (e.g., piping to
74             // `stamp` in the rust CI).
75             let out = format!(" {}/{}\n", self.test_count + 1, self.total_test_count);
76             self.write_plain(&out)?;
77         }
78
79         self.test_count += 1;
80         Ok(())
81     }
82
83     pub fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> {
84         match self.out {
85             OutputLocation::Pretty(ref mut term) => {
86                 if self.use_color {
87                     term.fg(color)?;
88                 }
89                 term.write_all(word.as_bytes())?;
90                 if self.use_color {
91                     term.reset()?;
92                 }
93                 term.flush()
94             }
95             OutputLocation::Raw(ref mut stdout) => {
96                 stdout.write_all(word.as_bytes())?;
97                 stdout.flush()
98             }
99         }
100     }
101
102     pub fn write_plain<S: AsRef<str>>(&mut self, s: S) -> io::Result<()> {
103         let s = s.as_ref();
104         self.out.write_all(s.as_bytes())?;
105         self.out.flush()
106     }
107
108     pub fn write_outputs(&mut self, state: &ConsoleTestState) -> io::Result<()> {
109         self.write_plain("\nsuccesses:\n")?;
110         let mut successes = Vec::new();
111         let mut stdouts = String::new();
112         for &(ref f, ref stdout) in &state.not_failures {
113             successes.push(f.name.to_string());
114             if !stdout.is_empty() {
115                 stdouts.push_str(&format!("---- {} stdout ----\n", f.name));
116                 let output = String::from_utf8_lossy(stdout);
117                 stdouts.push_str(&output);
118                 stdouts.push('\n');
119             }
120         }
121         if !stdouts.is_empty() {
122             self.write_plain("\n")?;
123             self.write_plain(&stdouts)?;
124         }
125
126         self.write_plain("\nsuccesses:\n")?;
127         successes.sort();
128         for name in &successes {
129             self.write_plain(&format!("    {}\n", name))?;
130         }
131         Ok(())
132     }
133
134     pub fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> {
135         self.write_plain("\nfailures:\n")?;
136         let mut failures = Vec::new();
137         let mut fail_out = String::new();
138         for &(ref f, ref stdout) in &state.failures {
139             failures.push(f.name.to_string());
140             if !stdout.is_empty() {
141                 fail_out.push_str(&format!("---- {} stdout ----\n", f.name));
142                 let output = String::from_utf8_lossy(stdout);
143                 fail_out.push_str(&output);
144                 fail_out.push('\n');
145             }
146         }
147         if !fail_out.is_empty() {
148             self.write_plain("\n")?;
149             self.write_plain(&fail_out)?;
150         }
151
152         self.write_plain("\nfailures:\n")?;
153         failures.sort();
154         for name in &failures {
155             self.write_plain(&format!("    {}\n", name))?;
156         }
157         Ok(())
158     }
159
160     fn write_test_name(&mut self, desc: &TestDesc) -> io::Result<()> {
161         let name = desc.padded_name(self.max_name_len, desc.name.padding());
162         if let Some(test_mode) = desc.test_mode() {
163             self.write_plain(&format!("test {} - {} ... ", name, test_mode))?;
164         } else {
165             self.write_plain(&format!("test {} ... ", name))?;
166         }
167
168         Ok(())
169     }
170 }
171
172 impl<T: Write> OutputFormatter for TerseFormatter<T> {
173     fn write_run_start(&mut self, test_count: usize, shuffle_seed: Option<u64>) -> io::Result<()> {
174         self.total_test_count = test_count;
175         let noun = if test_count != 1 { "tests" } else { "test" };
176         let shuffle_seed_msg = if let Some(shuffle_seed) = shuffle_seed {
177             format!(" (shuffle seed: {})", shuffle_seed)
178         } else {
179             String::new()
180         };
181         self.write_plain(&format!("\nrunning {} {}{}\n", test_count, noun, shuffle_seed_msg))
182     }
183
184     fn write_test_start(&mut self, desc: &TestDesc) -> io::Result<()> {
185         // Remnants from old libtest code that used the padding value
186         // in order to indicate benchmarks.
187         // When running benchmarks, terse-mode should still print their name as if
188         // it is the Pretty formatter.
189         if !self.is_multithreaded && desc.name.padding() == NamePadding::PadOnRight {
190             self.write_test_name(desc)?;
191         }
192
193         Ok(())
194     }
195
196     fn write_result(
197         &mut self,
198         desc: &TestDesc,
199         result: &TestResult,
200         _: Option<&time::TestExecTime>,
201         _: &[u8],
202         _: &ConsoleTestState,
203     ) -> io::Result<()> {
204         match *result {
205             TestResult::TrOk => self.write_ok(),
206             TestResult::TrFailed | TestResult::TrFailedMsg(_) | TestResult::TrTimedFail => {
207                 self.write_failed()
208             }
209             TestResult::TrIgnored => self.write_ignored(),
210             TestResult::TrAllowedFail => self.write_allowed_fail(),
211             TestResult::TrBench(ref bs) => {
212                 if self.is_multithreaded {
213                     self.write_test_name(desc)?;
214                 }
215                 self.write_bench()?;
216                 self.write_plain(&format!(": {}\n", fmt_bench_samples(bs)))
217             }
218         }
219     }
220
221     fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()> {
222         self.write_plain(&format!(
223             "test {} has been running for over {} seconds\n",
224             desc.name,
225             time::TEST_WARN_TIMEOUT_S
226         ))
227     }
228
229     fn write_run_finish(&mut self, state: &ConsoleTestState) -> io::Result<bool> {
230         if state.options.display_output {
231             self.write_outputs(state)?;
232         }
233         let success = state.failed == 0;
234         if !success {
235             self.write_failures(state)?;
236         }
237
238         self.write_plain("\ntest result: ")?;
239
240         if success {
241             // There's no parallelism at this point so it's safe to use color
242             self.write_pretty("ok", term::color::GREEN)?;
243         } else {
244             self.write_pretty("FAILED", term::color::RED)?;
245         }
246
247         let s = if state.allowed_fail > 0 {
248             format!(
249                 ". {} passed; {} failed ({} allowed); {} ignored; {} measured; {} filtered out",
250                 state.passed,
251                 state.failed + state.allowed_fail,
252                 state.allowed_fail,
253                 state.ignored,
254                 state.measured,
255                 state.filtered_out
256             )
257         } else {
258             format!(
259                 ". {} passed; {} failed; {} ignored; {} measured; {} filtered out",
260                 state.passed, state.failed, state.ignored, state.measured, state.filtered_out
261             )
262         };
263
264         self.write_plain(&s)?;
265
266         if let Some(ref exec_time) = state.exec_time {
267             let time_str = format!("; finished in {}", exec_time);
268             self.write_plain(&time_str)?;
269         }
270
271         self.write_plain("\n\n")?;
272
273         Ok(success)
274     }
275 }