diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml
index ec6493e9ed0..7efd1e59084 100644
--- a/.github/workflows/CICD.yml
+++ b/.github/workflows/CICD.yml
@@ -381,6 +381,7 @@ jobs:
- { os: ubuntu-latest , target: x86_64-unknown-netbsd, features: "feat_os_unix", use-cross: use-cross , skip-tests: true , check-only: true }
- { os: ubuntu-latest , target: x86_64-unknown-redox , features: feat_os_unix_redox , use-cross: redoxer , skip-tests: true , check-only: true }
- { os: ubuntu-latest , target: wasm32-wasip1, default-features: false, features: feat_wasm, skip-tests: true }
+ - { os: ubuntu-latest , target: wasm32-wasip2, default-features: false, features: feat_wasm, skip-tests: true }
- { os: macos-latest , target: aarch64-apple-darwin , features: feat_os_unix, workspace-tests: true } # M1 CPU
# PR #7964: chcon should not break build without the feature. cargo check is enough to detect it.
- { os: macos-latest , target: aarch64-apple-darwin , workspace-tests: true, check-only: true } # M1 CPU
diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml
index b450b3f5883..00b1e91579a 100644
--- a/.github/workflows/code-quality.yml
+++ b/.github/workflows/code-quality.yml
@@ -75,6 +75,7 @@ jobs:
- { os: macos-latest , features: all , workspace: true }
- { os: windows-latest , features: feat_os_windows }
- { os: ubuntu-latest , features: feat_wasm , target: wasm32-wasip1 }
+ - { os: ubuntu-latest , features: feat_wasm , target: wasm32-wasip2 }
steps:
- uses: actions/checkout@v6
with:
diff --git a/.github/workflows/wasi.yml b/.github/workflows/wasi.yml
index 320c0943f55..58bce32a3d3 100644
--- a/.github/workflows/wasi.yml
+++ b/.github/workflows/wasi.yml
@@ -20,14 +20,22 @@ jobs:
test_wasi:
name: Tests
runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ job:
+ - { target: wasm32-wasip1, rust-flags: "--cfg wasi_runner" }
+ - { target: wasm32-wasip2, rust-flags: "--cfg wasi_runner --cfg wasip2_runner" }
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
with:
- targets: wasm32-wasip1
+ targets: ${{ matrix.job.target }}
- uses: Swatinem/rust-cache@v2
+ with:
+ key: "${{ matrix.job.target }}"
- name: Install wasmtime
run: |
curl https://wasmtime.dev/install.sh -sSf | bash
@@ -35,33 +43,34 @@ jobs:
- name: Run unit tests
env:
CARGO_TARGET_WASM32_WASIP1_RUNNER: wasmtime
+ CARGO_TARGET_WASM32_WASIP2_RUNNER: wasmtime
run: |
- # Get all utilities and exclude ones that don't compile for wasm32-wasip1
+ # Get all utilities and exclude ones that don't compile for ${{ matrix.job.target }}
EXCLUDE="df|du|env|expr|more|tac|test"
UTILS=$(./util/show-utils.sh | tr ' ' '\n' | grep -vE "^($EXCLUDE)$" | sed 's/^/-p uu_/' | tr '\n' ' ')
- cargo test --target wasm32-wasip1 --no-default-features $UTILS
+ cargo test --target ${{ matrix.job.target }} --no-default-features $UTILS
- name: Run integration tests via wasmtime
env:
- RUSTFLAGS: --cfg wasi_runner
+ RUSTFLAGS: ${{ matrix.job.rust-flags }}
run: |
# Build the WASI binary
- cargo build --target wasm32-wasip1 --no-default-features --features feat_wasm
+ cargo build --target ${{ matrix.job.target }} --no-default-features --features feat_wasm
# Run host-compiled integration tests against the WASI binary.
# 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
+ # realpath seq sha1sum sha224sum sha256sum sha384sum
# sha512sum shred sleep sort split tail touch tsort uname uniq
# vdir yes
- UUTESTS_BINARY_PATH="$(pwd)/target/wasm32-wasip1/debug/coreutils.wasm" \
+ UUTESTS_BINARY_PATH="$(pwd)/target/${{ matrix.job.target }}/debug/coreutils.wasm" \
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_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:: \
- test_tee:: test_tr:: test_true:: test_truncate:: \
+ 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_rm:: test_rmdir:: test_shuf:: \
+ test_sum:: test_tee:: test_tr:: test_true:: test_truncate:: \
test_unexpand:: test_unlink:: test_wc::
diff --git a/Cargo.toml b/Cargo.toml
index cb46a50c8e9..58e9714deb7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -506,6 +506,7 @@ unexpected_cfgs = { level = "warn", check-cfg = [
'cfg(fuzzing)',
'cfg(target_os, values("cygwin"))',
'cfg(wasi_runner)',
+ 'cfg(wasip2_runner)',
] }
unused_qualifications = "warn"
diff --git a/docs/src/platforms.md b/docs/src/platforms.md
index 3bf70b52a42..3aa5bba7ec6 100644
--- a/docs/src/platforms.md
+++ b/docs/src/platforms.md
@@ -27,7 +27,7 @@ The platforms in tier 1 and the platforms that we test in CI are listed below.
| **FreeBSD** | `x86_64-unknown-freebsd` |
| **OpenBSD** | `x86_64-unknown-openbsd` |
| **Android** | `x86_64-linux-android` |
-| **wasm32** | `wasm32-wasip1` |
+| **wasm32** | `wasm32-wasip1`
`wasm32-wasip2` |
The platforms in tier 2 are more vague, but include:
diff --git a/docs/src/wasi-test-gaps.md b/docs/src/wasi-test-gaps.md
index 4789ef03dc9..819d21b6631 100644
--- a/docs/src/wasi-test-gaps.md
+++ b/docs/src/wasi-test-gaps.md
@@ -1,6 +1,6 @@
# WASI integration test gaps
-Tests annotated with `#[cfg_attr(wasi_runner, ignore = "...")]` are skipped when running integration tests against a WASI binary via wasmtime. This document tracks the reasons so that gaps in WASI support are visible in one place.
+Tests annotated with `#[cfg_attr(wasi_runner, ignore = "...")]` or `#[cfg_attr(wasip2_runner, ignore = "...")]` are skipped when running integration tests against a WASI binary via wasmtime. This document tracks the reasons so that gaps in WASI support are visible in one place.
To find all annotated tests: `grep -rn 'wasi_runner, ignore' tests/`
@@ -35,3 +35,7 @@ When stdin is a seekable file, wasmtime does not preserve the file position betw
## WASI: read_link on absolute paths fails under wasmtime via spawned test harness
`fs::read_link` on an absolute path inside the sandbox (e.g. `/file2`) returns `EPERM` when the WASI binary is launched through `std::process::Command` from the test harness, even though the same call works when wasmtime is invoked directly. This breaks `uucore::fs::canonicalize` for symlink sources, so tests that rely on following a symlink to compute a relative path are skipped.
+
+## WASI Preview2: exit with code has not been implemented
+
+Until the [wasi:cli/exit#exit-with-code](https://github.com/WebAssembly/WASI/blob/a1fc383d01eabaf3fac01de03c0ab1a01bfdd099/proposals/cli/wit/exit.wit#L16) will be made available in Rust stable toolchain, we will have to consider error exit code only as 1.
diff --git a/src/uu/yes/src/yes.rs b/src/uu/yes/src/yes.rs
index 98be0919381..1b150d69d26 100644
--- a/src/uu/yes/src/yes.rs
+++ b/src/uu/yes/src/yes.rs
@@ -58,15 +58,20 @@ pub fn uu_app() -> Command {
fn args_into_buffer<'a>(i: impl Iterator- ) -> UResult> {
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
- #[cfg(target_os = "wasi")]
+ #[cfg(all(target_os = "wasi", target_env = "p1"))]
use std::os::wasi::ffi::OsStrExt;
let mut buf = Vec::with_capacity(BUF_SIZE);
- // On Unix (and wasi), OsStrs are just &[u8]'s underneath...
- #[cfg(any(unix, target_os = "wasi"))]
+ // On Unix (and wasip1), OsStrs are just &[u8]'s underneath...
+ #[cfg(any(unix, all(target_os = "wasi", target_env = "p1")))]
for part in itertools::intersperse(i.map(|a| a.as_bytes()), b" ") {
buf.extend_from_slice(part);
}
+ // On WASI Preview2, OsStrs are guaranteed to be UTF-8 encoded
+ #[cfg(all(target_os = "wasi", target_env = "p2"))]
+ for part in itertools::intersperse(i.map(|a| a.as_encoded_bytes()), b" ") {
+ buf.extend_from_slice(part);
+ }
// But, on Windows, we must hop through a String.
#[cfg(not(any(unix, target_os = "wasi")))]
for part in itertools::intersperse(i.map(|a| a.to_str()), Some(" ")) {
diff --git a/src/uucore/src/lib/features/fs.rs b/src/uucore/src/lib/features/fs.rs
index 28544d65711..c8cc144068b 100644
--- a/src/uucore/src/lib/features/fs.rs
+++ b/src/uucore/src/lib/features/fs.rs
@@ -759,12 +759,21 @@ pub fn are_hardlinks_or_one_way_symlink_to_same_file(source: &Path, target: &Pat
pub fn path_ends_with_terminator(path: &Path) -> bool {
#[cfg(unix)]
use std::os::unix::prelude::OsStrExt;
- #[cfg(target_os = "wasi")]
+ #[cfg(all(target_os = "wasi", target_env = "p1"))]
use std::os::wasi::ffi::OsStrExt;
- path.as_os_str()
+
+ #[cfg(all(target_os = "wasi", target_env = "p2"))]
+ return path
+ .as_os_str()
+ .as_encoded_bytes()
+ .last()
+ .is_some_and(|&byte| byte == b'/');
+ #[cfg(not(all(target_os = "wasi", target_env = "p2")))]
+ return path
+ .as_os_str()
.as_bytes()
.last()
- .is_some_and(|&byte| byte == b'/')
+ .is_some_and(|&byte| byte == b'/');
}
#[cfg(windows)]
diff --git a/src/uucore/src/lib/lib.rs b/src/uucore/src/lib/lib.rs
index 4dfffed2995..16c511b419a 100644
--- a/src/uucore/src/lib/lib.rs
+++ b/src/uucore/src/lib/lib.rs
@@ -147,7 +147,7 @@ use std::io::{BufRead, BufReader};
use std::iter;
#[cfg(unix)]
use std::os::unix::ffi::{OsStrExt, OsStringExt};
-#[cfg(target_os = "wasi")]
+#[cfg(all(target_os = "wasi", target_env = "p1"))]
use std::os::wasi::ffi::{OsStrExt, OsStringExt};
use std::str;
use std::str::Utf8Chunk;
@@ -454,9 +454,12 @@ impl error::UError for NonUtf8OsStrError {}
/// and fails on other platforms if the string can't be coerced to UTF-8.
#[cfg_attr(any(unix, target_os = "wasi"), expect(clippy::unnecessary_wraps))]
pub fn os_str_as_bytes(os_string: &OsStr) -> Result<&[u8], NonUtf8OsStrError> {
- #[cfg(any(unix, target_os = "wasi"))]
+ #[cfg(any(unix, all(target_os = "wasi", target_env = "p1")))]
return Ok(os_string.as_bytes());
+ #[cfg(all(target_os = "wasi", target_env = "p2"))]
+ return Ok(os_string.as_encoded_bytes());
+
#[cfg(not(any(unix, target_os = "wasi")))]
os_string
.to_str()
@@ -471,8 +474,10 @@ pub fn os_str_as_bytes(os_string: &OsStr) -> Result<&[u8], NonUtf8OsStrError> {
/// This is always lossless on unix platforms,
/// and wraps [`OsStr::to_string_lossy`] on non-unix platforms.
pub fn os_str_as_bytes_lossy(os_string: &OsStr) -> Cow<'_, [u8]> {
- #[cfg(any(unix, target_os = "wasi"))]
+ #[cfg(any(unix, all(target_os = "wasi", target_env = "p1")))]
return Cow::from(os_string.as_bytes());
+ #[cfg(all(target_os = "wasi", target_env = "p2"))]
+ return Cow::from(os_string.as_encoded_bytes());
#[cfg(not(any(unix, target_os = "wasi")))]
match os_string.to_string_lossy() {
@@ -488,10 +493,10 @@ pub fn os_str_as_bytes_lossy(os_string: &OsStr) -> Cow<'_, [u8]> {
/// and fails on other platforms if the bytes can't be parsed as UTF-8.
#[cfg_attr(any(unix, target_os = "wasi"), expect(clippy::unnecessary_wraps))]
pub fn os_str_from_bytes(bytes: &[u8]) -> error::UResult> {
- #[cfg(any(unix, target_os = "wasi"))]
+ #[cfg(any(unix, all(target_os = "wasi", target_env = "p1")))]
return Ok(Cow::Borrowed(OsStr::from_bytes(bytes)));
- #[cfg(not(any(unix, target_os = "wasi")))]
+ #[cfg(not(any(unix, all(target_os = "wasi", target_env = "p1"))))]
Ok(Cow::Owned(OsString::from(str::from_utf8(bytes).map_err(
|_| error::UUsageError::new(1, "Unable to transform bytes into OsStr"),
)?)))
@@ -503,10 +508,10 @@ pub fn os_str_from_bytes(bytes: &[u8]) -> error::UResult> {
/// and fails on other platforms if the bytes can't be parsed as UTF-8.
#[cfg_attr(any(unix, target_os = "wasi"), expect(clippy::unnecessary_wraps))]
pub fn os_string_from_vec(vec: Vec) -> error::UResult {
- #[cfg(any(unix, target_os = "wasi"))]
+ #[cfg(any(unix, all(target_os = "wasi", target_env = "p1")))]
return Ok(OsString::from_vec(vec));
- #[cfg(not(any(unix, target_os = "wasi")))]
+ #[cfg(not(any(unix, all(target_os = "wasi", target_env = "p1"))))]
Ok(OsString::from(String::from_utf8(vec).map_err(|_| {
error::UUsageError::new(1, "invalid UTF-8 was detected in one or more arguments")
})?))
@@ -518,9 +523,9 @@ pub fn os_string_from_vec(vec: Vec) -> error::UResult {
/// and fails on other platforms if the bytes can't be parsed as UTF-8.
#[cfg_attr(any(unix, target_os = "wasi"), expect(clippy::unnecessary_wraps))]
pub fn os_string_to_vec(s: OsString) -> error::UResult> {
- #[cfg(any(unix, target_os = "wasi"))]
+ #[cfg(any(unix, all(target_os = "wasi", target_env = "p1")))]
let v = s.into_vec();
- #[cfg(not(any(unix, target_os = "wasi")))]
+ #[cfg(not(any(unix, all(target_os = "wasi", target_env = "p1"))))]
let v = s
.into_string()
.map_err(|_| {
diff --git a/src/uucore/src/lib/mods/display.rs b/src/uucore/src/lib/mods/display.rs
index 165070e9a53..249fed48d9a 100644
--- a/src/uucore/src/lib/mods/display.rs
+++ b/src/uucore/src/lib/mods/display.rs
@@ -32,7 +32,7 @@ use std::io::{self, BufWriter, Stdout, StdoutLock, Write as _};
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
-#[cfg(target_os = "wasi")]
+#[cfg(all(target_os = "wasi", target_env = "p1"))]
use std::os::wasi::ffi::OsStrExt;
// These used to be defined here, but they live in their own crate now.
@@ -76,10 +76,14 @@ pub trait OsWrite: io::Write {
/// On Windows, if the OS string is not valid Unicode, an error of kind
/// [`io::ErrorKind::InvalidData`] is returned.
fn write_all_os(&mut self, buf: &OsStr) -> io::Result<()> {
- #[cfg(any(unix, target_os = "wasi"))]
+ #[cfg(any(unix, all(target_os = "wasi", target_env = "p1")))]
{
self.write_all(buf.as_bytes())
}
+ #[cfg(all(target_os = "wasi", target_env = "p2"))]
+ {
+ self.write_all(buf.as_encoded_bytes())
+ }
#[cfg(not(any(unix, target_os = "wasi")))]
{
diff --git a/tests/by-util/test_cat.rs b/tests/by-util/test_cat.rs
index df2765b79b6..ff96f6375e2 100644
--- a/tests/by-util/test_cat.rs
+++ b/tests/by-util/test_cat.rs
@@ -87,6 +87,7 @@ fn test_no_options_big_input() {
#[test]
#[cfg(unix)]
+#[cfg_attr(wasi_runner, ignore)]
fn test_fifo_symlink() {
use std::io::Write;
use std::thread;
@@ -138,6 +139,7 @@ fn test_closes_file_descriptors() {
#[test]
#[cfg(unix)]
+#[cfg_attr(wasi_runner, ignore)]
fn test_broken_pipe() {
let mut cmd = new_ucmd!();
let mut child = cmd
@@ -514,6 +516,7 @@ fn test_squeeze_blank_before_numbering() {
/// This tests reading from Unix character devices
#[test]
#[cfg(unix)]
+#[cfg_attr(wasi_runner, ignore)]
fn test_dev_random() {
#[cfg(any(target_os = "linux", target_os = "android"))]
const DEV_RANDOM: &str = "/dev/urandom";
@@ -603,10 +606,15 @@ fn test_domain_socket() {
s.ucmd()
.args(&[socket_path])
.fails()
- .stderr_contains("No such device or address");
+ .stderr_contains(if cfg!(wasi_runner) {
+ "No such file or directory"
+ } else {
+ "No such device or address"
+ });
}
#[test]
+#[cfg_attr(wasi_runner, ignore)]
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 +630,7 @@ fn test_write_to_self_empty() {
}
#[test]
+#[cfg_attr(wasi_runner, ignore)]
fn test_write_to_self() {
let s = TestScenario::new(util_name!());
let file_path = s.fixtures.plus("first_file");
@@ -678,6 +687,7 @@ fn test_successful_write_to_read_write_self() {
///
/// `cat fx fx3 1<>fx3`
#[test]
+#[cfg_attr(wasi_runner, ignore)]
fn test_failed_write_to_read_write_self() {
let (at, mut ucmd) = at_and_ucmd!();
at.write("fx", "g");
@@ -708,9 +718,11 @@ fn test_error_loop() {
at.symlink_file("2", "1");
at.symlink_file("3", "2");
at.symlink_file("1", "3");
- ucmd.arg("1")
- .fails()
- .stderr_is("cat: 1: Too many levels of symbolic links\n");
+ ucmd.arg("1").fails().stderr_is(if cfg!(wasi_runner) {
+ "cat: 1: Operation not permitted\n"
+ } else {
+ "cat: 1: Too many levels of symbolic links\n"
+ });
}
#[test]
diff --git a/tests/by-util/test_numfmt.rs b/tests/by-util/test_numfmt.rs
index 7e6f940bc81..ebf6c7afecf 100644
--- a/tests/by-util/test_numfmt.rs
+++ b/tests/by-util/test_numfmt.rs
@@ -61,7 +61,7 @@ fn test_from_iec_i() {
fn test_from_iec_i_requires_suffix() {
new_ucmd!()
.args(&["--from=iec-i", "10M"])
- .fails_with_code(2)
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 })
.stderr_is("numfmt: missing 'i' suffix in input: '10M' (e.g Ki/Mi/Gi)\n");
}
@@ -69,7 +69,7 @@ fn test_from_iec_i_requires_suffix() {
fn test_from_iec_fails_if_i_suffix() {
new_ucmd!()
.args(&["--from=iec", "10Mi"])
- .fails_with_code(2)
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 })
.stderr_is("numfmt: invalid suffix in input '10Mi': 'i'\n");
}
@@ -266,7 +266,7 @@ fn test_suffixes() {
} else {
new_ucmd!()
.args(&args)
- .fails_with_code(2)
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 })
.stderr_only(format!("numfmt: invalid suffix in input: '1{c}'\n"));
}
}
@@ -282,7 +282,7 @@ fn test_invalid_following_valid_suffix() {
new_ucmd!()
.args(&args)
- .fails_with_code(2)
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 })
.stderr_only(format!(
"numfmt: invalid suffix in input '1{valid_suffix}{c}': '{c}'\n"
));
@@ -296,7 +296,7 @@ fn test_long_invalid_suffix() {
new_ucmd!()
.args(&args)
- .fails_with_code(2)
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 })
.stderr_only("numfmt: invalid suffix in input: '1500VVVVVVVV'\n");
}
@@ -323,7 +323,7 @@ fn test_should_report_invalid_number_with_interior_junk() {
fn test_should_report_invalid_number_with_sign_after_decimal() {
new_ucmd!()
.args(&["--", "-0.-1"])
- .fails_with_code(2)
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 })
.stderr_is("numfmt: invalid number: '-0.-1'\n");
}
@@ -753,7 +753,9 @@ fn test_suffix_with_padding() {
#[test]
fn test_invalid_stdin_number_returns_status_2() {
- new_ucmd!().pipe_in("hello").fails_with_code(2);
+ new_ucmd!()
+ .pipe_in("hello")
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 });
}
#[test]
@@ -761,7 +763,7 @@ fn test_invalid_stdin_number_in_middle_of_input() {
new_ucmd!()
.pipe_in("100\nhello\n200")
.ignore_stdin_write_error()
- .fails_with_code(2)
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 })
.stdout_is("100\n");
}
@@ -789,7 +791,7 @@ fn test_invalid_stdin_number_with_abort_returns_status_2() {
new_ucmd!()
.args(&["--invalid=abort"])
.pipe_in("4Q")
- .fails_with_code(2)
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 })
.stderr_only("numfmt: rejecting suffix in input: '4Q' (consider using --from)\n");
}
@@ -798,7 +800,7 @@ fn test_invalid_stdin_number_with_fail_returns_status_2() {
new_ucmd!()
.args(&["--invalid=fail"])
.pipe_in("4Q")
- .fails_with_code(2)
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 })
.stdout_is("4Q")
.stderr_is("numfmt: rejecting suffix in input: '4Q' (consider using --from)\n");
}
@@ -824,7 +826,7 @@ fn test_invalid_arg_number_with_ignore_returns_status_0() {
fn test_invalid_arg_number_with_abort_returns_status_2() {
new_ucmd!()
.args(&["--invalid=abort", "4Q"])
- .fails_with_code(2)
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 })
.stderr_only("numfmt: rejecting suffix in input: '4Q' (consider using --from)\n");
}
@@ -832,7 +834,7 @@ fn test_invalid_arg_number_with_abort_returns_status_2() {
fn test_invalid_arg_number_with_fail_returns_status_2() {
new_ucmd!()
.args(&["--invalid=fail", "4Q"])
- .fails_with_code(2)
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 })
.stdout_is("4Q\n")
.stderr_is("numfmt: rejecting suffix in input: '4Q' (consider using --from)\n");
}
@@ -897,7 +899,7 @@ fn test_valid_but_forbidden_suffix() {
for number in numbers {
new_ucmd!()
.arg(number)
- .fails_with_code(2)
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 })
.stderr_contains(format!(
"rejecting suffix in input: '{number}' (consider using --from)"
));
@@ -1281,7 +1283,7 @@ fn test_debug_reports_failed_conversions_summary() {
"Foo",
"3000",
])
- .fails_with_code(2)
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 })
.stdout_is("1.0k\nFoo\n3.0k\n")
.stderr_is(
"numfmt: invalid number: 'Foo'\nnumfmt: failed to convert some of the input numbers\n",
@@ -1293,7 +1295,7 @@ fn test_invalid_fail_with_fields_does_not_duplicate_output() {
new_ucmd!()
.args(&["--invalid=fail", "--field=2", "--from=si", "--to=iec"])
.pipe_in("A 1K x\nB Foo y\nC 3G z\n")
- .fails_with_code(2)
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 })
.stdout_is("A 1000 x\nB Foo y\nC 2.8G z\n")
.stderr_is("numfmt: invalid number: 'Foo'\n");
}
@@ -1302,7 +1304,7 @@ fn test_invalid_fail_with_fields_does_not_duplicate_output() {
fn test_abort_with_fields_preserves_partial_output() {
new_ucmd!()
.args(&["--field=3", "--from=auto", "Hello 40M World 90G"])
- .fails_with_code(2)
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 })
.stdout_is("Hello 40M ")
.stderr_is("numfmt: invalid number: 'World'\n");
}
@@ -1311,17 +1313,17 @@ fn test_abort_with_fields_preserves_partial_output() {
fn test_rejects_malformed_number_forms() {
new_ucmd!()
.args(&["--from=si", "12.K"])
- .fails_with_code(2)
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 })
.stderr_contains("invalid number: '12.K'");
new_ucmd!()
.args(&["--from=si", "--delimiter=,", "12. 2"])
- .fails_with_code(2)
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 })
.stderr_contains("invalid number: '12. 2'");
new_ucmd!()
.arg("..1")
- .fails_with_code(2)
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 })
.stderr_contains("invalid suffix in input: '..1'");
}
@@ -1379,12 +1381,12 @@ fn test_whitespace_mode_parses_custom_unit_separator_inputs() {
fn test_empty_delimiter_whitespace_rejection() {
new_ucmd!()
.args(&["-d", "", "--from=auto", "2 K"])
- .fails_with_code(2)
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 })
.stderr_contains("invalid suffix in input");
new_ucmd!()
.args(&["-d", "", "--from=si", "--unit-separator=", "1 K"])
- .fails_with_code(2)
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 })
.stderr_contains("invalid suffix in input");
}
@@ -1450,7 +1452,7 @@ fn test_large_integer_precision_loss_issue_11654() {
fn test_scientific_notation_rejected_by_gnu_issue_11655() {
new_ucmd!()
.arg("1e9")
- .fails_with_code(2)
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 })
.stderr_contains("invalid suffix in input");
}
@@ -1508,7 +1510,7 @@ fn test_invalid_utf8_input() {
// 0xFF is invalid UTF-8
new_ucmd!()
.pipe_in([b'1', b'0', b'\n', b'\xFF'])
- .fails_with_code(2)
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 })
.stdout_is("10\n")
.stderr_is("numfmt: invalid number: '\\377'\n");
}
@@ -1524,7 +1526,7 @@ fn test_format_value_too_large_issue_11936() {
for (args, hint) in cases {
new_ucmd!()
.args(&args)
- .fails_with_code(2)
+ .fails_with_code(if cfg!(wasip2_runner) { 1 } else { 2 })
.stderr_contains("value/precision too large")
.stderr_contains(hint);
}
diff --git a/tests/by-util/test_rm.rs b/tests/by-util/test_rm.rs
index 43c712e9c4b..d915107a220 100644
--- a/tests/by-util/test_rm.rs
+++ b/tests/by-util/test_rm.rs
@@ -337,6 +337,7 @@ fn test_no_operand() {
}
#[test]
+#[cfg_attr(wasi_runner, ignore)]
fn test_verbose_slash() {
let (at, mut ucmd) = at_and_ucmd!();
let dir = "test_rm_verbose_slash_directory";
@@ -774,6 +775,7 @@ fn test_current_or_parent_dir_rm4() {
at.touch(file_1);
at.touch(file_2);
+ #[cfg(not(wasi_runner))]
let answers = [
"rm: refusing to remove '.' or '..' directory: skipping 'd/.'",
"rm: refusing to remove '.' or '..' directory: skipping 'd/./'",
@@ -785,6 +787,20 @@ fn test_current_or_parent_dir_rm4() {
"rm: refusing to remove '.' or '..' directory: skipping '../'",
"rm: refusing to remove '.' or '..' directory: skipping '..'",
];
+ #[cfg(wasi_runner)]
+ let answers = [
+ "rm: refusing to remove '.' or '..' directory: skipping 'd/.'",
+ "rm: refusing to remove '.' or '..' directory: skipping 'd/./'",
+ "rm: refusing to remove '.' or '..' directory: skipping 'd/./'",
+ "rm: refusing to remove '.' or '..' directory: skipping 'd/..'",
+ "rm: it is dangerous to operate recursively on 'd/../' (same as '/')",
+ "rm: use --no-preserve-root to override this failsafe",
+ "rm: refusing to remove '.' or '..' directory: skipping '.'",
+ "rm: it is dangerous to operate recursively on './' (same as '/')",
+ "rm: use --no-preserve-root to override this failsafe",
+ "rm: it is dangerous to operate recursively on '../' (same as '/')",
+ "rm: use --no-preserve-root to override this failsafe",
+ ];
let std_err_str = ts
.ucmd()
.arg("-rf")
@@ -1001,7 +1017,11 @@ fn test_unreadable_and_nonempty_dir() {
ucmd.args(&["-r", "-f", "a"])
.fails()
- .stderr_only("rm: cannot remove 'a': Permission denied\n");
+ .stderr_only(if cfg!(wasi_runner) {
+ "rm: cannot remove 'a': Directory not empty\n"
+ } else {
+ "rm: cannot remove 'a': Permission denied\n"
+ });
assert!(at.dir_exists("a/b"));
assert!(at.dir_exists("a"));
}
@@ -1016,7 +1036,11 @@ fn test_recursive_remove_unreadable_subdir() {
at.set_mode("foo/bar", 0o0000);
let result = ucmd.args(&["-r", "-f", "foo"]).fails();
+
+ #[cfg(not(wasi_runner))]
result.stderr_contains("Permission denied");
+ #[cfg(wasi_runner)]
+ result.stderr_contains("Directory not empty");
result.stderr_contains("foo/bar");
at.set_mode("foo/bar", 0o0755);
@@ -1055,7 +1079,11 @@ fn test_inaccessible_dir_interactive() {
ucmd.args(&["-i", "-d", "dir"])
.pipe_in("y\n")
.succeeds()
- .stderr_only("rm: attempt removal of inaccessible directory 'dir'? ");
+ .stderr_only(if cfg!(wasi_runner) {
+ "rm: remove directory 'dir'? "
+ } else {
+ "rm: attempt removal of inaccessible directory 'dir'? "
+ });
assert!(!at.dir_exists("dir"));
}
@@ -1072,12 +1100,13 @@ fn test_inaccessible_dir_recursive() {
}
#[test]
+#[cfg_attr(wasip2_runner, ignore)]
#[cfg(any(target_os = "linux", target_os = "wasi"))]
fn test_non_utf8_paths() {
use std::ffi::OsStr;
#[cfg(target_os = "linux")]
use std::os::unix::ffi::OsStrExt;
- #[cfg(target_os = "wasi")]
+ #[cfg(all(target_os = "wasi", target_env = "p1"))]
use std::os::wasi::ffi::OsStrExt;
let scene = TestScenario::new(util_name!());
@@ -1309,8 +1338,9 @@ fn test_symlink_to_readonly_no_prompt() {
}
/// Test that --preserve-root properly detects symlinks pointing to root.
-#[cfg(unix)]
#[test]
+#[cfg(unix)]
+#[cfg_attr(wasi_runner, ignore)]
fn test_preserve_root_symlink_to_root() {
let (at, mut ucmd) = at_and_ucmd!();
@@ -1331,8 +1361,9 @@ fn test_preserve_root_symlink_to_root() {
}
/// Test that --preserve-root properly detects nested symlinks pointing to root.
-#[cfg(unix)]
#[test]
+#[cfg(unix)]
+#[cfg_attr(wasi_runner, ignore)]
fn test_preserve_root_nested_symlink_to_root() {
let (at, mut ucmd) = at_and_ucmd!();
diff --git a/tests/by-util/test_tee.rs b/tests/by-util/test_tee.rs
index 2948eaa045b..6a0b70b1bfd 100644
--- a/tests/by-util/test_tee.rs
+++ b/tests/by-util/test_tee.rs
@@ -19,10 +19,14 @@ use std::time::Duration;
#[test]
#[cfg(unix)]
fn test_error_stdin_directory() {
- new_ucmd!()
+ let cmd = new_ucmd!()
.set_stdin(std::fs::File::open(".").unwrap())
- .fails_with_code(1)
- .stderr_is("tee: read error: Is a directory\n");
+ .fails_with_code(1);
+ if cfg!(wasip2_runner) {
+ cmd.stderr_is("tee: read error: I/O error\n");
+ } else {
+ cmd.stderr_is("tee: read error: Is a directory\n");
+ }
}
#[test]
diff --git a/tests/by-util/test_tr.rs b/tests/by-util/test_tr.rs
index bfb490a56a5..d87455b04ac 100644
--- a/tests/by-util/test_tr.rs
+++ b/tests/by-util/test_tr.rs
@@ -30,7 +30,11 @@ fn test_invalid_input() {
if std::env::var("UUTESTS_WASM_RUNNER").is_ok() {
// On WASI the fluent translation key may appear instead of the
// translated text, but the OS error string is still present.
- cmd.stderr_contains("Is a directory");
+ if cfg!(wasip2_runner) {
+ cmd.stderr_contains("tr: tr-error-read-error: I/O error");
+ } else {
+ cmd.stderr_contains("Is a directory");
+ }
} else {
cmd.stderr_contains("tr: read error: Is a directory");
}
diff --git a/tests/by-util/test_wc.rs b/tests/by-util/test_wc.rs
index 62f100b4293..35ee50ed7a2 100644
--- a/tests/by-util/test_wc.rs
+++ b/tests/by-util/test_wc.rs
@@ -797,7 +797,11 @@ fn files0_from_dir() {
#[cfg(not(windows))]
macro_rules! dir_err {
($p:literal) => {
- concat!("wc: ", $p, ": read error: Is a directory\n")
+ if cfg!(wasip2_runner) {
+ concat!("wc: ", $p, ": read error: I/O error\n")
+ } else {
+ concat!("wc: ", $p, ": read error: Is a directory\n")
+ }
};
}
#[cfg(windows)]
diff --git a/tests/by-util/test_yes.rs b/tests/by-util/test_yes.rs
index c638915be94..6752fbc2def 100644
--- a/tests/by-util/test_yes.rs
+++ b/tests/by-util/test_yes.rs
@@ -96,11 +96,12 @@ fn test_piped_to_dev_full() {
}
#[test]
+#[cfg_attr(wasip2_runner, ignore)]
#[cfg(any(unix, target_os = "wasi"))]
fn test_non_utf8() {
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
- #[cfg(target_os = "wasi")]
+ #[cfg(all(target_os = "wasi", target_env = "p1"))]
use std::os::wasi::ffi::OsStrExt;
run(