sync test: show the dummy executor will trap (rather than panic) when a future inside it pends. async test: show that the executor is hooked up to a future that pends for a trivial amount of time. this adds tokio to the dev-dependencies of wiggle, it shouldn't end up increasing the build burden for the project as a whole since its already a dev-dependency.
168 lines
5.1 KiB
Rust
168 lines
5.1 KiB
Rust
use wasmtime::{Engine, Linker, Module, Store};
|
|
|
|
wiggle::from_witx!({
|
|
witx: ["$CARGO_MANIFEST_DIR/tests/atoms.witx"],
|
|
block_on: {
|
|
atoms::double_int_return_float
|
|
}
|
|
});
|
|
|
|
pub struct Ctx;
|
|
impl wiggle::GuestErrorType for types::Errno {
|
|
fn success() -> Self {
|
|
types::Errno::Ok
|
|
}
|
|
}
|
|
|
|
const TRIGGER_PENDING: u32 = 0;
|
|
|
|
#[wiggle::async_trait]
|
|
impl atoms::Atoms for Ctx {
|
|
fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> {
|
|
println!("INT FLOAT ARGS: {} {}", an_int, an_float);
|
|
Ok(())
|
|
}
|
|
async fn double_int_return_float(
|
|
&mut self,
|
|
an_int: u32,
|
|
) -> Result<types::AliasToFloat, types::Errno> {
|
|
if an_int == TRIGGER_PENDING {
|
|
// Define a Future that is pending forever. This is `futures::future::pending()`
|
|
// without incurring the dep.
|
|
use std::future::Future;
|
|
use std::pin::Pin;
|
|
use std::task::{Context, Poll};
|
|
struct Pending;
|
|
impl Future for Pending {
|
|
type Output = ();
|
|
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
Poll::Pending
|
|
}
|
|
}
|
|
// This await will pend, which should cause the dummy executor to Trap.
|
|
Pending.await;
|
|
}
|
|
Ok((an_int as f32) * 2.0)
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_sync_host_func() {
|
|
let engine = Engine::default();
|
|
let mut linker = Linker::new(&engine);
|
|
atoms::add_to_linker(&mut linker, |cx| cx).unwrap();
|
|
let mut store = store(&engine);
|
|
let shim_mod = shim_module(&engine);
|
|
let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap();
|
|
|
|
let results = shim_inst
|
|
.get_func(&mut store, "int_float_args_shim")
|
|
.unwrap()
|
|
.call(&mut store, &[0i32.into(), 123.45f32.into()])
|
|
.unwrap();
|
|
|
|
assert_eq!(results.len(), 1, "one return value");
|
|
assert_eq!(
|
|
results[0].unwrap_i32(),
|
|
types::Errno::Ok as i32,
|
|
"int_float_args errno"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_async_host_func() {
|
|
let engine = Engine::default();
|
|
let mut linker = Linker::new(&engine);
|
|
atoms::add_to_linker(&mut linker, |cx| cx).unwrap();
|
|
let mut store = store(&engine);
|
|
|
|
let shim_mod = shim_module(&engine);
|
|
let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap();
|
|
|
|
let input: i32 = 123;
|
|
let result_location: i32 = 0;
|
|
|
|
let results = shim_inst
|
|
.get_func(&mut store, "double_int_return_float_shim")
|
|
.unwrap()
|
|
.call(&mut store, &[input.into(), result_location.into()])
|
|
.unwrap();
|
|
|
|
assert_eq!(results.len(), 1, "one return value");
|
|
assert_eq!(
|
|
results[0].unwrap_i32(),
|
|
types::Errno::Ok as i32,
|
|
"double_int_return_float errno"
|
|
);
|
|
|
|
// The actual result is in memory:
|
|
let mem = shim_inst.get_memory(&mut store, "memory").unwrap();
|
|
let mut result_bytes: [u8; 4] = [0, 0, 0, 0];
|
|
mem.read(&store, result_location as usize, &mut result_bytes)
|
|
.unwrap();
|
|
let result = f32::from_le_bytes(result_bytes);
|
|
assert_eq!((input * 2) as f32, result);
|
|
}
|
|
|
|
#[test]
|
|
fn test_async_host_func_pending() {
|
|
let engine = Engine::default();
|
|
let mut linker = Linker::new(&engine);
|
|
atoms::add_to_linker(&mut linker, |cx| cx).unwrap();
|
|
let mut store = store(&engine);
|
|
|
|
let shim_mod = shim_module(&engine);
|
|
let shim_inst = linker.instantiate(&mut store, &shim_mod).unwrap();
|
|
|
|
let result_location: i32 = 0;
|
|
|
|
// This input triggers the host func pending forever
|
|
let input: i32 = TRIGGER_PENDING as i32;
|
|
let trap = shim_inst
|
|
.get_func(&mut store, "double_int_return_float_shim")
|
|
.unwrap()
|
|
.call(&mut store, &[input.into(), result_location.into()])
|
|
.unwrap_err();
|
|
assert!(
|
|
format!("{}", trap).contains("Cannot wait on pending future"),
|
|
"expected get a pending future Trap from dummy executor, got: {}",
|
|
trap
|
|
);
|
|
}
|
|
|
|
fn store(engine: &Engine) -> Store<Ctx> {
|
|
Store::new(engine, Ctx)
|
|
}
|
|
|
|
// Wiggle expects the caller to have an exported memory. Wasmtime can only
|
|
// provide this if the caller is a WebAssembly module, so we need to write
|
|
// a shim module:
|
|
fn shim_module(engine: &Engine) -> Module {
|
|
Module::new(
|
|
engine,
|
|
r#"
|
|
(module
|
|
(import "atoms" "int_float_args" (func $int_float_args (param i32 f32) (result i32)))
|
|
(import "atoms" "double_int_return_float" (func $double_int_return_float (param i32 i32) (result i32)))
|
|
|
|
(memory 1)
|
|
(export "memory" (memory 0))
|
|
|
|
(func $int_float_args_shim (param i32 f32) (result i32)
|
|
local.get 0
|
|
local.get 1
|
|
call $int_float_args
|
|
)
|
|
(func $double_int_return_float_shim (param i32 i32) (result i32)
|
|
local.get 0
|
|
local.get 1
|
|
call $double_int_return_float
|
|
)
|
|
(export "int_float_args_shim" (func $int_float_args_shim))
|
|
(export "double_int_return_float_shim" (func $double_int_return_float_shim))
|
|
)
|
|
"#,
|
|
)
|
|
.unwrap()
|
|
}
|