Skip to main content

nextest_runner/reporter/structured/
imp.rs

1// Copyright (c) The nextest Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Functionality for emitting structured, machine readable output in different
5//! formats.
6
7use super::{LibtestReporter, RecordReporter};
8use crate::{
9    errors::WriteEventError,
10    record::{RecordOpts, StoreSizes},
11    reporter::events::TestEvent,
12};
13use nextest_metadata::TestListSummary;
14use std::sync::Arc;
15
16/// A reporter for structured, machine-readable formats.
17///
18/// This reporter can emit output in multiple formats simultaneously:
19/// - Libtest-compatible JSON to stdout.
20/// - Recording to disk for later inspection.
21#[derive(Default)]
22pub struct StructuredReporter<'a> {
23    /// Libtest-compatible output written to stdout.
24    libtest: Option<LibtestReporter<'a>>,
25    /// Recording reporter for writing to disk.
26    record: Option<RecordReporter<'a>>,
27}
28
29impl<'a> StructuredReporter<'a> {
30    /// Creates a new `StructuredReporter`.
31    pub fn new() -> Self {
32        Self::default()
33    }
34
35    /// Sets libtest output for the `StructuredReporter`.
36    pub fn set_libtest(&mut self, libtest: LibtestReporter<'a>) -> &mut Self {
37        self.libtest = Some(libtest);
38        self
39    }
40
41    /// Sets the record reporter for the `StructuredReporter`.
42    pub fn set_record(&mut self, record: RecordReporter<'a>) -> &mut Self {
43        self.record = Some(record);
44        self
45    }
46
47    /// Writes metadata to the record reporter, if configured.
48    ///
49    /// This should be called once at the beginning of a test run.
50    pub fn write_meta(
51        &self,
52        cargo_metadata_json: Arc<String>,
53        test_list: TestListSummary,
54        opts: RecordOpts,
55    ) {
56        if let Some(record) = &self.record {
57            record.write_meta(cargo_metadata_json, test_list, opts);
58        }
59    }
60
61    /// Writes a test event to all configured reporters.
62    #[inline]
63    pub(crate) fn write_event(&mut self, event: &TestEvent<'a>) -> Result<(), WriteEventError> {
64        if let Some(libtest) = &mut self.libtest {
65            libtest.write_event(event)?;
66        }
67        if let Some(record) = &self.record {
68            // Clone the event for the record reporter since it runs in a separate thread.
69            record.write_event(event.clone());
70        }
71        Ok(())
72    }
73
74    /// Finishes writing to all configured reporters.
75    ///
76    /// Returns the sizes of the recording (compressed and uncompressed), or `None` if recording
77    /// was not enabled or an error occurred.
78    ///
79    /// This should be called at the end of a test run to ensure all data is flushed.
80    pub fn finish(self) -> Option<StoreSizes> {
81        if let Some(record) = self.record {
82            match record.finish() {
83                Ok(sizes) => Some(sizes),
84                Err(error) => {
85                    tracing::error!("error finishing run recording: {error}");
86                    None
87                }
88            }
89        } else {
90            None
91        }
92    }
93}