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::EvaluatableProfile,
15    errors::WriteEventError,
16    list::TestList,
17    reporter::{aggregator::EventAggregator, events::*, structured::StructuredReporter},
18};
19
20/// Standard error destination for the reporter.
21///
22/// This is usually a terminal, but can be an in-memory buffer for tests.
23pub enum ReporterStderr<'a> {
24    /// Produce output on the (possibly piped) terminal.
25    ///
26    /// If the terminal isn't piped, produce output to a progress bar.
27    Terminal,
28
29    /// Write output to a buffer.
30    Buffer(&'a mut Vec<u8>),
31}
32
33/// Test reporter builder.
34#[derive(Debug, Default)]
35pub struct ReporterBuilder {
36    no_capture: bool,
37    should_colorize: bool,
38    failure_output: Option<TestOutputDisplay>,
39    success_output: Option<TestOutputDisplay>,
40    status_level: Option<StatusLevel>,
41    final_status_level: Option<FinalStatusLevel>,
42
43    verbose: bool,
44    hide_progress_bar: bool,
45    no_output_indent: bool,
46}
47
48impl ReporterBuilder {
49    /// Sets no-capture mode.
50    ///
51    /// In this mode, `failure_output` and `success_output` will be ignored, and `status_level`
52    /// will be at least [`StatusLevel::Pass`].
53    pub fn set_no_capture(&mut self, no_capture: bool) -> &mut Self {
54        self.no_capture = no_capture;
55        self
56    }
57
58    /// Set to true if the reporter should colorize output.
59    pub fn set_colorize(&mut self, should_colorize: bool) -> &mut Self {
60        self.should_colorize = should_colorize;
61        self
62    }
63
64    /// Sets the conditions under which test failures are output.
65    pub fn set_failure_output(&mut self, failure_output: TestOutputDisplay) -> &mut Self {
66        self.failure_output = Some(failure_output);
67        self
68    }
69
70    /// Sets the conditions under which test successes are output.
71    pub fn set_success_output(&mut self, success_output: TestOutputDisplay) -> &mut Self {
72        self.success_output = Some(success_output);
73        self
74    }
75
76    /// Sets the kinds of statuses to output.
77    pub fn set_status_level(&mut self, status_level: StatusLevel) -> &mut Self {
78        self.status_level = Some(status_level);
79        self
80    }
81
82    /// Sets the kinds of statuses to output at the end of the run.
83    pub fn set_final_status_level(&mut self, final_status_level: FinalStatusLevel) -> &mut Self {
84        self.final_status_level = Some(final_status_level);
85        self
86    }
87
88    /// Sets verbose output.
89    pub fn set_verbose(&mut self, verbose: bool) -> &mut Self {
90        self.verbose = verbose;
91        self
92    }
93
94    /// Sets visibility of the progress bar.
95    /// The progress bar is also hidden if `no_capture` is set.
96    pub fn set_hide_progress_bar(&mut self, hide_progress_bar: bool) -> &mut Self {
97        self.hide_progress_bar = hide_progress_bar;
98        self
99    }
100
101    /// Set to true to disable indentation of captured test output.
102    pub fn set_no_output_indent(&mut self, no_output_indent: bool) -> &mut Self {
103        self.no_output_indent = no_output_indent;
104        self
105    }
106}
107
108impl ReporterBuilder {
109    /// Creates a new test reporter.
110    pub fn build<'a>(
111        &self,
112        test_list: &TestList,
113        profile: &EvaluatableProfile<'a>,
114        cargo_configs: &CargoConfigs,
115        output: ReporterStderr<'a>,
116        structured_reporter: StructuredReporter<'a>,
117    ) -> Reporter<'a> {
118        let aggregator = EventAggregator::new(profile);
119
120        let status_level = self.status_level.unwrap_or_else(|| profile.status_level());
121        let final_status_level = self
122            .final_status_level
123            .unwrap_or_else(|| profile.final_status_level());
124
125        let display_reporter = DisplayReporterBuilder {
126            default_filter: profile.default_filter().clone(),
127            status_levels: StatusLevels {
128                status_level,
129                final_status_level,
130            },
131            test_count: test_list.test_count(),
132            success_output: self.success_output,
133            failure_output: self.failure_output,
134            should_colorize: self.should_colorize,
135            no_capture: self.no_capture,
136            hide_progress_bar: self.hide_progress_bar,
137            no_output_indent: self.no_output_indent,
138        }
139        .build(cargo_configs, output);
140
141        Reporter {
142            display_reporter,
143            structured_reporter,
144            metadata_reporter: aggregator,
145        }
146    }
147}
148
149/// Functionality to report test results to stderr, JUnit, and/or structured,
150/// machine-readable results to stdout.
151pub struct Reporter<'a> {
152    /// Used to display results to standard error.
153    display_reporter: DisplayReporter<'a>,
154    /// Used to aggregate events for JUnit reports written to disk
155    metadata_reporter: EventAggregator<'a>,
156    /// Used to emit test events in machine-readable format(s) to stdout
157    structured_reporter: StructuredReporter<'a>,
158}
159
160impl<'a> Reporter<'a> {
161    /// Report a test event.
162    pub fn report_event(&mut self, event: TestEvent<'a>) -> Result<(), WriteEventError> {
163        self.write_event(event)
164    }
165
166    /// Mark the reporter done.
167    pub fn finish(&mut self) {
168        self.display_reporter.finish();
169    }
170
171    // ---
172    // Helper methods
173    // ---
174
175    /// Report this test event to the given writer.
176    fn write_event(&mut self, event: TestEvent<'a>) -> Result<(), WriteEventError> {
177        // TODO: write to all of these even if one of them fails?
178        self.display_reporter.write_event(&event)?;
179        self.structured_reporter.write_event(&event)?;
180        self.metadata_reporter.write_event(event)?;
181        Ok(())
182    }
183}