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
85 changes: 84 additions & 1 deletion .github/workflows/wasi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,94 @@ jobs:
run: |
curl https://wasmtime.dev/install.sh -sSf | bash
echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH
- name: Run tests
- name: Run unit tests
env:
CARGO_TARGET_WASM32_WASIP1_RUNNER: wasmtime
run: |
# Get all utilities and exclude ones that don't compile for wasm32-wasip1
EXCLUDE="dd|df|du|env|expr|mktemp|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
- name: Run integration tests via wasmtime
run: |
# Build the WASI binary
cargo build --target wasm32-wasip1 --no-default-features --features feat_wasm
# Run host-compiled integration tests against the WASI binary.
# TODO: add integration tests for these tools as WASI support is extended:
# arch b2sum cat cksum cp csplit date dir dircolors fmt join ln
# 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
# vdir yes
UUTESTS_BINARY_PATH="$(pwd)/target/wasm32-wasip1/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_nl:: test_numfmt:: \
test_od:: test_paste:: test_printf:: test_shuf:: test_sum:: \
test_tee:: test_tr:: test_true:: test_truncate:: \
test_unexpand:: test_unlink:: test_wc:: \
\
`# WASI sandbox: host paths (/proc, /sys, /dev) not visible in guest` \
--skip test_factor::test_parallel \
--skip test_cut::test_too_large \
--skip test_read_backwards_bytes_proc_fs_version \
--skip test_read_backwards_bytes_proc_fs_modules \
--skip test_read_backwards_lines_proc_fs_modules \
--skip test_read_backwards_bytes_sys_kernel_profiling \
--skip test_files_from_pseudo_filesystem \
--skip test_files0_stops_after_stdout_write_error \
--skip test_dev_zero_write_error_dev_full \
--skip test_dev_zero_closed_pipe \
--skip test_simd_respects_glibc_tunables \
--skip test_comm::test_comm_anonymous_pipes \
--skip test_comm::test_read_error \
--skip test_base64::test_read_error \
--skip test_expand::test_read_error \
--skip test_unexpand::test_read_error \
--skip test_od::test_skip_bytes_proc_file_without_seeking \
--skip test_shuf::write_errors_are_reported \
--skip test_tee::linux_only \
--skip test_tee::test_readonly \
\
`# WASI spec: argv/filenames must be valid UTF-8` \
--skip test_invalid_utf8_args \
--skip test_dirname_non_utf8_paths \
--skip test_trailing_dot_non_utf8 \
--skip test_8bit_non_utf8_delimiter \
--skip test_non_utf8_delimiter \
--skip test_echo_invalid_unicode_in_arguments \
--skip mb_invalid_unicode \
--skip non_utf_8_input \
--skip non_utf_8 \
--skip test_octal_escape_ambiguous_followed_by_non_utf8 \
--skip test_truncate_non_utf8_set \
--skip test_output_lossy_utf8 \
--skip non_utf8_paths \
--skip non_utf8_filename \
--skip non_utf8_name \
--skip test_invalid_unicode_in_filename \
--skip test_one_byte_section_delimiter \
--skip test_section_delimiter_non_utf8 \
--skip test_number_separator_non_utf8 \
\
`# WASI: no FIFO/mkfifo support` \
--skip test_fifo_error_reference_file_only \
--skip test_fifo_error_reference_and_size \
--skip test_fifo_error_size_only \
\
`# WASI: no pipe/signal support` \
--skip test_broken_pipe_no_error \
--skip test_tee_output_not_buffered \
\
`# WASI: no subprocess spawning (test shells out or invokes another binary)` \
--skip test_all_but_last_bytes_large_file_piped \
--skip test_all_but_last_lines_large_file \
--skip test_cmd_result_stderr_check_and_stderr_str_check \
\
`# WASI: stdin file position not preserved through wasmtime` \
--skip test_od::test_read_bytes \
--skip test_validate_stdin_offset_lines \
--skip test_validate_stdin_offset_bytes
16 changes: 8 additions & 8 deletions tests/by-util/test_basenc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ fn test_z85_not_padded_encode() {

#[test]
fn test_invalid_input() {
let error_message = if cfg!(windows) {
"basenc: .: Permission denied\n"
let cmd = new_ucmd!().args(&["--base32", "."]).fails();
if cfg!(windows) {
cmd.stderr_only("basenc: .: Permission denied\n");
} else if std::env::var("UUTESTS_WASM_RUNNER").is_ok() {
// wasi-libc may report a different error string than the host libc
cmd.stderr_contains("basenc: read error:");
} else {
"basenc: read error: Is a directory\n"
};
new_ucmd!()
.args(&["--base32", "."])
.fails()
.stderr_only(error_message);
cmd.stderr_only("basenc: read error: Is a directory\n");
}
}

#[test]
Expand Down
68 changes: 39 additions & 29 deletions tests/by-util/test_comm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,19 +451,23 @@ fn test_is_dir() {

#[test]
fn test_sorted() {
let expected_stderr =
"comm: file 2 is not in sorted order\ncomm: input is not in sorted order\n";

let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.write("comm1", "1\n3");
at.write("comm2", "3\n2");
scene
.ucmd()
.args(&["comm1", "comm2"])
.fails_with_code(1)
.stdout_is("1\n\t\t3\n\t2\n")
.stderr_is(expected_stderr);
let cmd = scene.ucmd().args(&["comm1", "comm2"]).run();
// WASI's strcoll (C locale only) may not detect unsorted input,
// but the comparison output is still correct.
if std::env::var("UUTESTS_WASM_RUNNER").is_ok() {
cmd.success().stdout_is("1\n\t\t3\n\t2\n");
} else {
cmd.failure()
.code_is(1)
.stdout_is("1\n\t\t3\n\t2\n")
.stderr_is(
"comm: file 2 is not in sorted order\ncomm: input is not in sorted order\n",
);
}
}

#[test]
Expand All @@ -490,16 +494,19 @@ fn test_both_inputs_out_of_order() {
at.write("file_a", "3\n1\n0\n");
at.write("file_b", "3\n2\n0\n");

scene
.ucmd()
.args(&["file_a", "file_b"])
.fails_with_code(1)
.stdout_is("\t\t3\n1\n0\n\t2\n\t0\n")
.stderr_is(
"comm: file 1 is not in sorted order\n\
comm: file 2 is not in sorted order\n\
comm: input is not in sorted order\n",
);
let cmd = scene.ucmd().args(&["file_a", "file_b"]).run();
if std::env::var("UUTESTS_WASM_RUNNER").is_ok() {
cmd.success().stdout_is("\t\t3\n1\n0\n\t2\n\t0\n");
} else {
cmd.failure()
.code_is(1)
.stdout_is("\t\t3\n1\n0\n\t2\n\t0\n")
.stderr_is(
"comm: file 1 is not in sorted order\n\
comm: file 2 is not in sorted order\n\
comm: input is not in sorted order\n",
);
}
}

#[test]
Expand All @@ -509,16 +516,19 @@ fn test_both_inputs_out_of_order_last_pair() {
at.write("file_a", "3\n1\n");
at.write("file_b", "3\n2\n");

scene
.ucmd()
.args(&["file_a", "file_b"])
.fails_with_code(1)
.stdout_is("\t\t3\n1\n\t2\n")
.stderr_is(
"comm: file 1 is not in sorted order\n\
comm: file 2 is not in sorted order\n\
comm: input is not in sorted order\n",
);
let cmd = scene.ucmd().args(&["file_a", "file_b"]).run();
if std::env::var("UUTESTS_WASM_RUNNER").is_ok() {
cmd.success().stdout_is("\t\t3\n1\n\t2\n");
} else {
cmd.failure()
.code_is(1)
.stdout_is("\t\t3\n1\n\t2\n")
.stderr_is(
"comm: file 1 is not in sorted order\n\
comm: file 2 is not in sorted order\n\
comm: input is not in sorted order\n",
);
}
}

#[test]
Expand Down
34 changes: 25 additions & 9 deletions tests/by-util/test_od.rs
Original file line number Diff line number Diff line change
Expand Up @@ -764,31 +764,47 @@ fn test_invalid_traditional_offsets_are_filenames() {

#[test]
fn test_traditional_offset_overflow_diagnosed() {
// The ERANGE message differs across platforms (e.g. "Result too large"
// on glibc vs "Result not representable" on wasi-libc), so when testing
// via a WASM runner we just check that stderr mentions the input value.
let wasm = std::env::var("UUTESTS_WASM_RUNNER").is_ok();
let erange = erange_message();
let long_octal = "7".repeat(255);
let long_decimal = format!("{}.", "9".repeat(254));
let long_hex = format!("0x{}", "f".repeat(253));

new_ucmd!()
let cmd = new_ucmd!()
.arg("-")
.arg(&long_octal)
.pipe_in(Vec::<u8>::new())
.fails_with_code(1)
.stderr_only(format!("od: {long_octal}: {erange}\n"));
.fails_with_code(1);
if wasm {
cmd.stderr_contains(&format!("od: {long_octal}:"));
} else {
cmd.stderr_only(format!("od: {long_octal}: {erange}\n"));
}

new_ucmd!()
let cmd = new_ucmd!()
.arg("-")
.arg(&long_decimal)
.pipe_in(Vec::<u8>::new())
.fails_with_code(1)
.stderr_only(format!("od: {long_decimal}: {erange}\n"));
.fails_with_code(1);
if wasm {
cmd.stderr_contains(&format!("od: {long_decimal}:"));
} else {
cmd.stderr_only(format!("od: {long_decimal}: {erange}\n"));
}

new_ucmd!()
let cmd = new_ucmd!()
.arg("-")
.arg(&long_hex)
.pipe_in(Vec::<u8>::new())
.fails_with_code(1)
.stderr_only(format!("od: {long_hex}: {erange}\n"));
.fails_with_code(1);
if wasm {
cmd.stderr_contains(&format!("od: {long_hex}:"));
} else {
cmd.stderr_only(format!("od: {long_hex}: {erange}\n"));
}
}

#[test]
Expand Down
20 changes: 14 additions & 6 deletions tests/by-util/test_tr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,20 @@ fn test_invalid_input() {
.fails_with_code(1)
.stderr_contains("tr: extra operand '<'");
#[cfg(unix)]
new_ucmd!()
.args(&["1", "1"])
// will test "tr 1 1 < ."
.set_stdin(std::process::Stdio::from(std::fs::File::open(".").unwrap()))
.fails_with_code(1)
.stderr_contains("tr: read error: Is a directory");
{
let cmd = new_ucmd!()
.args(&["1", "1"])
// will test "tr 1 1 < ."
.set_stdin(std::process::Stdio::from(std::fs::File::open(".").unwrap()))
.fails_with_code(1);
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");
} else {
cmd.stderr_contains("tr: read error: Is a directory");
}
}
}

#[test]
Expand Down
Loading