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, 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 Vec<u8>),
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}
50
51impl ReporterBuilder {
52    /// Sets no-capture mode.
53    ///
54    /// In this mode, `failure_output` and `success_output` will be ignored, and `status_level`
55    /// will be at least [`StatusLevel::Pass`].
56    pub fn set_no_capture(&mut self, no_capture: bool) -> &mut Self {
57        self.no_capture = no_capture;
58        self
59    }
60
61    /// Set to true if the reporter should colorize output.
62    pub fn set_colorize(&mut self, should_colorize: bool) -> &mut Self {
63        self.should_colorize = should_colorize;
64        self
65    }
66
67    /// Sets the conditions under which test failures are output.
68    pub fn set_failure_output(&mut self, failure_output: TestOutputDisplay) -> &mut Self {
69        self.failure_output = Some(failure_output);
70        self
71    }
72
73    /// Sets the conditions under which test successes are output.
74    pub fn set_success_output(&mut self, success_output: TestOutputDisplay) -> &mut Self {
75        self.success_output = Some(success_output);
76        self
77    }
78
79    /// Sets the kinds of statuses to output.
80    pub fn set_status_level(&mut self, status_level: StatusLevel) -> &mut Self {
81        self.status_level = Some(status_level);
82        self
83    }
84
85    /// Sets the kinds of statuses to output at the end of the run.
86    pub fn set_final_status_level(&mut self, final_status_level: FinalStatusLevel) -> &mut Self {
87        self.final_status_level = Some(final_status_level);
88        self
89    }
90
91    /// Sets verbose output.
92    pub fn set_verbose(&mut self, verbose: bool) -> &mut Self {
93        self.verbose = verbose;
94        self
95    }
96
97    /// Sets the way of displaying progress.
98    pub fn set_show_progress(&mut self, show_progress: ShowProgress) -> &mut Self {
99        self.show_progress = show_progress;
100        self
101    }
102
103    /// Set to true to disable indentation of captured test output.
104    pub fn set_no_output_indent(&mut self, no_output_indent: bool) -> &mut Self {
105        self.no_output_indent = no_output_indent;
106        self
107    }
108}
109
110impl ReporterBuilder {
111    /// Creates a new test reporter.
112    pub fn build<'a>(
113        &self,
114        test_list: &TestList,
115        profile: &EvaluatableProfile<'a>,
116        cargo_configs: &CargoConfigs,
117        output: ReporterStderr<'a>,
118        structured_reporter: StructuredReporter<'a>,
119    ) -> Reporter<'a> {
120        let aggregator = EventAggregator::new(profile);
121
122        let status_level = self.status_level.unwrap_or_else(|| profile.status_level());
123        let final_status_level = self
124            .final_status_level
125            .unwrap_or_else(|| profile.final_status_level());
126
127        let display_reporter = DisplayReporterBuilder {
128            default_filter: profile.default_filter().clone(),
129            status_levels: StatusLevels {
130                status_level,
131                final_status_level,
132            },
133            test_count: test_list.test_count(),
134            success_output: self.success_output,
135            failure_output: self.failure_output,
136            should_colorize: self.should_colorize,
137            no_capture: self.no_capture,
138            show_progress: self.show_progress,
139            no_output_indent: self.no_output_indent,
140        }
141        .build(cargo_configs, output);
142
143        Reporter {
144            display_reporter,
145            structured_reporter,
146            metadata_reporter: aggregator,
147        }
148    }
149}
150
151/// Functionality to report test results to stderr, JUnit, and/or structured,
152/// machine-readable results to stdout.
153pub struct Reporter<'a> {
154    /// Used to display results to standard error.
155    display_reporter: DisplayReporter<'a>,
156    /// Used to aggregate events for JUnit reports written to disk
157    metadata_reporter: EventAggregator<'a>,
158    /// Used to emit test events in machine-readable format(s) to stdout
159    structured_reporter: StructuredReporter<'a>,
160}
161
162impl<'a> Reporter<'a> {
163    /// Report a test event.
164    pub fn report_event(&mut self, event: TestEvent<'a>) -> Result<(), WriteEventError> {
165        self.write_event(event)
166    }
167
168    /// Mark the reporter done.
169    pub fn finish(&mut self) {
170        self.display_reporter.finish();
171    }
172
173    // ---
174    // Helper methods
175    // ---
176
177    /// Report this test event to the given writer.
178    fn write_event(&mut self, event: TestEvent<'a>) -> Result<(), WriteEventError> {
179        // TODO: write to all of these even if one of them fails?
180        self.display_reporter.write_event(&event)?;
181        self.structured_reporter.write_event(&event)?;
182        self.metadata_reporter.write_event(event)?;
183        Ok(())
184    }
185}