Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for combined test output #1088

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
/target
# Ignore non-reviewed insta snapshots
*.snap.new
25 changes: 4 additions & 21 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 15 additions & 3 deletions cargo-nextest/src/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -741,13 +741,16 @@
}

impl TestRunnerOpts {
fn to_builder(&self, no_capture: bool) -> Option<TestRunnerBuilder> {
fn to_builder(
&self,
cap_strat: nextest_runner::test_output::CaptureStrategy,
) -> Option<TestRunnerBuilder> {
if self.no_run {
return None;
}

let mut builder = TestRunnerBuilder::default();
builder.set_no_capture(no_capture);
builder.set_capture_strategy(cap_strat);
if let Some(retries) = self.retries {
builder.set_retries(RetryPolicy::new_without_delay(retries));
}
Expand Down Expand Up @@ -1546,6 +1549,15 @@
)?)
}
};
use nextest_runner::test_output::CaptureStrategy;

let cap_strat = if no_capture {
CaptureStrategy::None

Check warning on line 1555 in cargo-nextest/src/dispatch.rs

View check run for this annotation

Codecov / codecov/patch

cargo-nextest/src/dispatch.rs#L1555

Added line #L1555 was not covered by tests
} else if matches!(reporter_opts.message_format, MessageFormat::Human) {
CaptureStrategy::Split
} else {
CaptureStrategy::Combined

Check warning on line 1559 in cargo-nextest/src/dispatch.rs

View check run for this annotation

Codecov / codecov/patch

cargo-nextest/src/dispatch.rs#L1559

Added line #L1559 was not covered by tests
};

let filter_exprs = self.build_filtering_expressions()?;
let test_filter_builder = self.build_filter.make_test_filter_builder(filter_exprs)?;
Expand Down Expand Up @@ -1578,7 +1590,7 @@
}

let handler = SignalHandlerKind::Standard;
let runner_builder = match runner_opts.to_builder(no_capture) {
let runner_builder = match runner_opts.to_builder(cap_strat) {
Some(runner_builder) => runner_builder,
None => {
// This means --no-run was passed in. Exit.
Expand Down
5 changes: 4 additions & 1 deletion integration-tests/tests/integration/fixtures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,10 @@ pub fn check_run_output(stderr: &[u8], relocated: bool) {

for (result, name) in expected {
let reg = make_check_result_regex(*result, name);
assert!(reg.is_match(&output), "{name}: result didn't match");
assert!(
reg.is_match(&output),
"{name}: result didn't match\n\n--- output ---\n{output}\n--- end output ---"
);
}

let summary_reg = if relocated {
Expand Down
13 changes: 10 additions & 3 deletions nextest-runner/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ camino = { version = "1.1.6", features = ["serde1"] }
camino-tempfile = "1.1.1"
# config's "preserve_order" feature is needed for preserving the order of
# setup scripts in .config/nextest.toml.
config = { version = "0.13.4", default-features = false, features = ["toml", "preserve_order"] }
config = { version = "0.13.4", default-features = false, features = [
"toml",
"preserve_order",
] }
cargo_metadata = "0.18.1"
cfg-if = "1.0.0"
chrono = "0.4.31"
Expand All @@ -40,11 +43,12 @@ indicatif = "0.17.7"
is_ci = "1.1.1"
itertools = "0.12.0"
log = "0.4.20"
rand = "0.8.5"
memchr = "2.6"
miette = "5.10.0"
once_cell = "1.19.0"
owo-colors = "4.0.0"
pin-project-lite = "0.2.13"
rand = "0.8.5"
regex = "1.10.2"
semver = "1.0.20"
serde = { version = "1.0.193", features = ["derive"] }
Expand Down Expand Up @@ -106,11 +110,13 @@ nix = { version = "0.27.1", default-features = false, features = ["signal"] }
# https://docs.rs/winapi/0.3.9/src/winapi/lib.rs.html#35-37
# Otherwise nextest-runner runs into compilation issues with win32job.
winapi = { version = "0.3.9", features = ["std"] }
windows = { version = "0.52.0", features = [
windows-sys = { version = "0.52.0", features = [
"Win32_Foundation",
"Win32_Globalization",
"Win32_Security",
"Win32_System_Console",
"Win32_System_JobObjects",
"Win32_System_Pipes",
] }
win32job = "1.0.2"
dunce = "1.0.4"
Expand All @@ -131,6 +137,7 @@ self_update = { version = "0.39.0", optional = true }
color-eyre = { version = "0.6.2", default-features = false }
duct = "0.13.7"
indoc = "2.0.4"
insta = { version = "1.34.0", default-features = false }
maplit = "1.0.2"
pathdiff = { version = "0.2.1", features = ["camino"] }
pretty_assertions = "1.4.0"
Expand Down
10 changes: 8 additions & 2 deletions nextest-runner/src/cargo_config/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ pub fn relative_dir_for(config_path: &Utf8Path) -> Option<&Utf8Path> {
mod imp {
use super::*;
use std::{borrow::Borrow, cmp, ffi::OsStr, os::windows::prelude::OsStrExt};
use windows::Win32::Globalization::{
use windows_sys::Win32::Globalization::{
CompareStringOrdinal, CSTR_EQUAL, CSTR_GREATER_THAN, CSTR_LESS_THAN,
};

Expand Down Expand Up @@ -203,7 +203,13 @@ mod imp {
impl Ord for EnvKey {
fn cmp(&self, other: &Self) -> cmp::Ordering {
unsafe {
let result = CompareStringOrdinal(&self.utf16, &other.utf16, true);
let result = CompareStringOrdinal(
self.utf16.as_ptr(),
self.utf16.len() as _,
other.utf16.as_ptr(),
other.utf16.len() as _,
1, /* ignore case */
);
match result {
CSTR_LESS_THAN => cmp::Ordering::Less,
CSTR_EQUAL => cmp::Ordering::Equal,
Expand Down
2 changes: 1 addition & 1 deletion nextest-runner/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,7 @@ pub enum ConfigureHandleInheritanceError {
/// An error occurred. This can only happen on Windows.
#[cfg(windows)]
#[error("error configuring handle inheritance")]
WindowsError(#[from] windows::core::Error),
WindowsError(#[from] std::io::Error),
}

/// An error that occurs while building the test runner.
Expand Down
12 changes: 5 additions & 7 deletions nextest-runner/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,7 @@
exit_status.signal().map(AbortStatus::UnixSignal)
} else if #[cfg(windows)] {
exit_status.code().and_then(|code| {
let exception = windows::Win32::Foundation::NTSTATUS(code);
exception.is_err().then(|| AbortStatus::WindowsNtStatus(exception))
(code < 0).then(|| AbortStatus::WindowsNtStatus(code))

Check warning on line 197 in nextest-runner/src/helpers.rs

View check run for this annotation

Codecov / codecov/patch

nextest-runner/src/helpers.rs#L197

Added line #L197 was not covered by tests
})
} else {
None
Expand Down Expand Up @@ -226,19 +225,18 @@
}

#[cfg(windows)]
pub(crate) fn display_nt_status(nt_status: windows::Win32::Foundation::NTSTATUS) -> String {
pub(crate) fn display_nt_status(nt_status: windows_sys::Win32::Foundation::NTSTATUS) -> String {
// Convert the NTSTATUS to a Win32 error code.
let win32_code = unsafe { windows::Win32::Foundation::RtlNtStatusToDosError(nt_status) };
let win32_code = unsafe { windows_sys::Win32::Foundation::RtlNtStatusToDosError(nt_status) };

if win32_code == windows::Win32::Foundation::ERROR_MR_MID_NOT_FOUND.0 {
if win32_code == windows_sys::Win32::Foundation::ERROR_MR_MID_NOT_FOUND {
// The Win32 code was not found.
let nt_status = nt_status.0;
return format!("{nt_status:#x} ({nt_status})");
}

return format!(
"{:#x}: {}",
nt_status.0,
nt_status,
io::Error::from_raw_os_error(win32_code as i32)
);
}
Expand Down
1 change: 1 addition & 0 deletions nextest-runner/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub mod signal;
pub mod target_runner;
mod test_command;
pub mod test_filter;
pub mod test_output;
mod time;
#[cfg(feature = "self-update")]
pub mod update;
2 changes: 1 addition & 1 deletion nextest-runner/src/list/binary_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ impl<'g> BinaryListBuildState<'g> {
k == "lib" || k == "rlib" || k == "dylib" || k == "cdylib" || k == "staticlib"
}) {
(RustTestBinaryKind::LIB, BuildPlatform::Target)
} else if kind.get(0).map(String::as_str) == Some("proc-macro") {
} else if kind.first().map(String::as_str) == Some("proc-macro") {
(RustTestBinaryKind::PROC_MACRO, BuildPlatform::Host)
} else {
// Non-lib kinds should always have just one element. Grab the first one.
Expand Down
36 changes: 11 additions & 25 deletions nextest-runner/src/list/test_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -751,39 +751,25 @@
argv.push("--ignored");
}

let mut cmd = TestCommand::new(
let cmd = TestCommand::new(
lctx,
program.clone(),
&argv,
&self.cwd,
&self.package,
&self.non_test_binaries,
);
// Capture stdout and stderr, and close stdin.
cmd.command_mut()
.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped());

let child = cmd
.spawn()
.map_err(|error| CreateTestListError::CommandExecFail {
binary_id: self.binary_id.clone(),
command: std::iter::once(program.clone())
.chain(argv.iter().map(|&s| s.to_owned()))
.collect(),
error,
})?;

let output = child.wait_with_output().await.map_err(|error| {
CreateTestListError::CommandExecFail {
binary_id: self.binary_id.clone(),
command: std::iter::once(program.clone())
.chain(argv.iter().map(|&s| s.to_owned()))
.collect(),
error,
}
})?;
let output =
cmd.wait_with_output()
.await
.map_err(|error| CreateTestListError::CommandExecFail {
binary_id: self.binary_id.clone(),
command: std::iter::once(program.clone())
.chain(argv.iter().map(|&s| s.to_owned()))
.collect(),
error,

Check warning on line 771 in nextest-runner/src/list/test_list.rs

View check run for this annotation

Codecov / codecov/patch

nextest-runner/src/list/test_list.rs#L767-L771

Added lines #L767 - L771 were not covered by tests
})?;

if output.status.success() {
String::from_utf8(output.stdout).map_err(|err| CreateTestListError::CommandNonUtf8 {
Expand Down
60 changes: 33 additions & 27 deletions nextest-runner/src/reporter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1280,7 +1280,7 @@
#[cfg(windows)]
fn write_windows_message_line(
&self,
nt_status: windows::Win32::Foundation::NTSTATUS,
nt_status: windows_sys::Win32::Foundation::NTSTATUS,
writer: &mut dyn Write,
) -> io::Result<()> {
write!(writer, "{:>12} ", "Message".style(self.styles.fail))?;
Expand Down Expand Up @@ -1343,36 +1343,42 @@
(self.styles.fail, self.styles.fail_output)
};

if !run_status.stdout.is_empty() {
write!(writer, "\n{}", "--- ".style(header_style))?;
let out_len = self.write_attempt(run_status, header_style, writer)?;
// The width is to align test instances.
write!(
writer,
"{:width$}",
"STDOUT:".style(header_style),
width = (21 - out_len)
)?;
self.write_instance(*test_instance, writer)?;
writeln!(writer, "{}", " ---".style(header_style))?;
{
let stdout = run_status.output.stdout();
if !stdout.is_empty() {
write!(writer, "\n{}", "--- ".style(header_style))?;
let out_len = self.write_attempt(run_status, header_style, writer)?;
// The width is to align test instances.
write!(
writer,
"{:width$}",
"STDOUT:".style(header_style),
width = (21 - out_len)
)?;
self.write_instance(*test_instance, writer)?;
writeln!(writer, "{}", " ---".style(header_style))?;

self.write_test_output(&run_status.stdout, writer)?;
self.write_test_output(&stdout, writer)?;
}

Check warning on line 1362 in nextest-runner/src/reporter.rs

View check run for this annotation

Codecov / codecov/patch

nextest-runner/src/reporter.rs#L1362

Added line #L1362 was not covered by tests
}

if !run_status.stderr.is_empty() {
write!(writer, "\n{}", "--- ".style(header_style))?;
let out_len = self.write_attempt(run_status, header_style, writer)?;
// The width is to align test instances.
write!(
writer,
"{:width$}",
"STDERR:".style(header_style),
width = (21 - out_len)
)?;
self.write_instance(*test_instance, writer)?;
writeln!(writer, "{}", " ---".style(header_style))?;
{
let stderr = run_status.output.stderr();
if !stderr.is_empty() {
write!(writer, "\n{}", "--- ".style(header_style))?;
let out_len = self.write_attempt(run_status, header_style, writer)?;
// The width is to align test instances.
write!(
writer,
"{:width$}",
"STDERR:".style(header_style),
width = (21 - out_len)
)?;
self.write_instance(*test_instance, writer)?;
writeln!(writer, "{}", " ---".style(header_style))?;

self.write_test_output(&run_status.stderr, writer)?;
self.write_test_output(&stderr, writer)?;
}
}

writeln!(writer)
Expand Down
Loading