1 use std
::{io, io::prelude::Write}
;
3 use super::OutputFormatter
;
5 bench
::fmt_bench_samples
,
6 console
::{ConsoleTestState, OutputLocation}
,
7 test_result
::TestResult
,
13 // insert a '\n' after 100 tests in quiet mode
14 const QUIET_MODE_MAX_COLUMN
: usize = 100;
16 pub(crate) struct TerseFormatter
<T
> {
17 out
: OutputLocation
<T
>,
19 is_multithreaded
: bool
,
20 /// Number of columns to fill when aligning names
24 total_test_count
: usize,
27 impl<T
: Write
> TerseFormatter
<T
> {
29 out
: OutputLocation
<T
>,
32 is_multithreaded
: bool
,
40 total_test_count
: 0, // initialized later, when write_run_start is called
44 pub fn write_ok(&mut self) -> io
::Result
<()> {
45 self.write_short_result(".", term
::color
::GREEN
)
48 pub fn write_failed(&mut self) -> io
::Result
<()> {
49 self.write_short_result("F", term
::color
::RED
)
52 pub fn write_ignored(&mut self) -> io
::Result
<()> {
53 self.write_short_result("i", term
::color
::YELLOW
)
56 pub fn write_allowed_fail(&mut self) -> io
::Result
<()> {
57 self.write_short_result("a", term
::color
::YELLOW
)
60 pub fn write_bench(&mut self) -> io
::Result
<()> {
61 self.write_pretty("bench", term
::color
::CYAN
)
64 pub fn write_short_result(
67 color
: term
::color
::Color
,
69 self.write_pretty(result
, color
)?
;
70 if self.test_count
% QUIET_MODE_MAX_COLUMN
== QUIET_MODE_MAX_COLUMN
- 1 {
71 // we insert a new line every 100 dots in order to flush the
72 // screen when dealing with line-buffered output (e.g., piping to
73 // `stamp` in the rust CI).
74 let out
= format
!(" {}/{}\n", self.test_count
+ 1, self.total_test_count
);
75 self.write_plain(&out
)?
;
82 pub fn write_pretty(&mut self, word
: &str, color
: term
::color
::Color
) -> io
::Result
<()> {
84 OutputLocation
::Pretty(ref mut term
) => {
88 term
.write_all(word
.as_bytes())?
;
94 OutputLocation
::Raw(ref mut stdout
) => {
95 stdout
.write_all(word
.as_bytes())?
;
101 pub fn write_plain
<S
: AsRef
<str>>(&mut self, s
: S
) -> io
::Result
<()> {
103 self.out
.write_all(s
.as_bytes())?
;
107 pub fn write_outputs(&mut self, state
: &ConsoleTestState
) -> io
::Result
<()> {
108 self.write_plain("\nsuccesses:\n")?
;
109 let mut successes
= Vec
::new();
110 let mut stdouts
= String
::new();
111 for &(ref f
, ref stdout
) in &state
.not_failures
{
112 successes
.push(f
.name
.to_string());
113 if !stdout
.is_empty() {
114 stdouts
.push_str(&format
!("---- {} stdout ----\n", f
.name
));
115 let output
= String
::from_utf8_lossy(stdout
);
116 stdouts
.push_str(&output
);
120 if !stdouts
.is_empty() {
121 self.write_plain("\n")?
;
122 self.write_plain(&stdouts
)?
;
125 self.write_plain("\nsuccesses:\n")?
;
127 for name
in &successes
{
128 self.write_plain(&format
!(" {}\n", name
))?
;
133 pub fn write_failures(&mut self, state
: &ConsoleTestState
) -> io
::Result
<()> {
134 self.write_plain("\nfailures:\n")?
;
135 let mut failures
= Vec
::new();
136 let mut fail_out
= String
::new();
137 for &(ref f
, ref stdout
) in &state
.failures
{
138 failures
.push(f
.name
.to_string());
139 if !stdout
.is_empty() {
140 fail_out
.push_str(&format
!("---- {} stdout ----\n", f
.name
));
141 let output
= String
::from_utf8_lossy(stdout
);
142 fail_out
.push_str(&output
);
146 if !fail_out
.is_empty() {
147 self.write_plain("\n")?
;
148 self.write_plain(&fail_out
)?
;
151 self.write_plain("\nfailures:\n")?
;
153 for name
in &failures
{
154 self.write_plain(&format
!(" {}\n", name
))?
;
159 fn write_test_name(&mut self, desc
: &TestDesc
) -> io
::Result
<()> {
160 let name
= desc
.padded_name(self.max_name_len
, desc
.name
.padding());
161 self.write_plain(&format
!("test {} ... ", name
))?
;
167 impl<T
: Write
> OutputFormatter
for TerseFormatter
<T
> {
168 fn write_run_start(&mut self, test_count
: usize) -> io
::Result
<()> {
169 self.total_test_count
= test_count
;
170 let noun
= if test_count
!= 1 { "tests" }
else { "test" }
;
171 self.write_plain(&format
!("\nrunning {} {}\n", test_count
, noun
))
174 fn write_test_start(&mut self, desc
: &TestDesc
) -> io
::Result
<()> {
175 // Remnants from old libtest code that used the padding value
176 // in order to indicate benchmarks.
177 // When running benchmarks, terse-mode should still print their name as if
178 // it is the Pretty formatter.
179 if !self.is_multithreaded
&& desc
.name
.padding() == NamePadding
::PadOnRight
{
180 self.write_test_name(desc
)?
;
190 _
: Option
<&time
::TestExecTime
>,
192 _
: &ConsoleTestState
,
193 ) -> io
::Result
<()> {
195 TestResult
::TrOk
=> self.write_ok(),
196 TestResult
::TrFailed
| TestResult
::TrFailedMsg(_
) | TestResult
::TrTimedFail
=> {
199 TestResult
::TrIgnored
=> self.write_ignored(),
200 TestResult
::TrAllowedFail
=> self.write_allowed_fail(),
201 TestResult
::TrBench(ref bs
) => {
202 if self.is_multithreaded
{
203 self.write_test_name(desc
)?
;
206 self.write_plain(&format
!(": {}\n", fmt_bench_samples(bs
)))
211 fn write_timeout(&mut self, desc
: &TestDesc
) -> io
::Result
<()> {
212 self.write_plain(&format
!(
213 "test {} has been running for over {} seconds\n",
215 time
::TEST_WARN_TIMEOUT_S
219 fn write_run_finish(&mut self, state
: &ConsoleTestState
) -> io
::Result
<bool
> {
220 if state
.options
.display_output
{
221 self.write_outputs(state
)?
;
223 let success
= state
.failed
== 0;
225 self.write_failures(state
)?
;
228 self.write_plain("\ntest result: ")?
;
231 // There's no parallelism at this point so it's safe to use color
232 self.write_pretty("ok", term
::color
::GREEN
)?
;
234 self.write_pretty("FAILED", term
::color
::RED
)?
;
237 let s
= if state
.allowed_fail
> 0 {
239 ". {} passed; {} failed ({} allowed); {} ignored; {} measured; {} filtered out\n\n",
241 state
.failed
+ state
.allowed_fail
,
249 ". {} passed; {} failed; {} ignored; {} measured; {} filtered out\n\n",
250 state
.passed
, state
.failed
, state
.ignored
, state
.measured
, state
.filtered_out
254 self.write_plain(&s
)?
;