blob: 6e0f70d767a2c33102c486d3a4f3bea22c5340db [file] [log] [blame] [edit]
use anyhow::{Context, Result};
use libtest_mimic::{Arguments, Trial};
use pretty_assertions::assert_eq;
use std::fs;
use std::path::Path;
use wit_component::WitPrinter;
use wit_parser::{PackageId, Resolve, UnresolvedPackageGroup};
/// Tests the encoding of a WIT package as a WebAssembly binary.
///
/// This test looks in the `interfaces/` directory for test cases. Each test
/// case is a `*.wit` file or a folder which contains `*.wit` files as part of a
/// multi-file package. Each tests `foo.wit` is accompanied with a `foo.wat` for
/// the WebAssembly text format encoding of the package. Additionally each test
/// has a `foo.print.wit` which is the machine-printed version of the WIT
/// document as decoded from the binary encoded interface.
///
/// Run the test with the environment variable `BLESS` set to update
/// the baseline files.
fn main() -> Result<()> {
env_logger::init();
let mut trials = Vec::new();
for entry in fs::read_dir("tests/interfaces")? {
let path = entry?.path();
let name = match path.file_name().and_then(|s| s.to_str()) {
Some(s) => s,
None => continue,
};
let is_dir = path.is_dir();
let is_test = is_dir || name.ends_with(".wit");
if is_test {
trials.push(Trial::test(name.to_string(), move || {
run_test(&path, is_dir)
.context(format!("failed test `{}`", path.display()))
.map_err(|e| format!("{e:?}").into())
}));
}
}
let mut args = Arguments::from_args();
if cfg!(target_family = "wasm") && !cfg!(target_feature = "atomics") {
args.test_threads = Some(1);
}
libtest_mimic::run(&args, trials).exit();
}
fn run_test(path: &Path, is_dir: bool) -> Result<()> {
let mut resolve = Resolve::new();
let package = if is_dir {
resolve.push_dir(path)?.0
} else {
resolve.push_file(path)?
};
assert_print(&resolve, package, path, is_dir)?;
// First convert the WIT package to a binary WebAssembly output, then
// convert that binary wasm to textual wasm, then assert it matches the
// expectation.
let wasm = wit_component::encode(&resolve, package)?;
let wat = wasmprinter::print_bytes(&wasm)?;
assert_output(&path.with_extension("wat"), &wat)?;
wasmparser::Validator::new()
.validate_all(&wasm)
.context("failed to validate wasm output")?;
// Next decode a fresh WIT package from the WebAssembly generated. Print
// this package's documents and assert they all match the expectations.
let decoded = wit_component::decode(&wasm)?;
let decoded_package = decoded.package();
let resolve = decoded.resolve();
assert_print(resolve, decoded.package(), path, is_dir)?;
// Finally convert the decoded package to wasm again and make sure it
// matches the prior wasm.
let wasm2 = wit_component::encode(resolve, decoded_package)?;
if wasm != wasm2 {
let wat2 = wasmprinter::print_bytes(&wasm)?;
assert_eq!(wat, wat2, "document did not roundtrip correctly");
}
Ok(())
}
fn assert_print(resolve: &Resolve, pkg_id: PackageId, path: &Path, is_dir: bool) -> Result<()> {
let output = WitPrinter::default().print(resolve, pkg_id, &[])?;
let pkg = &resolve.packages[pkg_id];
let expected = if is_dir {
path.join(format!("{}.wit.print", &pkg.name.name))
} else {
path.with_extension("wit.print")
};
assert_output(&expected, &output)?;
UnresolvedPackageGroup::parse("foo.wit", &output).context("failed to parse printed output")?;
Ok(())
}
fn assert_output(expected: &Path, actual: &str) -> Result<()> {
let actual = actual.replace(
concat!("\"", env!("CARGO_PKG_VERSION"), "\""),
"\"$CARGO_PKG_VERSION\"",
);
if std::env::var_os("BLESS").is_some() {
fs::write(expected, actual).with_context(|| format!("failed to write {expected:?}"))?;
} else {
assert_eq!(
fs::read_to_string(expected)
.with_context(|| format!("failed to read {expected:?}"))?
.replace("\r\n", "\n"),
actual,
"expectation `{}` did not match actual",
expected.display(),
);
}
Ok(())
}