Skip to content

Commit ca92e99

Browse files
authored
Replace custom "test harnesses" with libtest-mimic (#1450)
* Replace custom "test harnesses" with `libtest-mimic` There's a number of tests suites in this repository which are based on "drop a file here and it's a test now". Up to now these have been powered by a custom "test harness" copied around which implements at filtering mechanism but little else. The `libtest-mimic` crate provides a `test`-crate-lookalike API which enables dynamically building the list of tests at program start time (aka read the filesystem) and then afterwards looks and feels similar to the `test` crate's CLI and UX. This enables a nicer dev experience when running these tests and additionally enables using runners like `cargo nextest` with this repository. * Don't be so fancy about running a single test * Disable threads in CLI test
1 parent fdbbdd5 commit ca92e99

8 files changed

Lines changed: 112 additions & 166 deletions

File tree

Cargo.lock

Lines changed: 39 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ url = "2.0.0"
5656
pretty_assertions = "1.3.0"
5757
semver = "1.0.0"
5858
smallvec = "1.11.1"
59+
libtest-mimic = "0.7.0"
5960

6061
wasm-compose = { version = "0.201.0", path = "crates/wasm-compose" }
6162
wasm-encoder = { version = "0.201.0", path = "crates/wasm-encoder" }
@@ -136,6 +137,7 @@ tempfile = "3.1"
136137
diff = "0.1"
137138
wast = { path = 'crates/wast' }
138139
pretty_assertions = { workspace = true }
140+
libtest-mimic = { workspace = true }
139141

140142
[[test]]
141143
name = "cli"

crates/wast/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ bumpalo = "3.14.0"
2424

2525
[dev-dependencies]
2626
anyhow = { workspace = true }
27-
rayon = { workspace = true }
27+
libtest-mimic = { workspace = true }
2828
wasmparser = { path = "../wasmparser" }
2929
wat = { path = "../wat" }
3030

crates/wast/tests/parse-fail.rs

Lines changed: 12 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,46 +4,28 @@
44
//! Use `BLESS=1` in the environment to auto-update `*.err` files. Be sure to
55
//! look at the diff!
66
7-
use rayon::prelude::*;
7+
use libtest_mimic::{Arguments, Trial};
88
use std::env;
99
use std::path::{Path, PathBuf};
1010

1111
fn main() {
1212
let mut tests = Vec::new();
1313
find_tests("tests/parse-fail".as_ref(), &mut tests);
14-
let filter = std::env::args().nth(1);
15-
1614
let bless = env::var("BLESS").is_ok();
17-
let tests = tests
18-
.iter()
19-
.filter(|test| {
20-
if let Some(filter) = &filter {
21-
if let Some(s) = test.file_name().and_then(|s| s.to_str()) {
22-
if !s.contains(filter) {
23-
return false;
24-
}
25-
}
26-
}
27-
true
28-
})
29-
.collect::<Vec<_>>();
30-
31-
println!("running {} tests\n", tests.len());
32-
33-
let errors = tests
34-
.par_iter()
35-
.filter_map(|test| run_test(test, bless).err())
36-
.collect::<Vec<_>>();
3715

38-
if !errors.is_empty() {
39-
for msg in errors.iter() {
40-
eprintln!("{}", msg);
41-
}
42-
43-
panic!("{} tests failed", errors.len())
16+
let mut trials = Vec::new();
17+
for test in tests {
18+
let trial = Trial::test(format!("{test:?}"), move || {
19+
run_test(&test, bless).map_err(|e| format!("{e:?}").into())
20+
});
21+
trials.push(trial);
4422
}
4523

46-
println!("test result: ok. {} passed\n", tests.len());
24+
let mut args = Arguments::from_args();
25+
if cfg!(target_family = "wasm") && !cfg!(target_feature = "atomics") {
26+
args.test_threads = Some(1);
27+
}
28+
libtest_mimic::run(&args, trials).exit();
4729
}
4830

4931
fn run_test(test: &Path, bless: bool) -> anyhow::Result<()> {

crates/wit-parser/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ decoding = ['dep:wasmparser']
4444
wat = ['decoding', 'dep:wat']
4545

4646
[dev-dependencies]
47-
rayon = "1"
4847
env_logger = { workspace = true }
4948
pretty_assertions = { workspace = true }
5049
serde_json = { workspace = true }
5150
wit-parser = { path = '.', features = ['serde', 'wat'] }
51+
libtest-mimic = { workspace = true }
5252

5353
[[test]]
5454
name = "all"

crates/wit-parser/tests/all.rs

Lines changed: 16 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -8,61 +8,36 @@
88
//! cargo test --test all foo.wit
99
1010
use anyhow::{bail, Context, Result};
11+
use libtest_mimic::{Arguments, Trial};
1112
use pretty_assertions::StrComparison;
12-
use rayon::prelude::*;
1313
use std::env;
1414
use std::ffi::OsStr;
1515
use std::fs;
1616
use std::io;
1717
use std::path::{Path, PathBuf};
1818
use std::str;
19-
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
2019
use wit_parser::*;
2120

2221
fn main() {
2322
env_logger::init();
2423
let tests = find_tests();
25-
let filter = std::env::args().nth(1);
2624

27-
let tests = tests
28-
.par_iter()
29-
.filter_map(|test| {
30-
if let Some(filter) = &filter {
31-
if let Some(s) = test.to_str() {
32-
if !s.contains(filter) {
33-
return None;
34-
}
35-
}
36-
}
37-
Some(test)
38-
})
39-
.collect::<Vec<_>>();
40-
41-
println!("running {} test files\n", tests.len());
42-
43-
let ntests = AtomicUsize::new(0);
44-
let errors = tests
45-
.par_iter()
46-
.filter_map(|test| {
47-
Runner { ntests: &ntests }
48-
.run(test)
25+
let mut trials = Vec::new();
26+
for test in tests {
27+
let trial = Trial::test(format!("{test:?}"), move || {
28+
Runner {}
29+
.run(&test)
4930
.context(format!("test {:?} failed", test))
50-
.err()
51-
})
52-
.collect::<Vec<_>>();
53-
54-
if !errors.is_empty() {
55-
for msg in errors.iter() {
56-
eprintln!("{:?}", msg);
57-
}
58-
59-
panic!("{} tests failed", errors.len())
31+
.map_err(|e| format!("{e:?}").into())
32+
});
33+
trials.push(trial);
6034
}
6135

62-
println!(
63-
"test result: ok. {} directives passed\n",
64-
ntests.load(SeqCst)
65-
);
36+
let mut args = Arguments::from_args();
37+
if cfg!(target_family = "wasm") && !cfg!(target_feature = "atomics") {
38+
args.test_threads = Some(1);
39+
}
40+
libtest_mimic::run(&args, trials).exit();
6641
}
6742

6843
/// Recursively finds all tests in a whitelisted set of directories which we
@@ -95,11 +70,9 @@ fn find_tests() -> Vec<PathBuf> {
9570
}
9671
}
9772

98-
struct Runner<'a> {
99-
ntests: &'a AtomicUsize,
100-
}
73+
struct Runner {}
10174

102-
impl Runner<'_> {
75+
impl Runner {
10376
fn run(&mut self, test: &Path) -> Result<()> {
10477
let mut resolve = Resolve::new();
10578
let result = resolve.push_path(test);
@@ -164,13 +137,8 @@ impl Runner<'_> {
164137
);
165138
}
166139
}
167-
self.bump_ntests();
168140
Ok(())
169141
}
170-
171-
fn bump_ntests(&self) {
172-
self.ntests.fetch_add(1, SeqCst);
173-
}
174142
}
175143

176144
fn normalize(s: &str, extension: &str) -> String {

tests/cli.rs

Lines changed: 15 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -26,59 +26,36 @@
2626
//! to look at the diff!
2727
2828
use anyhow::{anyhow, bail, Context, Result};
29+
use libtest_mimic::{Arguments, Trial};
2930
use pretty_assertions::StrComparison;
30-
use rayon::prelude::*;
3131
use std::env;
3232
use std::io::Write;
3333
use std::path::{Path, PathBuf};
3434
use std::process::{Command, Output, Stdio};
3535

3636
fn main() {
37-
// This test suite can't run on wasm since it involves spawning
38-
// subprocesses.
39-
if cfg!(target_family = "wasm") {
40-
return;
41-
}
42-
4337
let mut tests = Vec::new();
4438
find_tests("tests/cli".as_ref(), &mut tests);
45-
let filter = std::env::args().nth(1);
46-
4739
let bless = env::var("BLESS").is_ok();
48-
let tests = tests
49-
.iter()
50-
.filter(|test| {
51-
if let Some(filter) = &filter {
52-
if let Some(s) = test.file_name().and_then(|s| s.to_str()) {
53-
if !s.contains(filter) {
54-
return false;
55-
}
56-
}
57-
}
58-
true
59-
})
60-
.collect::<Vec<_>>();
61-
62-
println!("running {} tests\n", tests.len());
6340

64-
let errors = tests
65-
.par_iter()
66-
.filter_map(|test| {
67-
run_test(test, bless)
41+
let mut trials = Vec::new();
42+
for test in tests {
43+
let trial = Trial::test(format!("{test:?}"), move || {
44+
run_test(&test, bless)
6845
.with_context(|| format!("failed test {test:?}"))
69-
.err()
46+
.map_err(|e| format!("{e:?}").into())
7047
})
71-
.collect::<Vec<_>>();
72-
73-
if !errors.is_empty() {
74-
for msg in errors.iter() {
75-
eprintln!("{:?}", msg);
76-
}
77-
78-
panic!("{} tests failed", errors.len())
48+
// This test suite can't run on wasm since it involves spawning
49+
// subprocesses.
50+
.with_ignored_flag(cfg!(target_family = "wasm"));
51+
trials.push(trial);
7952
}
8053

81-
println!("test result: ok. {} passed\n", tests.len());
54+
let mut args = Arguments::from_args();
55+
if cfg!(target_family = "wasm") && !cfg!(target_feature = "atomics") {
56+
args.test_threads = Some(1);
57+
}
58+
libtest_mimic::run(&args, trials).exit();
8259
}
8360

8461
fn wasm_tools_exe() -> Command {

0 commit comments

Comments
 (0)