use crate::{
errors::RecordReporterError,
output_spec::LiveSpec,
record::{RecordOpts, RunRecorder, StoreSizes, TestEventSummary},
reporter::events::TestEvent,
};
use nextest_metadata::TestListSummary;
use std::{
any::Any,
sync::{Arc, mpsc},
thread::JoinHandle,
};
#[derive(Debug)]
pub struct RecordReporter<'a> {
sender: Option<mpsc::SyncSender<RecordEvent>>,
handle: JoinHandle<Result<StoreSizes, RecordReporterError>>,
_marker: std::marker::PhantomData<&'a ()>,
}
impl<'a> RecordReporter<'a> {
pub fn new(run_recorder: RunRecorder) -> Self {
let (sender, receiver) = mpsc::sync_channel(128);
let handle = std::thread::spawn(move || {
let mut writer = RecordReporterWriter { run_recorder };
while let Ok(event) = receiver.recv() {
writer.handle_event(event)?;
}
writer.finish()
});
Self {
sender: Some(sender),
handle,
_marker: std::marker::PhantomData,
}
}
pub fn write_meta(
&self,
cargo_metadata_json: Arc<String>,
test_list: TestListSummary,
opts: RecordOpts,
) {
let event = RecordEvent::Meta {
cargo_metadata_json,
test_list,
opts,
};
_ = self
.sender
.as_ref()
.expect("sender is always Some")
.send(event);
}
pub fn write_event(&self, event: TestEvent<'_>) {
let Some(summary) = TestEventSummary::from_test_event(event) else {
return;
};
let event = RecordEvent::TestEvent(summary);
_ = self
.sender
.as_ref()
.expect("sender is always Some")
.send(event);
}
pub fn finish(mut self) -> Result<StoreSizes, RecordReporterError> {
let sender = self.sender.take();
std::mem::drop(sender);
match self.handle.join() {
Ok(result) => result,
Err(panic_payload) => Err(RecordReporterError::WriterPanic {
message: panic_payload_to_string(panic_payload),
}),
}
}
}
fn panic_payload_to_string(payload: Box<dyn Any + Send + 'static>) -> String {
if let Some(s) = payload.downcast_ref::<&str>() {
(*s).to_owned()
} else if let Some(s) = payload.downcast_ref::<String>() {
s.clone()
} else {
"(unknown panic payload)".to_owned()
}
}
struct RecordReporterWriter {
run_recorder: RunRecorder,
}
impl RecordReporterWriter {
fn handle_event(&mut self, event: RecordEvent) -> Result<(), RecordReporterError> {
match event {
RecordEvent::Meta {
cargo_metadata_json,
test_list,
opts,
} => self
.run_recorder
.write_meta(&cargo_metadata_json, &test_list, &opts)
.map_err(RecordReporterError::RunStore),
RecordEvent::TestEvent(event) => self
.run_recorder
.write_event(event)
.map_err(RecordReporterError::RunStore),
}
}
fn finish(self) -> Result<StoreSizes, RecordReporterError> {
self.run_recorder
.finish()
.map_err(RecordReporterError::RunStore)
}
}
#[derive(Debug)]
enum RecordEvent {
Meta {
cargo_metadata_json: Arc<String>,
test_list: TestListSummary,
opts: RecordOpts,
},
TestEvent(TestEventSummary<LiveSpec>),
}