Skip to content

Commit 13baeee

Browse files
authored
Rollup merge of rust-lang#102794 - dtolnay:termination, r=thomcc
Make tests capture the error printed by a Result return An error returned by tests previously would get written directly to stderr, instead of to the capture buffer set up by the test harness. This PR makes it write to the capture buffer so that it can be integrated as part of the test output by build tools such as `buck test`, since being able to read the error message returned by a test is pretty critical to debugging why the test failed. <br> **Before:** ```rust // tests/test.rs #[test] fn test() -> Result<(), &'static str> { println!("STDOUT"); eprintln!("STDERR"); Err("RESULT") } ``` ```console $ cargo build --test test $ target/debug/deps/test-???????????????? -Z unstable-options --format=json { "type": "suite", "event": "started", "test_count": 1 } { "type": "test", "event": "started", "name": "test" } Error: "RESULT" { "type": "test", "name": "test", "event": "failed", "stdout": "STDOUT\nSTDERR\n" } { "type": "suite", "event": "failed", "passed": 0, "failed": 1, "ignored": 0, "measured": 0, "filtered_out": 0, "exec_time": 0.00040313 } ``` **After:** ```console $ target/debug/deps/test-???????????????? -Z unstable-options --format=json { "type": "suite", "event": "started", "test_count": 1 } { "type": "test", "event": "started", "name": "test" } { "type": "test", "name": "test", "event": "failed", "stdout": "STDOUT\nSTDERR\nError: \"RESULT\"" } { "type": "suite", "event": "failed", "passed": 0, "failed": 1, "ignored": 0, "measured": 0, "filtered_out": 0, "exec_time": 0.000261894 } ```
2 parents c4187f2 + 293f662 commit 13baeee

File tree

3 files changed

+24
-9
lines changed

3 files changed

+24
-9
lines changed

library/std/src/io/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ use crate::sys_common::memchr;
262262

263263
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
264264
pub use self::buffered::WriterPanicked;
265+
pub(crate) use self::stdio::attempt_print_to_stderr;
265266
#[unstable(feature = "internal_output_capture", issue = "none")]
266267
#[doc(no_inline, hidden)]
267268
pub use self::stdio::set_output_capture;

library/std/src/io/stdio.rs

+22-6
Original file line numberDiff line numberDiff line change
@@ -999,7 +999,18 @@ fn print_to<T>(args: fmt::Arguments<'_>, global_s: fn() -> T, label: &str)
999999
where
10001000
T: Write,
10011001
{
1002-
if OUTPUT_CAPTURE_USED.load(Ordering::Relaxed)
1002+
if print_to_buffer_if_capture_used(args) {
1003+
// Successfully wrote to capture buffer.
1004+
return;
1005+
}
1006+
1007+
if let Err(e) = global_s().write_fmt(args) {
1008+
panic!("failed printing to {label}: {e}");
1009+
}
1010+
}
1011+
1012+
fn print_to_buffer_if_capture_used(args: fmt::Arguments<'_>) -> bool {
1013+
OUTPUT_CAPTURE_USED.load(Ordering::Relaxed)
10031014
&& OUTPUT_CAPTURE.try_with(|s| {
10041015
// Note that we completely remove a local sink to write to in case
10051016
// our printing recursively panics/prints, so the recursive
@@ -1009,14 +1020,19 @@ where
10091020
s.set(Some(w));
10101021
})
10111022
}) == Ok(Some(()))
1012-
{
1013-
// Successfully wrote to capture buffer.
1023+
}
1024+
1025+
/// Used by impl Termination for Result to print error after `main` or a test
1026+
/// has returned. Should avoid panicking, although we can't help it if one of
1027+
/// the Display impls inside args decides to.
1028+
pub(crate) fn attempt_print_to_stderr(args: fmt::Arguments<'_>) {
1029+
if print_to_buffer_if_capture_used(args) {
10141030
return;
10151031
}
10161032

1017-
if let Err(e) = global_s().write_fmt(args) {
1018-
panic!("failed printing to {label}: {e}");
1019-
}
1033+
// Ignore error if the write fails, for example because stderr is already
1034+
// closed. There is not much point panicking at this point.
1035+
let _ = stderr().write_fmt(args);
10201036
}
10211037

10221038
#[unstable(

library/std/src/process.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -2200,9 +2200,7 @@ impl<T: Termination, E: fmt::Debug> Termination for Result<T, E> {
22002200
match self {
22012201
Ok(val) => val.report(),
22022202
Err(err) => {
2203-
// Ignore error if the write fails, for example because stderr is
2204-
// already closed. There is not much point panicking at this point.
2205-
let _ = writeln!(io::stderr(), "Error: {err:?}");
2203+
io::attempt_print_to_stderr(format_args_nl!("Error: {err:?}"));
22062204
ExitCode::FAILURE
22072205
}
22082206
}

0 commit comments

Comments
 (0)