nextest_runner/reporter/
imp.rs

1// Copyright (c) The nextest Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Prints out and aggregates test execution statuses.
5//!
6//! The main structure in this module is [`TestReporter`].
7
8use super::{
9    FinalStatusLevel, MaxProgressRunning, StatusLevel, TestOutputDisplay,
10    displayer::{DisplayReporter, DisplayReporterBuilder, ShowTerminalProgress, StatusLevels},
11};
12use crate::{
13    config::core::EvaluatableProfile,
14    errors::WriteEventError,
15    list::TestList,
16    reporter::{
17        aggregator::EventAggregator, displayer::ShowProgress, events::*,
18        structured::StructuredReporter,
19    },
20    write_str::WriteStr,
21};
22
23/// Output destination for the reporter.
24///
25/// This is usually a terminal, but can be an in-memory buffer for tests.
26pub enum ReporterOutput<'a> {
27    /// Produce output on the (possibly piped) terminal.
28    ///
29    /// If the terminal isn't piped, produce output to a progress bar.
30    Terminal,
31
32    /// Write output to a buffer.
33    Writer(&'a mut (dyn WriteStr + Send)),
34}
35
36/// Test reporter builder.
37#[derive(Debug, Default)]
38pub struct ReporterBuilder {
39    no_capture: bool,
40    should_colorize: bool,
41    failure_output: Option<TestOutputDisplay>,
42    success_output: Option<TestOutputDisplay>,
43    status_level: Option<StatusLevel>,
44    final_status_level: Option<FinalStatusLevel>,
45
46    verbose: bool,
47    show_progress: ShowProgress,
48    no_output_indent: bool,
49    max_progress_running: MaxProgressRunning,
50}
51
52impl ReporterBuilder {
53    /// Sets no-capture mode.
54    ///
55    /// In this mode, `failure_output` and `success_output` will be ignored, and `status_level`
56    /// will be at least [`StatusLevel::Pass`].
57    pub fn set_no_capture(&mut self, no_capture: bool) -> &mut Self {
58        self.no_capture = no_capture;
59        self
60    }
61
62    /// Set to true if the reporter should colorize output.
63    pub fn set_colorize(&mut self, should_colorize: bool) -> &mut Self {
64        self.should_colorize = should_colorize;
65        self
66    }
67
68    /// Sets the conditions under which test failures are output.
69    pub fn set_failure_output(&mut self, failure_output: TestOutputDisplay) -> &mut Self {
70        self.failure_output = Some(failure_output);
71        self
72    }
73
74    /// Sets the conditions under which test successes are output.
75    pub fn set_success_output(&mut self, success_output: TestOutputDisplay) -> &mut Self {
76        self.success_output = Some(success_output);
77        self
78    }
79
80    /// Sets the kinds of statuses to output.
81    pub fn set_status_level(&mut self, status_level: StatusLevel) -> &mut Self {
82        self.status_level = Some(status_level);
83        self
84    }
85
86    /// Sets the kinds of statuses to output at the end of the run.
87    pub fn set_final_status_level(&mut self, final_status_level: FinalStatusLevel) -> &mut Self {
88        self.final_status_level = Some(final_status_level);
89        self
90    }
91
92    /// Sets verbose output.
93    pub fn set_verbose(&mut self, verbose: bool) -> &mut Self {
94        self.verbose = verbose;
95        self
96    }
97
98    /// Sets the way of displaying progress.
99    pub fn set_show_progress(&mut self, show_progress: ShowProgress) -> &mut Self {
100        self.show_progress = show_progress;
101        self
102    }
103
104    /// Set to true to disable indentation of captured test output.
105    pub fn set_no_output_indent(&mut self, no_output_indent: bool) -> &mut Self {
106        self.no_output_indent = no_output_indent;
107        self
108    }
109
110    /// Sets the maximum number of running tests to display in the progress bar.
111    ///
112    /// When more tests are running than this limit, only the first N tests are shown
113    /// with a summary line indicating how many more tests are running.
114    pub fn set_max_progress_running(
115        &mut self,
116        max_progress_running: MaxProgressRunning,
117    ) -> &mut Self {
118        self.max_progress_running = max_progress_running;
119        self
120    }
121}
122
123impl ReporterBuilder {
124    /// Creates a new test reporter.
125    pub fn build<'a>(
126        &self,
127        test_list: &TestList,
128        profile: &EvaluatableProfile<'a>,
129        show_term_progress: ShowTerminalProgress,
130        output: ReporterOutput<'a>,
131        structured_reporter: StructuredReporter<'a>,
132    ) -> Reporter<'a> {
133        let aggregator = EventAggregator::new(test_list.mode(), profile);
134
135        let status_level = self.status_level.unwrap_or_else(|| profile.status_level());
136        let final_status_level = self
137            .final_status_level
138            .unwrap_or_else(|| profile.final_status_level());
139
140        let display_reporter = DisplayReporterBuilder {
141            mode: test_list.mode(),
142            default_filter: profile.default_filter().clone(),
143            status_levels: StatusLevels {
144                status_level,
145                final_status_level,
146            },
147            test_count: test_list.test_count(),
148            success_output: self.success_output,
149            failure_output: self.failure_output,
150            should_colorize: self.should_colorize,
151            no_capture: self.no_capture,
152            verbose: self.verbose,
153            show_progress: self.show_progress,
154            no_output_indent: self.no_output_indent,
155            max_progress_running: self.max_progress_running,
156            show_term_progress,
157        }
158        .build(output);
159
160        Reporter {
161            display_reporter,
162            structured_reporter,
163            metadata_reporter: aggregator,
164        }
165    }
166}
167
168/// Functionality to report test results to stderr, JUnit, and/or structured,
169/// machine-readable results to stdout.
170pub struct Reporter<'a> {
171    /// Used to display results to standard error.
172    display_reporter: DisplayReporter<'a>,
173    /// Used to aggregate events for JUnit reports written to disk
174    metadata_reporter: EventAggregator<'a>,
175    /// Used to emit test events in machine-readable format(s) to stdout
176    structured_reporter: StructuredReporter<'a>,
177}
178
179impl<'a> Reporter<'a> {
180    /// Report a test event.
181    pub fn report_event(&mut self, event: ReporterEvent<'a>) -> Result<(), WriteEventError> {
182        match event {
183            ReporterEvent::Tick => {
184                self.tick();
185                Ok(())
186            }
187            ReporterEvent::Test(event) => self.write_event(event),
188        }
189    }
190
191    /// Mark the reporter done.
192    pub fn finish(&mut self) {
193        self.display_reporter.finish();
194    }
195
196    // ---
197    // Helper methods
198    // ---
199
200    /// Tick the reporter, updating displayed state.
201    fn tick(&mut self) {
202        self.display_reporter.tick();
203    }
204
205    /// Report this test event to the given writer.
206    fn write_event(&mut self, event: Box<TestEvent<'a>>) -> Result<(), WriteEventError> {
207        // TODO: write to all of these even if one of them fails?
208        self.display_reporter.write_event(&event)?;
209        self.structured_reporter.write_event(&event)?;
210        self.metadata_reporter.write_event(event)?;
211        Ok(())
212    }
213}