use anyhow::{anyhow, Result};
use pretty_assertions::assert_eq;
use std::{fs, path::Path};
use wasm_encoder::{Encode, Section};
use wit_component::{ComponentEncoder, DecodedWasm, DocumentPrinter, StringEncoding};
use wit_parser::{Resolve, UnresolvedPackage};
#[test]
fn component_encoding_via_flags() -> Result<()> {
drop(env_logger::try_init());
for entry in fs::read_dir("tests/components")? {
let path = entry?.path();
if !path.is_dir() {
continue;
}
let test_case = path.file_stem().unwrap().to_str().unwrap();
println!("testing {test_case}");
let module_path = path.join("module.wat");
let module = read_core_module(&module_path)?;
let mut encoder = ComponentEncoder::default().module(&module)?.validate(true);
encoder = add_adapters(encoder, &path)?;
let component_path = path.join("component.wat");
let component_wit_path = path.join("component.wit");
let error_path = path.join("error.txt");
let bytes = match encoder.encode() {
Ok(bytes) => bytes,
Err(err) => {
assert_output(&format!("{err:?}"), &error_path)?;
continue;
}
};
let wat = wasmprinter::print_bytes(&bytes)?;
assert_output(&wat, &component_path)?;
let (doc, resolve) = match wit_component::decode("component", &bytes)? {
DecodedWasm::WitPackage(..) => unreachable!(),
DecodedWasm::Component(resolve, world) => (resolve.worlds[world].document, resolve),
};
let wit = DocumentPrinter::default().print(&resolve, doc)?;
assert_output(&wit, &component_wit_path)?;
}
Ok(())
}
fn add_adapters(mut encoder: ComponentEncoder, path: &Path) -> Result<ComponentEncoder> {
for adapter in glob::glob(path.join("adapt-*.wat").to_str().unwrap())? {
let adapter = adapter?;
let wasm = read_core_module(&adapter)?;
let stem = adapter.file_stem().unwrap().to_str().unwrap();
let name = stem.trim_start_matches("adapt-");
encoder = encoder.adapter(&name, &wasm)?;
}
Ok(encoder)
}
fn read_core_module(path: &Path) -> Result<Vec<u8>> {
let mut wasm = wat::parse_file(path)?;
let interface = path.with_extension("wit");
let mut resolve = Resolve::default();
let pkg = resolve.push(
UnresolvedPackage::parse_file(&interface)?,
&Default::default(),
)?;
let doc = *resolve.packages[pkg].documents.iter().next().unwrap().1;
let doc = &resolve.documents[doc];
let world = doc
.default_world
.ok_or_else(|| anyhow!("no default world specified"))?;
let encoded = wit_component::metadata::encode(&resolve, world, StringEncoding::UTF8)?;
let section = wasm_encoder::CustomSection {
name: "component-type",
data: &encoded,
};
wasm.push(section.id());
section.encode(&mut wasm);
Ok(wasm)
}
fn assert_output(contents: &str, path: &Path) -> Result<()> {
let contents = contents.replace("\r\n", "\n");
if std::env::var_os("BLESS").is_some() {
fs::write(path, contents)?;
} else {
match fs::read_to_string(path) {
Ok(expected) => {
assert_eq!(
expected.replace("\r\n", "\n").trim(),
contents.trim(),
"failed baseline comparison ({})",
path.display(),
);
}
Err(_) => {
panic!("expected {path:?} to contain\n{contents}");
}
}
}
Ok(())
}