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