nextest_runner/reporter/structured/
recorder.rs1use crate::{
7 errors::RecordReporterError,
8 record::{RecordOpts, RunRecorder, StoreSizes, TestEventSummary},
9 reporter::events::TestEvent,
10 test_output::ChildSingleOutput,
11};
12use nextest_metadata::TestListSummary;
13use std::{
14 any::Any,
15 sync::{Arc, mpsc},
16 thread::JoinHandle,
17};
18
19#[derive(Debug)]
25pub struct RecordReporter<'a> {
26 sender: Option<mpsc::SyncSender<RecordEvent>>,
28 handle: JoinHandle<Result<StoreSizes, RecordReporterError>>,
29 _marker: std::marker::PhantomData<&'a ()>,
30}
31
32impl<'a> RecordReporter<'a> {
33 pub fn new(run_recorder: RunRecorder) -> Self {
35 let (sender, receiver) = mpsc::sync_channel(128);
37 let handle = std::thread::spawn(move || {
38 let mut writer = RecordReporterWriter { run_recorder };
39 while let Ok(event) = receiver.recv() {
40 writer.handle_event(event)?;
41 }
42
43 writer.finish()
45 });
46
47 Self {
48 sender: Some(sender),
49 handle,
50 _marker: std::marker::PhantomData,
51 }
52 }
53
54 pub fn write_meta(
58 &self,
59 cargo_metadata_json: Arc<String>,
60 test_list: TestListSummary,
61 opts: RecordOpts,
62 ) {
63 let event = RecordEvent::Meta {
64 cargo_metadata_json,
65 test_list,
66 opts,
67 };
68 _ = self
71 .sender
72 .as_ref()
73 .expect("sender is always Some")
74 .send(event);
75 }
76
77 pub fn write_event(&self, event: TestEvent<'_>) {
82 let Some(summary) = TestEventSummary::from_test_event(event) else {
83 return;
85 };
86 let event = RecordEvent::TestEvent(summary);
87 _ = self
90 .sender
91 .as_ref()
92 .expect("sender is always Some")
93 .send(event);
94 }
95
96 pub fn finish(mut self) -> Result<StoreSizes, RecordReporterError> {
103 let sender = self.sender.take();
105 std::mem::drop(sender);
106
107 match self.handle.join() {
109 Ok(result) => result,
110 Err(panic_payload) => Err(RecordReporterError::WriterPanic {
111 message: panic_payload_to_string(panic_payload),
112 }),
113 }
114 }
115}
116
117fn panic_payload_to_string(payload: Box<dyn Any + Send + 'static>) -> String {
119 if let Some(s) = payload.downcast_ref::<&str>() {
120 (*s).to_owned()
121 } else if let Some(s) = payload.downcast_ref::<String>() {
122 s.clone()
123 } else {
124 "(unknown panic payload)".to_owned()
125 }
126}
127
128struct RecordReporterWriter {
130 run_recorder: RunRecorder,
131}
132
133impl RecordReporterWriter {
134 fn handle_event(&mut self, event: RecordEvent) -> Result<(), RecordReporterError> {
135 match event {
136 RecordEvent::Meta {
137 cargo_metadata_json,
138 test_list,
139 opts,
140 } => self
141 .run_recorder
142 .write_meta(&cargo_metadata_json, &test_list, &opts)
143 .map_err(RecordReporterError::RunStore),
144 RecordEvent::TestEvent(event) => self
145 .run_recorder
146 .write_event(event)
147 .map_err(RecordReporterError::RunStore),
148 }
149 }
150
151 fn finish(self) -> Result<StoreSizes, RecordReporterError> {
152 self.run_recorder
153 .finish()
154 .map_err(RecordReporterError::RunStore)
155 }
156}
157
158#[derive(Debug)]
160enum RecordEvent {
161 Meta {
163 cargo_metadata_json: Arc<String>,
164 test_list: TestListSummary,
165 opts: RecordOpts,
166 },
167 TestEvent(TestEventSummary<ChildSingleOutput>),
169}