mirror of
https://github.com/sharkdp/hyperfine.git
synced 2026-11-01 17:00:18 +00:00
Handle broken pipe errors on stdout gracefully (#932)
Replace `println!` calls on the results paths with `writeln!` on `io::stdout()` and propagate the `io::Error` to `main`, where `ErrorKind::BrokenPipe` is turned into a quiet `exit(0)`. This matches the conventional behavior of command-line tools when their output pipe is closed early (e.g. `hyperfine ... | head -n 0`), and fixes panics in all run-output paths: the per-benchmark header and results, the speed comparison summary, and `-`-style exports to stdout. The warnings and "Note" outputs to stderr are converted the same way. --------- Co-authored-by: Mathjk <Mathjk@users.noreply.github.com> Co-authored-by: David Peter <mail@david-peter.de>
This commit is contained in:
7 files changed
+118
-30
No files matched your search
@@ -1,5 +1,5 @@
|
||||
mod common;
|
||||
use common::hyperfine;
|
||||
use common::{hyperfine, hyperfine_raw_command};
|
||||
|
||||
use predicates::prelude::*;
|
||||
|
||||
@@ -33,6 +33,47 @@ fn one_run_is_supported() {
|
||||
.success();
|
||||
}
|
||||
|
||||
/// Regression test: hyperfine must not panic when writing to a closed
|
||||
/// stdout pipe (e.g. `hyperfine ... | head -n 1` or a downstream process
|
||||
/// that terminates early). A `BrokenPipe` error should result in a quiet
|
||||
/// exit with code 0 instead of a `println!` panic.
|
||||
#[test]
|
||||
fn exits_quietly_when_stdout_is_closed() {
|
||||
use std::process::Stdio;
|
||||
|
||||
for args in [
|
||||
vec!["--runs=1", "echo dummy benchmark"],
|
||||
vec!["--runs=1", "--export-json=-", "echo dummy benchmark"],
|
||||
vec!["--runs=1", "echo dummy benchmark", "echo second benchmark"],
|
||||
] {
|
||||
// A pipe whose read end has been closed before hyperfine is even
|
||||
// started: every write to it will fail with `BrokenPipe`.
|
||||
let (reader, writer) = std::io::pipe().expect("failed to create pipe");
|
||||
drop(reader);
|
||||
|
||||
let output = hyperfine_raw_command()
|
||||
.args(&args)
|
||||
.stdout(writer)
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
.expect("failed to spawn hyperfine")
|
||||
.wait_with_output()
|
||||
.expect("failed to wait for hyperfine");
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
|
||||
assert!(
|
||||
!stderr.contains("panicked"),
|
||||
"hyperfine panicked while writing to a closed pipe:\n{}",
|
||||
stderr
|
||||
);
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"hyperfine did not exit cleanly; stderr:\n{}",
|
||||
stderr
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_run_commands_without_a_shell() {
|
||||
hyperfine()
|
||||
|
||||
Reference in new issue
Block a user