From 0ca9039fd7483c7b03dde30a666be97df750cecc Mon Sep 17 00:00:00 2001 From: oech3 <79379754+oech3@users.noreply.github.com> Date: Mon, 27 Apr 2026 14:11:02 +0900 Subject: [PATCH] tee: remove unsafe from test code --- Cargo.lock | 1 + Cargo.toml | 1 + src/uu/tee/Cargo.toml | 3 ++ tests/by-util/test_tee.rs | 101 +++++++++++++------------------------- 4 files changed, 38 insertions(+), 68 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 124fba7bb..bc0e3717f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4276,6 +4276,7 @@ version = "0.8.0" dependencies = [ "clap", "fluent", + "rustix", "uucore", ] diff --git a/Cargo.toml b/Cargo.toml index 4bbd582e4..b3ebd336e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -672,6 +672,7 @@ nix = { workspace = true, features = [ "user", ] } rlimit = { workspace = true } +rustix.workspace = true [build-dependencies] diff --git a/src/uu/tee/Cargo.toml b/src/uu/tee/Cargo.toml index 70246151c..703b43089 100644 --- a/src/uu/tee/Cargo.toml +++ b/src/uu/tee/Cargo.toml @@ -24,6 +24,9 @@ clap = { workspace = true } uucore = { workspace = true, features = ["libc", "parser", "signals"] } fluent = { workspace = true } +[target.'cfg(unix)'.dev-dependencies] +rustix = { workspace = true } + [[bin]] name = "tee" path = "src/main.rs" diff --git a/tests/by-util/test_tee.rs b/tests/by-util/test_tee.rs index 10370f7e8..50c8dbf49 100644 --- a/tests/by-util/test_tee.rs +++ b/tests/by-util/test_tee.rs @@ -263,38 +263,19 @@ mod linux_only { use uutests::new_ucmd; fn make_broken_pipe() -> File { - use libc::c_int; - use std::os::unix::io::FromRawFd; - - let mut fds: [c_int; 2] = [0, 0]; - assert_eq!( - unsafe { libc::pipe(std::ptr::from_mut::(&mut fds[0])) }, - 0, - "Failed to create pipe" - ); - + let (read, write) = rustix::pipe::pipe().expect("Failed to create pipe"); // Drop the read end of the pipe - let _ = unsafe { File::from_raw_fd(fds[0]) }; - + drop(read); // Make the write end of the pipe into a Rust File - unsafe { File::from_raw_fd(fds[1]) } + write.into() } fn make_hanging_read() -> File { - use libc::c_int; - use std::os::unix::io::FromRawFd; - - let mut fds: [c_int; 2] = [0, 0]; - assert_eq!( - unsafe { libc::pipe(std::ptr::from_mut::(&mut fds[0])) }, - 0, - "Failed to create pipe" - ); - + let (read, write) = rustix::pipe::pipe().expect("Failed to create pipe"); // PURPOSELY leak the write end of the pipe, so the read end hangs. - + std::mem::forget(write); // Return the read end of the pipe - unsafe { File::from_raw_fd(fds[0]) } + read.into() } fn run_tee(proc: &mut UCommand) -> (String, CmdResult) { @@ -745,56 +726,40 @@ fn test_output_error_flag_without_value_defaults_warn_nopipe() { #[cfg(all(unix, not(target_os = "freebsd")))] #[test] fn test_output_error_presence_only_broken_pipe_unix() { - use std::fs::File; - use std::os::unix::io::FromRawFd; + let (read, write) = rustix::pipe::pipe().expect("Failed to create pipe"); + // Close the read end to simulate a broken pipe on stdout + drop(read); + let write: std::fs::File = write.into(); + let content = (0..10_000).map(|_| "x").collect::(); + let result = new_ucmd!() + .arg("--output-error") // presence-only flag + .set_stdout(write) + .pipe_in(content.as_bytes()) + .run(); - 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]); - let write_end = File::from_raw_fd(fds[1]); - - let content = (0..10_000).map(|_| "x").collect::(); - let result = new_ucmd!() - .arg("--output-error") // presence-only flag - .set_stdout(write_end) - .pipe_in(content.as_bytes()) - .run(); - - // Assert that a status was produced (i.e., process exited) and no crash occurred. - assert!(result.try_exit_status().is_some(), "process did not exit"); - } + // Assert that a status was produced (i.e., process exited) and no crash occurred. + assert!(result.try_exit_status().is_some(), "process did not exit"); } // Skip on FreeBSD due to repeated CI hangs in FreeBSD VM (see PR #8684) #[cfg(all(unix, not(target_os = "freebsd")))] #[test] fn test_broken_pipe_early_termination_stdout_only() { - use std::fs::File; - use std::os::unix::io::FromRawFd; - - // Create a broken stdout by creating a pipe and dropping the read end - 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 immediately to simulate a broken pipe - let _read_end = File::from_raw_fd(fds[0]); - let write_end = File::from_raw_fd(fds[1]); - - let content = (0..10_000).map(|_| "x").collect::(); - let mut proc = new_ucmd!(); - let result = proc - .set_stdout(write_end) - .ignore_stdin_write_error() - .pipe_in(content.as_bytes()) - .run(); - - // GNU tee exits nonzero on broken pipe unless configured otherwise; implementation - // details vary by mode, but we should not panic and should return an exit status. - // Assert that a status was produced (i.e., process exited) and no crash occurred. - assert!(result.try_exit_status().is_some(), "process did not exit"); - } + let (read, write) = rustix::pipe::pipe().expect("Failed to create pipe"); + // Create a broken stdout + drop(read); + let write: std::fs::File = write.into(); + let content = (0..10_000).map(|_| "x").collect::(); + let mut proc = new_ucmd!(); + let result = proc + .set_stdout(write) + .ignore_stdin_write_error() + .pipe_in(content.as_bytes()) + .run(); + // GNU tee exits nonzero on broken pipe unless configured otherwise; implementation + // details vary by mode, but we should not panic and should return an exit status. + // Assert that a status was produced (i.e., process exited) and no crash occurred. + assert!(result.try_exit_status().is_some(), "process did not exit"); } #[test]