//! 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 { 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) { 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::() { *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"), } }