Skip to content

Commit a5f8d0c

Browse files
authored
Use libtest-mimic for wit-component tests (#1542)
* Use libtest-mimic for wit-component tests Makes it easier to run individual tests and provides better output of the test binaries as well. * Fix tests on wasm
1 parent af30a53 commit a5f8d0c

4 files changed

Lines changed: 137 additions & 110 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/wit-component/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ glob = "0.3.0"
3636
pretty_assertions = "1.3.0"
3737
env_logger = { workspace = true }
3838
wat = { workspace = true }
39+
libtest-mimic = { workspace = true }
3940

4041
[target.'cfg(not(target_family = "wasm"))'.dev-dependencies]
4142
wasmtime = { workspace = true }
@@ -44,3 +45,11 @@ wasmtime = { workspace = true }
4445
dummy-module = ['dep:wat']
4546
wat = ['dep:wast', 'dep:wat']
4647
semver-check = ['dummy-module']
48+
49+
[[test]]
50+
name = "components"
51+
harness = false
52+
53+
[[test]]
54+
name = "interfaces"
55+
harness = false

crates/wit-component/tests/components.rs

Lines changed: 114 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use anyhow::{bail, Context, Error, Result};
2+
use libtest_mimic::{Arguments, Trial};
23
use pretty_assertions::assert_eq;
34
use std::{borrow::Cow, fs, path::Path};
45
use wasm_encoder::{Encode, Section};
@@ -50,137 +51,145 @@ use wit_parser::{PackageId, Resolve, UnresolvedPackage};
5051
///
5152
/// Run the test with the environment variable `BLESS` set to update
5253
/// either `component.wat` or `error.txt` depending on the outcome of the encoding.
53-
#[test]
54-
fn component_encoding_via_flags() -> Result<()> {
54+
fn main() -> Result<()> {
5555
drop(env_logger::try_init());
5656

57+
let mut trials = Vec::new();
5758
for entry in fs::read_dir("tests/components")? {
5859
let path = entry?.path();
5960
if !path.is_dir() {
6061
continue;
6162
}
6263

63-
let test_case = path.file_stem().unwrap().to_str().unwrap();
64-
println!("testing {test_case}");
64+
trials.push(Trial::test(path.to_str().unwrap().to_string(), move || {
65+
run_test(&path).map_err(|e| format!("{e:?}").into())
66+
}));
67+
}
6568

66-
let mut resolve = Resolve::default();
67-
let (pkg, _) = resolve.push_dir(&path)?;
69+
let mut args = Arguments::from_args();
70+
if cfg!(target_family = "wasm") && !cfg!(target_feature = "atomics") {
71+
args.test_threads = Some(1);
72+
}
73+
libtest_mimic::run(&args, trials).exit();
74+
}
6875

69-
let module_path = path.join("module.wat");
70-
let mut adapters = glob::glob(path.join("adapt-*.wat").to_str().unwrap())?;
71-
let result = if module_path.is_file() {
72-
let module = read_core_module(&module_path, &resolve, pkg)?;
73-
adapters
74-
.try_fold(
75-
ComponentEncoder::default().module(&module)?.validate(true),
76-
|encoder, path| {
77-
let (name, wasm) = read_name_and_module("adapt-", &path?, &resolve, pkg)?;
78-
Ok::<_, Error>(encoder.adapter(&name, &wasm)?)
79-
},
80-
)?
81-
.encode()
82-
} else {
83-
let mut libs = glob::glob(path.join("lib-*.wat").to_str().unwrap())?
84-
.map(|path| Ok(("lib-", path?, false)))
85-
.chain(
86-
glob::glob(path.join("dlopen-lib-*.wat").to_str().unwrap())?
87-
.map(|path| Ok(("dlopen-lib-", path?, true))),
88-
)
89-
.collect::<Result<Vec<_>>>()?;
76+
fn run_test(path: &Path) -> Result<()> {
77+
let test_case = path.file_stem().unwrap().to_str().unwrap();
9078

91-
// Sort list to ensure deterministic order, which determines priority in cases of duplicate symbols:
92-
libs.sort_by(|(_, a, _), (_, b, _)| a.cmp(b));
79+
let mut resolve = Resolve::default();
80+
let (pkg, _) = resolve.push_dir(&path)?;
9381

94-
let mut linker = Linker::default().validate(true);
82+
let module_path = path.join("module.wat");
83+
let mut adapters = glob::glob(path.join("adapt-*.wat").to_str().unwrap())?;
84+
let result = if module_path.is_file() {
85+
let module = read_core_module(&module_path, &resolve, pkg)?;
86+
adapters
87+
.try_fold(
88+
ComponentEncoder::default().module(&module)?.validate(true),
89+
|encoder, path| {
90+
let (name, wasm) = read_name_and_module("adapt-", &path?, &resolve, pkg)?;
91+
Ok::<_, Error>(encoder.adapter(&name, &wasm)?)
92+
},
93+
)?
94+
.encode()
95+
} else {
96+
let mut libs = glob::glob(path.join("lib-*.wat").to_str().unwrap())?
97+
.map(|path| Ok(("lib-", path?, false)))
98+
.chain(
99+
glob::glob(path.join("dlopen-lib-*.wat").to_str().unwrap())?
100+
.map(|path| Ok(("dlopen-lib-", path?, true))),
101+
)
102+
.collect::<Result<Vec<_>>>()?;
95103

96-
if path.join("stub-missing-functions").is_file() {
97-
linker = linker.stub_missing_functions(true);
98-
}
104+
// Sort list to ensure deterministic order, which determines priority in cases of duplicate symbols:
105+
libs.sort_by(|(_, a, _), (_, b, _)| a.cmp(b));
99106

100-
if path.join("use-built-in-libdl").is_file() {
101-
linker = linker.use_built_in_libdl(true);
102-
}
107+
let mut linker = Linker::default().validate(true);
103108

104-
let linker =
105-
libs.into_iter()
106-
.try_fold(linker, |linker, (prefix, path, dl_openable)| {
107-
let (name, wasm) = read_name_and_module(prefix, &path, &resolve, pkg)?;
108-
Ok::<_, Error>(linker.library(&name, &wasm, dl_openable)?)
109-
})?;
109+
if path.join("stub-missing-functions").is_file() {
110+
linker = linker.stub_missing_functions(true);
111+
}
110112

111-
adapters
112-
.try_fold(linker, |linker, path| {
113-
let (name, wasm) = read_name_and_module("adapt-", &path?, &resolve, pkg)?;
114-
Ok::<_, Error>(linker.adapter(&name, &wasm)?)
115-
})?
116-
.encode()
117-
};
118-
let component_path = path.join("component.wat");
119-
let component_wit_path = path.join("component.wit.print");
120-
let error_path = path.join("error.txt");
113+
if path.join("use-built-in-libdl").is_file() {
114+
linker = linker.use_built_in_libdl(true);
115+
}
121116

122-
let bytes = match result {
123-
Ok(bytes) => {
124-
if test_case.starts_with("error-") {
125-
bail!("expected an error but got success");
126-
}
127-
bytes
117+
let linker = libs
118+
.into_iter()
119+
.try_fold(linker, |linker, (prefix, path, dl_openable)| {
120+
let (name, wasm) = read_name_and_module(prefix, &path, &resolve, pkg)?;
121+
Ok::<_, Error>(linker.library(&name, &wasm, dl_openable)?)
122+
})?;
123+
124+
adapters
125+
.try_fold(linker, |linker, path| {
126+
let (name, wasm) = read_name_and_module("adapt-", &path?, &resolve, pkg)?;
127+
Ok::<_, Error>(linker.adapter(&name, &wasm)?)
128+
})?
129+
.encode()
130+
};
131+
let component_path = path.join("component.wat");
132+
let component_wit_path = path.join("component.wit.print");
133+
let error_path = path.join("error.txt");
134+
135+
let bytes = match result {
136+
Ok(bytes) => {
137+
if test_case.starts_with("error-") {
138+
bail!("expected an error but got success");
128139
}
129-
Err(err) => {
130-
if !test_case.starts_with("error-") {
131-
return Err(err);
132-
}
133-
assert_output(&format!("{err:?}"), &error_path)?;
134-
continue;
140+
bytes
141+
}
142+
Err(err) => {
143+
if !test_case.starts_with("error-") {
144+
return Err(err);
135145
}
136-
};
146+
assert_output(&format!("{err:?}"), &error_path)?;
147+
return Ok(());
148+
}
149+
};
137150

138-
let wat = wasmprinter::print_bytes(&bytes)?;
139-
assert_output(&wat, &component_path)?;
140-
let (pkg, resolve) = match wit_component::decode(&bytes)? {
141-
DecodedWasm::WitPackage(..) => unreachable!(),
142-
DecodedWasm::Component(resolve, world) => {
143-
(resolve.worlds[world].package.unwrap(), resolve)
144-
}
145-
};
146-
let wit = WitPrinter::default().print(&resolve, pkg)?;
147-
assert_output(&wit, &component_wit_path)?;
151+
let wat = wasmprinter::print_bytes(&bytes)?;
152+
assert_output(&wat, &component_path)?;
153+
let (pkg, resolve) = match wit_component::decode(&bytes)? {
154+
DecodedWasm::WitPackage(..) => unreachable!(),
155+
DecodedWasm::Component(resolve, world) => (resolve.worlds[world].package.unwrap(), resolve),
156+
};
157+
let wit = WitPrinter::default().print(&resolve, pkg)?;
158+
assert_output(&wit, &component_wit_path)?;
148159

149-
UnresolvedPackage::parse(&component_wit_path, &wit)
150-
.context("failed to parse printed WIT")?;
160+
UnresolvedPackage::parse(&component_wit_path, &wit).context("failed to parse printed WIT")?;
151161

152-
// Check that the producer data got piped through properly
153-
let metadata = wasm_metadata::Metadata::from_binary(&bytes)?;
154-
match metadata {
155-
// Depends on the ComponentEncoder always putting the first module as the 0th child:
156-
wasm_metadata::Metadata::Component { children, .. } => match children[0].as_ref() {
157-
wasm_metadata::Metadata::Module { producers, .. } => {
158-
let producers = producers.as_ref().expect("child module has producers");
159-
let processed_by = producers
160-
.get("processed-by")
161-
.expect("child has processed-by section");
162+
// Check that the producer data got piped through properly
163+
let metadata = wasm_metadata::Metadata::from_binary(&bytes)?;
164+
match metadata {
165+
// Depends on the ComponentEncoder always putting the first module as the 0th child:
166+
wasm_metadata::Metadata::Component { children, .. } => match children[0].as_ref() {
167+
wasm_metadata::Metadata::Module { producers, .. } => {
168+
let producers = producers.as_ref().expect("child module has producers");
169+
let processed_by = producers
170+
.get("processed-by")
171+
.expect("child has processed-by section");
172+
assert_eq!(
173+
processed_by
174+
.get("wit-component")
175+
.expect("wit-component producer present"),
176+
env!("CARGO_PKG_VERSION")
177+
);
178+
if module_path.is_file() {
162179
assert_eq!(
163180
processed_by
164-
.get("wit-component")
165-
.expect("wit-component producer present"),
166-
env!("CARGO_PKG_VERSION")
181+
.get("my-fake-bindgen")
182+
.expect("added bindgen field present"),
183+
"123.45"
167184
);
168-
if module_path.is_file() {
169-
assert_eq!(
170-
processed_by
171-
.get("my-fake-bindgen")
172-
.expect("added bindgen field present"),
173-
"123.45"
174-
);
175-
} else {
176-
// Otherwise, we used `Linker`, which synthesizes the
177-
// "main" module and thus won't have `my-fake-bindgen`
178-
}
185+
} else {
186+
// Otherwise, we used `Linker`, which synthesizes the
187+
// "main" module and thus won't have `my-fake-bindgen`
179188
}
180-
_ => panic!("expected child to be a module"),
181-
},
182-
_ => panic!("expected top level metadata of component"),
183-
}
189+
}
190+
_ => panic!("expected child to be a module"),
191+
},
192+
_ => panic!("expected top level metadata of component"),
184193
}
185194

186195
Ok(())

crates/wit-component/tests/interfaces.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use anyhow::{Context, Result};
2+
use libtest_mimic::{Arguments, Trial};
23
use pretty_assertions::assert_eq;
34
use std::fs;
45
use std::path::Path;
@@ -17,10 +18,10 @@ use wit_parser::{PackageId, Resolve, UnresolvedPackage};
1718
///
1819
/// Run the test with the environment variable `BLESS` set to update
1920
/// the baseline files.
20-
#[test]
21-
fn interface_encoding() -> Result<()> {
21+
fn main() -> Result<()> {
2222
env_logger::init();
2323

24+
let mut trials = Vec::new();
2425
for entry in fs::read_dir("tests/interfaces")? {
2526
let path = entry?.path();
2627
let name = match path.file_name().and_then(|s| s.to_str()) {
@@ -30,15 +31,22 @@ fn interface_encoding() -> Result<()> {
3031
let is_dir = path.is_dir();
3132
let is_test = is_dir || name.ends_with(".wit");
3233
if is_test {
33-
run_test(&path, is_dir).context(format!("failed test `{}`", path.display()))?;
34+
trials.push(Trial::test(name.to_string(), move || {
35+
run_test(&path, is_dir)
36+
.context(format!("failed test `{}`", path.display()))
37+
.map_err(|e| format!("{e:?}").into())
38+
}));
3439
}
3540
}
3641

37-
Ok(())
42+
let mut args = Arguments::from_args();
43+
if cfg!(target_family = "wasm") && !cfg!(target_feature = "atomics") {
44+
args.test_threads = Some(1);
45+
}
46+
libtest_mimic::run(&args, trials).exit();
3847
}
3948

4049
fn run_test(path: &Path, is_dir: bool) -> Result<()> {
41-
println!("running test at {path:?}");
4250
let mut resolve = Resolve::new();
4351
let package = if is_dir {
4452
resolve.push_dir(path)?.0

0 commit comments

Comments
 (0)