Improved tests to use available compilers

This commit is contained in:
Syrus 2020-04-16 11:52:11 -07:00
parent 5f5928dfbd
commit b301ac85be
14 changed files with 428 additions and 467 deletions

View File

@ -96,7 +96,7 @@ wabt = "0.9.1"
# Don't add the backend features in default, please add them on the Makefile
# since we might want to autoconfigure them depending on the availability on the host.
default = ["wasi", "wabt"]
"loader-kernel" = ["wasmer-kernel-loader"]
loader-kernel = ["wasmer-kernel-loader"]
debug = ["fern", "log/max_level_debug", "log/release_max_level_debug"]
trace = ["fern", "log/max_level_trace", "log/release_max_level_trace"]
docs = ["wasmer-runtime/docs"]

View File

@ -351,6 +351,10 @@ pub mod error {
/// Idea for generic trait; consider rename; it will need to be moved somewhere else
pub trait CompiledModule {
fn new(bytes: impl AsRef<[u8]>) -> error::CompileResult<Module>;
fn new_with_compiler(
bytes: impl AsRef<[u8]>,
compiler: Box<dyn compiler::Compiler>,
) -> error::CompileResult<Module>;
fn from_binary(bytes: impl AsRef<[u8]>) -> error::CompileResult<Module>;
fn from_binary_unchecked(bytes: impl AsRef<[u8]>) -> error::CompileResult<Module>;
fn from_file(file: impl AsRef<std::path::Path>) -> Result<Module, error::CompileFromFileError>;
@ -365,6 +369,14 @@ impl CompiledModule for Module {
wasmer_runtime_core::compile_with(bytes, &compiler::default_compiler())
}
fn new_with_compiler(
bytes: impl AsRef<[u8]>,
compiler: Box<dyn compiler::Compiler>,
) -> error::CompileResult<Module> {
let bytes = bytes.as_ref();
wasmer_runtime_core::compile_with(bytes, &*compiler)
}
fn from_binary(bytes: impl AsRef<[u8]>) -> error::CompileResult<Module> {
let bytes = bytes.as_ref();
wasmer_runtime_core::compile_with(bytes, &compiler::default_compiler())

View File

@ -0,0 +1,10 @@
(module
(type (;0;) (func))
(func (;0;) (type 0)
i32.const 0
call_indirect (type 0))
(table (;0;) 1 anyfunc)
(export "stack-overflow" (func 0))
(elem (;0;) (i32.const 0) 0))
(assert_exhaustion (invoke "stack-overflow"))

View File

@ -1,3 +1,8 @@
#![cfg(test)]
#[macro_use]
mod utils;
static TEST_WAT: &str = r#"
(module
(import "env" "outside-global" (global $outside-global (mut i32)))
@ -47,8 +52,11 @@ fn append_custom_section(
wasm.extend(custom_section_contents);
}
#[test]
fn custom_section_parsing_works() {
wasmer_backends! {
use super::{TEST_WAT, append_custom_section};
#[test]
fn custom_section_parsing_works() {
use wasmer::{CompiledModule, Module};
let wasm = {
let mut wasm = wabt::wat2wasm(TEST_WAT).unwrap();
@ -58,23 +66,23 @@ fn custom_section_parsing_works() {
wasm
};
let module = Module::new(wasm).unwrap();
let module = Module::new_with_compiler(wasm, get_compiler()).unwrap();
assert_eq!(
module.custom_sections("test_custom_section"),
Some(&[b"hello, world!".to_vec(), b"goodbye, world!".to_vec()][..])
);
}
}
#[test]
fn module_exports_are_ordered() {
#[test]
fn module_exports_are_ordered() {
use wasmer::types::{ElementType, FuncSig, GlobalDescriptor, TableDescriptor, Type};
use wasmer::{export, CompiledModule, Module};
let wasm = wabt::wat2wasm(TEST_WAT).unwrap();
// TODO: review error messages when `CompiledModule` is not in scope. My hypothesis is that they'll be
// misleading, if so we may want to do something about it.
let module = Module::new(wasm).unwrap();
let module = Module::new_with_compiler(wasm, get_compiler()).unwrap();
let exports = module.exports();
assert_eq!(
exports,
@ -112,31 +120,31 @@ fn module_exports_are_ordered() {
},
]
);
}
}
#[test]
fn module_imports_are_correct() {
#[test]
fn module_imports_are_correct() {
use wasmer::{CompiledModule, Module};
let wasm = wabt::wat2wasm(TEST_WAT).unwrap();
// TODO: review error messages when `CompiledModule` is not in scope. My hypothesis is that they'll be
// misleading, if so we may want to do something about it.
let module = Module::new(wasm).unwrap();
let module = Module::new_with_compiler(wasm, get_compiler()).unwrap();
// TODO: test this more later
let imports = module.imports();
assert_eq!(imports.len(), 2);
}
}
#[test]
fn module_can_be_instantiated() {
#[test]
fn module_can_be_instantiated() {
use wasmer::wasm::{Global, Value};
use wasmer::{func, imports, CompiledModule, Module};
let wasm = wabt::wat2wasm(TEST_WAT).unwrap();
// TODO: review error messages when `CompiledModule` is not in scope. My hypothesis is that they'll be
// misleading, if so we may want to do something about it.
let module = Module::new(wasm).unwrap();
let module = Module::new_with_compiler(wasm, get_compiler()).unwrap();
let outside_global = Global::new_mutable(Value::I32(15));
let import_object = imports! {
@ -148,17 +156,17 @@ fn module_can_be_instantiated() {
let instance = module.instantiate(&import_object);
assert!(instance.is_ok());
}
}
#[test]
fn exports_work() {
#[test]
fn exports_work() {
use wasmer::wasm::{Global, Value};
use wasmer::{func, imports, CompiledModule, Func, Module, Table};
let wasm = wabt::wat2wasm(TEST_WAT).unwrap();
// TODO: review error messages when `CompiledModule` is not in scope. My hypothesis is that they'll be
// misleading, if so we may want to do something about it.
let module = Module::new(wasm).unwrap();
let module = Module::new_with_compiler(wasm, get_compiler()).unwrap();
let outside_global = Global::new_mutable(Value::I32(15));
@ -192,6 +200,7 @@ fn exports_work() {
assert_eq!(outside_global.get(), Value::I32(15 * 2));
update_outside_global.call().unwrap();
assert_eq!(outside_global.get(), Value::I32(15 * 2 * 2));
}
}
// copied from Rust stdlib: https://doc.rust-lang.org/nightly/nightly-rustc/src/serialize/leb128.rs.html#4-14

View File

@ -1,3 +1,6 @@
#[macro_use]
mod utils;
use std::{convert::TryInto, sync::Arc};
use wabt::wat2wasm;
use wasmer::compiler::{compile_with, compiler_for_backend, Backend};
@ -386,98 +389,99 @@ fn callback_fn_trap_with_vmctx(vmctx: &mut vm::Ctx, n: i32) -> Result<i32, Strin
}
macro_rules! test {
($backend:expr, $test_name:ident, $function:ident( $( $inputs:ty ),* ) -> $output:ty, ( $( $arguments:expr ),* ) == $expected_value:expr) => {
($test_name:ident, $function:ident( $( $inputs:ty ),* ) -> $output:ty, ( $( $arguments:expr ),* ) == $expected_value:expr) => {
#[cfg(all(unix, target_arch = "x86_64"))]
#[test]
fn $test_name() {
imported_functions_forms($backend, &|instance| {
imported_functions_forms(get_backend(), &|instance| {
call_and_assert!(instance, $function( $( $inputs ),* ) -> $output, ( $( $arguments ),* ) == $expected_value);
});
}
};
}
}
macro_rules! tests_for_backend {
($backend:expr) => {
test!($backend, test_fn, function_fn(i32) -> i32, (1) == Ok(2));
test!($backend, test_closure, function_closure(i32) -> i32, (1) == Ok(2));
test!($backend, test_fn_dynamic, function_fn_dynamic(i32) -> i32, (1) == Ok(2));
test!($backend, test_fn_dynamic_panic, function_fn_dynamic_panic(i32) -> i32, (1) == Err(RuntimeError(Box::new("test"))));
wasmer_backends! {
use super::*;
test!( test_fn, function_fn(i32) -> i32, (1) == Ok(2));
test!( test_closure, function_closure(i32) -> i32, (1) == Ok(2));
test!( test_fn_dynamic, function_fn_dynamic(i32) -> i32, (1) == Ok(2));
test!( test_fn_dynamic_panic, function_fn_dynamic_panic(i32) -> i32, (1) == Err(RuntimeError(Box::new("test"))));
test!(
$backend,
test_closure_dynamic_0,
function_closure_dynamic_0(()) -> (),
() == Ok(())
);
test!(
$backend,
test_closure_dynamic_1,
function_closure_dynamic_1(i32) -> i32,
(1) == Ok(1 + shift + SHIFT)
);
test!(
$backend,
test_closure_dynamic_2,
function_closure_dynamic_2(i32, i64) -> i64,
(1, 2) == Ok(1 + 2 + shift as i64 + SHIFT as i64)
);
test!(
$backend,
test_closure_dynamic_3,
function_closure_dynamic_3(i32, i64, f32) -> f32,
(1, 2, 3.) == Ok(1. + 2. + 3. + shift as f32 + SHIFT as f32)
);
test!(
$backend,
test_closure_dynamic_4,
function_closure_dynamic_4(i32, i64, f32, f64) -> f64,
(1, 2, 3., 4.) == Ok(1. + 2. + 3. + 4. + shift as f64 + SHIFT as f64)
);
test!(
$backend,
test_closure_with_env,
function_closure_with_env(i32) -> i32,
(1) == Ok(2 + shift + SHIFT)
);
test!($backend, test_fn_with_vmctx, function_fn_with_vmctx(i32) -> i32, (1) == Ok(2 + SHIFT));
test!( test_fn_with_vmctx, function_fn_with_vmctx(i32) -> i32, (1) == Ok(2 + SHIFT));
test!(
$backend,
test_closure_with_vmctx,
function_closure_with_vmctx(i32) -> i32,
(1) == Ok(2 + SHIFT)
);
test!(
$backend,
test_closure_with_vmctx_and_env,
function_closure_with_vmctx_and_env(i32) -> i32,
(1) == Ok(2 + shift + SHIFT)
);
test!(
$backend,
test_fn_trap,
function_fn_trap(i32) -> i32,
(1) == Err(RuntimeError(Box::new(format!("foo {}", 2))))
);
test!(
$backend,
test_closure_trap,
function_closure_trap(i32) -> i32,
(1) == Err(RuntimeError(Box::new(format!("bar {}", 2))))
);
test!(
$backend,
test_fn_trap_with_vmctx,
function_fn_trap_with_vmctx(i32) -> i32,
(1) == Err(RuntimeError(Box::new(format!("baz {}", 2 + SHIFT))))
);
test!(
$backend,
test_closure_trap_with_vmctx,
function_closure_trap_with_vmctx(i32) -> i32,
(1) == Err(RuntimeError(Box::new(format!("qux {}", 2 + SHIFT))))
);
test!(
$backend,
test_closure_trap_with_vmctx_and_env,
function_closure_trap_with_vmctx_and_env(i32) -> i32,
(1) == Err(RuntimeError(Box::new(format!("! {}", 2 + shift + SHIFT))))
@ -485,29 +489,6 @@ macro_rules! tests_for_backend {
#[test]
fn runtime_core_new_api() {
runtime_core_new_api_works($backend)
runtime_core_new_api_works(get_backend())
}
}
}
#[cfg(feature = "backend-singlepass")]
#[cfg(test)]
mod singlepass {
use super::*;
tests_for_backend!(Backend::Singlepass);
}
#[cfg(feature = "backend-cranelift")]
#[cfg(test)]
mod cranelift {
use super::*;
tests_for_backend!(Backend::Cranelift);
}
#[cfg(feature = "backend-llvm")]
#[cfg(test)]
mod llvm {
use super::*;
tests_for_backend!(Backend::LLVM);
}

View File

@ -9,26 +9,26 @@
unreachable_patterns
)]
use wabt::wat2wasm;
use wasmer::compiler::Compiler;
use wasmer_llvm_backend::LLVMCompiler;
mod llvm {
use wabt::wat2wasm;
use wasmer::compiler::Compiler;
use wasmer::compiler::CompilerConfig;
use wasmer::compiler::{compile_with, compile_with_config_with, BackendCompilerConfig};
use wasmer::imports;
use wasmer_llvm_backend::LLVMCompiler;
use wasmer_llvm_backend::{InkwellModule, LLVMBackendConfig, LLVMCallbacks};
pub fn get_compiler() -> impl Compiler {
use std::cell::RefCell;
use std::rc::Rc;
pub fn get_compiler() -> impl Compiler {
LLVMCompiler::new()
}
}
use wasmer::compiler::CompilerConfig;
use wasmer::compiler::{compile_with, compile_with_config_with, BackendCompilerConfig};
use wasmer::imports;
use wasmer_llvm_backend::{InkwellModule, LLVMBackendConfig, LLVMCallbacks};
use std::cell::RefCell;
use std::rc::Rc;
#[test]
fn crash_return_with_float_on_stack() {
#[test]
fn crash_return_with_float_on_stack() {
const MODULE: &str = r#"
(module
(module
(type (func))
(type (func (param f64) (result f64)))
(func $_start (type 0))
@ -37,25 +37,25 @@ fn crash_return_with_float_on_stack() {
f64.const 0x0p+0
f64.mul
return))
"#;
"#;
let wasm_binary = wat2wasm(MODULE.as_bytes()).expect("WAST not valid or malformed");
let module = compile_with(&wasm_binary, &get_compiler()).unwrap();
module.instantiate(&imports! {}).unwrap();
}
}
#[derive(Debug, Default)]
pub struct RecordPreOptIR {
#[derive(Debug, Default)]
pub struct RecordPreOptIR {
preopt_ir: String,
}
}
impl LLVMCallbacks for RecordPreOptIR {
impl LLVMCallbacks for RecordPreOptIR {
fn preopt_ir_callback(&mut self, module: &InkwellModule) {
self.preopt_ir = module.print_to_string().to_string();
}
}
}
#[test]
fn crash_select_with_mismatched_pending() {
#[test]
fn crash_select_with_mismatched_pending() {
const WAT: &str = r#"
(module
(func (param f64) (result f64)
@ -65,7 +65,7 @@ fn crash_select_with_mismatched_pending() {
f64.const 0x0p+0
i32.const 0
select))
"#;
"#;
let record_pre_opt_ir = Rc::new(RefCell::new(RecordPreOptIR::default()));
let compiler_config = CompilerConfig {
backend_specific_config: Some(BackendCompilerConfig(Box::new(LLVMBackendConfig {
@ -74,7 +74,8 @@ fn crash_select_with_mismatched_pending() {
..Default::default()
};
let wasm_binary = wat2wasm(WAT.as_bytes()).expect("WAST not valid or malformed");
let module = compile_with_config_with(&wasm_binary, compiler_config, &get_compiler()).unwrap();
let module =
compile_with_config_with(&wasm_binary, compiler_config, &get_compiler()).unwrap();
module.instantiate(&imports! {}).unwrap();
const LLVM: &str = r#"
%s3 = fadd double 0.000000e+00, %s2
@ -83,5 +84,7 @@ fn crash_select_with_mismatched_pending() {
%s5 = select i1 false, double %2, double 0.000000e+00
br label %return
"#;
// println!("Compiler IR {}", record_pre_opt_ir.borrow().preopt_ir);
assert!(&record_pre_opt_ir.borrow().preopt_ir.contains(LLVM));
}
}

View File

@ -1,64 +0,0 @@
use wabt::wat2wasm;
use wasmer::compiler::compile;
use wasmer::{
import::ImportObject,
wasm::{Instance, Value},
DynFunc,
};
#[test]
fn override_works() {
let instance = create_module_1();
let result = instance
.exports
.get::<DynFunc>("call-overwritten-element")
.unwrap()
.call(&[])
.unwrap();
assert_eq!(result, vec![Value::I32(66)]);
println!("result: {:?}", result);
}
fn create_module_1() -> Instance {
let module_str = r#"(module
(type (;0;) (func (result i32)))
(import "spectest" "table" (table (;0;) 10 anyfunc))
(func (;0;) (type 0) (result i32)
i32.const 65)
(func (;1;) (type 0) (result i32)
i32.const 66)
(func (;2;) (type 0) (result i32)
i32.const 9
call_indirect (type 0))
(export "call-overwritten-element" (func 2))
(elem (;0;) (i32.const 9) 0)
(elem (;1;) (i32.const 9) 1))
"#;
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
let module = compile(&wasm_binary[..]).expect("WASM can't be compiled");
module
.instantiate(&generate_imports())
.expect("WASM can't be instantiated")
}
static IMPORT_MODULE: &str = r#"
(module
(type $t0 (func (param i32)))
(type $t1 (func))
(func $print_i32 (export "print_i32") (type $t0) (param $lhs i32))
(func $print (export "print") (type $t1))
(table $table (export "table") 10 20 anyfunc)
(memory $memory (export "memory") 1 2)
(global $global_i32 (export "global_i32") i32 (i32.const 666)))
"#;
pub fn generate_imports() -> ImportObject {
let wasm_binary = wat2wasm(IMPORT_MODULE.as_bytes()).expect("WAST not valid or malformed");
let module = compile(&wasm_binary[..]).expect("WASM can't be compiled");
let instance = module
.instantiate(&ImportObject::new())
.expect("WASM can't be instantiated");
let mut imports = ImportObject::new();
imports.register("spectest", instance);
imports
}

View File

@ -1,14 +1,6 @@
mod utils;
use std::path::Path;
#[cfg(not(any(
feature = "backend-llvm",
feature = "backend-cranelift",
feature = "backend-singlepass"
)))]
compile_error!("No compiler backend detected: please specify at least one compiler backend!");
use wasmer_wast::Wast;
// The generated tests (from build.rs) look like:

View File

@ -1,39 +0,0 @@
#[cfg(test)]
mod tests {
use wabt::wat2wasm;
use wasmer::error::{CallError, ExceptionCode, RuntimeError};
use wasmer::import::ImportObject;
// The semantics of stack overflow are documented at:
// https://webassembly.org/docs/semantics/#stack-overflow
#[test]
#[ignore]
fn test_stack_overflow() {
let module_str = r#"(module
(type (;0;) (func))
(func (;0;) (type 0)
i32.const 0
call_indirect (type 0))
(table (;0;) 1 anyfunc)
(export "stack-overflow" (func 0))
(elem (;0;) (i32.const 0) 0))
"#;
let wasm_binary = wat2wasm(module_str.as_bytes()).expect("WAST not valid or malformed");
let module = wasmer::compiler::compile(&wasm_binary[..]).expect("WASM can't be compiled");
let instance = module
.instantiate(&ImportObject::new())
.expect("WASM can't be instantiated");
let result = instance.call("stack-overflow", &[]);
match result {
Err(err) => match err {
CallError::Runtime(RuntimeError(e)) => {
e.downcast::<ExceptionCode>()
.expect("expecting exception code");
}
_ => unimplemented!(),
},
Ok(_) => panic!("should fail with error due to stack overflow"),
}
}
}

View File

@ -7,6 +7,7 @@ use wasmer::compiler::Backend;
///
/// This function errors if the backend doesn't exist or
/// is not enabled.
#[allow(dead_code)]
pub fn get_backend_from_str(backend: &str) -> Result<Backend> {
match backend {
#[cfg(feature = "backend-singlepass")]

47
tests/utils/macros.rs Normal file
View File

@ -0,0 +1,47 @@
#[macro_export]
macro_rules! wasmer_backends {
{ $($code:item)* } => {
#[cfg(feature = "backend-singlepass")]
#[cfg(test)]
mod singlepass {
use wasmer::compiler::{Backend, Compiler, compiler_for_backend};
#[allow(dead_code)]
fn get_backend() -> Backend {
Backend::Singlepass
}
#[allow(dead_code)]
fn get_compiler() -> Box<dyn Compiler> {
compiler_for_backend(get_backend()).expect("Backend must have a compiler")
}
$($code)*
}
#[cfg(feature = "backend-cranelift")]
#[cfg(test)]
mod cranelift {
use wasmer::compiler::{Backend, Compiler, compiler_for_backend};
#[allow(dead_code)]
fn get_backend() -> Backend {
Backend::Cranelift
}
#[allow(dead_code)]
fn get_compiler() -> Box<dyn Compiler> {
compiler_for_backend(get_backend()).expect("Backend must have a compiler")
}
$($code)*
}
#[cfg(feature = "backend-llvm")]
#[cfg(test)]
mod llvm {
use wasmer::compiler::{Backend, Compiler, compiler_for_backend};
#[allow(dead_code)]
fn get_backend() -> Backend {
Backend::LLVM
}
#[allow(dead_code)]
fn get_compiler() -> Box<dyn Compiler> {
compiler_for_backend(get_backend()).expect("Backend must have a compiler")
}
$($code)*
}
};
}

View File

@ -1,6 +1,8 @@
mod backend;
mod file_descriptor;
mod stdio;
#[macro_use]
mod macros;
pub use backend::get_backend_from_str;
pub use stdio::StdioCapturer;

View File

@ -1,3 +1,5 @@
#![allow(dead_code)]
use super::file_descriptor::FileDescriptor;
use libc;
use std::io;

View File

@ -1,14 +1,18 @@
#![cfg(test)]
use wasmer::{compiler::compile, vm::Ctx, Func};
use wasmer_wasi::{state::*, *};
use std::ffi::c_void;
#[macro_use]
mod utils;
// TODO: fix this test!
#[ignore]
#[cfg(not(feature = "singlepass"))]
#[test]
fn serializing_works() {
wasmer_backends! {
use wasmer::{compiler::compile_with, vm::Ctx, Func};
use wasmer_wasi::{state::*, *};
use std::ffi::c_void;
// TODO: fix this test!
#[ignore]
#[cfg(not(feature = "singlepass"))]
#[test]
fn serializing_works() {
let args = vec![
b"program_name".into_iter().cloned().collect(),
b"arg1".into_iter().cloned().collect(),
@ -18,7 +22,7 @@ fn serializing_works() {
b"GOROOT=$HOME/.cargo/bin".into_iter().cloned().collect(),
];
let wasm_binary = include_bytes!("wasi_test_resources/unstable/fd_read.wasm");
let module = compile(&wasm_binary[..])
let module = compile_with(&wasm_binary[..], &*get_compiler())
.map_err(|e| format!("Can't compile module: {:?}", e))
.unwrap();
@ -57,9 +61,10 @@ fn serializing_works() {
let second_entry: Func<(), i32> = instance.exports.get("second_entry").unwrap();
let result = second_entry.call().unwrap();
assert_eq!(result, true as i32);
}
}
#[allow(clippy::mut_from_ref)]
pub(crate) fn get_wasi_state(ctx: &Ctx) -> &mut WasiState {
#[allow(clippy::mut_from_ref)]
pub(crate) fn get_wasi_state(ctx: &Ctx) -> &mut WasiState {
unsafe { state::get_wasi_state(&mut *(ctx as *const Ctx as *mut Ctx)) }
}
}