diff options
| author | Dylan DPC <99973273+Dylan-DPC@users.noreply.github.com> | 2022-10-10 13:43:41 +0530 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-10-10 13:43:41 +0530 |
| commit | 302bf31826cdbbbe9a8ed9e0c4c7149d9010bfdd (patch) | |
| tree | 990e2de1a6a4f332c0197341b7e734e0f583b865 /library/std/src | |
| parent | 58d533dfc12f9ecd2bf1afa2837b6221a8ddbfdf (diff) | |
| parent | 293f662ca94d6f1eac749da55f5ff924c87fd1d5 (diff) | |
Rollup merge of #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 }
```
Diffstat (limited to 'library/std/src')
| -rw-r--r-- | library/std/src/io/mod.rs | 1 | ||||
| -rw-r--r-- | library/std/src/io/stdio.rs | 28 | ||||
| -rw-r--r-- | library/std/src/process.rs | 4 |
3 files changed, 24 insertions, 9 deletions
diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index eeace2c43c4..cceef539b90 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -262,6 +262,7 @@ use crate::sys_common::memchr; #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] pub use self::buffered::WriterPanicked; +pub(crate) use self::stdio::attempt_print_to_stderr; #[unstable(feature = "internal_output_capture", issue = "none")] #[doc(no_inline, hidden)] pub use self::stdio::set_output_capture; diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 2dc12a18a8a..4ccb2bf3231 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -999,7 +999,18 @@ fn print_to<T>(args: fmt::Arguments<'_>, global_s: fn() -> T, label: &str) where T: Write, { - if OUTPUT_CAPTURE_USED.load(Ordering::Relaxed) + if print_to_buffer_if_capture_used(args) { + // Successfully wrote to capture buffer. + return; + } + + if let Err(e) = global_s().write_fmt(args) { + panic!("failed printing to {label}: {e}"); + } +} + +fn print_to_buffer_if_capture_used(args: fmt::Arguments<'_>) -> bool { + OUTPUT_CAPTURE_USED.load(Ordering::Relaxed) && OUTPUT_CAPTURE.try_with(|s| { // Note that we completely remove a local sink to write to in case // our printing recursively panics/prints, so the recursive @@ -1009,14 +1020,19 @@ where s.set(Some(w)); }) }) == Ok(Some(())) - { - // Successfully wrote to capture buffer. +} + +/// Used by impl Termination for Result to print error after `main` or a test +/// has returned. Should avoid panicking, although we can't help it if one of +/// the Display impls inside args decides to. +pub(crate) fn attempt_print_to_stderr(args: fmt::Arguments<'_>) { + if print_to_buffer_if_capture_used(args) { return; } - if let Err(e) = global_s().write_fmt(args) { - panic!("failed printing to {label}: {e}"); - } + // Ignore error if the write fails, for example because stderr is already + // closed. There is not much point panicking at this point. + let _ = stderr().write_fmt(args); } #[unstable( diff --git a/library/std/src/process.rs b/library/std/src/process.rs index d91d4fa64ca..e08ea8f9a5f 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -2200,9 +2200,7 @@ impl<T: Termination, E: fmt::Debug> Termination for Result<T, E> { match self { Ok(val) => val.report(), Err(err) => { - // Ignore error if the write fails, for example because stderr is - // already closed. There is not much point panicking at this point. - let _ = writeln!(io::stderr(), "Error: {err:?}"); + io::attempt_print_to_stderr(format_args_nl!("Error: {err:?}")); ExitCode::FAILURE } } |
