wasmer/lib/wasi/build/wasitests.rs
2019-05-17 15:31:02 -07:00

146 lines
4.5 KiB
Rust

//! This file will run at build time to autogenerate the WASI regression tests
//! It will compile the files indicated in TESTS, to:executable and .wasm
//! - Compile with the native rust target to get the expected output
//! - Compile with the latest WASI target to get the wasm
//! - Generate the test that will compare the output of running the .wasm file
//! with wasmer with the expected output
use glob::glob;
use std::collections::HashSet;
use std::fs;
use std::path::PathBuf;
use std::process::Command;
use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
static BANNER: &str = "// !!! THIS IS A GENERATED FILE !!!
// ANY MANUAL EDITS MAY BE OVERWRITTEN AT ANY TIME
// Files autogenerated with cargo build (build/wasitests.rs).\n";
pub fn compile(file: &str, ignores: &HashSet<String>) -> Option<String> {
dbg!(file);
let mut output_path = PathBuf::from(file);
output_path.set_extension("out");
assert!(file.ends_with(".rs"));
let normalized_name = {
let mut nn = file.to_lowercase();
nn.truncate(file.len() - 3);
nn
};
Command::new("rustc")
.arg("+nightly")
.arg(file)
.arg("-o")
.arg(&normalized_name)
.output()
.expect("Failed to compile program to native code");
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let normal_path = PathBuf::from(&normalized_name);
let mut perm = normal_path
.metadata()
.expect("native executable")
.permissions();
perm.set_mode(0o766);
fs::set_permissions(normal_path, perm).expect("set permissions");
}
let rs_module_name = {
let temp = PathBuf::from(&normalized_name);
temp.file_name().unwrap().to_string_lossy().to_string()
};
let result = Command::new(&normalized_name)
.output()
.expect("Failed to execute native program");
let wasm_out_name = format!("{}.wasm", &normalized_name);
Command::new("rustc")
.arg("+nightly")
.arg("--target=wasm32-wasi")
.arg(file)
.arg("-o")
.arg(&wasm_out_name)
.output()
.expect("Failed to compile program to native code");
let ignored = if ignores.contains(&rs_module_name) {
"\n#[ignore]"
} else {
""
};
let contents = format!(
"#[test]{ignore}
fn test_{rs_module_name}() {{
assert_wasi_output!(
\"../../{module_path}\",
\"{rs_module_name}\",
vec![],
\"../../{test_output_path}\"
);
}}
",
ignore = ignored,
module_path = wasm_out_name,
rs_module_name = rs_module_name,
test_output_path = format!("{}.out", normalized_name),
);
let rust_test_filepath = format!(
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/{}.rs"),
normalized_name,
);
fs::write(&rust_test_filepath, contents.as_bytes()).expect("writing test file");
fs::write(&output_path, result.stdout).expect("writing output to file");
Some(rs_module_name)
}
pub fn build() {
let rust_test_modpath = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/wasitests/mod.rs");
let mut modules: Vec<String> = Vec::new();
let ignores = read_ignore_list();
for entry in glob("wasitests/*.rs").unwrap() {
match entry {
Ok(path) => {
let test = path.to_str().unwrap();
if let Some(module_name) = compile(test, &ignores) {
modules.push(module_name);
}
}
Err(e) => println!("{:?}", e),
}
}
modules.sort();
let mut modules: Vec<String> = modules.iter().map(|m| format!("mod {};", m)).collect();
assert!(modules.len() > 0, "Expected > 0 modules found");
modules.insert(0, BANNER.to_string());
modules.insert(1, "// The _common module is not autogenerated. It provides common macros for the wasitests\n#[macro_use]\nmod _common;".to_string());
// We add an empty line
modules.push("".to_string());
let modfile: String = modules.join("\n");
let source = fs::read(&rust_test_modpath).unwrap();
// We only modify the mod file if has changed
if source != modfile.as_bytes() {
fs::write(&rust_test_modpath, modfile.as_bytes()).unwrap();
}
}
fn read_ignore_list() -> HashSet<String> {
let f = File::open("wasitests/ignores.txt").unwrap();
let f = BufReader::new(f);
f.lines()
.filter_map(Result::ok)
.map(|v| v.to_lowercase())
.collect()
}