diff --git a/nextest-runner/src/errors.rs b/nextest-runner/src/errors.rs index ed4da697fc6..3bb6d59a1de 100644 --- a/nextest-runner/src/errors.rs +++ b/nextest-runner/src/errors.rs @@ -165,6 +165,35 @@ impl ConfigParseOverrideError { } } +/// An execution error was returned while running a test. +/// +/// Internal error type. +#[derive(Debug, Error)] +#[non_exhaustive] +pub(crate) enum RunTestError { + #[error("error spawning test process")] + Spawn(#[source] std::io::Error), + + #[error("error waiting for setup script to exit")] + Wait(#[source] std::io::Error), + + #[error("error collecting test output")] + CollectOutput(#[from] CollectTestOutputError), +} + +/// An error was returned while collecting test output. +#[derive(Debug, Error)] +#[non_exhaustive] +pub enum CollectTestOutputError { + /// An error occurred while reading standard output. + #[error("error reading standard output")] + ReadStdout(#[source] std::io::Error), + + /// An error occurred while reading standard error. + #[error("error reading standard error")] + ReadStderr(#[source] std::io::Error), +} + /// An unknown test group was specified in the config. #[derive(Clone, Debug, Eq, PartialEq)] #[non_exhaustive] diff --git a/nextest-runner/src/runner.rs b/nextest-runner/src/runner.rs index b5f6b3a3413..22199e857a7 100644 --- a/nextest-runner/src/runner.rs +++ b/nextest-runner/src/runner.rs @@ -8,7 +8,9 @@ use crate::{ config::{NextestProfile, RetryPolicy, TestGroup, TestSettings, TestThreads}, double_spawn::DoubleSpawnInfo, - errors::{ConfigureHandleInheritanceError, TestRunnerBuildError}, + errors::{ + CollectTestOutputError, ConfigureHandleInheritanceError, RunTestError, TestRunnerBuildError, + }, list::{TestExecuteContext, TestInstance, TestList}, reporter::{ CancelReason, FinalStatusLevel, StatusLevel, TestEvent, TestEventKind, TestOutputDisplay, @@ -657,7 +659,7 @@ impl<'a> TestRunnerInner<'a> { run_sender: &UnboundedSender>, forward_receiver: &mut tokio::sync::broadcast::Receiver, delay_before_start: Duration, - ) -> std::io::Result { + ) -> Result { let ctx = TestExecuteContext { double_spawn: &self.double_spawn, target_runner: &self.target_runner, @@ -682,7 +684,7 @@ impl<'a> TestRunnerInner<'a> { .stderr(std::process::Stdio::piped()); }; - let mut child = cmd.spawn()?; + let mut child = cmd.spawn().map_err(RunTestError::Spawn)?; // If assigning the child to the job fails, ignore this. This can happen if the process has // exited. @@ -821,7 +823,7 @@ impl<'a> TestRunnerInner<'a> { (res, leaked) }; - let output = res?; + let output = res.map_err(RunTestError::Wait)?; let exit_status = output; let status = status.unwrap_or_else(|| { @@ -869,11 +871,13 @@ fn collect_output<'a>( stdout: &'a mut BytesMut, child_stderr: Option, stderr: &'a mut BytesMut, -) -> impl Future> + 'a { +) -> impl Future> + 'a { // Set up futures for reading from stdout and stderr. let stdout_fut = async { if let Some(mut child_stdout) = child_stdout { - read_all_to_bytes(stdout, &mut child_stdout).await + read_all_to_bytes(stdout, &mut child_stdout) + .await + .map_err(CollectTestOutputError::ReadStdout) } else { Ok(()) } @@ -881,7 +885,9 @@ fn collect_output<'a>( let stderr_fut = async { if let Some(mut child_stderr) = child_stderr { - read_all_to_bytes(stderr, &mut child_stderr).await + read_all_to_bytes(stderr, &mut child_stderr) + .await + .map_err(CollectTestOutputError::ReadStderr) } else { Ok(()) }