From 1755cdcf534987df398cdee923c36c428462e85d Mon Sep 17 00:00:00 2001 From: Alex Zaslavsky Date: Sat, 25 May 2024 17:10:12 -0700 Subject: [PATCH] Add component test for multi-package WIT source --- crates/wit-component/tests/components.rs | 178 +++++++++--------- .../multi-package/baz/qux/component.wat | 90 +++++++++ .../multi-package/baz/qux/component.wit.print | 13 ++ .../multi-package/baz/qux/module.wat | 7 + .../multi-package/foo/bar/component.wat | 82 ++++++++ .../multi-package/foo/bar/component.wit.print | 13 ++ .../multi-package/foo/bar/module.wat | 6 + .../tests/components/multi-package/module.wit | 24 +++ 8 files changed, 328 insertions(+), 85 deletions(-) create mode 100644 crates/wit-component/tests/components/multi-package/baz/qux/component.wat create mode 100644 crates/wit-component/tests/components/multi-package/baz/qux/component.wit.print create mode 100644 crates/wit-component/tests/components/multi-package/baz/qux/module.wat create mode 100644 crates/wit-component/tests/components/multi-package/foo/bar/component.wat create mode 100644 crates/wit-component/tests/components/multi-package/foo/bar/component.wit.print create mode 100644 crates/wit-component/tests/components/multi-package/foo/bar/module.wat create mode 100644 crates/wit-component/tests/components/multi-package/module.wit diff --git a/crates/wit-component/tests/components.rs b/crates/wit-component/tests/components.rs index 010235b5f1..5fa87a0bf4 100644 --- a/crates/wit-component/tests/components.rs +++ b/crates/wit-component/tests/components.rs @@ -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)?) }, )? @@ -106,97 +113,98 @@ fn run_test(path: &Path) -> Result<()> { ) .collect::>>()?; - // 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(()) diff --git a/crates/wit-component/tests/components/multi-package/baz/qux/component.wat b/crates/wit-component/tests/components/multi-package/baz/qux/component.wat new file mode 100644 index 0000000000..b028617cac --- /dev/null +++ b/crates/wit-component/tests/components/multi-package/baz/qux/component.wat @@ -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") + ) +) diff --git a/crates/wit-component/tests/components/multi-package/baz/qux/component.wit.print b/crates/wit-component/tests/components/multi-package/baz/qux/component.wit.print new file mode 100644 index 0000000000..71d2002137 --- /dev/null +++ b/crates/wit-component/tests/components/multi-package/baz/qux/component.wit.print @@ -0,0 +1,13 @@ +package root:component; + +world root { + import qux: interface { + type x = s8; + + qux1: func(x: list); + + qux2: func(); + + qux3: func(x: x); + } +} diff --git a/crates/wit-component/tests/components/multi-package/baz/qux/module.wat b/crates/wit-component/tests/components/multi-package/baz/qux/module.wat new file mode 100644 index 0000000000..8eb6e37d5a --- /dev/null +++ b/crates/wit-component/tests/components/multi-package/baz/qux/module.wat @@ -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) +) diff --git a/crates/wit-component/tests/components/multi-package/foo/bar/component.wat b/crates/wit-component/tests/components/multi-package/foo/bar/component.wat new file mode 100644 index 0000000000..82caf152ba --- /dev/null +++ b/crates/wit-component/tests/components/multi-package/foo/bar/component.wat @@ -0,0 +1,82 @@ +(component + (type (;0;) + (instance + (type (;0;) (record (field "a" u8))) + (export (;1;) "x" (type (eq 0))) + (type (;2;) (func (param "x" string))) + (export (;0;) "bar1" (func (type 2))) + (type (;3;) (func (param "x" 1))) + (export (;1;) "bar2" (func (type 3))) + ) + ) + (import "bar" (instance (;0;) (type 0))) + (core module (;0;) + (type (;0;) (func (param i32 i32))) + (type (;1;) (func (param i32))) + (type (;2;) (func (param i32 i32 i32 i32) (result i32))) + (import "bar" "bar1" (func (;0;) (type 0))) + (import "bar" "bar2" (func (;1;) (type 1))) + (func (;2;) (type 2) (param i32 i32 i32 i32) (result i32) + unreachable + ) + (memory (;0;) 1) + (export "memory" (memory 0)) + (export "cabi_realloc" (func 2)) + (@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-bar-bar1 (;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-bar-bar1)) + (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 "bar2" (func (;0;))) + (core func (;1;) (canon lower (func 0))) + (core instance (;1;) + (export "bar1" (func 0)) + (export "bar2" (func 1)) + ) + (core instance (;2;) (instantiate 0 + (with "bar" (instance 1)) + ) + ) + (alias core export 2 "memory" (core memory (;0;))) + (alias core export 2 "cabi_realloc" (core func (;2;))) + (alias core export 0 "$imports" (core table (;0;))) + (alias export 0 "bar1" (func (;1;))) + (core func (;3;) (canon lower (func 1) (memory 0) string-encoding=utf8)) + (core instance (;3;) + (export "$imports" (table 0)) + (export "0" (func 3)) + ) + (core instance (;4;) (instantiate 2 + (with "" (instance 3)) + ) + ) + (@producers + (processed-by "wit-component" "$CARGO_PKG_VERSION") + ) +) diff --git a/crates/wit-component/tests/components/multi-package/foo/bar/component.wit.print b/crates/wit-component/tests/components/multi-package/foo/bar/component.wit.print new file mode 100644 index 0000000000..f3cc24edf7 --- /dev/null +++ b/crates/wit-component/tests/components/multi-package/foo/bar/component.wit.print @@ -0,0 +1,13 @@ +package root:component; + +world root { + import bar: interface { + record x { + a: u8, + } + + bar1: func(x: string); + + bar2: func(x: x); + } +} diff --git a/crates/wit-component/tests/components/multi-package/foo/bar/module.wat b/crates/wit-component/tests/components/multi-package/foo/bar/module.wat new file mode 100644 index 0000000000..7a56c61661 --- /dev/null +++ b/crates/wit-component/tests/components/multi-package/foo/bar/module.wat @@ -0,0 +1,6 @@ +(module + (import "bar" "bar1" (func (param i32 i32))) + (import "bar" "bar2" (func (param i32))) + (memory (export "memory") 1) + (func (export "cabi_realloc") (param i32 i32 i32 i32) (result i32) unreachable) +) diff --git a/crates/wit-component/tests/components/multi-package/module.wit b/crates/wit-component/tests/components/multi-package/module.wit new file mode 100644 index 0000000000..838ad7305b --- /dev/null +++ b/crates/wit-component/tests/components/multi-package/module.wit @@ -0,0 +1,24 @@ +package foo:bar { + world module { + import bar: interface { + record x { + a: u8 + } + + bar1: func(x: string); + bar2: func(x: x); + } + } +} + +package baz:qux { + world module { + import qux: interface { + type x = s8; + + qux1: func(x: list); + qux2: func(); + qux3: func(x: x); + } + } +}