mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-03-28 07:51:07 +00:00
* Adding in wrapper file to fix circular dependency with Webpack 5 * Running rustfmt * Fixing unit tests
236 lines
6.5 KiB
Rust
236 lines
6.5 KiB
Rust
//! A test suite to check the reference JS and wasm output of the `wasm-bindgen`
|
|
//! library.
|
|
//!
|
|
//! This is intended as an end-to-end integration test where we can track
|
|
//! changes to the JS and wasm output.
|
|
//!
|
|
//! Tests are located in `reference/*.rs` files and are accompanied with sibling
|
|
//! `*.js` files and `*.wat` files with the expected output of the `*.rs`
|
|
//! compilation. Use `BLESS=1` in the environment to automatically update all
|
|
//! tests.
|
|
|
|
use anyhow::{bail, Result};
|
|
use assert_cmd::prelude::*;
|
|
use rayon::prelude::*;
|
|
use std::env;
|
|
use std::fs;
|
|
use std::path::{Path, PathBuf};
|
|
use std::process::Command;
|
|
|
|
fn main() -> Result<()> {
|
|
let filter = env::args().nth(1);
|
|
|
|
let mut tests = Vec::new();
|
|
let dir = env::current_dir()?.join("tests/reference");
|
|
for entry in dir.read_dir()? {
|
|
let path = entry?.path();
|
|
if path.extension().and_then(|s| s.to_str()) != Some("rs") {
|
|
continue;
|
|
}
|
|
if let Some(filter) = &filter {
|
|
if !path.display().to_string().contains(filter) {
|
|
continue;
|
|
}
|
|
}
|
|
tests.push(path);
|
|
}
|
|
tests.sort();
|
|
|
|
let errs = tests
|
|
.par_iter()
|
|
.filter_map(|t| runtest(t).err().map(|e| (t, e)))
|
|
.collect::<Vec<_>>();
|
|
|
|
if errs.len() == 0 {
|
|
println!("{} tests passed", tests.len());
|
|
return Ok(());
|
|
}
|
|
eprintln!("failed tests:\n");
|
|
for (test, err) in errs {
|
|
eprintln!("{} failure\n{}", test.display(), tab(&format!("{:?}", err)));
|
|
}
|
|
bail!("tests failed");
|
|
}
|
|
|
|
fn runtest(test: &Path) -> Result<()> {
|
|
let contents = fs::read_to_string(test)?;
|
|
let td = tempfile::TempDir::new()?;
|
|
|
|
let manifest = format!(
|
|
"
|
|
[package]
|
|
name = \"reference-test\"
|
|
authors = []
|
|
version = \"1.0.0\"
|
|
edition = '2018'
|
|
|
|
[dependencies]
|
|
wasm-bindgen = {{ path = '{}' }}
|
|
|
|
[lib]
|
|
crate-type = ['cdylib']
|
|
path = '{}'
|
|
",
|
|
repo_root().display(),
|
|
test.display(),
|
|
);
|
|
let interface_types = contents.contains("// interface-types");
|
|
|
|
fs::write(td.path().join("Cargo.toml"), manifest)?;
|
|
let target_dir = target_dir();
|
|
let mut cargo = Command::new("cargo");
|
|
cargo
|
|
.current_dir(td.path())
|
|
.arg("build")
|
|
.arg("--target")
|
|
.arg("wasm32-unknown-unknown")
|
|
.env("CARGO_TARGET_DIR", &target_dir);
|
|
exec(&mut cargo)?;
|
|
|
|
let wasm = target_dir
|
|
.join("wasm32-unknown-unknown")
|
|
.join("debug")
|
|
.join("reference_test.wasm");
|
|
|
|
let mut bindgen = Command::cargo_bin("wasm-bindgen")?;
|
|
bindgen
|
|
.arg("--out-dir")
|
|
.arg(td.path())
|
|
.arg(&wasm)
|
|
.arg("--no-typescript");
|
|
if contents.contains("// enable-anyref") {
|
|
bindgen.env("WASM_BINDGEN_ANYREF", "1");
|
|
}
|
|
if interface_types {
|
|
bindgen.env("WASM_INTERFACE_TYPES", "1");
|
|
}
|
|
exec(&mut bindgen)?;
|
|
|
|
if interface_types {
|
|
let wasm = td.path().join("reference_test.wasm");
|
|
wit_validator::validate(&fs::read(&wasm)?)?;
|
|
let wat = sanitize_wasm(&wasm)?;
|
|
assert_same(&wat, &test.with_extension("wat"))?;
|
|
} else {
|
|
let js = fs::read_to_string(td.path().join("reference_test_bg.js"))?;
|
|
assert_same(&js, &test.with_extension("js"))?;
|
|
let wat = sanitize_wasm(&td.path().join("reference_test_bg.wasm"))?;
|
|
assert_same(&wat, &test.with_extension("wat"))?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn assert_same(output: &str, expected: &Path) -> Result<()> {
|
|
if env::var("BLESS").is_ok() {
|
|
fs::write(expected, output)?;
|
|
} else {
|
|
let expected = fs::read_to_string(&expected)?;
|
|
diff(&expected, output)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn sanitize_wasm(wasm: &Path) -> Result<String> {
|
|
// Clean up the wasm module by removing all function
|
|
// implementations/instructions, data sections, etc. This'll help us largely
|
|
// only deal with exports/imports which is all we're really interested in.
|
|
let mut module = walrus::ModuleConfig::new()
|
|
.on_parse(wit_walrus::on_parse)
|
|
.parse_file(wasm)?;
|
|
for func in module.funcs.iter_mut() {
|
|
let local = match &mut func.kind {
|
|
walrus::FunctionKind::Local(l) => l,
|
|
_ => continue,
|
|
};
|
|
local.block_mut(local.entry_block()).instrs.truncate(0);
|
|
}
|
|
let ids = module.data.iter().map(|d| d.id()).collect::<Vec<_>>();
|
|
for id in ids {
|
|
module.data.delete(id);
|
|
}
|
|
for mem in module.memories.iter_mut() {
|
|
mem.data_segments.drain();
|
|
}
|
|
let ids = module
|
|
.exports
|
|
.iter()
|
|
.filter(|e| match e.item {
|
|
walrus::ExportItem::Global(_) => true,
|
|
_ => false,
|
|
})
|
|
.map(|d| d.id())
|
|
.collect::<Vec<_>>();
|
|
for id in ids {
|
|
module.exports.delete(id);
|
|
}
|
|
walrus::passes::gc::run(&mut module);
|
|
let mut wat = wit_printer::print_bytes(&module.emit_wasm())?;
|
|
wat.push_str("\n");
|
|
Ok(wat)
|
|
}
|
|
|
|
fn diff(a: &str, b: &str) -> Result<()> {
|
|
if a == b {
|
|
return Ok(());
|
|
}
|
|
let mut s = String::new();
|
|
for result in diff::lines(a, b) {
|
|
match result {
|
|
diff::Result::Both(l, _) => {
|
|
s.push_str(" ");
|
|
s.push_str(l);
|
|
}
|
|
diff::Result::Left(l) => {
|
|
s.push_str("-");
|
|
s.push_str(l);
|
|
}
|
|
diff::Result::Right(l) => {
|
|
s.push_str("+");
|
|
s.push_str(l);
|
|
}
|
|
}
|
|
s.push_str("\n");
|
|
}
|
|
bail!("found a difference:\n\n{}", s);
|
|
}
|
|
|
|
fn target_dir() -> PathBuf {
|
|
let mut dir = PathBuf::from(env::current_exe().unwrap());
|
|
dir.pop(); // current exe
|
|
if dir.ends_with("deps") {
|
|
dir.pop();
|
|
}
|
|
dir.pop(); // debug and/or release
|
|
return dir;
|
|
}
|
|
|
|
fn repo_root() -> PathBuf {
|
|
let mut repo_root = env::current_dir().unwrap();
|
|
repo_root.pop(); // remove 'cli'
|
|
repo_root.pop(); // remove 'crates'
|
|
repo_root
|
|
}
|
|
|
|
fn exec(cmd: &mut Command) -> Result<()> {
|
|
let output = cmd.output()?;
|
|
if output.status.success() {
|
|
return Ok(());
|
|
}
|
|
let mut err = format!("command failed {:?}", cmd);
|
|
err.push_str(&format!("\nstatus: {}", output.status));
|
|
err.push_str(&format!(
|
|
"\nstderr:\n{}",
|
|
tab(&String::from_utf8_lossy(&output.stderr))
|
|
));
|
|
err.push_str(&format!(
|
|
"\nstdout:\n{}",
|
|
tab(&String::from_utf8_lossy(&output.stdout))
|
|
));
|
|
bail!("{}", err);
|
|
}
|
|
|
|
fn tab(s: &str) -> String {
|
|
format!(" {}", s.replace("\n", "\n "))
|
|
}
|