You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1590 lines
46 KiB
1590 lines
46 KiB
#![cfg(not(miri))]
|
|
|
|
use anyhow::{bail, Result};
|
|
use std::fs::File;
|
|
use std::io::Write;
|
|
use std::path::Path;
|
|
use std::process::{Command, Output, Stdio};
|
|
use tempfile::{NamedTempFile, TempDir};
|
|
|
|
// Run the wasmtime CLI with the provided args and return the `Output`.
|
|
// If the `stdin` is `Some`, opens the file and redirects to the child's stdin.
|
|
pub fn run_wasmtime_for_output(args: &[&str], stdin: Option<&Path>) -> Result<Output> {
|
|
let mut cmd = get_wasmtime_command()?;
|
|
cmd.args(args);
|
|
if let Some(file) = stdin {
|
|
cmd.stdin(File::open(file)?);
|
|
}
|
|
cmd.output().map_err(Into::into)
|
|
}
|
|
|
|
/// Get the Wasmtime CLI as a [Command].
|
|
pub fn get_wasmtime_command() -> Result<Command> {
|
|
// Figure out the Wasmtime binary from the current executable.
|
|
let runner = std::env::vars()
|
|
.filter(|(k, _v)| k.starts_with("CARGO_TARGET") && k.ends_with("RUNNER"))
|
|
.next();
|
|
let mut me = std::env::current_exe()?;
|
|
me.pop(); // chop off the file name
|
|
me.pop(); // chop off `deps`
|
|
me.push("wasmtime");
|
|
|
|
// If we're running tests with a "runner" then we might be doing something
|
|
// like cross-emulation, so spin up the emulator rather than the tests
|
|
// itself, which may not be natively executable.
|
|
let mut cmd = if let Some((_, runner)) = runner {
|
|
let mut parts = runner.split_whitespace();
|
|
let mut cmd = Command::new(parts.next().unwrap());
|
|
for arg in parts {
|
|
cmd.arg(arg);
|
|
}
|
|
cmd.arg(&me);
|
|
cmd
|
|
} else {
|
|
Command::new(&me)
|
|
};
|
|
|
|
// Ignore this if it's specified in the environment to allow tests to run in
|
|
// "default mode" by default.
|
|
cmd.env_remove("WASMTIME_NEW_CLI");
|
|
|
|
Ok(cmd)
|
|
}
|
|
|
|
// Run the wasmtime CLI with the provided args and, if it succeeds, return
|
|
// the standard output in a `String`.
|
|
fn run_wasmtime(args: &[&str]) -> Result<String> {
|
|
let output = run_wasmtime_for_output(args, None)?;
|
|
if !output.status.success() {
|
|
bail!(
|
|
"Failed to execute wasmtime with: {:?}\n{}",
|
|
args,
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
}
|
|
Ok(String::from_utf8(output.stdout).unwrap())
|
|
}
|
|
|
|
fn build_wasm(wat_path: impl AsRef<Path>) -> Result<NamedTempFile> {
|
|
let mut wasm_file = NamedTempFile::new()?;
|
|
let wasm = wat::parse_file(wat_path)?;
|
|
wasm_file.write(&wasm)?;
|
|
Ok(wasm_file)
|
|
}
|
|
|
|
// Very basic use case: compile binary wasm file and run specific function with arguments.
|
|
#[test]
|
|
fn run_wasmtime_simple() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/simple.wat")?;
|
|
run_wasmtime(&[
|
|
"run",
|
|
"--invoke",
|
|
"simple",
|
|
"-Ccache=n",
|
|
wasm.path().to_str().unwrap(),
|
|
"4",
|
|
])?;
|
|
Ok(())
|
|
}
|
|
|
|
// Wasmtime shall fail when not enough arguments were provided.
|
|
#[test]
|
|
fn run_wasmtime_simple_fail_no_args() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/simple.wat")?;
|
|
assert!(
|
|
run_wasmtime(&[
|
|
"run",
|
|
"-Ccache=n",
|
|
"--invoke",
|
|
"simple",
|
|
wasm.path().to_str().unwrap(),
|
|
])
|
|
.is_err(),
|
|
"shall fail"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn run_coredump_smoketest() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/coredump_smoketest.wat")?;
|
|
let coredump_file = NamedTempFile::new()?;
|
|
let coredump_arg = format!("-Dcoredump={}", coredump_file.path().display());
|
|
let err = run_wasmtime(&[
|
|
"run",
|
|
"--invoke",
|
|
"a",
|
|
"-Ccache=n",
|
|
&coredump_arg,
|
|
wasm.path().to_str().unwrap(),
|
|
])
|
|
.unwrap_err();
|
|
assert!(err.to_string().contains(&format!(
|
|
"core dumped at {}",
|
|
coredump_file.path().display()
|
|
)));
|
|
Ok(())
|
|
}
|
|
|
|
// Running simple wat
|
|
#[test]
|
|
fn run_wasmtime_simple_wat() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/simple.wat")?;
|
|
run_wasmtime(&[
|
|
"run",
|
|
"--invoke",
|
|
"simple",
|
|
"-Ccache=n",
|
|
wasm.path().to_str().unwrap(),
|
|
"4",
|
|
])?;
|
|
assert_eq!(
|
|
run_wasmtime(&[
|
|
"run",
|
|
"--invoke",
|
|
"get_f32",
|
|
"-Ccache=n",
|
|
wasm.path().to_str().unwrap(),
|
|
])?,
|
|
"100\n"
|
|
);
|
|
assert_eq!(
|
|
run_wasmtime(&[
|
|
"run",
|
|
"--invoke",
|
|
"get_f64",
|
|
"-Ccache=n",
|
|
wasm.path().to_str().unwrap(),
|
|
])?,
|
|
"100\n"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
// Running a wat that traps.
|
|
#[test]
|
|
fn run_wasmtime_unreachable_wat() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/unreachable.wat")?;
|
|
let output = run_wasmtime_for_output(&[wasm.path().to_str().unwrap(), "-Ccache=n"], None)?;
|
|
|
|
assert_ne!(output.stderr, b"");
|
|
assert_eq!(output.stdout, b"");
|
|
assert!(!output.status.success());
|
|
|
|
let code = output
|
|
.status
|
|
.code()
|
|
.expect("wasmtime process should exit normally");
|
|
|
|
// Test for the specific error code Wasmtime uses to indicate a trap return.
|
|
#[cfg(unix)]
|
|
assert_eq!(code, 128 + libc::SIGABRT);
|
|
#[cfg(windows)]
|
|
assert_eq!(code, 3);
|
|
Ok(())
|
|
}
|
|
|
|
// Run a simple WASI hello world, snapshot0 edition.
|
|
#[test]
|
|
fn hello_wasi_snapshot0() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/hello_wasi_snapshot0.wat")?;
|
|
for preview2 in ["-Spreview2=n", "-Spreview2=y"] {
|
|
let stdout = run_wasmtime(&["-Ccache=n", preview2, wasm.path().to_str().unwrap()])?;
|
|
assert_eq!(stdout, "Hello, world!\n");
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
// Run a simple WASI hello world, snapshot1 edition.
|
|
#[test]
|
|
fn hello_wasi_snapshot1() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/hello_wasi_snapshot1.wat")?;
|
|
let stdout = run_wasmtime(&["-Ccache=n", wasm.path().to_str().unwrap()])?;
|
|
assert_eq!(stdout, "Hello, world!\n");
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn timeout_in_start() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/iloop-start.wat")?;
|
|
let output = run_wasmtime_for_output(
|
|
&[
|
|
"run",
|
|
"-Wtimeout=1ms",
|
|
"-Ccache=n",
|
|
wasm.path().to_str().unwrap(),
|
|
],
|
|
None,
|
|
)?;
|
|
assert!(!output.status.success());
|
|
assert_eq!(output.stdout, b"");
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
assert!(
|
|
stderr.contains("wasm trap: interrupt"),
|
|
"bad stderr: {}",
|
|
stderr
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn timeout_in_invoke() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/iloop-invoke.wat")?;
|
|
let output = run_wasmtime_for_output(
|
|
&[
|
|
"run",
|
|
"-Wtimeout=1ms",
|
|
"-Ccache=n",
|
|
wasm.path().to_str().unwrap(),
|
|
],
|
|
None,
|
|
)?;
|
|
assert!(!output.status.success());
|
|
assert_eq!(output.stdout, b"");
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
assert!(
|
|
stderr.contains("wasm trap: interrupt"),
|
|
"bad stderr: {}",
|
|
stderr
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
// Exit with a valid non-zero exit code, snapshot0 edition.
|
|
#[test]
|
|
fn exit2_wasi_snapshot0() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/exit2_wasi_snapshot0.wat")?;
|
|
|
|
for preview2 in ["-Spreview2=n", "-Spreview2=y"] {
|
|
let output = run_wasmtime_for_output(
|
|
&["-Ccache=n", preview2, wasm.path().to_str().unwrap()],
|
|
None,
|
|
)?;
|
|
assert_eq!(output.status.code().unwrap(), 2);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
// Exit with a valid non-zero exit code, snapshot1 edition.
|
|
#[test]
|
|
fn exit2_wasi_snapshot1() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/exit2_wasi_snapshot1.wat")?;
|
|
let output = run_wasmtime_for_output(&["-Ccache=n", wasm.path().to_str().unwrap()], None)?;
|
|
assert_eq!(output.status.code().unwrap(), 2);
|
|
Ok(())
|
|
}
|
|
|
|
// Exit with a valid non-zero exit code, snapshot0 edition.
|
|
#[test]
|
|
fn exit125_wasi_snapshot0() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/exit125_wasi_snapshot0.wat")?;
|
|
for preview2 in ["-Spreview2=n", "-Spreview2=y"] {
|
|
let output = run_wasmtime_for_output(
|
|
&["-Ccache=n", preview2, wasm.path().to_str().unwrap()],
|
|
None,
|
|
)?;
|
|
dbg!(&output);
|
|
if cfg!(windows) {
|
|
assert_eq!(output.status.code().unwrap(), 1);
|
|
} else {
|
|
assert_eq!(output.status.code().unwrap(), 125);
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
// Exit with a valid non-zero exit code, snapshot1 edition.
|
|
#[test]
|
|
fn exit125_wasi_snapshot1() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/exit125_wasi_snapshot1.wat")?;
|
|
let output = run_wasmtime_for_output(&["-Ccache=n", wasm.path().to_str().unwrap()], None)?;
|
|
if cfg!(windows) {
|
|
assert_eq!(output.status.code().unwrap(), 1);
|
|
} else {
|
|
assert_eq!(output.status.code().unwrap(), 125);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
// Exit with an invalid non-zero exit code, snapshot0 edition.
|
|
#[test]
|
|
fn exit126_wasi_snapshot0() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/exit126_wasi_snapshot0.wat")?;
|
|
|
|
for preview2 in ["-Spreview2=n", "-Spreview2=y"] {
|
|
let output = run_wasmtime_for_output(
|
|
&["-Ccache=n", preview2, wasm.path().to_str().unwrap()],
|
|
None,
|
|
)?;
|
|
assert_eq!(output.status.code().unwrap(), 1);
|
|
assert!(output.stdout.is_empty());
|
|
assert!(String::from_utf8_lossy(&output.stderr).contains("invalid exit status"));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
// Exit with an invalid non-zero exit code, snapshot1 edition.
|
|
#[test]
|
|
fn exit126_wasi_snapshot1() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/exit126_wasi_snapshot1.wat")?;
|
|
let output = run_wasmtime_for_output(&[wasm.path().to_str().unwrap(), "-Ccache=n"], None)?;
|
|
assert_eq!(output.status.code().unwrap(), 1);
|
|
assert!(output.stdout.is_empty());
|
|
assert!(String::from_utf8_lossy(&output.stderr).contains("invalid exit status"));
|
|
Ok(())
|
|
}
|
|
|
|
// Run a minimal command program.
|
|
#[test]
|
|
fn minimal_command() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/minimal-command.wat")?;
|
|
let stdout = run_wasmtime(&["-Ccache=n", wasm.path().to_str().unwrap()])?;
|
|
assert_eq!(stdout, "");
|
|
Ok(())
|
|
}
|
|
|
|
// Run a minimal reactor program.
|
|
#[test]
|
|
fn minimal_reactor() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/minimal-reactor.wat")?;
|
|
let stdout = run_wasmtime(&["-Ccache=n", wasm.path().to_str().unwrap()])?;
|
|
assert_eq!(stdout, "");
|
|
Ok(())
|
|
}
|
|
|
|
// Attempt to call invoke on a command.
|
|
#[test]
|
|
fn command_invoke() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/minimal-command.wat")?;
|
|
run_wasmtime(&[
|
|
"run",
|
|
"--invoke",
|
|
"_start",
|
|
"-Ccache=n",
|
|
wasm.path().to_str().unwrap(),
|
|
])?;
|
|
Ok(())
|
|
}
|
|
|
|
// Attempt to call invoke on a command.
|
|
#[test]
|
|
fn reactor_invoke() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/minimal-reactor.wat")?;
|
|
run_wasmtime(&[
|
|
"run",
|
|
"--invoke",
|
|
"_initialize",
|
|
"-Ccache=n",
|
|
wasm.path().to_str().unwrap(),
|
|
])?;
|
|
Ok(())
|
|
}
|
|
|
|
// Run the greeter test, which runs a preloaded reactor and a command.
|
|
#[test]
|
|
fn greeter() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/greeter_command.wat")?;
|
|
let stdout = run_wasmtime(&[
|
|
"run",
|
|
"-Ccache=n",
|
|
"--preload",
|
|
"reactor=tests/all/cli_tests/greeter_reactor.wat",
|
|
wasm.path().to_str().unwrap(),
|
|
])?;
|
|
assert_eq!(
|
|
stdout,
|
|
"Hello _initialize\nHello _start\nHello greet\nHello done\n"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
// Run the greeter test, but this time preload a command.
|
|
#[test]
|
|
fn greeter_preload_command() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/greeter_reactor.wat")?;
|
|
let stdout = run_wasmtime(&[
|
|
"run",
|
|
"-Ccache=n",
|
|
"--preload",
|
|
"reactor=tests/all/cli_tests/hello_wasi_snapshot1.wat",
|
|
wasm.path().to_str().unwrap(),
|
|
])?;
|
|
assert_eq!(stdout, "Hello _initialize\n");
|
|
Ok(())
|
|
}
|
|
|
|
// Run the greeter test, which runs a preloaded reactor and a command.
|
|
#[test]
|
|
fn greeter_preload_callable_command() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/greeter_command.wat")?;
|
|
let stdout = run_wasmtime(&[
|
|
"run",
|
|
"-Ccache=n",
|
|
"--preload",
|
|
"reactor=tests/all/cli_tests/greeter_callable_command.wat",
|
|
wasm.path().to_str().unwrap(),
|
|
])?;
|
|
assert_eq!(stdout, "Hello _start\nHello callable greet\nHello done\n");
|
|
Ok(())
|
|
}
|
|
|
|
// Ensure successful WASI exit call with FPR saving frames on stack for Windows x64
|
|
// See https://github.com/bytecodealliance/wasmtime/issues/1967
|
|
#[test]
|
|
fn exit_with_saved_fprs() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/exit_with_saved_fprs.wat")?;
|
|
let output = run_wasmtime_for_output(&["-Ccache=n", wasm.path().to_str().unwrap()], None)?;
|
|
assert_eq!(output.status.code().unwrap(), 0);
|
|
assert!(output.stdout.is_empty());
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn run_cwasm() -> Result<()> {
|
|
let td = TempDir::new()?;
|
|
let cwasm = td.path().join("foo.cwasm");
|
|
let stdout = run_wasmtime(&[
|
|
"compile",
|
|
"tests/all/cli_tests/simple.wat",
|
|
"-o",
|
|
cwasm.to_str().unwrap(),
|
|
])?;
|
|
assert_eq!(stdout, "");
|
|
let stdout = run_wasmtime(&["run", "--allow-precompiled", cwasm.to_str().unwrap()])?;
|
|
assert_eq!(stdout, "");
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
#[test]
|
|
fn hello_wasi_snapshot0_from_stdin() -> Result<()> {
|
|
// Run a simple WASI hello world, snapshot0 edition.
|
|
// The module is piped from standard input.
|
|
let wasm = build_wasm("tests/all/cli_tests/hello_wasi_snapshot0.wat")?;
|
|
for preview2 in ["-Spreview2=n", "-Spreview2=y"] {
|
|
let stdout = {
|
|
let path = wasm.path();
|
|
let args: &[&str] = &["-Ccache=n", preview2, "-"];
|
|
let output = run_wasmtime_for_output(args, Some(path))?;
|
|
if !output.status.success() {
|
|
bail!(
|
|
"Failed to execute wasmtime with: {:?}\n{}",
|
|
args,
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
}
|
|
Ok::<_, anyhow::Error>(String::from_utf8(output.stdout).unwrap())
|
|
}?;
|
|
assert_eq!(stdout, "Hello, world!\n");
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn specify_env() -> Result<()> {
|
|
// By default no env is inherited
|
|
let output = get_wasmtime_command()?
|
|
.args(&["run", "tests/all/cli_tests/print_env.wat"])
|
|
.env("THIS_WILL_NOT", "show up in the output")
|
|
.output()?;
|
|
assert!(output.status.success());
|
|
assert_eq!(String::from_utf8_lossy(&output.stdout), "");
|
|
|
|
// Specify a single env var
|
|
let output = get_wasmtime_command()?
|
|
.args(&[
|
|
"run",
|
|
"--env",
|
|
"FOO=bar",
|
|
"tests/all/cli_tests/print_env.wat",
|
|
])
|
|
.output()?;
|
|
assert!(output.status.success());
|
|
assert_eq!(String::from_utf8_lossy(&output.stdout), "FOO=bar\n");
|
|
|
|
// Inherit a single env var
|
|
let output = get_wasmtime_command()?
|
|
.args(&["run", "--env", "FOO", "tests/all/cli_tests/print_env.wat"])
|
|
.env("FOO", "bar")
|
|
.output()?;
|
|
assert!(output.status.success());
|
|
assert_eq!(String::from_utf8_lossy(&output.stdout), "FOO=bar\n");
|
|
|
|
// Inherit a nonexistent env var
|
|
let output = get_wasmtime_command()?
|
|
.args(&[
|
|
"run",
|
|
"--env",
|
|
"SURELY_THIS_ENV_VAR_DOES_NOT_EXIST_ANYWHERE_RIGHT",
|
|
"tests/all/cli_tests/print_env.wat",
|
|
])
|
|
.output()?;
|
|
assert!(!output.status.success());
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
#[test]
|
|
fn run_cwasm_from_stdin() -> Result<()> {
|
|
use std::process::Stdio;
|
|
|
|
let td = TempDir::new()?;
|
|
let cwasm = td.path().join("foo.cwasm");
|
|
let stdout = run_wasmtime(&[
|
|
"compile",
|
|
"tests/all/cli_tests/simple.wat",
|
|
"-o",
|
|
cwasm.to_str().unwrap(),
|
|
])?;
|
|
assert_eq!(stdout, "");
|
|
|
|
// If stdin is literally the file itself then that should work
|
|
let args: &[&str] = &["run", "--allow-precompiled", "-"];
|
|
let output = get_wasmtime_command()?
|
|
.args(args)
|
|
.stdin(File::open(&cwasm)?)
|
|
.output()?;
|
|
assert!(output.status.success(), "a file as stdin should work");
|
|
|
|
// If stdin is a pipe, that should also work
|
|
let input = std::fs::read(&cwasm)?;
|
|
let mut child = get_wasmtime_command()?
|
|
.args(args)
|
|
.stdin(Stdio::piped())
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped())
|
|
.spawn()?;
|
|
let mut stdin = child.stdin.take().unwrap();
|
|
let t = std::thread::spawn(move || {
|
|
let _ = stdin.write_all(&input);
|
|
});
|
|
let output = child.wait_with_output()?;
|
|
assert!(output.status.success());
|
|
t.join().unwrap();
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "wasi-threads")]
|
|
#[test]
|
|
fn run_threads() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/threads.wat")?;
|
|
let stdout = run_wasmtime(&[
|
|
"run",
|
|
"-Wthreads",
|
|
"-Sthreads",
|
|
"-Ccache=n",
|
|
wasm.path().to_str().unwrap(),
|
|
])?;
|
|
|
|
assert!(
|
|
stdout
|
|
== "Called _start\n\
|
|
Running wasi_thread_start\n\
|
|
Running wasi_thread_start\n\
|
|
Running wasi_thread_start\n\
|
|
Done\n"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "wasi-threads")]
|
|
#[test]
|
|
fn run_simple_with_wasi_threads() -> Result<()> {
|
|
// We expect to be able to run Wasm modules that do not have correct
|
|
// wasi-thread entry points or imported shared memory as long as no threads
|
|
// are spawned.
|
|
let wasm = build_wasm("tests/all/cli_tests/simple.wat")?;
|
|
let stdout = run_wasmtime(&[
|
|
"run",
|
|
"-Wthreads",
|
|
"-Sthreads",
|
|
"-Ccache=n",
|
|
"--invoke",
|
|
"simple",
|
|
wasm.path().to_str().unwrap(),
|
|
"4",
|
|
])?;
|
|
assert_eq!(stdout, "4\n");
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn wasm_flags() -> Result<()> {
|
|
// Any argument after the wasm module should be interpreted as for the
|
|
// command itself
|
|
let stdout = run_wasmtime(&[
|
|
"run",
|
|
"--",
|
|
"tests/all/cli_tests/print-arguments.wat",
|
|
"--argument",
|
|
"-for",
|
|
"the",
|
|
"command",
|
|
])?;
|
|
assert_eq!(
|
|
stdout,
|
|
"\
|
|
print-arguments.wat\n\
|
|
--argument\n\
|
|
-for\n\
|
|
the\n\
|
|
command\n\
|
|
"
|
|
);
|
|
let stdout = run_wasmtime(&["run", "--", "tests/all/cli_tests/print-arguments.wat", "-"])?;
|
|
assert_eq!(
|
|
stdout,
|
|
"\
|
|
print-arguments.wat\n\
|
|
-\n\
|
|
"
|
|
);
|
|
let stdout = run_wasmtime(&["run", "--", "tests/all/cli_tests/print-arguments.wat", "--"])?;
|
|
assert_eq!(
|
|
stdout,
|
|
"\
|
|
print-arguments.wat\n\
|
|
--\n\
|
|
"
|
|
);
|
|
let stdout = run_wasmtime(&[
|
|
"run",
|
|
"--",
|
|
"tests/all/cli_tests/print-arguments.wat",
|
|
"--",
|
|
"--",
|
|
"-a",
|
|
"b",
|
|
])?;
|
|
assert_eq!(
|
|
stdout,
|
|
"\
|
|
print-arguments.wat\n\
|
|
--\n\
|
|
--\n\
|
|
-a\n\
|
|
b\n\
|
|
"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn name_same_as_builtin_command() -> Result<()> {
|
|
// a bare subcommand shouldn't run successfully
|
|
let output = get_wasmtime_command()?
|
|
.current_dir("tests/all/cli_tests")
|
|
.arg("run")
|
|
.output()?;
|
|
assert!(!output.status.success());
|
|
|
|
// a `--` prefix should let everything else get interpreted as a wasm
|
|
// module and arguments, even if the module has a name like `run`
|
|
let output = get_wasmtime_command()?
|
|
.current_dir("tests/all/cli_tests")
|
|
.arg("--")
|
|
.arg("run")
|
|
.output()?;
|
|
assert!(output.status.success(), "expected success got {output:#?}");
|
|
|
|
// Passing options before the subcommand should work and doesn't require
|
|
// `--` to disambiguate
|
|
let output = get_wasmtime_command()?
|
|
.current_dir("tests/all/cli_tests")
|
|
.arg("-Ccache=n")
|
|
.arg("run")
|
|
.output()?;
|
|
assert!(output.status.success(), "expected success got {output:#?}");
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(unix)]
|
|
fn run_just_stdin_argument() -> Result<()> {
|
|
let output = get_wasmtime_command()?
|
|
.arg("-")
|
|
.stdin(File::open("tests/all/cli_tests/simple.wat")?)
|
|
.output()?;
|
|
assert!(output.status.success());
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn wasm_flags_without_subcommand() -> Result<()> {
|
|
let output = get_wasmtime_command()?
|
|
.current_dir("tests/all/cli_tests/")
|
|
.arg("print-arguments.wat")
|
|
.arg("-foo")
|
|
.arg("bar")
|
|
.output()?;
|
|
assert!(output.status.success());
|
|
assert_eq!(
|
|
String::from_utf8_lossy(&output.stdout),
|
|
"\
|
|
print-arguments.wat\n\
|
|
-foo\n\
|
|
bar\n\
|
|
"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn wasi_misaligned_pointer() -> Result<()> {
|
|
let output = get_wasmtime_command()?
|
|
.arg("./tests/all/cli_tests/wasi_misaligned_pointer.wat")
|
|
.output()?;
|
|
assert!(!output.status.success());
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
assert!(
|
|
stderr.contains("Pointer not aligned"),
|
|
"bad stderr: {stderr}",
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
#[cfg_attr(not(feature = "component-model"), ignore)]
|
|
fn hello_with_preview2() -> Result<()> {
|
|
let wasm = build_wasm("tests/all/cli_tests/hello_wasi_snapshot1.wat")?;
|
|
let stdout = run_wasmtime(&["-Ccache=n", "-Spreview2", wasm.path().to_str().unwrap()])?;
|
|
assert_eq!(stdout, "Hello, world!\n");
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
#[cfg_attr(not(feature = "component-model"), ignore)]
|
|
fn component_missing_feature() -> Result<()> {
|
|
let path = "tests/all/cli_tests/empty-component.wat";
|
|
let wasm = build_wasm(path)?;
|
|
let output = get_wasmtime_command()?
|
|
.arg("-Ccache=n")
|
|
.arg("-Wcomponent-model=n")
|
|
.arg(wasm.path())
|
|
.output()?;
|
|
assert!(!output.status.success());
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
assert!(
|
|
stderr.contains("cannot execute a component without `--wasm component-model`"),
|
|
"bad stderr: {stderr}"
|
|
);
|
|
|
|
// also tests with raw *.wat input
|
|
let output = get_wasmtime_command()?
|
|
.arg("-Ccache=n")
|
|
.arg("-Wcomponent-model=n")
|
|
.arg(path)
|
|
.output()?;
|
|
assert!(!output.status.success());
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
assert!(
|
|
stderr.contains("cannot execute a component without `--wasm component-model`"),
|
|
"bad stderr: {stderr}"
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
#[cfg_attr(not(feature = "component-model"), ignore)]
|
|
fn component_enabled_by_default() -> Result<()> {
|
|
let path = "tests/all/cli_tests/component-basic.wat";
|
|
let wasm = build_wasm(path)?;
|
|
let output = get_wasmtime_command()?
|
|
.arg("-Ccache=n")
|
|
.arg(wasm.path())
|
|
.output()?;
|
|
assert!(output.status.success());
|
|
|
|
// also tests with raw *.wat input
|
|
let output = get_wasmtime_command()?
|
|
.arg("-Ccache=n")
|
|
.arg(path)
|
|
.output()?;
|
|
assert!(output.status.success());
|
|
|
|
Ok(())
|
|
}
|
|
|
|
// If the text format is invalid then the filename should be mentioned in the
|
|
// error message.
|
|
#[test]
|
|
fn bad_text_syntax() -> Result<()> {
|
|
let output = get_wasmtime_command()?
|
|
.arg("-Ccache=n")
|
|
.arg("tests/all/cli_tests/bad-syntax.wat")
|
|
.output()?;
|
|
assert!(!output.status.success());
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
assert!(
|
|
stderr.contains("--> tests/all/cli_tests/bad-syntax.wat"),
|
|
"bad stderr: {stderr}"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
#[cfg_attr(not(feature = "component-model"), ignore)]
|
|
fn run_basic_component() -> Result<()> {
|
|
let path = "tests/all/cli_tests/component-basic.wat";
|
|
let wasm = build_wasm(path)?;
|
|
|
|
// Run both the `*.wasm` binary and the text format
|
|
run_wasmtime(&[
|
|
"-Ccache=n",
|
|
"-Wcomponent-model",
|
|
wasm.path().to_str().unwrap(),
|
|
])?;
|
|
run_wasmtime(&["-Ccache=n", "-Wcomponent-model", path])?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
#[cfg_attr(not(feature = "component-model"), ignore)]
|
|
fn run_precompiled_component() -> Result<()> {
|
|
let td = TempDir::new()?;
|
|
let cwasm = td.path().join("component-basic.cwasm");
|
|
let stdout = run_wasmtime(&[
|
|
"compile",
|
|
"tests/all/cli_tests/component-basic.wat",
|
|
"-o",
|
|
cwasm.to_str().unwrap(),
|
|
"-Wcomponent-model",
|
|
])?;
|
|
assert_eq!(stdout, "");
|
|
let stdout = run_wasmtime(&[
|
|
"run",
|
|
"-Wcomponent-model",
|
|
"--allow-precompiled",
|
|
cwasm.to_str().unwrap(),
|
|
])?;
|
|
assert_eq!(stdout, "");
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn memory_growth_failure() -> Result<()> {
|
|
let output = get_wasmtime_command()?
|
|
.args(&[
|
|
"run",
|
|
"-Wmemory64",
|
|
"-Wtrap-on-grow-failure",
|
|
"tests/all/cli_tests/memory-grow-failure.wat",
|
|
])
|
|
.output()?;
|
|
assert!(!output.status.success());
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
assert!(
|
|
stderr.contains("forcing a memory growth failure to be a trap"),
|
|
"bad stderr: {stderr}"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn table_growth_failure() -> Result<()> {
|
|
let output = get_wasmtime_command()?
|
|
.args(&[
|
|
"run",
|
|
"-Wtrap-on-grow-failure",
|
|
"tests/all/cli_tests/table-grow-failure.wat",
|
|
])
|
|
.output()?;
|
|
assert!(!output.status.success());
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
assert!(
|
|
stderr.contains("forcing trap when growing table"),
|
|
"bad stderr: {stderr}"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn table_growth_failure2() -> Result<()> {
|
|
let output = get_wasmtime_command()?
|
|
.args(&[
|
|
"run",
|
|
"-Wtrap-on-grow-failure",
|
|
"tests/all/cli_tests/table-grow-failure2.wat",
|
|
])
|
|
.output()?;
|
|
assert!(!output.status.success());
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
assert!(
|
|
stderr.contains("forcing a table growth failure to be a trap"),
|
|
"bad stderr: {stderr}"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn option_group_help() -> Result<()> {
|
|
run_wasmtime(&["run", "-Whelp"])?;
|
|
run_wasmtime(&["run", "-O", "help"])?;
|
|
run_wasmtime(&["run", "--codegen", "help"])?;
|
|
run_wasmtime(&["run", "--debug=help"])?;
|
|
run_wasmtime(&["run", "-Shelp"])?;
|
|
run_wasmtime(&["run", "-Whelp-long"])?;
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn option_group_comma_separated() -> Result<()> {
|
|
run_wasmtime(&[
|
|
"run",
|
|
"-Wrelaxed-simd,simd",
|
|
"tests/all/cli_tests/simple.wat",
|
|
])?;
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn option_group_boolean_parsing() -> Result<()> {
|
|
run_wasmtime(&["run", "-Wrelaxed-simd", "tests/all/cli_tests/simple.wat"])?;
|
|
run_wasmtime(&["run", "-Wrelaxed-simd=n", "tests/all/cli_tests/simple.wat"])?;
|
|
run_wasmtime(&["run", "-Wrelaxed-simd=y", "tests/all/cli_tests/simple.wat"])?;
|
|
run_wasmtime(&["run", "-Wrelaxed-simd=no", "tests/all/cli_tests/simple.wat"])?;
|
|
run_wasmtime(&[
|
|
"run",
|
|
"-Wrelaxed-simd=yes",
|
|
"tests/all/cli_tests/simple.wat",
|
|
])?;
|
|
run_wasmtime(&[
|
|
"run",
|
|
"-Wrelaxed-simd=true",
|
|
"tests/all/cli_tests/simple.wat",
|
|
])?;
|
|
run_wasmtime(&[
|
|
"run",
|
|
"-Wrelaxed-simd=false",
|
|
"tests/all/cli_tests/simple.wat",
|
|
])?;
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn preview2_stdin() -> Result<()> {
|
|
let test = "tests/all/cli_tests/count-stdin.wat";
|
|
let cmd = || -> Result<_> {
|
|
let mut cmd = get_wasmtime_command()?;
|
|
cmd.arg("--invoke=count").arg("-Spreview2").arg(test);
|
|
Ok(cmd)
|
|
};
|
|
|
|
// read empty pipe is ok
|
|
let output = cmd()?.output()?;
|
|
assert!(output.status.success());
|
|
assert_eq!(String::from_utf8_lossy(&output.stdout), "0\n");
|
|
|
|
// read itself is ok
|
|
let file = File::open(test)?;
|
|
let size = file.metadata()?.len();
|
|
let output = cmd()?.stdin(File::open(test)?).output()?;
|
|
assert!(output.status.success());
|
|
assert_eq!(String::from_utf8_lossy(&output.stdout), format!("{size}\n"));
|
|
|
|
// read piped input ok is ok
|
|
let mut child = cmd()?
|
|
.stdin(Stdio::piped())
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped())
|
|
.spawn()?;
|
|
let mut stdin = child.stdin.take().unwrap();
|
|
std::thread::spawn(move || {
|
|
stdin.write_all(b"hello").unwrap();
|
|
});
|
|
let output = child.wait_with_output()?;
|
|
assert!(output.status.success());
|
|
assert_eq!(String::from_utf8_lossy(&output.stdout), "5\n");
|
|
|
|
let count_up_to = |n: usize| -> Result<_> {
|
|
let mut child = get_wasmtime_command()?
|
|
.arg("--invoke=count-up-to")
|
|
.arg("-Spreview2")
|
|
.arg(test)
|
|
.arg(n.to_string())
|
|
.stdin(Stdio::piped())
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped())
|
|
.spawn()?;
|
|
let mut stdin = child.stdin.take().unwrap();
|
|
let t = std::thread::spawn(move || {
|
|
let mut written = 0;
|
|
let bytes = [0; 64 * 1024];
|
|
loop {
|
|
written += match stdin.write(&bytes) {
|
|
Ok(n) => n,
|
|
Err(_) => break written,
|
|
};
|
|
}
|
|
});
|
|
let output = child.wait_with_output()?;
|
|
assert!(output.status.success());
|
|
let written = t.join().unwrap();
|
|
let read = String::from_utf8_lossy(&output.stdout)
|
|
.trim()
|
|
.parse::<usize>()
|
|
.unwrap();
|
|
// The test reads in 1000 byte chunks so make sure that it doesn't read
|
|
// more than 1000 bytes than requested.
|
|
assert!(read < n + 1000, "test read too much {read}");
|
|
Ok(written)
|
|
};
|
|
|
|
// wasmtime shouldn't eat information that the guest never actually tried to
|
|
// read.
|
|
//
|
|
// NB: this may be a bit flaky. Exactly how much we wrote in the above
|
|
// helper thread depends on how much the OS buffers for us. For now give
|
|
// some some slop and assume that OSes are unlikely to buffer more than
|
|
// that.
|
|
let slop = 256 * 1024;
|
|
for amt in [0, 100, 100_000] {
|
|
let written = count_up_to(amt)?;
|
|
assert!(written < slop + amt, "wrote too much {written}");
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn old_cli_warn_if_ambiguous_flags() -> Result<()> {
|
|
// This is accepted in the old CLI parser and the new but it's interpreted
|
|
// differently so a warning should be printed.
|
|
let output = get_wasmtime_command()?
|
|
.args(&["tests/all/cli_tests/simple.wat", "--invoke", "get_f32"])
|
|
.output()?;
|
|
assert_eq!(String::from_utf8_lossy(&output.stdout), "100\n");
|
|
assert_eq!(
|
|
String::from_utf8_lossy(&output.stderr),
|
|
"\
|
|
warning: this CLI invocation of Wasmtime will be parsed differently in future
|
|
Wasmtime versions -- see this online issue for more information:
|
|
https://github.com/bytecodealliance/wasmtime/issues/7384
|
|
|
|
Wasmtime will now execute with the old (<= Wasmtime 13) CLI parsing,
|
|
however this behavior can also be temporarily configured with an
|
|
environment variable:
|
|
|
|
- WASMTIME_NEW_CLI=0 to indicate old semantics are desired and silence this warning, or
|
|
- WASMTIME_NEW_CLI=1 to indicate new semantics are desired and use the latest behavior
|
|
warning: using `--invoke` with a function that returns values is experimental and may break in the future
|
|
"
|
|
);
|
|
|
|
// Test disabling the warning
|
|
let output = get_wasmtime_command()?
|
|
.args(&["tests/all/cli_tests/simple.wat", "--invoke", "get_f32"])
|
|
.env("WASMTIME_NEW_CLI", "0")
|
|
.output()?;
|
|
assert_eq!(String::from_utf8_lossy(&output.stdout), "100\n");
|
|
assert_eq!(
|
|
String::from_utf8_lossy(&output.stderr),
|
|
"\
|
|
warning: using `--invoke` with a function that returns values is experimental and may break in the future
|
|
"
|
|
);
|
|
|
|
// Test forcing the new behavior where nothing happens because the file is
|
|
// invoked with `--invoke` as its own argument.
|
|
let output = get_wasmtime_command()?
|
|
.args(&["tests/all/cli_tests/simple.wat", "--invoke", "get_f32"])
|
|
.env("WASMTIME_NEW_CLI", "1")
|
|
.output()?;
|
|
assert_eq!(String::from_utf8_lossy(&output.stdout), "");
|
|
assert_eq!(String::from_utf8_lossy(&output.stderr), "");
|
|
|
|
// This is unambiguous
|
|
let output = get_wasmtime_command()?
|
|
.args(&["--invoke", "get_f32", "tests/all/cli_tests/simple.wat"])
|
|
.output()?;
|
|
assert_eq!(String::from_utf8_lossy(&output.stdout), "100\n");
|
|
assert_eq!(
|
|
String::from_utf8_lossy(&output.stderr),
|
|
"\
|
|
warning: using `--invoke` with a function that returns values is experimental and may break in the future
|
|
"
|
|
);
|
|
|
|
// This fails to parse in the old but succeeds in the new, so it should run
|
|
// under the new semantics with no warning.
|
|
let output = get_wasmtime_command()?
|
|
.args(&["run", "tests/all/cli_tests/print-arguments.wat", "--arg"])
|
|
.output()?;
|
|
assert_eq!(
|
|
String::from_utf8_lossy(&output.stdout),
|
|
"print-arguments.wat\n--arg\n"
|
|
);
|
|
assert_eq!(String::from_utf8_lossy(&output.stderr), "");
|
|
|
|
// Old behavior can be forced however
|
|
let output = get_wasmtime_command()?
|
|
.args(&["run", "tests/all/cli_tests/print-arguments.wat", "--arg"])
|
|
.env("WASMTIME_NEW_CLI", "0")
|
|
.output()?;
|
|
assert!(!output.status.success());
|
|
|
|
// This works in both the old and the new, so no warnings
|
|
let output = get_wasmtime_command()?
|
|
.args(&["run", "tests/all/cli_tests/print-arguments.wat", "arg"])
|
|
.output()?;
|
|
assert_eq!(
|
|
String::from_utf8_lossy(&output.stdout),
|
|
"print-arguments.wat\narg\n"
|
|
);
|
|
assert_eq!(String::from_utf8_lossy(&output.stderr), "");
|
|
|
|
// This works in both the old and the new, so no warnings
|
|
let output = get_wasmtime_command()?
|
|
.args(&[
|
|
"run",
|
|
"--",
|
|
"tests/all/cli_tests/print-arguments.wat",
|
|
"--arg",
|
|
])
|
|
.output()?;
|
|
assert_eq!(
|
|
String::from_utf8_lossy(&output.stdout),
|
|
"print-arguments.wat\n--arg\n"
|
|
);
|
|
assert_eq!(String::from_utf8_lossy(&output.stderr), "");
|
|
|
|
// Old flags still work, but with a warning
|
|
let output = get_wasmtime_command()?
|
|
.args(&[
|
|
"run",
|
|
"--max-wasm-stack",
|
|
"1000000",
|
|
"tests/all/cli_tests/print-arguments.wat",
|
|
])
|
|
.output()?;
|
|
assert_eq!(
|
|
String::from_utf8_lossy(&output.stdout),
|
|
"print-arguments.wat\n"
|
|
);
|
|
assert_eq!(
|
|
String::from_utf8_lossy(&output.stderr),
|
|
"\
|
|
warning: this CLI invocation of Wasmtime is going to break in the future -- for
|
|
more information see this issue online:
|
|
https://github.com/bytecodealliance/wasmtime/issues/7384
|
|
|
|
Wasmtime will now execute with the old (<= Wasmtime 13) CLI parsing,
|
|
however this behavior can also be temporarily configured with an
|
|
environment variable:
|
|
|
|
- WASMTIME_NEW_CLI=0 to indicate old semantics are desired and silence this warning, or
|
|
- WASMTIME_NEW_CLI=1 to indicate new semantics are desired and see the error
|
|
"
|
|
);
|
|
|
|
// Old flags warning is suppressible.
|
|
let output = get_wasmtime_command()?
|
|
.args(&[
|
|
"run",
|
|
"--max-wasm-stack",
|
|
"1000000",
|
|
"tests/all/cli_tests/print-arguments.wat",
|
|
])
|
|
.env("WASMTIME_NEW_CLI", "0")
|
|
.output()?;
|
|
assert_eq!(
|
|
String::from_utf8_lossy(&output.stdout),
|
|
"print-arguments.wat\n"
|
|
);
|
|
assert_eq!(String::from_utf8_lossy(&output.stderr), "");
|
|
|
|
// the `--dir` flag prints no warning when used with `::`
|
|
let dir = tempfile::tempdir()?;
|
|
std::fs::write(dir.path().join("bar.txt"), b"And stood awhile in thought")?;
|
|
let output = get_wasmtime_command()?
|
|
.args(&[
|
|
"run",
|
|
&format!("--dir={}::/", dir.path().to_str().unwrap()),
|
|
test_programs_artifacts::CLI_FILE_READ,
|
|
])
|
|
.output()?;
|
|
assert_eq!(String::from_utf8_lossy(&output.stdout), "");
|
|
assert_eq!(String::from_utf8_lossy(&output.stderr), "");
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn float_args() -> Result<()> {
|
|
let result = run_wasmtime(&[
|
|
"--invoke",
|
|
"echo_f32",
|
|
"tests/all/cli_tests/simple.wat",
|
|
"1.0",
|
|
])?;
|
|
assert_eq!(result, "1\n");
|
|
let result = run_wasmtime(&[
|
|
"--invoke",
|
|
"echo_f64",
|
|
"tests/all/cli_tests/simple.wat",
|
|
"1.1",
|
|
])?;
|
|
assert_eq!(result, "1.1\n");
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn mpk_without_pooling() -> Result<()> {
|
|
let output = get_wasmtime_command()?
|
|
.args(&[
|
|
"run",
|
|
"-O",
|
|
"memory-protection-keys=y",
|
|
"--invoke",
|
|
"echo_f32",
|
|
"tests/all/cli_tests/simple.wat",
|
|
"1.0",
|
|
])
|
|
.env("WASMTIME_NEW_CLI", "1")
|
|
.output()?;
|
|
assert!(!output.status.success());
|
|
Ok(())
|
|
}
|
|
|
|
mod test_programs {
|
|
use super::{get_wasmtime_command, run_wasmtime};
|
|
use anyhow::Result;
|
|
use std::io::{Read, Write};
|
|
use std::process::Stdio;
|
|
use test_programs_artifacts::*;
|
|
|
|
macro_rules! assert_test_exists {
|
|
($name:ident) => {
|
|
#[allow(unused_imports)]
|
|
use self::$name as _;
|
|
};
|
|
}
|
|
foreach_cli!(assert_test_exists);
|
|
|
|
#[test]
|
|
fn cli_hello_stdout() -> Result<()> {
|
|
run_wasmtime(&[
|
|
"run",
|
|
"-Wcomponent-model",
|
|
CLI_HELLO_STDOUT_COMPONENT,
|
|
"gussie",
|
|
"sparky",
|
|
"willa",
|
|
])?;
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn cli_args() -> Result<()> {
|
|
run_wasmtime(&[
|
|
"run",
|
|
"-Wcomponent-model",
|
|
CLI_ARGS_COMPONENT,
|
|
"hello",
|
|
"this",
|
|
"",
|
|
"is an argument",
|
|
"with 🚩 emoji",
|
|
])?;
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn cli_stdin() -> Result<()> {
|
|
let mut child = get_wasmtime_command()?
|
|
.args(&["run", "-Wcomponent-model", CLI_STDIN_COMPONENT])
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped())
|
|
.stdin(Stdio::piped())
|
|
.spawn()?;
|
|
child
|
|
.stdin
|
|
.take()
|
|
.unwrap()
|
|
.write_all(b"So rested he by the Tumtum tree")
|
|
.unwrap();
|
|
let output = child.wait_with_output()?;
|
|
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
|
|
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
|
|
assert!(output.status.success());
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn cli_splice_stdin() -> Result<()> {
|
|
let mut child = get_wasmtime_command()?
|
|
.args(&["run", "-Wcomponent-model", CLI_SPLICE_STDIN_COMPONENT])
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped())
|
|
.stdin(Stdio::piped())
|
|
.spawn()?;
|
|
let msg = "So rested he by the Tumtum tree";
|
|
child
|
|
.stdin
|
|
.take()
|
|
.unwrap()
|
|
.write_all(msg.as_bytes())
|
|
.unwrap();
|
|
let output = child.wait_with_output()?;
|
|
assert!(output.status.success());
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
if !stderr.is_empty() {
|
|
eprintln!("{stderr}");
|
|
}
|
|
|
|
assert_eq!(
|
|
format!(
|
|
"before splice\n{msg}\ncompleted splicing {} bytes\n",
|
|
msg.as_bytes().len()
|
|
),
|
|
stdout
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn cli_env() -> Result<()> {
|
|
run_wasmtime(&[
|
|
"run",
|
|
"-Wcomponent-model",
|
|
"--env=frabjous=day",
|
|
"--env=callooh=callay",
|
|
CLI_ENV_COMPONENT,
|
|
])?;
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn cli_file_read() -> Result<()> {
|
|
let dir = tempfile::tempdir()?;
|
|
|
|
std::fs::write(dir.path().join("bar.txt"), b"And stood awhile in thought")?;
|
|
|
|
run_wasmtime(&[
|
|
"run",
|
|
"-Wcomponent-model",
|
|
&format!("--dir={}::/", dir.path().to_str().unwrap()),
|
|
CLI_FILE_READ_COMPONENT,
|
|
])?;
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn cli_file_append() -> Result<()> {
|
|
let dir = tempfile::tempdir()?;
|
|
|
|
std::fs::File::create(dir.path().join("bar.txt"))?
|
|
.write_all(b"'Twas brillig, and the slithy toves.\n")?;
|
|
|
|
run_wasmtime(&[
|
|
"run",
|
|
"-Wcomponent-model",
|
|
&format!("--dir={}::/", dir.path().to_str().unwrap()),
|
|
CLI_FILE_APPEND_COMPONENT,
|
|
])?;
|
|
|
|
let contents = std::fs::read(dir.path().join("bar.txt"))?;
|
|
assert_eq!(
|
|
std::str::from_utf8(&contents).unwrap(),
|
|
"'Twas brillig, and the slithy toves.\n\
|
|
Did gyre and gimble in the wabe;\n\
|
|
All mimsy were the borogoves,\n\
|
|
And the mome raths outgrabe.\n"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn cli_file_dir_sync() -> Result<()> {
|
|
let dir = tempfile::tempdir()?;
|
|
|
|
std::fs::File::create(dir.path().join("bar.txt"))?
|
|
.write_all(b"'Twas brillig, and the slithy toves.\n")?;
|
|
|
|
run_wasmtime(&[
|
|
"run",
|
|
"-Wcomponent-model",
|
|
&format!("--dir={}::/", dir.path().to_str().unwrap()),
|
|
CLI_FILE_DIR_SYNC_COMPONENT,
|
|
])?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn cli_exit_success() -> Result<()> {
|
|
run_wasmtime(&["run", "-Wcomponent-model", CLI_EXIT_SUCCESS_COMPONENT])?;
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn cli_exit_default() -> Result<()> {
|
|
run_wasmtime(&["run", "-Wcomponent-model", CLI_EXIT_DEFAULT_COMPONENT])?;
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn cli_exit_failure() -> Result<()> {
|
|
let output = get_wasmtime_command()?
|
|
.args(&["run", "-Wcomponent-model", CLI_EXIT_FAILURE_COMPONENT])
|
|
.output()?;
|
|
assert!(!output.status.success());
|
|
assert_eq!(output.status.code(), Some(1));
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn cli_exit_panic() -> Result<()> {
|
|
let output = get_wasmtime_command()?
|
|
.args(&["run", "-Wcomponent-model", CLI_EXIT_PANIC_COMPONENT])
|
|
.output()?;
|
|
assert!(!output.status.success());
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
assert!(stderr.contains("Curiouser and curiouser!"));
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn cli_directory_list() -> Result<()> {
|
|
let dir = tempfile::tempdir()?;
|
|
|
|
std::fs::File::create(dir.path().join("foo.txt"))?;
|
|
std::fs::File::create(dir.path().join("bar.txt"))?;
|
|
std::fs::File::create(dir.path().join("baz.txt"))?;
|
|
std::fs::create_dir(dir.path().join("sub"))?;
|
|
std::fs::File::create(dir.path().join("sub").join("wow.txt"))?;
|
|
std::fs::File::create(dir.path().join("sub").join("yay.txt"))?;
|
|
|
|
run_wasmtime(&[
|
|
"run",
|
|
"-Wcomponent-model",
|
|
&format!("--dir={}::/", dir.path().to_str().unwrap()),
|
|
CLI_DIRECTORY_LIST_COMPONENT,
|
|
])?;
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn cli_default_clocks() -> Result<()> {
|
|
run_wasmtime(&["run", "-Wcomponent-model", CLI_DEFAULT_CLOCKS_COMPONENT])?;
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn cli_export_cabi_realloc() -> Result<()> {
|
|
run_wasmtime(&[
|
|
"run",
|
|
"-Wcomponent-model",
|
|
CLI_EXPORT_CABI_REALLOC_COMPONENT,
|
|
])?;
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn run_wasi_http_component() -> Result<()> {
|
|
let output = super::run_wasmtime_for_output(
|
|
&[
|
|
"-Ccache=no",
|
|
"-Wcomponent-model",
|
|
"-Scommon,http,preview2",
|
|
HTTP_OUTBOUND_REQUEST_RESPONSE_BUILD_COMPONENT,
|
|
],
|
|
None,
|
|
)?;
|
|
println!("{}", String::from_utf8_lossy(&output.stderr));
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
println!("{}", stdout);
|
|
assert!(stdout.starts_with("Called _start\n"));
|
|
assert!(stdout.ends_with("Done\n"));
|
|
assert!(output.status.success());
|
|
Ok(())
|
|
}
|
|
|
|
// Test to ensure that prints in the guest aren't buffered on the host by
|
|
// accident. The test here will print something without a newline and then
|
|
// wait for input on stdin, and the test here is to ensure that the
|
|
// character shows up here even as the guest is waiting on input via stdin.
|
|
#[test]
|
|
fn cli_stdio_write_flushes() -> Result<()> {
|
|
fn run(args: &[&str]) -> Result<()> {
|
|
println!("running {args:?}");
|
|
let mut child = get_wasmtime_command()?
|
|
.args(args)
|
|
.stdin(Stdio::piped())
|
|
.stdout(Stdio::piped())
|
|
.spawn()?;
|
|
let mut stdout = child.stdout.take().unwrap();
|
|
let mut buf = [0; 10];
|
|
match stdout.read(&mut buf) {
|
|
Ok(2) => assert_eq!(&buf[..2], b"> "),
|
|
e => panic!("unexpected read result {e:?}"),
|
|
}
|
|
drop(stdout);
|
|
drop(child.stdin.take().unwrap());
|
|
let status = child.wait()?;
|
|
assert!(status.success());
|
|
Ok(())
|
|
}
|
|
|
|
run(&["run", "-Spreview2=n", CLI_STDIO_WRITE_FLUSHES])?;
|
|
run(&["run", "-Spreview2=y", CLI_STDIO_WRITE_FLUSHES])?;
|
|
run(&[
|
|
"run",
|
|
"-Wcomponent-model",
|
|
CLI_STDIO_WRITE_FLUSHES_COMPONENT,
|
|
])?;
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn cli_no_tcp() -> Result<()> {
|
|
let output = super::run_wasmtime_for_output(
|
|
&[
|
|
"-Wcomponent-model",
|
|
// Turn on network but turn off TCP
|
|
"-Sinherit-network,tcp=no",
|
|
CLI_NO_TCP_COMPONENT,
|
|
],
|
|
None,
|
|
)?;
|
|
println!("{}", String::from_utf8_lossy(&output.stderr));
|
|
assert!(output.status.success());
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn cli_no_udp() -> Result<()> {
|
|
let output = super::run_wasmtime_for_output(
|
|
&[
|
|
"-Wcomponent-model",
|
|
// Turn on network but turn off UDP
|
|
"-Sinherit-network,udp=no",
|
|
CLI_NO_UDP_COMPONENT,
|
|
],
|
|
None,
|
|
)?;
|
|
println!("{}", String::from_utf8_lossy(&output.stderr));
|
|
assert!(output.status.success());
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn cli_no_ip_name_lookup() -> Result<()> {
|
|
let output = super::run_wasmtime_for_output(
|
|
&[
|
|
"-Wcomponent-model",
|
|
// Turn on network but ensure name lookup is disabled
|
|
"-Sinherit-network,allow-ip-name-lookup=no",
|
|
CLI_NO_IP_NAME_LOOKUP_COMPONENT,
|
|
],
|
|
None,
|
|
)?;
|
|
println!("{}", String::from_utf8_lossy(&output.stderr));
|
|
assert!(output.status.success());
|
|
Ok(())
|
|
}
|
|
}
|
|
|