Skip to content

Commit

Permalink
Add component test for multi-package WIT source
Browse files Browse the repository at this point in the history
  • Loading branch information
azaslavsky committed May 27, 2024
1 parent 0fe4459 commit 1755cdc
Show file tree
Hide file tree
Showing 8 changed files with 328 additions and 85 deletions.
178 changes: 93 additions & 85 deletions crates/wit-component/tests/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,24 +75,31 @@ fn main() -> Result<()> {

fn run_test(path: &Path) -> Result<()> {
let test_case = path.file_stem().unwrap().to_str().unwrap();
let mut resolve = Resolve::default();
let outputs = resolve.push_dir(&path).context("failed to push directory into resolve")?;
if outputs.len() != 1 {
// TODO(azaslavsky): Support this.
bail!("Multi-packages are not yet supported in component tests");
let mut resolve = Resolve::default();
let pkgs = resolve.push_dir(&path)?;
let pkg_count = pkgs.len();

for (pkg_id, _) in pkgs {
// If this test case contained multiple packages, create separate sub-directories for
// each.
let mut path = path.to_path_buf();
if pkg_count > 1 {
let pkg_name = &resolve.packages[pkg_id].name;
path.push(pkg_name.namespace.clone());
path.push(pkg_name.name.clone());
fs::create_dir_all(path.clone())?;
}

let (pkg, _) = outputs[0];

let module_path = path.join("module.wat");
let mut adapters = glob::glob(path.join("adapt-*.wat").to_str().unwrap())?;
let result = if module_path.is_file() {
let module = read_core_module(&module_path, &resolve, pkg)
let module = read_core_module(&module_path, &resolve, pkg_id)
.with_context(|| format!("failed to read core module at {module_path:?}"))?;
adapters
.try_fold(
ComponentEncoder::default().module(&module)?.validate(true),
|encoder, path| {
let (name, wasm) = read_name_and_module("adapt-", &path?, &resolve, pkg)?;
let (name, wasm) = read_name_and_module("adapt-", &path?, &resolve, pkg_id)?;
Ok::<_, Error>(encoder.adapter(&name, &wasm)?)
},
)?
Expand All @@ -106,97 +113,98 @@ fn run_test(path: &Path) -> Result<()> {
)
.collect::<Result<Vec<_>>>()?;

// Sort list to ensure deterministic order, which determines priority in cases of duplicate symbols:
libs.sort_by(|(_, a, _), (_, b, _)| a.cmp(b));
// Sort list to ensure deterministic order, which determines priority in cases of duplicate symbols:
libs.sort_by(|(_, a, _), (_, b, _)| a.cmp(b));

let mut linker = Linker::default().validate(true);
let mut linker = Linker::default().validate(true);

if path.join("stub-missing-functions").is_file() {
linker = linker.stub_missing_functions(true);
}
if path.join("stub-missing-functions").is_file() {
linker = linker.stub_missing_functions(true);
}

if path.join("use-built-in-libdl").is_file() {
linker = linker.use_built_in_libdl(true);
}
if path.join("use-built-in-libdl").is_file() {
linker = linker.use_built_in_libdl(true);
}

let linker = libs
.into_iter()
.try_fold(linker, |linker, (prefix, path, dl_openable)| {
let (name, wasm) = read_name_and_module(prefix, &path, &resolve, pkg)?;
Ok::<_, Error>(linker.library(&name, &wasm, dl_openable)?)
})?;
let linker = libs
.into_iter()
.try_fold(linker, |linker, (prefix, path, dl_openable)| {
let (name, wasm) = read_name_and_module(prefix, &path, &resolve, pkg_id)?;
Ok::<_, Error>(linker.library(&name, &wasm, dl_openable)?)
})?;

adapters
.try_fold(linker, |linker, path| {
let (name, wasm) = read_name_and_module("adapt-", &path?, &resolve, pkg)?;
Ok::<_, Error>(linker.adapter(&name, &wasm)?)
})?
.encode()
};
let component_path = path.join("component.wat");
let component_wit_path = path.join("component.wit.print");
let error_path = path.join("error.txt");
adapters
.try_fold(linker, |linker, path| {
let (name, wasm) = read_name_and_module("adapt-", &path?, &resolve, pkg_id)?;
Ok::<_, Error>(linker.adapter(&name, &wasm)?)
})?
.encode()
};
let component_path = path.join("component.wat");
let component_wit_path = path.join("component.wit.print");
let error_path = path.join("error.txt");

let bytes = match result {
Ok(bytes) => {
if test_case.starts_with("error-") {
bail!("expected an error but got success");
let bytes = match result {
Ok(bytes) => {
if test_case.starts_with("error-") {
bail!("expected an error but got success");
}
bytes
}
bytes
}
Err(err) => {
if !test_case.starts_with("error-") {
return Err(err);
Err(err) => {
if !test_case.starts_with("error-") {
return Err(err);
}
assert_output(&format!("{err:?}"), &error_path)?;
return Ok(());
}
assert_output(&format!("{err:?}"), &error_path)?;
return Ok(());
}
};
};

let wat = wasmprinter::print_bytes(&bytes).context("failed to print bytes")?;
assert_output(&wat, &component_path)?;
let (pkg, resolve) = match wit_component::decode(&bytes).context("failed to decode resolve")? {
DecodedWasm::WitPackages(..) => unreachable!(),
DecodedWasm::Component(resolve, world) => (resolve.worlds[world].package.unwrap(), resolve),
};
let wit = WitPrinter::default()
.print(&resolve, vec!(pkg))
.context("failed to print WIT")?;
assert_output(&wit, &component_wit_path)?;
let wat = wasmprinter::print_bytes(&bytes).context("failed to print bytes")?;
assert_output(&wat, &component_path)?;
let (pkg, resolve) = match wit_component::decode(&bytes).context("failed to decode resolve")? {
DecodedWasm::WitPackages(..) => unreachable!(),
DecodedWasm::Component(resolve, world) => (resolve.worlds[world].package.unwrap(), resolve),
};
let wit = WitPrinter::default()
.print(&resolve, vec!(pkg))
.context("failed to print WIT")?;
assert_output(&wit, &component_wit_path)?;

UnresolvedPackage::parse(&component_wit_path, &wit).context("failed to parse printed WIT")?;
UnresolvedPackage::parse(&component_wit_path, &wit).context("failed to parse printed WIT")?;

// Check that the producer data got piped through properly
let metadata = wasm_metadata::Metadata::from_binary(&bytes)?;
match metadata {
// Depends on the ComponentEncoder always putting the first module as the 0th child:
wasm_metadata::Metadata::Component { children, .. } => match children[0].as_ref() {
wasm_metadata::Metadata::Module { producers, .. } => {
let producers = producers.as_ref().expect("child module has producers");
let processed_by = producers
.get("processed-by")
.expect("child has processed-by section");
assert_eq!(
processed_by
.get("wit-component")
.expect("wit-component producer present"),
env!("CARGO_PKG_VERSION")
);
if module_path.is_file() {
// Check that the producer data got piped through properly
let metadata = wasm_metadata::Metadata::from_binary(&bytes)?;
match metadata {
// Depends on the ComponentEncoder always putting the first module as the 0th child:
wasm_metadata::Metadata::Component { children, .. } => match children[0].as_ref() {
wasm_metadata::Metadata::Module { producers, .. } => {
let producers = producers.as_ref().expect("child module has producers");
let processed_by = producers
.get("processed-by")
.expect("child has processed-by section");
assert_eq!(
processed_by
.get("my-fake-bindgen")
.expect("added bindgen field present"),
"123.45"
.get("wit-component")
.expect("wit-component producer present"),
env!("CARGO_PKG_VERSION")
);
} else {
// Otherwise, we used `Linker`, which synthesizes the
// "main" module and thus won't have `my-fake-bindgen`
if module_path.is_file() {
assert_eq!(
processed_by
.get("my-fake-bindgen")
.expect("added bindgen field present"),
"123.45"
);
} else {
// Otherwise, we used `Linker`, which synthesizes the
// "main" module and thus won't have `my-fake-bindgen`
}
}
}
_ => panic!("expected child to be a module"),
},
_ => panic!("expected top level metadata of component"),
_ => panic!("expected child to be a module"),
},
_ => panic!("expected top level metadata of component"),
}
}

Ok(())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
(component
(type (;0;)
(instance
(type (;0;) s8)
(export (;1;) "x" (type (eq 0)))
(type (;2;) (list string))
(type (;3;) (func (param "x" 2)))
(export (;0;) "qux1" (func (type 3)))
(type (;4;) (func))
(export (;1;) "qux2" (func (type 4)))
(type (;5;) (func (param "x" 1)))
(export (;2;) "qux3" (func (type 5)))
)
)
(import "qux" (instance (;0;) (type 0)))
(core module (;0;)
(type (;0;) (func (param i32 i32)))
(type (;1;) (func))
(type (;2;) (func (param i32)))
(type (;3;) (func (param i32 i32 i32 i32) (result i32)))
(import "qux" "qux1" (func (;0;) (type 0)))
(import "qux" "qux2" (func (;1;) (type 1)))
(import "qux" "qux3" (func (;2;) (type 2)))
(func (;3;) (type 3) (param i32 i32 i32 i32) (result i32)
unreachable
)
(memory (;0;) 1)
(export "memory" (memory 0))
(export "cabi_realloc" (func 3))
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
(processed-by "my-fake-bindgen" "123.45")
)
)
(core module (;1;)
(type (;0;) (func (param i32 i32)))
(func $indirect-qux-qux1 (;0;) (type 0) (param i32 i32)
local.get 0
local.get 1
i32.const 0
call_indirect (type 0)
)
(table (;0;) 1 1 funcref)
(export "0" (func $indirect-qux-qux1))
(export "$imports" (table 0))
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
(core module (;2;)
(type (;0;) (func (param i32 i32)))
(import "" "0" (func (;0;) (type 0)))
(import "" "$imports" (table (;0;) 1 1 funcref))
(elem (;0;) (i32.const 0) func 0)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
(core instance (;0;) (instantiate 1))
(alias core export 0 "0" (core func (;0;)))
(alias export 0 "qux2" (func (;0;)))
(core func (;1;) (canon lower (func 0)))
(alias export 0 "qux3" (func (;1;)))
(core func (;2;) (canon lower (func 1)))
(core instance (;1;)
(export "qux1" (func 0))
(export "qux2" (func 1))
(export "qux3" (func 2))
)
(core instance (;2;) (instantiate 0
(with "qux" (instance 1))
)
)
(alias core export 2 "memory" (core memory (;0;)))
(alias core export 2 "cabi_realloc" (core func (;3;)))
(alias core export 0 "$imports" (core table (;0;)))
(alias export 0 "qux1" (func (;2;)))
(core func (;4;) (canon lower (func 2) (memory 0) string-encoding=utf8))
(core instance (;3;)
(export "$imports" (table 0))
(export "0" (func 4))
)
(core instance (;4;) (instantiate 2
(with "" (instance 3))
)
)
(@producers
(processed-by "wit-component" "$CARGO_PKG_VERSION")
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package root:component;

world root {
import qux: interface {
type x = s8;

qux1: func(x: list<string>);

qux2: func();

qux3: func(x: x);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
(module
(import "qux" "qux1" (func (param i32 i32)))
(import "qux" "qux2" (func))
(import "qux" "qux3" (func (param i32)))
(memory (export "memory") 1)
(func (export "cabi_realloc") (param i32 i32 i32 i32) (result i32) unreachable)
)
Loading

0 comments on commit 1755cdc

Please sign in to comment.