]> git.lizzy.rs Git - rust.git/blob - src/libtest/formatters/pretty.rs
Rollup merge of #64691 - estebank:unexpected-variant, r=Centril
[rust.git] / src / libtest / formatters / pretty.rs
1 use super::*;
2
3 pub(crate) struct PrettyFormatter<T> {
4     out: OutputLocation<T>,
5     use_color: bool,
6
7     /// Number of columns to fill when aligning names
8     max_name_len: usize,
9
10     is_multithreaded: bool,
11 }
12
13 impl<T: Write> PrettyFormatter<T> {
14     pub fn new(
15         out: OutputLocation<T>,
16         use_color: bool,
17         max_name_len: usize,
18         is_multithreaded: bool,
19     ) -> Self {
20         PrettyFormatter {
21             out,
22             use_color,
23             max_name_len,
24             is_multithreaded,
25         }
26     }
27
28     #[cfg(test)]
29     pub fn output_location(&self) -> &OutputLocation<T> {
30         &self.out
31     }
32
33     pub fn write_ok(&mut self, exec_time: Option<&TestExecTime>) -> io::Result<()> {
34         self.write_short_result("ok", term::color::GREEN, exec_time)
35     }
36
37     pub fn write_failed(&mut self, exec_time: Option<&TestExecTime>) -> io::Result<()> {
38         self.write_short_result("FAILED", term::color::RED, exec_time)
39     }
40
41     pub fn write_ignored(&mut self, exec_time: Option<&TestExecTime>) -> io::Result<()> {
42         self.write_short_result("ignored", term::color::YELLOW, exec_time)
43     }
44
45     pub fn write_allowed_fail(&mut self, exec_time: Option<&TestExecTime>) -> io::Result<()> {
46         self.write_short_result("FAILED (allowed)", term::color::YELLOW, exec_time)
47     }
48
49     pub fn write_bench(&mut self) -> io::Result<()> {
50         self.write_pretty("bench", term::color::CYAN)
51     }
52
53     pub fn write_short_result(
54         &mut self,
55         result: &str,
56         color: term::color::Color,
57         exec_time: Option<&TestExecTime>,
58     ) -> io::Result<()> {
59         self.write_pretty(result, color)?;
60         if let Some(exec_time) = exec_time {
61             self.write_plain(format!(" {}", exec_time))?;
62         }
63         self.write_plain("\n")
64     }
65
66     pub fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> {
67         match self.out {
68             Pretty(ref mut term) => {
69                 if self.use_color {
70                     term.fg(color)?;
71                 }
72                 term.write_all(word.as_bytes())?;
73                 if self.use_color {
74                     term.reset()?;
75                 }
76                 term.flush()
77             }
78             Raw(ref mut stdout) => {
79                 stdout.write_all(word.as_bytes())?;
80                 stdout.flush()
81             }
82         }
83     }
84
85     pub fn write_plain<S: AsRef<str>>(&mut self, s: S) -> io::Result<()> {
86         let s = s.as_ref();
87         self.out.write_all(s.as_bytes())?;
88         self.out.flush()
89     }
90
91     pub fn write_successes(&mut self, state: &ConsoleTestState) -> io::Result<()> {
92         self.write_plain("\nsuccesses:\n")?;
93         let mut successes = Vec::new();
94         let mut stdouts = String::new();
95         for &(ref f, ref stdout) in &state.not_failures {
96             successes.push(f.name.to_string());
97             if !stdout.is_empty() {
98                 stdouts.push_str(&format!("---- {} stdout ----\n", f.name));
99                 let output = String::from_utf8_lossy(stdout);
100                 stdouts.push_str(&output);
101                 stdouts.push_str("\n");
102             }
103         }
104         if !stdouts.is_empty() {
105             self.write_plain("\n")?;
106             self.write_plain(&stdouts)?;
107         }
108
109         self.write_plain("\nsuccesses:\n")?;
110         successes.sort();
111         for name in &successes {
112             self.write_plain(&format!("    {}\n", name))?;
113         }
114         Ok(())
115     }
116
117     pub fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> {
118         self.write_plain("\nfailures:\n")?;
119         let mut failures = Vec::new();
120         let mut fail_out = String::new();
121         for &(ref f, ref stdout) in &state.failures {
122             failures.push(f.name.to_string());
123             if !stdout.is_empty() {
124                 fail_out.push_str(&format!("---- {} stdout ----\n", f.name));
125                 let output = String::from_utf8_lossy(stdout);
126                 fail_out.push_str(&output);
127                 fail_out.push_str("\n");
128             }
129         }
130         if !fail_out.is_empty() {
131             self.write_plain("\n")?;
132             self.write_plain(&fail_out)?;
133         }
134
135         self.write_plain("\nfailures:\n")?;
136         failures.sort();
137         for name in &failures {
138             self.write_plain(&format!("    {}\n", name))?;
139         }
140         Ok(())
141     }
142
143     fn write_test_name(&mut self, desc: &TestDesc) -> io::Result<()> {
144         let name = desc.padded_name(self.max_name_len, desc.name.padding());
145         self.write_plain(&format!("test {} ... ", name))?;
146
147         Ok(())
148     }
149 }
150
151 impl<T: Write> OutputFormatter for PrettyFormatter<T> {
152     fn write_run_start(&mut self, test_count: usize) -> io::Result<()> {
153         let noun = if test_count != 1 { "tests" } else { "test" };
154         self.write_plain(&format!("\nrunning {} {}\n", test_count, noun))
155     }
156
157     fn write_test_start(&mut self, desc: &TestDesc) -> io::Result<()> {
158         // When running tests concurrently, we should not print
159         // the test's name as the result will be mis-aligned.
160         // When running the tests serially, we print the name here so
161         // that the user can see which test hangs.
162         if !self.is_multithreaded {
163             self.write_test_name(desc)?;
164         }
165
166         Ok(())
167     }
168
169     fn write_result(
170         &mut self,
171         desc: &TestDesc,
172         result: &TestResult,
173         exec_time: Option<&TestExecTime>,
174         _: &[u8],
175         _: &ConsoleTestState,
176     ) -> io::Result<()> {
177         if self.is_multithreaded {
178             self.write_test_name(desc)?;
179         }
180
181         match *result {
182             TrOk => self.write_ok(exec_time),
183             TrFailed | TrFailedMsg(_) => self.write_failed(exec_time),
184             TrIgnored => self.write_ignored(exec_time),
185             TrAllowedFail => self.write_allowed_fail(exec_time),
186             TrBench(ref bs) => {
187                 self.write_bench()?;
188                 self.write_plain(&format!(": {}\n", fmt_bench_samples(bs)))
189             }
190         }
191     }
192
193     fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()> {
194         if self.is_multithreaded {
195             self.write_test_name(desc)?;
196         }
197
198         self.write_plain(&format!(
199             "test {} has been running for over {} seconds\n",
200             desc.name, TEST_WARN_TIMEOUT_S
201         ))
202     }
203
204     fn write_run_finish(&mut self, state: &ConsoleTestState) -> io::Result<bool> {
205         if state.options.display_output {
206             self.write_successes(state)?;
207         }
208         let success = state.failed == 0;
209         if !success {
210             self.write_failures(state)?;
211         }
212
213         self.write_plain("\ntest result: ")?;
214
215         if success {
216             // There's no parallelism at this point so it's safe to use color
217             self.write_pretty("ok", term::color::GREEN)?;
218         } else {
219             self.write_pretty("FAILED", term::color::RED)?;
220         }
221
222         let s = if state.allowed_fail > 0 {
223             format!(
224                 ". {} passed; {} failed ({} allowed); {} ignored; {} measured; {} filtered out\n\n",
225                 state.passed,
226                 state.failed + state.allowed_fail,
227                 state.allowed_fail,
228                 state.ignored,
229                 state.measured,
230                 state.filtered_out
231             )
232         } else {
233             format!(
234                 ". {} passed; {} failed; {} ignored; {} measured; {} filtered out\n\n",
235                 state.passed, state.failed, state.ignored, state.measured, state.filtered_out
236             )
237         };
238
239         self.write_plain(&s)?;
240
241         Ok(success)
242     }
243 }