mirror of
https://github.com/sharkdp/bat.git
synced 2026-10-26 20:16:33 +00:00
Exit quietly on broken pipe in auxiliary output paths
The main output path has exited with status 0 on EPIPE since #232, but auxiliary outputs still used println!/print!/io::stdout().flush().unwrap() and panicked (exit code 101) when the reader of a piped stdout went away early, e.g. 'bat --completion bash | head -n0'. Route them through the existing convention instead: write!/writeln! to io::stdout() and '?' so that Error::Io(BrokenPipe) reaches default_error_handler, which exits with status 0. This covers --completion, --config-file, --diagnostic (bugreport's print() becomes format() + writeln!), --generate-config-file, the 'cache' subcommand progress messages, the OSC title sequence of --set-terminal-title, and the 'built without feature' notices. Closes #4034
This commit is contained in:
1 parent
4608fc959a
commit
a9b112b6fe
6 files changed
+133
-50
No files matched your search
@@ -25,10 +25,8 @@ mod unix {
|
||||
use unix::*;
|
||||
|
||||
mod utils;
|
||||
use utils::command::{bat, bat_with_config};
|
||||
use utils::command::{bat, bat_raw_command, bat_raw_command_with_config, bat_with_config};
|
||||
|
||||
#[cfg(unix)]
|
||||
use utils::command::bat_raw_command;
|
||||
use utils::mocked_pagers;
|
||||
|
||||
const EXAMPLES_DIR: &str = "tests/examples";
|
||||
@@ -60,6 +58,67 @@ fn stdin() {
|
||||
.stdout("foo\nbar\n");
|
||||
}
|
||||
|
||||
/// Runs `bat` with `args` while its stdout is a pipe whose read end has
|
||||
/// already been closed, so the very first write fails with `BrokenPipe`
|
||||
/// (EPIPE on Unix, `ERROR_NO_DATA` on Windows).
|
||||
fn assert_quiet_exit_when_stdout_is_closed(command: &mut std::process::Command, args: &[&str]) {
|
||||
use std::process::Stdio;
|
||||
|
||||
let (pipe_reader, pipe_writer) = std::io::pipe().expect("could not create a pipe");
|
||||
drop(pipe_reader);
|
||||
|
||||
let output = command
|
||||
.args(args)
|
||||
.stdin(Stdio::null())
|
||||
.stdout(pipe_writer)
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
.and_then(|child| child.wait_with_output())
|
||||
.expect("could not run bat");
|
||||
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"bat {args:?} should exit quietly when stdout is closed, status: {}, stderr: {}",
|
||||
output.status,
|
||||
String::from_utf8_lossy(&output.stderr),
|
||||
);
|
||||
assert!(
|
||||
output.stderr.is_empty(),
|
||||
"bat {args:?} should not print to stderr when stdout is closed, stderr: {}",
|
||||
String::from_utf8_lossy(&output.stderr),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auxiliary_output_does_not_panic_on_closed_stdout() {
|
||||
// Auxiliary outputs used to be written via `println!`, which panics when
|
||||
// the reader of a piped stdout is already gone (e.g. `bat --config-file |
|
||||
// head -n 0`). The main output path has exited quietly on `BrokenPipe`
|
||||
// since #232; auxiliary outputs should behave the same way.
|
||||
assert_quiet_exit_when_stdout_is_closed(&mut bat_raw_command(), &["--config-file"]);
|
||||
assert_quiet_exit_when_stdout_is_closed(&mut bat_raw_command(), &["--diagnostic"]);
|
||||
|
||||
// The `cache` subcommand is only enabled when the working directory does
|
||||
// not contain a file named `cache` (see `cache_clear`), and it conflicts
|
||||
// with top-level flags like `--no-config`. Run it from a temporary
|
||||
// directory that doubles as `BAT_CACHE_PATH`, leaving the real cache alone.
|
||||
let temp_dir = tempdir().expect("could not create a temporary directory");
|
||||
let mut command = bat_raw_command_with_config();
|
||||
command
|
||||
.current_dir(temp_dir.path())
|
||||
.env("BAT_CONFIG_PATH", temp_dir.path().join("config"))
|
||||
.env("BAT_CACHE_PATH", temp_dir.path());
|
||||
assert_quiet_exit_when_stdout_is_closed(&mut command, &["cache", "--clear"]);
|
||||
}
|
||||
|
||||
#[cfg(feature = "application")]
|
||||
#[test]
|
||||
fn completion_does_not_panic_on_closed_stdout() {
|
||||
for shell in ["bash", "fish", "ps1", "zsh"] {
|
||||
assert_quiet_exit_when_stdout_is_closed(&mut bat_raw_command(), &["--completion", shell]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn concatenate() {
|
||||
bat()
|
||||
|
||||
Reference in new issue
Block a user