tinywasm-cli 0.9.0

Minimal command-line interface for TinyWasm
Documentation
use std::fs;

use assert_cmd::Command;
use predicates::prelude::*;
use tempfile::tempdir;

fn write_module(dir: &tempfile::TempDir, name: &str, source: &str) -> String {
    let path = dir.path().join(name);
    fs::write(&path, source).unwrap();
    path.to_string_lossy().into_owned()
}

#[test]
fn run_invoke_accepts_positional_args() {
    let dir = tempdir().unwrap();
    let module = write_module(
        &dir,
        "add.wat",
        r#"(module
            (func (export "add") (param i32 i32) (result i32)
                local.get 0
                local.get 1
                i32.add))"#,
    );

    Command::cargo_bin("tinywasm")
        .unwrap()
        .args(["run", "--invoke", "add", &module, "1", "2"])
        .assert()
        .success()
        .stdout(predicate::str::contains("i32(3)"));
}

#[test]
fn compile_and_run_twasm() {
    let dir = tempdir().unwrap();
    let input = write_module(
        &dir,
        "add.wat",
        r#"(module
            (func (export "add") (param i32 i32) (result i32)
                local.get 0
                local.get 1
                i32.add))"#,
    );
    let output = dir.path().join("add.twasm");

    Command::cargo_bin("tinywasm")
        .unwrap()
        .args(["compile", &input, "-o", output.to_str().unwrap()])
        .assert()
        .success();

    Command::cargo_bin("tinywasm")
        .unwrap()
        .args(["run", "--invoke", "add", output.to_str().unwrap(), "3", "4"])
        .assert()
        .success()
        .stdout(predicate::str::contains("i32(7)"));
}

#[test]
fn bare_run_requires_default_entrypoint() {
    let dir = tempdir().unwrap();
    let module = write_module(&dir, "add.wat", r#"(module (func (export "add") (result i32) i32.const 1))"#);

    Command::cargo_bin("tinywasm")
        .unwrap()
        .arg(&module)
        .assert()
        .failure()
        .stderr(predicate::str::contains("no start function or `_start` export"));
}

#[test]
fn inspect_lists_exports() {
    let dir = tempdir().unwrap();
    let module = write_module(
        &dir,
        "inspect.wat",
        r#"(module
            (memory (export "memory") 1)
            (func (export "answer") (result i32) i32.const 42))"#,
    );

    Command::cargo_bin("tinywasm").unwrap().args(["inspect", &module]).assert().success().stdout(
        predicate::str::contains("answer: func () -> (i32)").and(predicate::str::contains("memory: memory[i32]")),
    );
}

#[test]
fn dump_prints_lowered_instructions() {
    let dir = tempdir().unwrap();
    let module = write_module(&dir, "dump.wat", r#"(module (func (export "noop")))"#);

    Command::cargo_bin("tinywasm")
        .unwrap()
        .args(["dump", &module])
        .assert()
        .success()
        .stdout(predicate::str::contains("func[0]").and(predicate::str::contains("0000:")));
}

#[test]
fn run_accepts_wat_from_stdin() {
    Command::cargo_bin("tinywasm")
        .unwrap()
        .args(["run", "--invoke", "add", "-", "8", "9"])
        .write_stdin(
            r#"(module
                (func (export "add") (param i32 i32) (result i32)
                    local.get 0
                    local.get 1
                    i32.add))"#,
        )
        .assert()
        .success()
        .stdout(predicate::str::contains("i32(17)"));
}

#[test]
fn wast_command_runs_simple_spec_script() {
    let dir = tempdir().unwrap();
    let path = dir.path().join("simple.wast");
    fs::write(
        &path,
        "(module (func (export \"add\") (result i32) i32.const 1))\n(assert_return (invoke \"add\") (i32.const 1))",
    )
    .unwrap();

    Command::cargo_bin("tinywasm")
        .unwrap()
        .args(["wast", path.to_str().unwrap()])
        .assert()
        .success()
        .stdout(predicate::str::contains("Tests Passed:"));
}