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, StatusLevels},
11};
12use crate::{
13    cargo_config::CargoConfigs,
14    config::core::EvaluatableProfile,
15    errors::WriteEventError,
16    list::TestList,
17    reporter::{
18        aggregator::EventAggregator, displayer::ShowProgress, events::*,
19        structured::StructuredReporter,
20    },
21};
22
23/// Standard error destination for the reporter.
24///
25/// This is usually a terminal, but can be an in-memory buffer for tests.
26pub enum ReporterStderr<'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    Buffer(&'a mut String),
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        cargo_configs: &CargoConfigs,
130        output: ReporterStderr<'a>,
131        structured_reporter: StructuredReporter<'a>,
132    ) -> Reporter<'a> {
133        let aggregator = EventAggregator::new(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            default_filter: profile.default_filter().clone(),
142            status_levels: StatusLevels {
143                status_level,
144                final_status_level,
145            },
146            test_count: test_list.test_count(),
147            success_output: self.success_output,
148            failure_output: self.failure_output,
149            should_colorize: self.should_colorize,
150            no_capture: self.no_capture,
151            show_progress: self.show_progress,
152            no_output_indent: self.no_output_indent,
153            max_progress_running: self.max_progress_running,
154        }
155        .build(cargo_configs, output);
156
157        Reporter {
158            display_reporter,
159            structured_reporter,
160            metadata_reporter: aggregator,
161        }
162    }
163}
164
165/// Functionality to report test results to stderr, JUnit, and/or structured,
166/// machine-readable results to stdout.
167pub struct Reporter<'a> {
168    /// Used to display results to standard error.
169    display_reporter: DisplayReporter<'a>,
170    /// Used to aggregate events for JUnit reports written to disk
171    metadata_reporter: EventAggregator<'a>,
172    /// Used to emit test events in machine-readable format(s) to stdout
173    structured_reporter: StructuredReporter<'a>,
174}
175
176impl<'a> Reporter<'a> {
177    /// Report a test event.
178    pub fn report_event(&mut self, event: ReporterEvent<'a>) -> Result<(), WriteEventError> {
179        match event {
180            ReporterEvent::Tick => {
181                self.tick();
182                Ok(())
183            }
184            ReporterEvent::Test(event) => self.write_event(event),
185        }
186    }
187
188    /// Mark the reporter done.
189    pub fn finish(&mut self) {
190        self.display_reporter.finish();
191    }
192
193    // ---
194    // Helper methods
195    // ---
196
197    /// Tick the reporter, updating displayed state.
198    fn tick(&mut self) {
199        self.display_reporter.tick();
200    }
201
202    /// Report this test event to the given writer.
203    fn write_event(&mut self, event: Box<TestEvent<'a>>) -> Result<(), WriteEventError> {
204        // TODO: write to all of these even if one of them fails?
205        self.display_reporter.write_event(&event)?;
206        self.structured_reporter.write_event(&event)?;
207        self.metadata_reporter.write_event(event)?;
208        Ok(())
209    }
210}