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(()) }