wasmer/lib/spectests/tests/spectest.rs

1230 lines
54 KiB
Rust
Raw Normal View History

2019-08-04 22:44:27 +00:00
#![deny(
bad_style,
dead_code,
unused_imports,
unused_variables,
unused_unsafe,
unreachable_patterns
)]
2019-07-26 03:31:19 +00:00
#[cfg(test)]
mod tests {
// TODO fix spec failures
// TODO fix panics and remove panic handlers
2019-08-04 22:44:27 +00:00
// TODO do something with messages _message, message: _, msg: _
2019-08-04 22:13:47 +00:00
// TODO consider git submodule for spectests? & separate dir for simd/extra tests
// TODO cleanup refactor
2019-08-04 19:18:57 +00:00
// TODO Files could be run with multiple threads
// TODO Allow running WAST &str directly (E.g. for use outside of spectests)
use std::sync::{Arc, Mutex};
2019-08-02 22:00:35 +00:00
struct SpecFailure {
file: String,
line: u64,
kind: String,
message: String,
}
struct TestReport {
failures: Vec<SpecFailure>,
passed: u32,
failed: u32,
2019-08-04 22:13:47 +00:00
allowed_failure: u32,
2019-08-02 22:00:35 +00:00
}
impl TestReport {
2019-08-04 22:44:27 +00:00
pub fn count_passed(&mut self) {
2019-08-02 22:00:35 +00:00
self.passed += 1;
}
2019-08-04 22:44:27 +00:00
pub fn has_failures(&self) -> bool {
2019-08-02 22:00:35 +00:00
self.failed > 0
}
2019-08-04 22:44:27 +00:00
pub fn add_failure(
2019-08-04 22:13:47 +00:00
&mut self,
failure: SpecFailure,
testkey: &str,
excludes: &HashMap<String, Exclude>,
) {
2019-08-04 22:44:27 +00:00
if excludes.contains_key(testkey) {
2019-08-04 22:13:47 +00:00
self.allowed_failure += 1;
return;
}
2019-08-06 02:57:07 +00:00
let platform_key = format!("{}:{}", testkey, get_platform());
if excludes.contains_key(&platform_key) {
self.allowed_failure += 1;
return;
}
2019-08-02 22:00:35 +00:00
self.failed += 1;
self.failures.push(failure);
}
}
2019-07-26 03:31:19 +00:00
#[cfg(feature = "clif")]
fn get_compiler() -> impl Compiler {
use wasmer_clif_backend::CraneliftCompiler;
CraneliftCompiler::new()
}
#[cfg(feature = "llvm")]
fn get_compiler() -> impl Compiler {
use wasmer_llvm_backend::LLVMCompiler;
LLVMCompiler::new()
}
#[cfg(feature = "singlepass")]
fn get_compiler() -> impl Compiler {
use wasmer_singlepass_backend::SinglePassCompiler;
SinglePassCompiler::new()
}
#[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))]
fn get_compiler() -> impl Compiler {
panic!("compiler not specified, activate a compiler via features");
use wasmer_clif_backend::CraneliftCompiler;
CraneliftCompiler::new()
}
#[cfg(feature = "clif")]
fn get_compiler_name() -> &'static str {
"clif"
}
#[cfg(feature = "llvm")]
fn get_compiler_name() -> &'static str {
"llvm"
}
#[cfg(feature = "singlepass")]
fn get_compiler_name() -> &'static str {
"singlepass"
}
2019-08-06 02:57:07 +00:00
#[cfg(unix)]
fn get_platform() -> &'static str {
"unix"
}
#[cfg(windows)]
fn get_platform() -> &'static str {
"windows"
}
#[cfg(not(any(feature = "llvm", feature = "clif", feature = "singlepass")))]
fn get_compiler_name() -> &'static str {
panic!("compiler not specified, activate a compiler via features");
"unknown"
}
fn with_instance<F, R>(
maybe_instance: Option<Arc<Mutex<Instance>>>,
named_modules: &HashMap<String, Arc<Mutex<Instance>>>,
module: &Option<String>,
f: F,
) -> Option<R>
where
R: Sized,
F: FnOnce(&Instance) -> R,
{
let ref ins = module
.as_ref()
.and_then(|name| named_modules.get(name).cloned())
.or(maybe_instance)?;
let guard = ins.lock().unwrap();
Some(f(guard.borrow()))
}
use glob::glob;
use std::collections::HashMap;
2019-08-04 22:44:27 +00:00
use std::fs;
use std::panic::AssertUnwindSafe;
2019-07-26 03:31:19 +00:00
use std::path::PathBuf;
use wabt::script::{Action, Command, CommandKind, ScriptParser, Value};
2019-08-04 21:36:17 +00:00
use wasmer_runtime_core::backend::{Compiler, CompilerConfig, Features};
2019-07-26 03:31:19 +00:00
use wasmer_runtime_core::error::CompileError;
use wasmer_runtime_core::import::ImportObject;
use wasmer_runtime_core::Instance;
2019-08-04 16:19:50 +00:00
use wasmer_runtime_core::{
2019-08-04 19:37:39 +00:00
export::Export,
2019-08-04 16:19:50 +00:00
global::Global,
2019-08-04 19:37:39 +00:00
import::LikeNamespace,
2019-08-04 16:19:50 +00:00
memory::Memory,
table::Table,
types::{ElementType, MemoryDescriptor, TableDescriptor},
units::Pages,
};
2019-08-04 19:37:39 +00:00
use wasmer_runtime_core::{func, imports, vm::Ctx};
2019-07-26 03:31:19 +00:00
fn parse_and_run(
path: &PathBuf,
excludes: &HashMap<String, Exclude>,
) -> Result<TestReport, String> {
2019-08-02 22:00:35 +00:00
let mut test_report = TestReport {
failures: vec![],
passed: 0,
failed: 0,
2019-08-04 22:13:47 +00:00
allowed_failure: 0,
2019-08-02 22:00:35 +00:00
};
2019-07-26 03:31:19 +00:00
let filename = path.file_name().unwrap().to_str().unwrap();
let source = fs::read(&path).unwrap();
let backend = get_compiler_name();
let platform = get_platform();
let star_key = format!("{}:{}:*", backend, filename);
let platform_star_key = format!("{}:{}:*:{}", backend, filename, platform);
if (excludes.contains_key(&star_key) && *excludes.get(&star_key).unwrap() == Exclude::Skip)
|| (excludes.contains_key(&platform_star_key)
&& *excludes.get(&platform_star_key).unwrap() == Exclude::Skip)
{
return Ok(test_report);
}
2019-07-26 03:31:19 +00:00
let mut features = wabt::Features::new();
features.enable_simd();
2019-08-09 02:15:20 +00:00
features.enable_threads();
2019-07-26 03:31:19 +00:00
let mut parser: ScriptParser =
ScriptParser::from_source_and_name_with_features(&source, filename, features)
.expect(&format!("Failed to parse script {}", &filename));
2019-08-02 17:36:38 +00:00
use std::panic;
let mut instance: Option<Arc<Mutex<Instance>>> = None;
2019-08-04 20:20:09 +00:00
let mut named_modules: HashMap<String, Arc<Mutex<Instance>>> = HashMap::new();
2019-08-03 23:31:43 +00:00
let mut registered_modules: HashMap<String, Arc<Mutex<Instance>>> = HashMap::new();
2019-08-04 21:36:17 +00:00
//
2019-08-04 20:44:55 +00:00
2019-07-26 03:31:19 +00:00
while let Some(Command { kind, line }) =
parser.next().map_err(|e| format!("Parse err: {:?}", e))?
{
let test_key = format!("{}:{}:{}", backend, filename, line);
2019-08-06 02:57:07 +00:00
let test_platform_key = format!("{}:{}:{}:{}", backend, filename, line, platform);
// Use this line to debug which test is running
2019-08-10 21:33:08 +00:00
println!("Running test: {}", test_key);
2019-08-06 02:57:07 +00:00
if (excludes.contains_key(&test_key)
&& *excludes.get(&test_key).unwrap() == Exclude::Skip)
|| (excludes.contains_key(&test_platform_key)
&& *excludes.get(&test_platform_key).unwrap() == Exclude::Skip)
{
// println!("Skipping test: {}", test_key);
continue;
}
2019-07-26 03:31:19 +00:00
match kind {
CommandKind::Module { module, name } => {
2019-08-03 23:31:43 +00:00
// println!("Module");
2019-08-04 21:36:17 +00:00
let result = panic::catch_unwind(AssertUnwindSafe(|| {
let spectest_import_object =
get_spectest_import_object(&registered_modules);
2019-08-04 20:44:55 +00:00
let config = CompilerConfig {
2019-08-09 02:15:20 +00:00
features: Features {
simd: true,
threads: true,
},
2019-08-04 20:44:55 +00:00
..Default::default()
};
let module = wasmer_runtime_core::compile_with_config(
&module.into_vec(),
&get_compiler(),
config,
)
.expect("WASM can't be compiled");
2019-08-02 17:36:38 +00:00
let i = module
2019-08-04 16:19:50 +00:00
.instantiate(&spectest_import_object)
2019-08-02 17:36:38 +00:00
.expect("WASM can't be instantiated");
i
2019-08-04 21:36:17 +00:00
}));
2019-08-02 17:36:38 +00:00
match result {
Err(e) => {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line: line,
kind: format!("{}", "Module"),
2019-08-04 22:13:47 +00:00
message: format!("caught panic {:?}", e),
},
&test_key,
excludes,
);
2019-08-02 17:36:38 +00:00
instance = None;
}
Ok(i) => {
let i = Arc::new(Mutex::new(i));
2019-08-04 20:20:09 +00:00
if name.is_some() {
named_modules.insert(name.unwrap(), Arc::clone(&i));
2019-08-04 20:20:09 +00:00
}
2019-08-02 17:36:38 +00:00
instance = Some(i);
}
}
2019-07-26 03:31:19 +00:00
}
CommandKind::AssertReturn { action, expected } => {
2019-08-02 22:00:35 +00:00
match action {
Action::Invoke {
module,
field,
args,
} => {
let maybe_call_result = with_instance(
instance.clone(),
&named_modules,
&module,
|instance| {
let params: Vec<wasmer_runtime_core::types::Value> =
args.iter().cloned().map(convert_value).collect();
instance.call(&field, &params[..])
2019-08-04 20:20:09 +00:00
},
);
if maybe_call_result.is_none() {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line: line,
kind: format!("{}", "AssertReturn"),
2019-08-04 22:13:47 +00:00
message: format!("No instance available: {:?}", &module),
},
&test_key,
excludes,
);
2019-08-02 22:00:35 +00:00
} else {
let call_result = maybe_call_result.unwrap();
2019-08-03 23:31:43 +00:00
match call_result {
Err(e) => {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line,
kind: format!("{}", "AssertReturn"),
2019-08-04 22:13:47 +00:00
message: format!("Call failed {:?}", e),
},
&test_key,
excludes,
);
2019-08-03 23:31:43 +00:00
}
Ok(values) => {
for (i, v) in values.iter().enumerate() {
let expected_value =
convert_wabt_value(*expected.get(i).unwrap());
let v = convert_wasmer_value(v.clone());
if v != expected_value {
2019-08-04 22:44:27 +00:00
test_report.add_failure(SpecFailure {
2019-08-03 23:31:43 +00:00
file: filename.to_string(),
line,
kind: format!("{}", "AssertReturn"),
2019-08-03 23:31:43 +00:00
message: format!(
"result {:?} ({:?}) does not match expected {:?} ({:?})",
v, to_hex(v.clone()), expected_value, to_hex(expected_value.clone())
),
2019-08-04 22:13:47 +00:00
}, &test_key, excludes);
2019-08-03 23:31:43 +00:00
} else {
2019-08-04 22:44:27 +00:00
test_report.count_passed();
2019-08-03 23:31:43 +00:00
}
}
}
}
2019-08-02 22:00:35 +00:00
}
}
2019-08-04 19:37:39 +00:00
Action::Get { module, field } => {
let maybe_call_result = with_instance(
instance.clone(),
&named_modules,
&module,
|instance| {
instance
.get_export(&field)
.expect(&format!("missing global {:?}", &field))
2019-08-04 20:20:09 +00:00
},
);
if maybe_call_result.is_none() {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line: line,
kind: format!("{}", "AssertReturn Get"),
2019-08-04 22:13:47 +00:00
message: format!("No instance available {:?}", &module),
},
&test_key,
excludes,
);
2019-08-04 19:37:39 +00:00
} else {
let export: Export = maybe_call_result.unwrap();
2019-08-04 20:20:09 +00:00
match export {
Export::Global(g) => {
let value = g.get();
let expected_value =
convert_value(*expected.get(0).unwrap());
if value == expected_value {
2019-08-04 22:44:27 +00:00
test_report.count_passed();
2019-08-04 20:20:09 +00:00
} else {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line: line,
kind: format!("{}", "AssertReturn Get"),
2019-08-04 22:13:47 +00:00
message: format!(
"Expected Global {:?} got: {:?}",
expected_value, value
),
},
&test_key,
excludes,
);
2019-08-04 19:37:39 +00:00
}
}
2019-08-04 20:20:09 +00:00
_ => {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line: line,
kind: format!("{}", "AssertReturn Get"),
2019-08-04 22:13:47 +00:00
message: format!("Expected Global"),
},
&test_key,
excludes,
);
2019-08-04 20:20:09 +00:00
}
2019-08-04 19:37:39 +00:00
}
}
}
2019-08-02 22:00:35 +00:00
}
// println!("in assert return");
2019-07-26 03:31:19 +00:00
}
2019-08-03 23:31:43 +00:00
CommandKind::AssertReturnCanonicalNan { action } => match action {
Action::Invoke {
module,
field,
args,
} => {
let maybe_call_result =
with_instance(instance.clone(), &named_modules, &module, |instance| {
let params: Vec<wasmer_runtime_core::types::Value> =
args.iter().cloned().map(convert_value).collect();
instance.call(&field, &params[..])
});
if maybe_call_result.is_none() {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line: line,
kind: format!("{}", "AssertReturnCanonicalNan"),
2019-08-04 22:13:47 +00:00
message: format!("No instance available {:?}", &module),
},
&test_key,
excludes,
);
2019-08-03 23:31:43 +00:00
} else {
let call_result = maybe_call_result.unwrap();
2019-08-03 23:31:43 +00:00
match call_result {
Err(e) => {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line,
kind: format!("{}", "AssertReturnCanonicalNan"),
2019-08-04 22:13:47 +00:00
message: format!("Call failed {:?}", e),
},
&test_key,
excludes,
);
2019-08-03 23:31:43 +00:00
}
Ok(values) => {
2019-08-04 22:44:27 +00:00
for v in values.iter() {
2019-08-03 23:31:43 +00:00
if is_canonical_nan(v.clone()) {
2019-08-04 22:44:27 +00:00
test_report.count_passed();
2019-08-03 23:31:43 +00:00
} else {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line,
kind: format!(
"{:?}",
"AssertReturnCanonicalNan"
),
message: format!(
"value is not canonical nan {:?}",
v
),
},
&test_key,
excludes,
);
2019-08-03 23:31:43 +00:00
}
}
}
}
}
}
_ => panic!("unexpected action in assert return canonical nan"),
},
CommandKind::AssertReturnArithmeticNan { action } => match action {
Action::Invoke {
module,
field,
args,
} => {
let maybe_call_result =
with_instance(instance.clone(), &named_modules, &module, |instance| {
let params: Vec<wasmer_runtime_core::types::Value> =
args.iter().cloned().map(convert_value).collect();
instance.call(&field, &params[..])
});
if maybe_call_result.is_none() {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line: line,
kind: format!("{}", "AssertReturnArithmeticNan"),
2019-08-04 22:13:47 +00:00
message: format!("No instance available"),
},
&test_key,
excludes,
);
2019-08-03 23:31:43 +00:00
} else {
let call_result = maybe_call_result.unwrap();
2019-08-03 23:31:43 +00:00
match call_result {
Err(e) => {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line,
kind: format!("{}", "AssertReturnArithmeticNan"),
2019-08-04 22:13:47 +00:00
message: format!("Call failed {:?}", e),
},
&test_key,
excludes,
);
2019-08-03 23:31:43 +00:00
}
Ok(values) => {
2019-08-04 22:44:27 +00:00
for v in values.iter() {
2019-08-03 23:31:43 +00:00
if is_arithmetic_nan(v.clone()) {
2019-08-04 22:44:27 +00:00
test_report.count_passed();
2019-08-03 23:31:43 +00:00
} else {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line,
kind: format!(
"{:?}",
"AssertReturnArithmeticNan"
),
message: format!(
"value is not arithmetic nan {:?}",
v
),
},
&test_key,
excludes,
);
2019-08-03 23:31:43 +00:00
}
}
}
}
}
}
_ => panic!("unexpected action in assert return arithmetic nan"),
},
2019-08-04 22:44:27 +00:00
CommandKind::AssertTrap { action, message: _ } => match action {
2019-08-03 23:31:43 +00:00
Action::Invoke {
module,
field,
args,
} => {
let maybe_call_result =
with_instance(instance.clone(), &named_modules, &module, |instance| {
let params: Vec<wasmer_runtime_core::types::Value> =
args.iter().cloned().map(convert_value).collect();
instance.call(&field, &params[..])
});
if maybe_call_result.is_none() {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line: line,
kind: format!("{}", "AssertTrap"),
2019-08-04 22:13:47 +00:00
message: format!("No instance available"),
},
&test_key,
excludes,
);
2019-08-03 23:31:43 +00:00
} else {
let call_result = maybe_call_result.unwrap();
2019-08-03 23:31:43 +00:00
use wasmer_runtime_core::error::{CallError, RuntimeError};
match call_result {
Err(e) => {
match e {
CallError::Resolve(_) => {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line,
kind: format!("{}", "AssertTrap"),
2019-08-04 22:13:47 +00:00
message: format!("expected trap, got {:?}", e),
},
&test_key,
excludes,
);
2019-08-03 23:31:43 +00:00
}
CallError::Runtime(r) => {
match r {
RuntimeError::Trap { .. } => {
// TODO assert message?
2019-08-04 22:44:27 +00:00
test_report.count_passed()
2019-08-03 23:31:43 +00:00
}
RuntimeError::Error { .. } => {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line,
kind: format!("{}", "AssertTrap"),
2019-08-04 22:13:47 +00:00
message: format!(
2019-08-03 23:31:43 +00:00
"expected trap, got Runtime:Error {:?}",
r
),
2019-08-04 22:13:47 +00:00
},
&test_key,
excludes,
);
2019-08-03 23:31:43 +00:00
}
}
}
}
}
Ok(values) => {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line,
kind: format!("{}", "AssertTrap"),
2019-08-04 22:13:47 +00:00
message: format!("expected trap, got {:?}", values),
},
&test_key,
excludes,
);
2019-08-03 23:31:43 +00:00
}
}
}
}
_ => println!("unexpected action"),
},
2019-08-04 22:44:27 +00:00
CommandKind::AssertInvalid { module, message: _ } => {
2019-08-03 23:31:43 +00:00
// println!("AssertInvalid");
2019-08-02 22:00:35 +00:00
let result = panic::catch_unwind(|| {
2019-08-04 20:44:55 +00:00
let config = CompilerConfig {
2019-08-09 02:15:20 +00:00
features: Features {
simd: true,
threads: true,
},
2019-08-04 20:44:55 +00:00
..Default::default()
};
wasmer_runtime_core::compile_with_config(
&module.into_vec(),
&get_compiler(),
config,
)
2019-08-02 22:00:35 +00:00
});
match result {
Ok(module) => {
2019-08-04 22:44:27 +00:00
if let Err(CompileError::InternalError { msg: _ }) = module {
test_report.count_passed();
2019-08-03 23:31:43 +00:00
// println!("expected: {:?}", message);
// println!("actual: {:?}", msg);
2019-08-04 22:44:27 +00:00
} else if let Err(CompileError::ValidationError { msg: _ }) = module {
test_report.count_passed();
2019-08-03 23:31:43 +00:00
// println!("validation expected: {:?}", message);
// println!("validation actual: {:?}", msg);
2019-08-02 22:00:35 +00:00
} else {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line: line,
kind: format!("{}", "AssertInvalid"),
2019-08-04 22:13:47 +00:00
message: "Should be invalid".to_string(),
},
&test_key,
excludes,
);
2019-08-02 22:00:35 +00:00
}
}
Err(p) => {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line: line,
kind: format!("{}", "AssertInvalid"),
2019-08-04 22:13:47 +00:00
message: format!("caught panic {:?}", p),
},
&test_key,
excludes,
);
2019-08-02 22:00:35 +00:00
}
}
}
2019-08-04 22:44:27 +00:00
CommandKind::AssertMalformed { module, message: _ } => {
2019-08-03 23:31:43 +00:00
// println!("AssertMalformed");
2019-08-02 17:36:38 +00:00
let result = panic::catch_unwind(|| {
2019-08-04 20:44:55 +00:00
let config = CompilerConfig {
2019-08-09 02:15:20 +00:00
features: Features {
simd: true,
threads: true,
},
2019-08-04 20:44:55 +00:00
..Default::default()
};
wasmer_runtime_core::compile_with_config(
&module.into_vec(),
&get_compiler(),
config,
)
2019-08-02 17:36:38 +00:00
});
match result {
Ok(module) => {
2019-08-04 22:44:27 +00:00
if let Err(CompileError::InternalError { msg: _ }) = module {
test_report.count_passed();
2019-08-03 23:31:43 +00:00
// println!("expected: {:?}", message);
// println!("actual: {:?}", msg);
2019-08-04 22:44:27 +00:00
} else if let Err(CompileError::ValidationError { msg: _ }) = module {
test_report.count_passed();
2019-08-03 23:31:43 +00:00
// println!("validation expected: {:?}", message);
// println!("validation actual: {:?}", msg);
2019-08-02 17:36:38 +00:00
} else {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line: line,
kind: format!("{}", "AssertMalformed"),
2019-08-04 22:13:47 +00:00
message: format!("should be malformed"),
},
&test_key,
excludes,
);
2019-08-02 17:36:38 +00:00
}
}
Err(p) => {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line: line,
kind: format!("{}", "AssertMalformed"),
2019-08-04 22:13:47 +00:00
message: format!("caught panic {:?}", p),
},
&test_key,
excludes,
);
2019-08-02 17:36:38 +00:00
}
2019-07-26 03:31:19 +00:00
}
}
2019-08-04 22:44:27 +00:00
CommandKind::AssertUninstantiable {
module: _,
message: _,
} => println!("AssertUninstantiable not yet implmented "),
CommandKind::AssertExhaustion { action, message: _ } => {
2019-08-04 19:18:57 +00:00
match action {
Action::Invoke {
module,
field,
args,
} => {
let maybe_call_result = with_instance(
instance.clone(),
&named_modules,
&module,
|instance| {
let params: Vec<wasmer_runtime_core::types::Value> =
args.iter().cloned().map(convert_value).collect();
instance.call(&field, &params[..])
2019-08-04 20:20:09 +00:00
},
);
if maybe_call_result.is_none() {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line: line,
kind: format!("{}", "AssertExhaustion"),
2019-08-04 22:13:47 +00:00
message: format!("No instance available"),
},
&test_key,
excludes,
);
2019-08-04 19:18:57 +00:00
} else {
let call_result = maybe_call_result.unwrap();
2019-08-04 19:18:57 +00:00
match call_result {
2019-08-04 22:44:27 +00:00
Err(_e) => {
// TODO is specific error required?
test_report.count_passed();
2019-08-04 19:18:57 +00:00
}
Ok(values) => {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line,
kind: format!("{}", "AssertExhaustion"),
2019-08-04 22:13:47 +00:00
message: format!(
"Expected call failure, got {:?}",
values
),
},
&test_key,
excludes,
);
2019-08-04 19:18:57 +00:00
}
}
}
}
2019-08-04 19:37:39 +00:00
_ => println!("unexpected action in assert exhaustion"),
2019-08-04 19:18:57 +00:00
}
}
2019-08-04 22:44:27 +00:00
CommandKind::AssertUnlinkable { module, message: _ } => {
let result = panic::catch_unwind(AssertUnwindSafe(|| {
let spectest_import_object =
get_spectest_import_object(&registered_modules);
2019-08-04 20:44:55 +00:00
let config = CompilerConfig {
2019-08-09 02:15:20 +00:00
features: Features {
simd: true,
threads: true,
},
2019-08-04 20:44:55 +00:00
..Default::default()
};
let module = wasmer_runtime_core::compile_with_config(
&module.into_vec(),
&get_compiler(),
config,
)
.expect("WASM can't be compiled");
module.instantiate(&spectest_import_object)
}));
match result {
Err(e) => {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line: line,
kind: format!("{}", "AssertUnlinkable"),
2019-08-04 22:13:47 +00:00
message: format!("caught panic {:?}", e),
},
&test_key,
excludes,
);
}
Ok(result) => match result {
Ok(_) => {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line: line,
kind: format!("{}", "AssertUnlinkable"),
2019-08-04 22:13:47 +00:00
message: format!(
"instantiate successful, expected unlinkable"
),
},
&test_key,
excludes,
);
}
Err(e) => match e {
wasmer_runtime_core::error::Error::LinkError(_) => {
2019-08-04 22:44:27 +00:00
test_report.count_passed();
}
_ => {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line: line,
kind: format!("{}", "AssertUnlinkable"),
2019-08-04 22:13:47 +00:00
message: format!("expected link error, got {:?}", e),
},
&test_key,
excludes,
);
}
},
},
}
}
2019-08-04 16:19:50 +00:00
CommandKind::Register { name, as_name } => {
let instance: Option<Arc<Mutex<Instance>>> = match name {
2019-08-04 21:36:17 +00:00
Some(ref name) => {
let i = named_modules.get(name);
match i {
Some(ins) => Some(Arc::clone(ins)),
2019-08-04 21:36:17 +00:00
None => None,
}
}
None => match instance {
Some(ref i) => Some(Arc::clone(i)),
2019-08-04 21:36:17 +00:00
None => None,
},
};
if let Some(ins) = instance {
registered_modules.insert(as_name, ins);
} else {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line: line,
kind: format!("{}", "Register"),
2019-08-04 22:13:47 +00:00
message: format!("No instance available"),
},
&test_key,
excludes,
);
2019-08-04 21:36:17 +00:00
}
2019-08-04 16:19:50 +00:00
}
CommandKind::PerformAction(ref action) => match action {
Action::Invoke {
module,
field,
args,
} => {
let maybe_call_result =
with_instance(instance.clone(), &named_modules, &module, |instance| {
let params: Vec<wasmer_runtime_core::types::Value> =
args.iter().cloned().map(convert_value).collect();
instance.call(&field, &params[..])
});
if maybe_call_result.is_none() {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line: line,
kind: format!("{}", "PerformAction"),
2019-08-04 22:13:47 +00:00
message: format!("No instance available"),
},
&test_key,
excludes,
);
2019-08-04 16:19:50 +00:00
} else {
let call_result = maybe_call_result.unwrap();
2019-08-04 20:20:09 +00:00
match call_result {
Err(e) => {
2019-08-04 22:44:27 +00:00
test_report.add_failure(
2019-08-04 22:13:47 +00:00
SpecFailure {
file: filename.to_string(),
line,
kind: format!("{}", "PerformAction"),
2019-08-04 22:13:47 +00:00
message: format!("Call failed {:?}", e),
},
&test_key,
excludes,
);
2019-08-04 20:20:09 +00:00
}
2019-08-04 22:44:27 +00:00
Ok(_values) => {
test_report.count_passed();
2019-08-04 16:19:50 +00:00
}
}
}
}
2019-08-04 22:44:27 +00:00
Action::Get { module, field } => println!(
"Action Get not implemented {:?} {:?} {:?} {:?}",
module, field, filename, line
),
2019-08-04 16:19:50 +00:00
},
2019-07-26 03:31:19 +00:00
}
}
2019-08-02 22:00:35 +00:00
Ok(test_report)
2019-07-26 03:31:19 +00:00
}
2019-08-03 23:31:43 +00:00
fn is_canonical_nan(val: wasmer_runtime_core::types::Value) -> bool {
match val {
wasmer_runtime_core::types::Value::F32(x) => x.is_canonical_nan(),
wasmer_runtime_core::types::Value::F64(x) => x.is_canonical_nan(),
_ => panic!("value is not a float {:?}", val),
}
}
fn is_arithmetic_nan(val: wasmer_runtime_core::types::Value) -> bool {
match val {
wasmer_runtime_core::types::Value::F32(x) => x.is_quiet_nan(),
wasmer_runtime_core::types::Value::F64(x) => x.is_quiet_nan(),
_ => panic!("value is not a float {:?}", val),
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum SpectestValue {
I32(i32),
I64(i64),
F32(u32),
F64(u64),
V128(u128),
}
fn convert_wasmer_value(other: wasmer_runtime_core::types::Value) -> SpectestValue {
match other {
wasmer_runtime_core::types::Value::I32(v) => SpectestValue::I32(v),
wasmer_runtime_core::types::Value::I64(v) => SpectestValue::I64(v),
wasmer_runtime_core::types::Value::F32(v) => SpectestValue::F32(v.to_bits()),
wasmer_runtime_core::types::Value::F64(v) => SpectestValue::F64(v.to_bits()),
wasmer_runtime_core::types::Value::V128(v) => SpectestValue::V128(v),
}
}
fn convert_wabt_value(other: Value<f32, f64>) -> SpectestValue {
match other {
Value::I32(v) => SpectestValue::I32(v),
Value::I64(v) => SpectestValue::I64(v),
Value::F32(v) => SpectestValue::F32(v.to_bits()),
Value::F64(v) => SpectestValue::F64(v.to_bits()),
Value::V128(v) => SpectestValue::V128(v),
}
}
2019-08-03 23:31:43 +00:00
fn convert_value(other: Value<f32, f64>) -> wasmer_runtime_core::types::Value {
match other {
Value::I32(v) => wasmer_runtime_core::types::Value::I32(v),
Value::I64(v) => wasmer_runtime_core::types::Value::I64(v),
Value::F32(v) => wasmer_runtime_core::types::Value::F32(v),
Value::F64(v) => wasmer_runtime_core::types::Value::F64(v),
Value::V128(v) => wasmer_runtime_core::types::Value::V128(v),
}
}
fn to_hex(v: SpectestValue) -> String {
2019-08-03 23:31:43 +00:00
match v {
SpectestValue::I32(v) => format!("{:#x}", v),
SpectestValue::I64(v) => format!("{:#x}", v),
SpectestValue::F32(v) => format!("{:#x}", v),
SpectestValue::F64(v) => format!("{:#x}", v),
SpectestValue::V128(v) => format!("{:#x}", v),
2019-08-03 23:31:43 +00:00
}
}
fn print(_ctx: &mut Ctx) {
println!("");
}
2019-08-04 22:44:27 +00:00
fn print_i32(_ctx: &mut Ctx, val: i32) {
2019-08-04 16:19:50 +00:00
println!("{}", val);
}
2019-08-06 01:25:12 +00:00
fn print_f32(_ctx: &mut Ctx, val: f32) {
println!("{}", val);
}
fn print_f64(_ctx: &mut Ctx, val: f64) {
println!("{}", val);
}
fn print_i32_f32(_ctx: &mut Ctx, val: i32, val2: f32) {
println!("{} {}", val, val2);
}
fn print_f64_f64(_ctx: &mut Ctx, val: f64, val2: f64) {
println!("{} {}", val, val2);
}
fn get_spectest_import_object(
registered_modules: &HashMap<String, Arc<Mutex<Instance>>>,
) -> ImportObject {
2019-09-20 16:59:36 +00:00
let memory_desc = MemoryDescriptor::new(Pages(1), Some(Pages(2)), false).unwrap();
2019-09-20 16:54:05 +00:00
let memory = Memory::new(memory_desc).unwrap();
2019-08-04 16:19:50 +00:00
let global_i32 = Global::new(wasmer_runtime_core::types::Value::I32(666));
let global_f32 = Global::new(wasmer_runtime_core::types::Value::F32(666.0));
let global_f64 = Global::new(wasmer_runtime_core::types::Value::F64(666.0));
let table = Table::new(TableDescriptor {
element: ElementType::Anyfunc,
minimum: 10,
maximum: Some(20),
})
.unwrap();
2019-08-04 21:36:17 +00:00
let mut import_object = imports! {
2019-08-04 16:19:50 +00:00
"spectest" => {
"print" => func!(print),
2019-08-04 16:19:50 +00:00
"print_i32" => func!(print_i32),
2019-08-06 01:25:12 +00:00
"print_f32" => func!(print_f32),
"print_f64" => func!(print_f64),
"print_i32_f32" => func!(print_i32_f32),
"print_f64_f64" => func!(print_f64_f64),
2019-08-04 16:19:50 +00:00
"table" => table,
2019-08-04 22:44:27 +00:00
"memory" => memory,
2019-08-04 16:19:50 +00:00
"global_i32" => global_i32,
"global_f32" => global_f32,
"global_f64" => global_f64,
},
2019-08-04 21:36:17 +00:00
};
for (name, instance) in registered_modules.iter() {
import_object.register(name.clone(), Arc::clone(instance));
2019-08-04 16:19:50 +00:00
}
2019-08-04 21:36:17 +00:00
import_object
2019-08-04 16:19:50 +00:00
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum Exclude {
Skip,
Fail,
}
2019-08-04 22:44:27 +00:00
use core::borrow::Borrow;
use std::fs::File;
2019-08-04 22:44:27 +00:00
use std::io::{BufRead, BufReader};
/// Reads the excludes.txt file into a hash map
fn read_excludes() -> HashMap<String, Exclude> {
let mut excludes_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
excludes_path.push("tests");
excludes_path.push("excludes.txt");
let input = File::open(excludes_path).unwrap();
let buffered = BufReader::new(input);
let mut result = HashMap::new();
for line in buffered.lines() {
let mut line = line.unwrap();
if line.trim().is_empty() || line.starts_with("#") {
// ignore line
} else {
if line.contains("#") {
// Allow end of line comment
let l: Vec<&str> = line.split('#').collect();
line = l.get(0).unwrap().to_string();
}
//println!("exclude line {}", line);
// <backend>:<exclude-kind>:<test-file-name>:<test-file-line>
let split: Vec<&str> = line.trim().split(':').collect();
2019-08-06 02:57:07 +00:00
let kind = match *split.get(1).unwrap() {
"skip" => Exclude::Skip,
"fail" => Exclude::Fail,
_ => panic!("unknown exclude kind"),
};
2019-08-06 02:57:07 +00:00
let has_platform = split.len() > 4;
let backend = split.get(0).unwrap();
let testfile = split.get(2).unwrap();
let line = split.get(3).unwrap();
2019-08-06 02:57:07 +00:00
let key = if has_platform {
let platform = split.get(4).unwrap();
format!("{}:{}:{}:{}", backend, testfile, line, platform)
} else {
format!("{}:{}:{}", backend, testfile, line)
};
result.insert(key, kind);
}
}
result
}
2019-07-26 03:31:19 +00:00
#[test]
fn test_run_spectests() {
let mut success = true;
2019-08-02 22:00:35 +00:00
let mut test_reports = vec![];
2019-07-26 03:31:19 +00:00
let excludes = read_excludes();
let mut glob_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
glob_path.push("spectests");
glob_path.push("*.wast");
let glob_str = glob_path.to_str().unwrap();
for entry in glob(glob_str).expect("Failed to read glob pattern") {
match entry {
Ok(wast_path) => {
let result = parse_and_run(&wast_path, &excludes);
match result {
Ok(test_report) => {
2019-08-04 22:44:27 +00:00
if test_report.has_failures() {
success = false
}
test_reports.push(test_report);
}
Err(e) => {
success = false;
println!("Unexpected test run error: {:?}", e)
}
2019-08-02 22:00:35 +00:00
}
}
Err(e) => panic!("glob err: {:?}", e),
2019-07-26 03:31:19 +00:00
}
}
2019-08-02 22:00:35 +00:00
// Print summary
let mut failures = vec![];
let mut total_passed = 0;
let mut total_failed = 0;
2019-08-04 22:13:47 +00:00
let mut total_allowed_failures = 0;
2019-08-02 22:00:35 +00:00
for mut test_report in test_reports.into_iter() {
total_passed += test_report.passed;
total_failed += test_report.failed;
2019-08-04 22:13:47 +00:00
total_allowed_failures += test_report.allowed_failure;
2019-08-02 22:00:35 +00:00
failures.append(&mut test_report.failures);
}
println!("");
2019-08-04 22:13:47 +00:00
println!("Failures:");
let backend = get_compiler_name();
2019-08-02 22:00:35 +00:00
for failure in failures.iter() {
// To print excludes for all failures:
2019-08-02 22:00:35 +00:00
println!(
"{}:fail:{}:{} # {} - {}",
backend, failure.file, failure.line, failure.kind, failure.message
2019-08-02 22:00:35 +00:00
);
}
println!("");
println!("");
2019-08-04 22:13:47 +00:00
println!("Spec tests summary report: ");
println!(
"total: {}",
total_passed + total_failed + total_allowed_failures
);
println!("passed: {}", total_passed);
println!("failed: {}", total_failed);
println!("allowed failures: {}", total_allowed_failures);
println!("");
2019-07-26 03:31:19 +00:00
assert!(success, "tests passed")
}
2019-08-03 23:31:43 +00:00
/// Bit pattern of an f32 value:
/// 1-bit sign + 8-bit mantissa + 23-bit exponent = 32 bits
///
/// Bit pattern of an f64 value:
/// 1-bit sign + 11-bit mantissa + 52-bit exponent = 64 bits
///
/// NOTE: On some old platforms (PA-RISC, some MIPS) quiet NaNs (qNaN) have
/// their mantissa MSB unset and set for signaling NaNs (sNaN).
///
/// Links:
/// * https://en.wikipedia.org/wiki/Floating-point_arithmetic
/// * https://github.com/WebAssembly/spec/issues/286
/// * https://en.wikipedia.org/wiki/NaN
///
pub trait NaNCheck {
fn is_quiet_nan(&self) -> bool;
fn is_canonical_nan(&self) -> bool;
}
impl NaNCheck for f32 {
/// The MSB of the mantissa must be set for a NaN to be a quiet NaN.
fn is_quiet_nan(&self) -> bool {
let mantissa_msb = 0b1 << 22;
self.is_nan() && (self.to_bits() & mantissa_msb) != 0
2019-08-03 23:31:43 +00:00
}
/// For a NaN to be canonical, the MSB of the mantissa must be set and
/// all other mantissa bits must be unset.
2019-08-03 23:31:43 +00:00
fn is_canonical_nan(&self) -> bool {
return self.to_bits() == 0xFFC0_0000 || self.to_bits() == 0x7FC0_0000;
2019-08-03 23:31:43 +00:00
}
}
impl NaNCheck for f64 {
/// The MSB of the mantissa must be set for a NaN to be a quiet NaN.
fn is_quiet_nan(&self) -> bool {
let mantissa_msb = 0b1 << 51;
self.is_nan() && (self.to_bits() & mantissa_msb) != 0
2019-08-03 23:31:43 +00:00
}
/// For a NaN to be canonical, the MSB of the mantissa must be set and
/// all other mantissa bits must be unset.
2019-08-03 23:31:43 +00:00
fn is_canonical_nan(&self) -> bool {
self.to_bits() == 0x7FF8_0000_0000_0000 || self.to_bits() == 0xFFF8_0000_0000_0000
2019-08-03 23:31:43 +00:00
}
}
2019-07-26 03:31:19 +00:00
}