diff --git a/nextest-runner/src/reporter.rs b/nextest-runner/src/reporter.rs index a66052784ab..413ca7ebcb8 100644 --- a/nextest-runner/src/reporter.rs +++ b/nextest-runner/src/reporter.rs @@ -389,14 +389,14 @@ impl<'a> TestReporter<'a> { } fn update_progress_bar(event: &TestEvent<'_>, styles: &Styles, progress_bar: &ProgressBar) { - match event { - TestEvent::TestStarted { + match &event.kind { + TestEventKind::TestStarted { current_stats, running, cancel_state, .. } - | TestEvent::TestFinished { + | TestEventKind::TestFinished { current_stats, running, cancel_state, @@ -410,7 +410,7 @@ fn update_progress_bar(event: &TestEvent<'_>, styles: &Styles, progress_bar: &Pr progress_bar.set_length(current_stats.initial_run_count as u64); progress_bar.set_position(current_stats.finished_count as u64); } - TestEvent::RunBeginCancel { reason, .. } => { + TestEventKind::RunBeginCancel { reason, .. } => { let running_state = RunningState::Canceling(*reason); progress_bar.set_prefix(running_state.progress_bar_prefix(styles)); } @@ -564,8 +564,8 @@ impl<'a> TestReporterImpl<'a> { event: &TestEvent<'a>, writer: &mut impl Write, ) -> io::Result<()> { - match event { - TestEvent::RunStarted { test_list, .. } => { + match &event.kind { + TestEventKind::RunStarted { test_list, .. } => { write!(writer, "{:>12} ", "Starting".style(self.styles.pass))?; let count_style = self.styles.count; @@ -596,7 +596,7 @@ impl<'a> TestReporterImpl<'a> { writeln!(writer)?; } - TestEvent::TestStarted { test_instance, .. } => { + TestEventKind::TestStarted { test_instance, .. } => { // In no-capture mode, print out a test start event. if self.no_capture { // The spacing is to align test instances. @@ -609,7 +609,7 @@ impl<'a> TestReporterImpl<'a> { writeln!(writer)?; } } - TestEvent::TestSlow { + TestEventKind::TestSlow { test_instance, retry_data, elapsed, @@ -647,7 +647,7 @@ impl<'a> TestReporterImpl<'a> { writeln!(writer)?; } - TestEvent::TestAttemptFailedWillRetry { + TestEventKind::TestAttemptFailedWillRetry { test_instance, run_status, delay_before_next_attempt, @@ -701,7 +701,7 @@ impl<'a> TestReporterImpl<'a> { } } } - TestEvent::TestRetryStarted { + TestEventKind::TestRetryStarted { test_instance, retry_data: RetryData { @@ -719,7 +719,7 @@ impl<'a> TestReporterImpl<'a> { self.write_instance(*test_instance, writer)?; writeln!(writer)?; } - TestEvent::TestFinished { + TestEventKind::TestFinished { test_instance, success_output, failure_output, @@ -759,7 +759,7 @@ impl<'a> TestReporterImpl<'a> { )); } } - TestEvent::TestSkipped { + TestEventKind::TestSkipped { test_instance, reason, } => { @@ -771,7 +771,7 @@ impl<'a> TestReporterImpl<'a> { .push((*test_instance, FinalOutput::Skipped(*reason))); } } - TestEvent::RunBeginCancel { running, reason } => { + TestEventKind::RunBeginCancel { running, reason } => { self.cancel_status = self.cancel_status.max(Some(*reason)); write!(writer, "{:>12} ", "Canceling".style(self.styles.fail))?; @@ -789,7 +789,7 @@ impl<'a> TestReporterImpl<'a> { running.style(self.styles.count) )?; } - TestEvent::RunPaused { running } => { + TestEventKind::RunPaused { running } => { writeln!( writer, "{:>12} {} running tests due to {}", @@ -798,7 +798,7 @@ impl<'a> TestReporterImpl<'a> { "signal".style(self.styles.count), )?; } - TestEvent::RunContinued { running } => { + TestEventKind::RunContinued { running } => { writeln!( writer, "{:>12} {} running tests due to {}", @@ -807,7 +807,7 @@ impl<'a> TestReporterImpl<'a> { "signal".style(self.styles.count), )?; } - TestEvent::RunFinished { + TestEventKind::RunFinished { start_time: _start_time, elapsed, run_stats, @@ -1292,9 +1292,22 @@ fn short_status_str(result: ExecutionResult) -> Cow<'static, str> { /// A test event. /// -/// Events are produced by a [`TestRunner`](crate::runner::TestRunner) and consumed by a [`TestReporter`]. +/// Events are produced by a [`TestRunner`](crate::runner::TestRunner) and consumed by a +/// [`TestReporter`]. #[derive(Clone, Debug)] -pub enum TestEvent<'a> { +pub struct TestEvent<'a> { + /// The amount of time elapsed since the start of the test run. + pub elapsed: Duration, + + /// The kind of test event this is. + pub kind: TestEventKind<'a>, +} + +/// The kind of test event this is. +/// +/// Forms part of [`TestEvent`]. +#[derive(Clone, Debug)] +pub enum TestEventKind<'a> { /// The test run started. RunStarted { /// The list of tests that will be run. diff --git a/nextest-runner/src/reporter/aggregator.rs b/nextest-runner/src/reporter/aggregator.rs index f4ea2ebe04d..3c18d0067ce 100644 --- a/nextest-runner/src/reporter/aggregator.rs +++ b/nextest-runner/src/reporter/aggregator.rs @@ -3,13 +3,14 @@ //! Metadata management. +use super::TestEvent; #[cfg(any(unix, windows))] use crate::runner::AbortStatus; use crate::{ config::{NextestJunitConfig, NextestProfile}, errors::WriteEventError, list::TestInstance, - reporter::TestEvent, + reporter::TestEventKind, runner::{ExecuteStatus, ExecutionDescription, ExecutionResult}, }; use camino::Utf8PathBuf; @@ -60,16 +61,17 @@ impl<'cfg> MetadataJunit<'cfg> { } pub(crate) fn write_event(&mut self, event: TestEvent<'cfg>) -> Result<(), WriteEventError> { - match event { - TestEvent::RunStarted { .. } - | TestEvent::RunPaused { .. } - | TestEvent::RunContinued { .. } => {} - TestEvent::TestStarted { .. } => {} - TestEvent::TestSlow { .. } => {} - TestEvent::TestAttemptFailedWillRetry { .. } | TestEvent::TestRetryStarted { .. } => { + match event.kind { + TestEventKind::RunStarted { .. } + | TestEventKind::RunPaused { .. } + | TestEventKind::RunContinued { .. } => {} + TestEventKind::TestStarted { .. } => {} + TestEventKind::TestSlow { .. } => {} + TestEventKind::TestAttemptFailedWillRetry { .. } + | TestEventKind::TestRetryStarted { .. } => { // Retries are recorded in TestFinished. } - TestEvent::TestFinished { + TestEventKind::TestFinished { test_instance, run_statuses, junit_store_success_output, @@ -189,7 +191,7 @@ impl<'cfg> MetadataJunit<'cfg> { testsuite.add_test_case(testcase); } - TestEvent::TestSkipped { .. } => { + TestEventKind::TestSkipped { .. } => { // TODO: report skipped tests? causes issues if we want to aggregate runs across // skipped and non-skipped tests. Probably needs to be made configurable. @@ -201,8 +203,8 @@ impl<'cfg> MetadataJunit<'cfg> { // // testsuite.add_testcase(testcase); } - TestEvent::RunBeginCancel { .. } => {} - TestEvent::RunFinished { + TestEventKind::RunBeginCancel { .. } => {} + TestEventKind::RunFinished { run_id, start_time, elapsed, diff --git a/nextest-runner/src/runner.rs b/nextest-runner/src/runner.rs index 849f83b2e04..43fe92a4971 100644 --- a/nextest-runner/src/runner.rs +++ b/nextest-runner/src/runner.rs @@ -10,7 +10,9 @@ use crate::{ double_spawn::DoubleSpawnInfo, errors::{ConfigureHandleInheritanceError, TestRunnerBuildError}, list::{TestExecuteContext, TestInstance, TestList}, - reporter::{CancelReason, FinalStatusLevel, StatusLevel, TestEvent, TestOutputDisplay}, + reporter::{ + CancelReason, FinalStatusLevel, StatusLevel, TestEvent, TestEventKind, TestOutputDisplay, + }, signal::{JobControlEvent, ShutdownEvent, SignalEvent, SignalHandler, SignalHandlerKind}, target_runner::TargetRunner, time::{StopwatchEnd, StopwatchStart}, @@ -1262,17 +1264,27 @@ where } fn run_started(&mut self, test_list: &'a TestList) -> Result<(), E> { - (self.callback)(TestEvent::RunStarted { + self.basic_callback(TestEventKind::RunStarted { test_list, run_id: self.run_id, }) } + #[inline] + fn basic_callback(&mut self, kind: TestEventKind<'a>) -> Result<(), E> { + let event = TestEvent { + elapsed: self.stopwatch.elapsed(), + kind, + }; + (self.callback)(event) + } + + #[inline] fn callback( &mut self, - test_event: TestEvent<'a>, + kind: TestEventKind<'a>, ) -> Result, InternalError> { - (self.callback)(test_event).map_err(InternalError::Error)?; + self.basic_callback(kind).map_err(InternalError::Error)?; Ok(None) } @@ -1283,7 +1295,7 @@ where match event { InternalEvent::Test(InternalTestEvent::Started { test_instance }) => { self.running += 1; - self.callback(TestEvent::TestStarted { + self.callback(TestEventKind::TestStarted { test_instance, current_stats: self.run_stats, running: self.running, @@ -1295,7 +1307,7 @@ where retry_data, elapsed, will_terminate, - }) => self.callback(TestEvent::TestSlow { + }) => self.callback(TestEventKind::TestSlow { test_instance, retry_data, elapsed, @@ -1306,7 +1318,7 @@ where failure_output, run_status, delay_before_next_attempt, - }) => self.callback(TestEvent::TestAttemptFailedWillRetry { + }) => self.callback(TestEventKind::TestAttemptFailedWillRetry { test_instance, failure_output, run_status, @@ -1315,7 +1327,7 @@ where InternalEvent::Test(InternalTestEvent::RetryStarted { test_instance, retry_data, - }) => self.callback(TestEvent::TestRetryStarted { + }) => self.callback(TestEventKind::TestRetryStarted { test_instance, retry_data, }), @@ -1333,7 +1345,7 @@ where // should this run be canceled because of a failure? let fail_cancel = self.fail_fast && !run_statuses.last_status().result.is_success(); - self.callback(TestEvent::TestFinished { + self.callback(TestEventKind::TestFinished { test_instance, success_output, failure_output, @@ -1359,7 +1371,7 @@ where reason, }) => { self.run_stats.skipped += 1; - self.callback(TestEvent::TestSkipped { + self.callback(TestEventKind::TestSkipped { test_instance, reason, }) @@ -1383,7 +1395,7 @@ where InternalEvent::Signal(SignalEvent::JobControl(JobControlEvent::Stop)) => { // Debounce stop signals. if !self.stopwatch.is_paused() { - self.callback(TestEvent::RunPaused { + self.callback(TestEventKind::RunPaused { running: self.running, })?; self.stopwatch.pause(); @@ -1397,7 +1409,7 @@ where // Debounce continue signals. if self.stopwatch.is_paused() { self.stopwatch.resume(); - self.callback(TestEvent::RunContinued { + self.callback(TestEventKind::RunContinued { running: self.running, })?; Ok(Some(JobControlEvent::Continue)) @@ -1426,7 +1438,7 @@ where fn begin_cancel(&mut self, reason: CancelReason) -> Result<(), E> { if self.cancel_state < Some(reason) { self.cancel_state = Some(reason); - (self.callback)(TestEvent::RunBeginCancel { + self.basic_callback(TestEventKind::RunBeginCancel { running: self.running, reason, })?; @@ -1436,7 +1448,7 @@ where fn run_finished(&mut self) -> Result<(), E> { let stopwatch_end = self.stopwatch.end(); - (self.callback)(TestEvent::RunFinished { + self.basic_callback(TestEventKind::RunFinished { start_time: stopwatch_end.start_time, run_id: self.run_id, elapsed: stopwatch_end.duration, diff --git a/nextest-runner/src/time/stopwatch.rs b/nextest-runner/src/time/stopwatch.rs index fb27274784e..16fbde4a9ad 100644 --- a/nextest-runner/src/time/stopwatch.rs +++ b/nextest-runner/src/time/stopwatch.rs @@ -63,6 +63,10 @@ impl StopwatchStart { } } + pub(crate) fn elapsed(&self) -> Duration { + self.instant.elapsed() - self.paused_time + } + pub(crate) fn end(&self) -> StopwatchEnd { StopwatchEnd { start_time: self.start_time, diff --git a/nextest-runner/tests/integration/fixtures.rs b/nextest-runner/tests/integration/fixtures.rs index 55edbda811a..ef7d3aebc4c 100644 --- a/nextest-runner/tests/integration/fixtures.rs +++ b/nextest-runner/tests/integration/fixtures.rs @@ -13,7 +13,7 @@ use nextest_runner::{ list::{ BinaryList, RustBuildMeta, RustTestArtifact, TestExecuteContext, TestList, TestListState, }, - reporter::TestEvent, + reporter::TestEventKind, reuse_build::PathMapper, runner::{ configure_handle_inheritance, AbortStatus, ExecutionResult, ExecutionStatuses, RunStats, @@ -416,12 +416,12 @@ pub(crate) fn execute_collect( let mut instance_statuses = HashMap::new(); configure_handle_inheritance(false).expect("configuring handle inheritance on Windows failed"); let run_stats = runner.execute(|event| { - let (test_instance, status) = match event { - TestEvent::TestSkipped { + let (test_instance, status) = match event.kind { + TestEventKind::TestSkipped { test_instance, reason, } => (test_instance, InstanceStatus::Skipped(reason)), - TestEvent::TestFinished { + TestEventKind::TestFinished { test_instance, run_statuses, ..