Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/skills/code-review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,10 @@ patches: assistants can reproduce GPL sources verbatim.
- Tests must be original (see the provenance rule above) and pick values that are
clearly distinct from GNU's fixtures.
- Use `.no_output()` rather than `.no_stdout().no_stderr()`.
- Anything that can't run under WASI needs `#[cfg_attr(wasi_runner, ignore)]`.
- Anything that can't run under WASI needs `#[uutests::wasi_ignore(ReasonKey)]`,
reusing an existing key from `tests/uutests_procs/src/lib.rs` when one fits and
adding a new key only when no existing reason matches the root cause. Don't use
a raw `#[cfg_attr(wasi_runner, ignore = "...")]`.
- If a GNU test was failing while the Rust suite passed, the PR should add a Rust
test so the gap can't reopen.

Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/wasi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ jobs:
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.
# #[uutests::wasi_ignore(ReasonKey)] in the test source files, which
# expands to #[cfg_attr(wasi_runner, ignore = "...")]; the reason keys
# live in tests/uutests_procs/src/lib.rs.
# The binary above is built without default features, so it has no
# caret diagnostics; the host test crate does have them, hence the
# not(wasi_runner) in the cfg guarding every `mod diagnostics`.
Expand Down
14 changes: 14 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,19 @@ add a Rust test so it cannot silently regress.

No tests, no merge.

### Tests that can't run under WASI

A test that can't run under the WASI runner is skipped with
`#[uutests::wasi_ignore(ReasonKey)]`, not a raw
`#[cfg_attr(wasi_runner, ignore = "...")]`. Every reason is defined once in
`tests/uutests_procs/src/lib.rs`.

Reuse an existing key when one fits the root cause. Add a new key only when no
existing reason matches, and keep the list small - prefer widening an existing
reason over introducing a near-duplicate. When a skip's real cause differs from
its key, switch the key rather than leaving it mislabeled. See
`docs/src/wasi-test-gaps.md`.

## Keep the PR description short

Describe the problem being solved and what changed. That is all.
Expand All @@ -55,4 +68,5 @@ rules, this file is only a pointer:
- `DEVELOPMENT.md` - build, test and environment setup
- `README.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`
- `docs/src/*.md` - platforms, performance, l10n, multicall, test coverage
- `docs/src/wasi-test-gaps.md` - WASI test skips and the `wasi_ignore` reason keys
- the per-utility `README.md` / `locales/*.ftl` files when touching a utility
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ members = [
"src/uucore",
"src/uucore_procs",
"tests/uutests",
"tests/uutests_procs",
Comment thread
eduardomourar marked this conversation as resolved.
# "fuzz", # TODO
]

Expand Down Expand Up @@ -119,6 +120,7 @@ rustc-hash = "2.1.1"
rustix = { version = "1.1.4", default-features = false }
self_cell = "1.0.4"
selinux = "0.6"
syn = { version = "2.0", features = ["full"] }
tempfile = "3.15.0"
terminal_size = "0.4.0"
textwrap = { version = "0.16.1", features = ["terminal_size"] }
Expand Down Expand Up @@ -161,6 +163,7 @@ uu_ls = { version = "0.13.0", path = "src/uu/ls" }
uu_base32 = { version = "0.13.0", path = "src/uu/base32" }
uu_checksum_common = { version = "0.13.0", path = "src/uu/checksum_common" }
uutests = { version = "0.13.0", path = "tests/uutests" }
uutests_procs = { version = "0.13.0", path = "tests/uutests_procs" }

# This is the linting configuration for all crates.
# In order to use these, all crates have `[lints] workspace = true` section.
Expand Down
6 changes: 4 additions & 2 deletions docs/src/wasi-test-gaps.md

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can remove this whole file and add those as comments in tests/uutests_procs/src/lib.rs

Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# 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 `#[uutests::wasi_ignore(ReasonKey)]` are skipped when running integration tests against a WASI binary via wasmtime. The macro expands to `#[cfg_attr(wasi_runner, ignore = "...")]`, resolving `ReasonKey` to its reason text. Every reason lives once, alphabetically, in `tests/uutests_procs/src/lib.rs`; this document gives the prose overview so that gaps in WASI support are visible in one place.

To find all annotated tests: `grep -rn 'wasi_runner, ignore' tests/`
When a test cannot run under WASI, annotate it with `#[uutests::wasi_ignore(ReasonKey)]` using the key that matches the root cause. Reuse an existing key whenever one fits rather than adding a near-duplicate; add a new key to `tests/uutests_procs/src/lib.rs` only when no existing reason describes the cause, and keep the key list and this document in sync. If the real cause of an existing skip turns out to differ from its key, switch the key (and refine the reason text) rather than leaving it mislabeled.

To find all annotated tests: `grep -rn 'wasi_ignore' tests/`. For the full, authoritative list of reason keys and their text, see `tests/uutests_procs/src/lib.rs`.

## Tools not yet covered by integration tests

Expand Down
4 changes: 2 additions & 2 deletions tests/by-util/test_base64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ fn test_version() {
.stdout_is(format!("base64 {}\n", uucore::crate_version!()));
}

#[uutests::wasi_ignore(NonUtf8ArgsUnsupported)]
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(wasi_runner, ignore = "WASI: argv/filenames must be valid UTF-8")]
fn test_base64_non_utf8_paths() {
use std::os::unix::ffi::OsStringExt;
let (at, mut ucmd) = at_and_ucmd!();
Expand Down Expand Up @@ -288,9 +288,9 @@ cyBvdmVyIHRoZSBsYXp5IGRvZy4=
);
}

#[uutests::wasi_ignore(HostPathsNotVisible)]
#[test]
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
#[cfg_attr(wasi_runner, ignore = "WASI sandbox: host paths not visible")]
fn test_read_error() {
new_ucmd!()
.arg("/proc/self/mem")
Expand Down
2 changes: 1 addition & 1 deletion tests/by-util/test_basename.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,8 @@ fn test_too_many_args_output() {
}

#[cfg(unix)]
#[uutests::wasi_ignore(NonUtf8ArgsUnsupported)]
#[test]
#[cfg_attr(wasi_runner, ignore = "WASI: argv/filenames must be valid UTF-8")]
fn test_invalid_utf8_args() {
let param = uucore::os_str_from_bytes(b"/tmp/some-\xc0-file.k\xf3")
.expect("Only unix platforms can test non-unicode names");
Expand Down
4 changes: 2 additions & 2 deletions tests/by-util/test_basenc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,9 @@ fn test_base16() {
}

// This test fails if the Base16 output buffer is not explicitly flushed.
#[uutests::wasi_ignore(HostPathsNotVisible)]
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(wasi_runner, ignore = "WASI sandbox: host paths not visible")]
fn test_base16_write_error_is_reported() {
new_ucmd!()
.arg("--base16")
Expand Down Expand Up @@ -407,9 +407,9 @@ fn test_file() {
.stdout_is("Zm9v\n");
}

#[uutests::wasi_ignore(NonUtf8ArgsUnsupported)]
#[test]
#[cfg(target_os = "linux")]
#[cfg_attr(wasi_runner, ignore = "WASI: argv/filenames must be valid UTF-8")]
fn test_file_with_non_utf8_name() {
use std::os::unix::ffi::OsStringExt;
let (at, mut ucmd) = at_and_ucmd!();
Expand Down
31 changes: 14 additions & 17 deletions tests/by-util/test_cat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ fn test_no_options_big_input() {
}
}

#[uutests::wasi_ignore(NoFifo)]
#[test]
#[cfg(unix)]
#[cfg_attr(wasi_runner, ignore = "WASI: no FIFO/mkfifo support")]
fn test_fifo_symlink() {
use std::io::Write;
use std::thread;
Expand Down Expand Up @@ -119,11 +119,11 @@ fn test_fifo_symlink() {
thread.join().unwrap();
}

#[uutests::wasi_ignore(NoRlimitSetrlimitSupport)]
#[test]
// 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.
Expand All @@ -139,9 +139,9 @@ fn test_closes_file_descriptors() {
.succeeds();
}

#[uutests::wasi_ignore(NoPipeSignalSupport)]
#[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
Expand Down Expand Up @@ -516,9 +516,9 @@ fn test_squeeze_blank_before_numbering() {
}

/// This tests reading from Unix character devices
#[uutests::wasi_ignore(HostPathsNotVisible)]
#[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";
Expand Down Expand Up @@ -547,9 +547,9 @@ fn test_dev_random() {

/// Reading from /dev/full should return an infinite amount of zero bytes.
/// Wikipedia says there is support on Linux, FreeBSD, and `NetBSD`.
#[uutests::wasi_ignore(HostPathsNotVisible)]
#[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())
Expand All @@ -563,9 +563,9 @@ fn test_dev_full() {
proc.kill();
}

#[uutests::wasi_ignore(HostPathsNotVisible)]
#[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!()
Expand All @@ -586,9 +586,9 @@ fn test_dev_full_show_all() {
// For some reason splice() on first of those files fails, resulting in
// fallback inside `write_fast`, the other splice succeeds, in effect
// without additional flush output gets reversed.
#[uutests::wasi_ignore(HostPathsNotVisible)]
#[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();
Expand All @@ -599,9 +599,9 @@ fn test_write_fast_fallthrough_uses_flush() {
.stdout_only(format!("{cmdline}abcde\nfghij\nklmno\npqrst\nuvwxyz\n")); // spell-checker:disable-line
}

#[uutests::wasi_ignore(NoUnixDomainSockets)]
#[test]
#[cfg(unix)]
#[cfg_attr(wasi_runner, ignore = "WASI: no Unix domain socket support")]
fn test_domain_socket() {
use std::os::unix::net::UnixListener;

Expand Down Expand Up @@ -630,8 +630,8 @@ fn test_write_to_self_empty() {
s.ucmd().set_stdout(file).arg("file.txt").succeeds();
}

#[uutests::wasi_ignore(CannotDetectUnsafeOverwrite)]
#[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");
Expand Down Expand Up @@ -689,8 +689,8 @@ fn test_cat_rw_self_succeeds() {
/// Test that cat handles self-referential input gracefully.
///
/// `cat fx fx3 1<>fx3`
#[uutests::wasi_ignore(CannotDetectUnsafeOverwrite)]
#[test]
#[cfg_attr(wasi_runner, ignore = "WASI: cannot detect unsafe overwrite")]
fn test_cat_rw_self_conflict_fails() {
let (at, mut ucmd) = at_and_ucmd!();
at.write("source", "a");
Expand All @@ -713,13 +713,10 @@ fn test_cat_rw_self_conflict_fails() {
assert_eq!(dest_contents, "acde");
}

#[uutests::wasi_ignore(SymlinkLoopNoEloop)]
#[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");
Expand Down Expand Up @@ -750,9 +747,9 @@ fn test_u_ignored() {
}
}

#[uutests::wasi_ignore(ErrnoMessageMismatch)]
#[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;

Expand All @@ -771,9 +768,9 @@ fn test_write_fast_read_error() {
ucmd.arg("foo").fails().stderr_contains("Permission denied");
}

#[uutests::wasi_ignore(NonUtf8ArgsUnsupported)]
#[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;
Expand All @@ -796,9 +793,9 @@ fn test_cat_non_utf8_paths() {
assert_eq!(output, "Hello, non-UTF-8 world!\n");
}

#[uutests::wasi_ignore(CannotDetectUnsafeOverwrite)]
#[test]
#[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!();

Expand Down
8 changes: 4 additions & 4 deletions tests/by-util/test_comm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -677,8 +677,8 @@ fn test_comm_eintr_handling() {
.stdout_contains("line3");
}

#[uutests::wasi_ignore(NonUtf8ArgsUnsupported)]
#[test]
#[cfg_attr(wasi_runner, ignore = "WASI: argv/filenames must be valid UTF-8")]
fn test_output_lossy_utf8() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
Expand All @@ -702,9 +702,9 @@ fn test_output_lossy_utf8() {
.stdout_is_bytes(b"\xfe\n\t\t\xff\n\t\xfe\n");
}

#[uutests::wasi_ignore(HostPathsNotVisible)]
#[test]
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg_attr(wasi_runner, ignore = "WASI sandbox: host paths not visible")]
fn test_comm_anonymous_pipes() {
use std::fmt::Write as _;
use std::{io::Write, os::fd::AsRawFd, process};
Expand Down Expand Up @@ -742,9 +742,9 @@ fn test_comm_anonymous_pipes() {
.stdout_is("99999\n");
}

#[uutests::wasi_ignore(HostPathsNotVisible)]
#[test]
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
#[cfg_attr(wasi_runner, ignore = "WASI sandbox: host paths not visible")]
fn test_read_error() {
new_ucmd!()
.arg("/proc/self/mem")
Expand All @@ -759,9 +759,9 @@ fn test_read_error() {
.stderr_contains("comm: /proc/self/mem: Input/output error");
}

#[uutests::wasi_ignore(NoLocaleData)]
#[test]
#[cfg(unix)]
#[cfg_attr(wasi_runner, ignore = "WASI: the guest does not inherit LC_ALL")]
fn test_locale_collation() {
// In a UTF-8 locale the collation puts `a1` before `a-b` and the byte order
// puts them the other way round. Reading a file `sort` produced with a byte
Expand Down
Loading
Loading