use anyhow::{bail, Context, Result};
use pretty_assertions::assert_eq;
use std::{fs, path::Path};
use wasm_encoder::{Encode, Section};
use wit_component::{ComponentEncoder, StringEncoding};
use wit_parser::World;
fn read_adapters(dir: &Path) -> Result<Vec<(String, Vec<u8>, World)>> {
glob::glob(dir.join("adapt-*.wat").to_str().unwrap())?
.map(|p| {
let p = p?;
let adapter =
wat::parse_file(&p).with_context(|| format!("expected file `{}`", p.display()))?;
let stem = p.file_stem().unwrap().to_str().unwrap();
let world = read_world(dir, &format!("{stem}-"))?;
Ok((
stem.trim_start_matches("adapt-").to_string(),
adapter,
world,
))
})
.collect::<Result<_>>()
}
#[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 component_path = path.join("component.wat");
let error_path = path.join("error.txt");
let module = wat::parse_file(&module_path)
.with_context(|| format!("expected file `{}`", module_path.display()))?;
let mut encoder = ComponentEncoder::default()
.module(&module)?
.validate(true)
.world(read_world(&path, "")?, StringEncoding::UTF8)?;
encoder = add_adapters(encoder, &path)?;
assert_output(test_case, &encoder, &component_path, &error_path)?;
}
Ok(())
}
#[test]
fn component_encoding_via_custom_sections() -> Result<()> {
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 component_path = path.join("component.wat");
let error_path = path.join("error.txt");
let mut module = wat::parse_file(&module_path)
.with_context(|| format!("expected file `{}`", module_path.display()))?;
let world = read_world(&path, "")?;
let contents = wit_component::metadata::encode(&world, StringEncoding::UTF8);
let section = wasm_encoder::CustomSection {
name: "component-type",
data: &contents,
};
module.push(section.id());
section.encode(&mut module);
let mut encoder = ComponentEncoder::default().module(&module)?.validate(true);
encoder = add_adapters(encoder, &path)?;
assert_output(test_case, &encoder, &component_path, &error_path)?;
}
Ok(())
}
fn read_world(path: &Path, prefix: &str) -> Result<World> {
let world_path = path.join(&format!("{prefix}world.wit"));
World::parse_file(&world_path)
}
fn add_adapters(mut encoder: ComponentEncoder, path: &Path) -> Result<ComponentEncoder> {
for (name, mut wasm, interfaces) in read_adapters(path)? {
let contents = wit_component::metadata::encode(&interfaces, StringEncoding::UTF8);
let section = wasm_encoder::CustomSection {
name: "component-type",
data: &contents,
};
wasm.push(section.id());
section.encode(&mut wasm);
encoder = encoder.adapter(&name, &wasm)?;
}
Ok(encoder)
}
fn assert_output(
test_case: &str,
encoder: &ComponentEncoder,
component_path: &Path,
error_path: &Path,
) -> Result<()> {
let r = encoder.encode();
let (output, baseline_path) = if error_path.is_file() {
match r {
Ok(_) => bail!("encoding should fail for test case `{}`", test_case),
Err(e) => (e.to_string(), &error_path),
}
} else {
(
wasmprinter::print_bytes(
&r.with_context(|| format!("failed to encode for test case `{}`", test_case))?,
)
.with_context(|| {
format!(
"failed to print component bytes for test case `{}`",
test_case
)
})?,
&component_path,
)
};
if std::env::var_os("BLESS").is_some() {
fs::write(&baseline_path, output)?;
} else {
assert_eq!(
fs::read_to_string(&baseline_path)?.replace("\r\n", "\n"),
output,
"failed baseline comparison for test case `{}` ({})",
test_case,
baseline_path.display(),
);
}
Ok(())
}