Skip to content

Commit b9b61f8

Browse files
committed
tests/sort: avoid bash by simulating broken pipe in Rust
1 parent 5c78e4e commit b9b61f8

File tree

1 file changed

+52
-10
lines changed

1 file changed

+52
-10
lines changed

tests/by-util/test_sort.rs

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,17 +1223,59 @@ fn test_sigpipe_panic() {
12231223
// but uutils currently returns 1 in that mode.
12241224
#[test]
12251225
#[cfg(unix)]
1226-
fn test_broken_pipe_exits_141_no_stderr() {
1226+
fn test_broken_pipe_exits_sigpipe_no_stderr() {
1227+
use std::io::{BufRead, BufReader, Write};
1228+
use std::os::unix::process::ExitStatusExt;
1229+
use std::process::{Command, Stdio};
1230+
12271231
let scene = TestScenario::new(util_name!());
1228-
let bin = scene.bin_path.clone().into_os_string();
1229-
scene
1230-
.cmd("bash")
1231-
.arg("-c")
1232-
.arg(r#"{ seq 1 10000 | "$BIN" sort -n 2>err | head -n1; }; echo ${PIPESTATUS[1]} >code"#)
1233-
.env("BIN", &bin)
1234-
.succeeds();
1235-
assert!(scene.fixtures.read("err").is_empty());
1236-
assert_eq!(scene.fixtures.read("code").trim(), "141");
1232+
let bin = scene.bin_path.clone();
1233+
1234+
// Run multicall: coreutils sort -n
1235+
let mut child = Command::new(bin)
1236+
.arg("sort")
1237+
.arg("-n")
1238+
.stdin(Stdio::piped())
1239+
.stdout(Stdio::piped())
1240+
.stderr(Stdio::piped())
1241+
.spawn()
1242+
.expect("spawn sort");
1243+
1244+
// Feed enough input that sort will try to write output.
1245+
{
1246+
let mut stdin = child.stdin.take().expect("take stdin");
1247+
for i in 1..=10000 {
1248+
writeln!(stdin, "{i}").expect("write stdin");
1249+
}
1250+
// drop(stdin) closes stdin
1251+
}
1252+
1253+
// Read a single output line, then close stdout to trigger SIGPIPE on the child
1254+
// the next time it writes (like `| head -n1`).
1255+
{
1256+
let stdout = child.stdout.take().expect("take stdout");
1257+
let mut r = BufReader::new(stdout);
1258+
let mut line = String::new();
1259+
let _ = r.read_line(&mut line).expect("read first line");
1260+
// drop(r) closes the read end
1261+
}
1262+
1263+
let output = child.wait_with_output().expect("wait");
1264+
1265+
// No "Broken pipe" diagnostic.
1266+
assert!(
1267+
output.stderr.is_empty(),
1268+
"expected empty stderr, got: {:?}",
1269+
String::from_utf8_lossy(&output.stderr)
1270+
);
1271+
1272+
// Shells report SIGPIPE as 128+13=141, but Rust exposes it as a signal.
1273+
assert_eq!(
1274+
output.status.signal(),
1275+
Some(libc::SIGPIPE),
1276+
"expected SIGPIPE; status={:?}",
1277+
output.status
1278+
);
12371279
}
12381280

12391281
#[test]

0 commit comments

Comments
 (0)