blob: ec9d4be941523192536c7cd47bb98e8342f6c888 [file] [log] [blame] [edit]
//! You can run this test suite with:
//!
//! cargo test --test all
//!
//! An argument can be passed as well to filter, based on filename, which test
//! to run
//!
//! cargo test --test all foo.wit
use anyhow::{bail, Context, Result};
use libtest_mimic::{Arguments, Trial};
use pretty_assertions::StrComparison;
use std::env;
use std::ffi::OsStr;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::str;
use wit_parser::*;
fn main() {
env_logger::init();
let tests = find_tests();
let mut trials = Vec::new();
for test in tests {
let trial = Trial::test(format!("{test:?}"), move || {
Runner {}
.run(&test)
.context(format!("test {:?} failed", test))
.map_err(|e| format!("{e:?}").into())
});
trials.push(trial);
}
let mut args = Arguments::from_args();
if cfg!(target_family = "wasm") && !cfg!(target_feature = "atomics") {
args.test_threads = Some(1);
}
libtest_mimic::run(&args, trials).exit();
}
/// Recursively finds all tests in a whitelisted set of directories which we
/// then load up and test in parallel.
fn find_tests() -> Vec<PathBuf> {
let mut tests = Vec::new();
find_tests("tests/ui".as_ref(), &mut tests);
find_tests("tests/ui/parse-fail".as_ref(), &mut tests);
tests.sort();
return tests;
fn find_tests(path: &Path, tests: &mut Vec<PathBuf>) {
for f in path.read_dir().unwrap() {
let f = f.unwrap();
let path = f.path();
if path.file_name().unwrap().to_str().unwrap() == "parse-fail" {
continue;
}
if f.file_type().unwrap().is_dir() {
tests.push(path);
continue;
}
match path.extension().and_then(|s| s.to_str()) {
Some("md") | Some("wit") | Some("wat") | Some("wasm") => {}
_ => continue,
}
tests.push(path);
}
}
}
struct Runner {}
impl Runner {
fn run(&mut self, test: &Path) -> Result<()> {
let mut resolve = Resolve::new();
resolve.features.insert("active".to_string());
let result = resolve.push_path(test);
let result = if test.iter().any(|s| s == "parse-fail") {
match result {
Ok(_) => bail!("expected test to not parse but it did"),
Err(mut e) => {
if let Some(err) = e.downcast_mut::<io::Error>() {
*err = io::Error::new(
io::ErrorKind::Other,
"some generic platform-agnostic error message",
);
}
format!("{:#}", e)
}
}
} else {
result?;
// format json string to human readable
let json_result = serde_json::to_string_pretty(&resolve)?;
// "foo.wit" => "foo.wit.json"
self.read_or_write_to_file(test, &json_result, "json")?;
return Ok(());
};
// "foo.wit" => "foo.wit.result"
// "foo.wit.md" => "foo.wit.md.result"
self.read_or_write_to_file(test, &result, "result")?;
return Ok(());
}
fn read_or_write_to_file(
&mut self,
test: &Path,
result: &str,
extension: &str,
) -> Result<(), anyhow::Error> {
let result_file = if test.extension() == Some(OsStr::new("md"))
&& test
.file_stem()
.and_then(|path| Path::new(path).extension())
== Some(OsStr::new("wit"))
{
test.with_extension(format!("md.{extension}"))
} else {
test.with_extension(format!("wit.{extension}"))
};
if env::var_os("BLESS").is_some() {
let normalized = normalize(&result, extension);
fs::write(&result_file, normalized)?;
} else {
let expected = fs::read_to_string(&result_file).context(format!(
"failed to read test expectation file {:?}\nthis can be fixed with BLESS=1",
result_file
))?;
let expected = normalize(&expected, extension);
let result = normalize(&result, extension);
if expected != result {
bail!(
"failed test: result is not as expected:{}",
StrComparison::new(&expected, &result),
);
}
}
Ok(())
}
}
fn normalize(s: &str, extension: &str) -> String {
let s = s.trim();
match extension {
// .result files have error messages with paths, so normalize Windows \ separators to /
"result" => s.replace("\\", "/").replace("\r\n", "\n"),
// .json files escape strings with \, so leave them alone
_ => s.replace("\r\n", "\n"),
}
}