mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-18 06:19:12 +00:00
179 lines
5.0 KiB
Rust
179 lines
5.0 KiB
Rust
//! Build library to generate a program which runs all the testsuites.
|
|
//!
|
|
//! By generating a separate `#[test]` test for each file, we allow cargo test
|
|
//! to automatically run the files in parallel.
|
|
//!
|
|
//! > This program is inspired/forked from:
|
|
//! > https://github.com/bytecodealliance/wasmtime/blob/master/build.rs
|
|
mod processors;
|
|
|
|
pub use crate::processors::{emscripten_processor, wasi_processor, wast_processor};
|
|
use anyhow::Context;
|
|
use std::collections::HashSet;
|
|
use std::fmt::Write;
|
|
use std::fs::File;
|
|
use std::io::{BufRead, BufReader};
|
|
use std::path::{Path, PathBuf};
|
|
use target_lexicon::Triple;
|
|
|
|
pub type Ignores = HashSet<String>;
|
|
pub struct Testsuite {
|
|
pub buffer: String,
|
|
pub path: Vec<String>,
|
|
pub ignores: Ignores,
|
|
}
|
|
|
|
impl Testsuite {
|
|
fn ignore_current(&self) -> bool {
|
|
let full = self.path.join("::");
|
|
if self.ignores.contains(&full) {
|
|
return true;
|
|
}
|
|
self.ignores.iter().any(|ignore| full.contains(ignore))
|
|
}
|
|
}
|
|
|
|
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
|
pub struct Test {
|
|
pub name: String,
|
|
pub body: String,
|
|
}
|
|
|
|
pub type ProcessorType = fn(&mut Testsuite, PathBuf) -> Option<Test>;
|
|
|
|
/// Generates an Ignores struct from a text file
|
|
pub fn build_ignores_from_textfile(path: PathBuf) -> anyhow::Result<Ignores> {
|
|
let mut ignores = HashSet::new();
|
|
let file = File::open(path)?;
|
|
let reader = BufReader::new(file);
|
|
let host = Triple::host().to_string();
|
|
for line in reader.lines() {
|
|
let line = line.unwrap();
|
|
// If the line has a `#` we discard all the content that comes after
|
|
let line = if line.contains('#') {
|
|
let l: Vec<&str> = line.split('#').collect();
|
|
l.get(0).unwrap().to_string()
|
|
} else {
|
|
line
|
|
};
|
|
|
|
let line = line.trim().to_string();
|
|
|
|
// If the lines contains ` on ` it means the test should be ignored
|
|
// on that platform
|
|
let (line, target) = if line.contains(" on ") {
|
|
let l: Vec<&str> = line.split(" on ").collect();
|
|
(
|
|
l.get(0).unwrap().to_string(),
|
|
Some(l.get(1).unwrap().to_string()),
|
|
)
|
|
} else {
|
|
(line, None)
|
|
};
|
|
if line.is_empty() {
|
|
continue;
|
|
}
|
|
|
|
// We skip the ignore if doesn't apply to the current
|
|
// host target
|
|
if target.map(|t| !host.contains(&t)).unwrap_or(false) {
|
|
continue;
|
|
}
|
|
|
|
ignores.insert(line);
|
|
}
|
|
Ok(ignores)
|
|
}
|
|
|
|
pub fn test_directory_module(
|
|
out: &mut Testsuite,
|
|
path: impl AsRef<Path>,
|
|
processor: ProcessorType,
|
|
) -> anyhow::Result<usize> {
|
|
let path = path.as_ref();
|
|
let testsuite = &extract_name(path);
|
|
with_test_module(out, testsuite, |out| test_directory(out, path, processor))
|
|
}
|
|
|
|
fn write_test(out: &mut Testsuite, testname: &str, body: &str) -> anyhow::Result<()> {
|
|
writeln!(out.buffer, "#[test]")?;
|
|
if out.ignore_current() {
|
|
writeln!(out.buffer, "#[ignore]")?;
|
|
}
|
|
writeln!(out.buffer, "fn r#{}() -> anyhow::Result<()> {{", &testname)?;
|
|
writeln!(out.buffer, "{}", body)?;
|
|
writeln!(out.buffer, "}}")?;
|
|
writeln!(out.buffer)?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn test_directory(
|
|
out: &mut Testsuite,
|
|
path: impl AsRef<Path>,
|
|
processor: ProcessorType,
|
|
) -> anyhow::Result<usize> {
|
|
let path = path.as_ref();
|
|
let mut dir_entries: Vec<_> = path
|
|
.read_dir()
|
|
.context(format!("failed to read {:?}", path))?
|
|
.map(|r| r.expect("reading testsuite directory entry"))
|
|
.filter_map(|dir_entry| processor(out, dir_entry.path()))
|
|
.collect();
|
|
|
|
dir_entries.sort();
|
|
|
|
for Test {
|
|
name: testname,
|
|
body,
|
|
} in dir_entries.iter()
|
|
{
|
|
out.path.push(testname.to_string());
|
|
write_test(out, &testname, &body).unwrap();
|
|
out.path.pop().unwrap();
|
|
}
|
|
|
|
Ok(dir_entries.len())
|
|
}
|
|
|
|
/// Extract a valid Rust identifier from the stem of a path.
|
|
pub fn extract_name(path: impl AsRef<Path>) -> String {
|
|
path.as_ref()
|
|
.file_stem()
|
|
.expect("filename should have a stem")
|
|
.to_str()
|
|
.expect("filename should be representable as a string")
|
|
.replace("-", "_")
|
|
.replace("/", "_")
|
|
}
|
|
|
|
pub fn with_test_module<T>(
|
|
out: &mut Testsuite,
|
|
testsuite: &str,
|
|
f: impl FnOnce(&mut Testsuite) -> anyhow::Result<T>,
|
|
) -> anyhow::Result<T> {
|
|
out.path.push(testsuite.to_string());
|
|
out.buffer.push_str("mod ");
|
|
out.buffer.push_str(testsuite);
|
|
out.buffer.push_str(" {\n");
|
|
|
|
let result = f(out)?;
|
|
|
|
out.buffer.push_str("}\n");
|
|
out.path.pop().unwrap();
|
|
Ok(result)
|
|
}
|
|
|
|
pub fn with_features(
|
|
mut out: &mut Testsuite,
|
|
features: &[&str],
|
|
f: impl Fn(&mut Testsuite) -> anyhow::Result<()> + Copy,
|
|
) -> anyhow::Result<()> {
|
|
for compiler in features.iter() {
|
|
writeln!(out.buffer, "#[cfg(feature=\"test-{}\")]", compiler)?;
|
|
writeln!(out.buffer, "#[cfg(test)]")?;
|
|
writeln!(out.buffer, "#[allow(non_snake_case)]")?;
|
|
with_test_module(&mut out, &compiler, f)?;
|
|
}
|
|
Ok(())
|
|
}
|