From 130893a19a1b385568cb07168766c9b20a4df035 Mon Sep 17 00:00:00 2001 From: naoNao89 <90588855+naoNao89@users.noreply.github.com> Date: Fri, 3 Oct 2025 16:39:55 +0700 Subject: [PATCH 1/2] tests(cat,stdbuf): Add broken-pipe robustness tests (#4627) Add test coverage for cat and stdbuf broken pipe handling: **cat tests:** - test_cat_broken_pipe_nonzero_and_message: Verify cat handles SIGPIPE without hanging or crashing and exits with nonzero status **stdbuf tests:** - test_permission_external_missing_lib: Handle missing external libstdbuf - test_no_such_external_missing_lib: Error handling in external lib mode - Guard existing tests with #[cfg(not(feature = "feat_external_libstdbuf"))] These tests address write-errors.sh from GNU test suite (#4627) and improve cross-platform robustness for stdbuf feat_external_libstdbuf builds. --- tests/by-util/test_cat.rs | 26 ++++++++++++++++++++++++++ tests/by-util/test_stdbuf.rs | 29 +++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/tests/by-util/test_cat.rs b/tests/by-util/test_cat.rs index c809231c7b0..ea3250e0526 100644 --- a/tests/by-util/test_cat.rs +++ b/tests/by-util/test_cat.rs @@ -18,6 +18,32 @@ use uutests::util::TestScenario; use uutests::util::vec_of_size; use uutests::util_name; +#[cfg(unix)] +// Verify cat handles a broken pipe on stdout without hanging or crashing and exits nonzero +#[test] +fn test_cat_broken_pipe_nonzero_and_message() { + use std::fs::File; + use std::os::unix::io::FromRawFd; + use uutests::new_ucmd; + + unsafe { + let mut fds: [libc::c_int; 2] = [0, 0]; + assert_eq!(libc::pipe(fds.as_mut_ptr()), 0, "Failed to create pipe"); + // Close the read end to simulate a broken pipe on stdout + let read_end = File::from_raw_fd(fds[0]); + // Explicitly drop the read-end so writers see EPIPE instead of blocking on a full pipe + std::mem::drop(read_end); + let write_end = File::from_raw_fd(fds[1]); + + let content = (0..10000).map(|_| "x").collect::(); + // On Unix, SIGPIPE should lead to a non-zero exit; ensure process exits and fails + new_ucmd!() + .set_stdout(write_end) + .pipe_in(content.as_bytes()) + .fails(); + } +} + #[test] fn test_output_simple() { new_ucmd!() diff --git a/tests/by-util/test_stdbuf.rs b/tests/by-util/test_stdbuf.rs index 8c3fef5870d..d2421cfbef0 100644 --- a/tests/by-util/test_stdbuf.rs +++ b/tests/by-util/test_stdbuf.rs @@ -15,6 +15,7 @@ fn invalid_input() { new_ucmd!().arg("-/").fails_with_code(125); } +#[cfg(not(feature = "feat_external_libstdbuf"))] #[test] fn test_permission() { new_ucmd!() @@ -24,6 +25,23 @@ fn test_permission() { .stderr_contains("Permission denied"); } +// TODO: Tests below are brittle when feat_external_libstdbuf is enabled and libstdbuf is not installed. +// Align stdbuf with GNU search order to enable deterministic testing without installation: +// 1) search for libstdbuf next to the stdbuf binary, 2) then in LIBSTDBUF_DIR, 3) then system locations. +// After implementing this, rework tests to provide a temporary symlink rather than depending on system state. + +#[cfg(feature = "feat_external_libstdbuf")] +#[test] +fn test_permission_external_missing_lib() { + // When built with external libstdbuf, running stdbuf fails early if lib is not installed + new_ucmd!() + .arg("-o1") + .arg(".") + .fails_with_code(1) + .stderr_contains("External libstdbuf not found"); +} + +#[cfg(not(feature = "feat_external_libstdbuf"))] #[test] fn test_no_such() { new_ucmd!() @@ -33,6 +51,17 @@ fn test_no_such() { .stderr_contains("No such file or directory"); } +#[cfg(feature = "feat_external_libstdbuf")] +#[test] +fn test_no_such_external_missing_lib() { + // With external lib mode and missing installation, stdbuf fails before spawning the command + new_ucmd!() + .arg("-o1") + .arg("no_such") + .fails_with_code(1) + .stderr_contains("External libstdbuf not found"); +} + // Disabled on x86_64-unknown-linux-musl because the cross-rs Docker image for this target // does not provide musl-compiled system utilities (like head), leading to dynamic linker errors // when preloading musl-compiled libstdbuf.so into glibc-compiled binaries. Same thing for FreeBSD. From 4999753b6e98ad617d5dba39d73941e5f790aa4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=E1=BA=A3=20th=E1=BA=BF=20gi=E1=BB=9Bi=20l=C3=A0=20Rust?= <90588855+naoNao89@users.noreply.github.com> Date: Fri, 3 Oct 2025 20:56:05 +0700 Subject: [PATCH 2/2] cspell: whitelist EPIPE to fix Style/spelling on PR #8798 (split from #8684 / tracked in #4627) --- .vscode/cspell.dictionaries/workspace.wordlist.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscode/cspell.dictionaries/workspace.wordlist.txt b/.vscode/cspell.dictionaries/workspace.wordlist.txt index 6fd3dadcea3..e1fffb92555 100644 --- a/.vscode/cspell.dictionaries/workspace.wordlist.txt +++ b/.vscode/cspell.dictionaries/workspace.wordlist.txt @@ -128,6 +128,7 @@ ENOSYS ENOTEMPTY EOPNOTSUPP EPERM +EPIPE EROFS # * vars/fcntl