diff --git a/nextest-runner/src/helpers.rs b/nextest-runner/src/helpers.rs index 0fd4959011b..7a81e3ae928 100644 --- a/nextest-runner/src/helpers.rs +++ b/nextest-runner/src/helpers.rs @@ -320,7 +320,11 @@ pub(crate) fn display_abort_status(abort_status: AbortStatus) -> String { }, #[cfg(windows)] AbortStatus::WindowsNtStatus(nt_status) => { - format!("code {}", crate::helpers::display_nt_status(nt_status)) + format!( + "code {}", + // TODO: pass down a style here + crate::helpers::display_nt_status(nt_status, Style::new()) + ) } } } @@ -351,18 +355,23 @@ pub(crate) fn signal_str(signal: i32) -> Option<&'static str> { } #[cfg(windows)] -pub(crate) fn display_nt_status(nt_status: windows_sys::Win32::Foundation::NTSTATUS) -> String { +pub(crate) fn display_nt_status( + nt_status: windows_sys::Win32::Foundation::NTSTATUS, + bold_style: Style, +) -> String { + // 10 characters ("0x" + 8 hex digits) is how an NTSTATUS with the high bit set is going to be + // displayed anyway. It also ensures alignment for the displayer. + let bolded_status = format!("{:#010x}", nt_status.style(bold_style)); // Convert the NTSTATUS to a Win32 error code. let win32_code = unsafe { windows_sys::Win32::Foundation::RtlNtStatusToDosError(nt_status) }; if win32_code == windows_sys::Win32::Foundation::ERROR_MR_MID_NOT_FOUND { // The Win32 code was not found. - return format!("{nt_status:#x} ({nt_status})"); + return format!("{bolded_status}"); } format!( - "{:#x}: {}", - nt_status, + "{bolded_status}: {}", io::Error::from_raw_os_error(win32_code as i32) ) } diff --git a/nextest-runner/src/reporter/displayer.rs b/nextest-runner/src/reporter/displayer.rs index 43aa5380731..3eabe398a1f 100644 --- a/nextest-runner/src/reporter/displayer.rs +++ b/nextest-runner/src/reporter/displayer.rs @@ -1323,7 +1323,7 @@ impl<'a> TestReporterImpl<'a> { leaked: _, } = last_status.result { - self.write_windows_message_line(nt_status, writer)?; + write_windows_message_line(nt_status, &self.styles, writer)?; } Ok(()) @@ -1397,7 +1397,7 @@ impl<'a> TestReporterImpl<'a> { leaked: _, } = last_status.result { - self.write_windows_message_line(nt_status, writer)?; + write_windows_message_line(nt_status, &self.styles, writer)?; } Ok(()) @@ -1815,23 +1815,6 @@ impl<'a> TestReporterImpl<'a> { write!(writer, "[>{:>7.3?}s] ", duration.as_secs_f64()) } - #[cfg(windows)] - fn write_windows_message_line( - &self, - nt_status: windows_sys::Win32::Foundation::NTSTATUS, - writer: &mut dyn Write, - ) -> io::Result<()> { - write!(writer, "{:>12} ", "Message".style(self.styles.fail))?; - write!(writer, "[ ] ")?; - writeln!( - writer, - "code {}", - crate::helpers::display_nt_status(nt_status) - )?; - - Ok(()) - } - fn write_setup_script_execute_status( &self, script_id: &ScriptId, @@ -2523,6 +2506,25 @@ fn short_status_str(result: ExecutionResult) -> Cow<'static, str> { } } +#[cfg(windows)] +fn write_windows_message_line( + nt_status: windows_sys::Win32::Foundation::NTSTATUS, + styles: &Styles, + writer: &mut dyn Write, +) -> io::Result<()> { + // Note that display_nt_status ensures that codes are aligned properly. For subsequent lines, + // use an indented displayer with 25 spaces (ensuring that message lines are aligned). + const INDENT: &str = " - "; + let mut indented = IndentWriter::new_skip_initial(INDENT, writer); + writeln!( + indented, + "{:>12} {}", + "with code".style(styles.fail), + crate::helpers::display_nt_status(nt_status, styles.count) + )?; + indented.flush() +} + fn write_final_warnings( final_stats: FinalRunStats, cancel_status: Option, @@ -3719,3 +3721,33 @@ mod tests { String::from_utf8(buf).unwrap() } } + +#[cfg(all(windows, test))] +mod windows_tests { + use super::*; + use windows_sys::Win32::{ + Foundation::{NTSTATUS, STATUS_CONTROL_C_EXIT, STATUS_CONTROL_STACK_VIOLATION}, + Globalization::SetThreadUILanguage, + }; + + #[test] + fn test_write_windows_message_line() { + unsafe { + // Set the thread UI language to US English for consistent output. + SetThreadUILanguage(0x0409); + } + + insta::assert_snapshot!("ctrl_c_code", to_message_line(STATUS_CONTROL_C_EXIT)); + insta::assert_snapshot!( + "stack_violation_code", + to_message_line(STATUS_CONTROL_STACK_VIOLATION), + ); + } + + #[track_caller] + fn to_message_line(status: NTSTATUS) -> String { + let mut buf = Vec::new(); + write_windows_message_line(status as i32, &Styles::default(), &mut buf).unwrap(); + String::from_utf8(buf).unwrap() + } +} diff --git a/nextest-runner/src/reporter/error_description.rs b/nextest-runner/src/reporter/error_description.rs index 640b6b7ffd8..32717d9a5a2 100644 --- a/nextest-runner/src/reporter/error_description.rs +++ b/nextest-runner/src/reporter/error_description.rs @@ -166,7 +166,9 @@ impl fmt::Display for UnitAbortDescription { write!( f, " with code {}", - crate::helpers::display_nt_status(exception) + // TODO: pass in bold style (probably need to not use + // fmt::Display) + crate::helpers::display_nt_status(exception, owo_colors::Style::new()) )?; } } diff --git a/nextest-runner/src/reporter/snapshots/nextest_runner__reporter__displayer__windows_tests__ctrl_c_code.snap b/nextest-runner/src/reporter/snapshots/nextest_runner__reporter__displayer__windows_tests__ctrl_c_code.snap new file mode 100644 index 00000000000..468c3776222 --- /dev/null +++ b/nextest-runner/src/reporter/snapshots/nextest_runner__reporter__displayer__windows_tests__ctrl_c_code.snap @@ -0,0 +1,7 @@ +--- +source: nextest-runner/src/reporter/displayer.rs +expression: buf +snapshot_kind: text +--- + with code 0xc000013a: {Application Exit by CTRL+C} + The application terminated as a result of a CTRL+C. (os error 572) diff --git a/nextest-runner/src/reporter/snapshots/nextest_runner__reporter__displayer__windows_tests__stack_violation_code.snap b/nextest-runner/src/reporter/snapshots/nextest_runner__reporter__displayer__windows_tests__stack_violation_code.snap new file mode 100644 index 00000000000..49fd5e954c3 --- /dev/null +++ b/nextest-runner/src/reporter/snapshots/nextest_runner__reporter__displayer__windows_tests__stack_violation_code.snap @@ -0,0 +1,6 @@ +--- +source: nextest-runner/src/reporter/displayer.rs +expression: to_message_line(STATUS_CONTROL_STACK_VIOLATION) +snapshot_kind: text +--- + with code 0xc00001b2: Invalid access to memory location. (os error 998)