From d4da0e0fc5e14f176150cb6362081ea472227c60 Mon Sep 17 00:00:00 2001 From: Anthony DePasquale Date: Sat, 8 Aug 2026 10:45:19 +0200 Subject: [PATCH 1/3] tests: enable cat integration coverage on WASI --- .github/workflows/wasi.yml | 4 ++-- src/uu/cat/src/platform/mod.rs | 6 +++++- tests/by-util/test_cat.rs | 22 ++++++++++++++++++++-- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/.github/workflows/wasi.yml b/.github/workflows/wasi.yml index 5072ccc573b..621d904d460 100644 --- a/.github/workflows/wasi.yml +++ b/.github/workflows/wasi.yml @@ -60,7 +60,7 @@ jobs: # Tests incompatible with WASI are annotated with # #[cfg_attr(wasi_runner, ignore)] in the test source files. # TODO: add integration tests for these tools as WASI support is extended: - # arch b2sum cat cksum cp csplit date dir dircolors fmt join + # arch b2sum cksum cp csplit date dir dircolors fmt join # ls md5sum mkdir mv nproc pathchk pr printenv ptx pwd readlink # realpath rm rmdir seq sha1sum sha224sum sha256sum sha384sum # sha512sum shred sleep sort split tail touch tsort uname uniq @@ -69,7 +69,7 @@ jobs: UUTESTS_WASM_RUNNER=wasmtime \ cargo test --test tests -- \ test_base32:: test_base64:: test_basenc:: test_basename:: \ - test_comm:: test_cut:: test_dirname:: test_echo:: \ + test_cat:: test_comm:: test_cut:: test_dirname:: test_echo:: \ test_expand:: test_factor:: test_false:: test_fold:: \ test_head:: test_link:: test_ln:: test_nl:: test_numfmt:: \ test_od:: test_paste:: test_printf:: test_shuf:: test_sum:: \ diff --git a/src/uu/cat/src/platform/mod.rs b/src/uu/cat/src/platform/mod.rs index cde1ee96cbb..77255eff14a 100644 --- a/src/uu/cat/src/platform/mod.rs +++ b/src/uu/cat/src/platform/mod.rs @@ -9,7 +9,11 @@ pub use self::unix::is_safe_overwrite; #[cfg(windows)] pub use self::windows::is_safe_overwrite; -// WASI: no fstat-based device/inode checks available; assume safe. +// WASI: when stdout is inherited from a host file descriptor, wasmtime +// reports its fstat as all-zero (st_dev == st_ino == 0), so the dev/inode +// comparison against any input file descriptor can never match. There is +// no reliable way to detect unsafe overwrite here; assume safe rather than +// risk a spurious error. #[cfg(target_os = "wasi")] pub fn is_safe_overwrite(_input: &I, _output: &O) -> bool { true diff --git a/tests/by-util/test_cat.rs b/tests/by-util/test_cat.rs index e0baf5095b0..1158078e9d1 100644 --- a/tests/by-util/test_cat.rs +++ b/tests/by-util/test_cat.rs @@ -2,7 +2,7 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore NOFILE nonewline cmdline +// spell-checker:ignore NOFILE nonewline cmdline setrlimit ELOOP #[cfg(any(target_os = "linux", target_os = "android"))] use rlimit::Resource; @@ -87,6 +87,7 @@ fn test_no_options_big_input() { #[test] #[cfg(unix)] +#[cfg_attr(wasi_runner, ignore = "WASI: no FIFO/mkfifo support")] fn test_fifo_symlink() { use std::io::Write; use std::thread; @@ -121,6 +122,7 @@ fn test_fifo_symlink() { // TODO(#7542): Re-enable on Android once we figure out why setting limit is broken. // #[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(target_os = "linux")] +#[cfg_attr(wasi_runner, ignore = "WASI: rlimit/setrlimit not supported")] fn test_closes_file_descriptors() { // Each file creates a pipe, which has two file descriptors. // If they are not closed then five is certainly too many. @@ -138,6 +140,7 @@ fn test_closes_file_descriptors() { #[test] #[cfg(unix)] +#[cfg_attr(wasi_runner, ignore = "WASI: no pipe/signal support")] fn test_broken_pipe() { let mut cmd = new_ucmd!(); let mut child = cmd @@ -514,6 +517,7 @@ fn test_squeeze_blank_before_numbering() { /// This tests reading from Unix character devices #[test] #[cfg(unix)] +#[cfg_attr(wasi_runner, ignore = "WASI sandbox: host paths not visible")] fn test_dev_random() { #[cfg(any(target_os = "linux", target_os = "android"))] const DEV_RANDOM: &str = "/dev/urandom"; @@ -544,6 +548,7 @@ fn test_dev_random() { /// Wikipedia says there is support on Linux, FreeBSD, and `NetBSD`. #[test] #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))] +#[cfg_attr(wasi_runner, ignore = "WASI sandbox: host paths not visible")] fn test_dev_full() { let mut proc = new_ucmd!() .set_stdout(Stdio::piped()) @@ -559,6 +564,7 @@ fn test_dev_full() { #[test] #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "netbsd"))] +#[cfg_attr(wasi_runner, ignore = "WASI sandbox: host paths not visible")] fn test_dev_full_show_all() { let buf_len = 2048; let mut proc = new_ucmd!() @@ -581,6 +587,7 @@ fn test_dev_full_show_all() { // without additional flush output gets reversed. #[test] #[cfg(target_os = "linux")] +#[cfg_attr(wasi_runner, ignore = "WASI sandbox: host paths not visible")] fn test_write_fast_fallthrough_uses_flush() { const PROC_INIT_CMDLINE: &str = "/proc/1/cmdline"; let cmdline = read_to_string(PROC_INIT_CMDLINE).unwrap(); @@ -593,6 +600,7 @@ fn test_write_fast_fallthrough_uses_flush() { #[test] #[cfg(unix)] +#[cfg_attr(wasi_runner, ignore = "WASI: no Unix domain socket support")] fn test_domain_socket() { use std::os::unix::net::UnixListener; @@ -607,6 +615,7 @@ fn test_domain_socket() { } #[test] +#[cfg_attr(wasi_runner, ignore = "WASI sandbox: host paths not visible")] fn test_write_to_self_empty() { // it's ok if the input file is also the output file if it's empty let s = TestScenario::new(util_name!()); @@ -622,6 +631,7 @@ fn test_write_to_self_empty() { } #[test] +#[cfg_attr(wasi_runner, ignore = "WASI: cannot detect unsafe overwrite")] fn test_write_to_self() { let s = TestScenario::new(util_name!()); let file_path = s.fixtures.plus("first_file"); @@ -678,6 +688,7 @@ fn test_successful_write_to_read_write_self() { /// /// `cat fx fx3 1<>fx3` #[test] +#[cfg_attr(wasi_runner, ignore = "WASI: cannot detect unsafe overwrite")] fn test_failed_write_to_read_write_self() { let (at, mut ucmd) = at_and_ucmd!(); at.write("fx", "g"); @@ -703,6 +714,10 @@ fn test_failed_write_to_read_write_self() { #[test] #[cfg(unix)] #[cfg(not(target_os = "openbsd"))] +#[cfg_attr( + wasi_runner, + ignore = "WASI: symlink loop traversal does not surface ELOOP ('Too many levels of symbolic links')" +)] fn test_error_loop() { let (at, mut ucmd) = at_and_ucmd!(); at.symlink_file("2", "1"); @@ -726,6 +741,7 @@ fn test_u_ignored() { #[test] #[cfg(unix)] +#[cfg_attr(wasi_runner, ignore = "WASI: errno/error-message mismatches")] fn test_write_fast_read_error() { use std::os::unix::fs::PermissionsExt; @@ -746,6 +762,7 @@ fn test_write_fast_read_error() { #[test] #[cfg(target_os = "linux")] +#[cfg_attr(wasi_runner, ignore = "WASI: argv/filenames must be valid UTF-8")] fn test_cat_non_utf8_paths() { use std::ffi::OsStr; use std::os::unix::ffi::OsStrExt; @@ -769,7 +786,8 @@ fn test_cat_non_utf8_paths() { } #[test] -#[cfg(unix)] +#[cfg(target_os = "linux")] +#[cfg_attr(wasi_runner, ignore = "WASI: cannot detect unsafe overwrite")] fn test_appending_same_input_output() { let (at, mut ucmd) = at_and_ucmd!(); From 66acfed0786b347c387376f2e6736dd78d0623d1 Mon Sep 17 00:00:00 2001 From: Anthony DePasquale Date: Sat, 8 Aug 2026 11:43:42 +0200 Subject: [PATCH 2/3] tests/cat: preserve Unix test coverage --- tests/by-util/test_cat.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/by-util/test_cat.rs b/tests/by-util/test_cat.rs index 1158078e9d1..844261b1971 100644 --- a/tests/by-util/test_cat.rs +++ b/tests/by-util/test_cat.rs @@ -786,7 +786,7 @@ fn test_cat_non_utf8_paths() { } #[test] -#[cfg(target_os = "linux")] +#[cfg(unix)] #[cfg_attr(wasi_runner, ignore = "WASI: cannot detect unsafe overwrite")] fn test_appending_same_input_output() { let (at, mut ucmd) = at_and_ucmd!(); From 57eef7d1417f870be46d1916faa7f8e70596fd8e Mon Sep 17 00:00:00 2001 From: Anthony DePasquale Date: Sat, 8 Aug 2026 12:31:31 +0200 Subject: [PATCH 3/3] tests: run empty self-copy cat case on WASI --- tests/by-util/test_cat.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/by-util/test_cat.rs b/tests/by-util/test_cat.rs index 844261b1971..b4394c1cd24 100644 --- a/tests/by-util/test_cat.rs +++ b/tests/by-util/test_cat.rs @@ -615,7 +615,6 @@ fn test_domain_socket() { } #[test] -#[cfg_attr(wasi_runner, ignore = "WASI sandbox: host paths not visible")] fn test_write_to_self_empty() { // it's ok if the input file is also the output file if it's empty let s = TestScenario::new(util_name!()); @@ -627,7 +626,7 @@ fn test_write_to_self_empty() { .open(&file_path) .unwrap(); - s.ucmd().set_stdout(file).arg(&file_path).succeeds(); + s.ucmd().set_stdout(file).arg("file.txt").succeeds(); } #[test]