wiggle: expand test suite
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.
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -3923,6 +3923,7 @@ dependencies = [
|
|||||||
"bitflags",
|
"bitflags",
|
||||||
"proptest",
|
"proptest",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"wasmtime",
|
"wasmtime",
|
||||||
"wiggle-macro",
|
"wiggle-macro",
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ maintenance = { status = "actively-developed" }
|
|||||||
wiggle-test = { path = "test-helpers" }
|
wiggle-test = { path = "test-helpers" }
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
proptest = "1.0.0"
|
proptest = "1.0.0"
|
||||||
|
tokio = { version = "1", features = ["rt-multi-thread","time", "macros"] }
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "wasmtime_async"
|
name = "wasmtime_async"
|
||||||
|
|||||||
@@ -933,7 +933,10 @@ impl From<GuestError> for Trap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_in_dummy_executor<F: std::future::Future>(future: F) -> Result<F::Output, Trap> {
|
#[cfg(feature = "wasmtime")]
|
||||||
|
pub fn run_in_dummy_executor<F: std::future::Future>(
|
||||||
|
future: F,
|
||||||
|
) -> Result<F::Output, wasmtime_crate::Trap> {
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
|
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
|
||||||
|
|
||||||
@@ -943,7 +946,7 @@ pub fn run_in_dummy_executor<F: std::future::Future>(future: F) -> Result<F::Out
|
|||||||
match f.as_mut().poll(&mut cx) {
|
match f.as_mut().poll(&mut cx) {
|
||||||
Poll::Ready(val) => return Ok(val),
|
Poll::Ready(val) => return Ok(val),
|
||||||
Poll::Pending =>
|
Poll::Pending =>
|
||||||
return Err(Trap::String("Cannot wait on pending future: must enable wiggle \"async\" future and execute on an async Store".to_owned()))
|
return Err(wasmtime_crate::Trap::new("Cannot wait on pending future: must enable wiggle \"async\" future and execute on an async Store"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dummy_waker() -> Waker {
|
fn dummy_waker() -> Waker {
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
use std::future::Future;
|
|
||||||
use std::pin::Pin;
|
|
||||||
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
|
|
||||||
use wasmtime::{Config, Engine, Linker, Module, Store};
|
use wasmtime::{Config, Engine, Linker, Module, Store};
|
||||||
|
|
||||||
wiggle::from_witx!({
|
wiggle::from_witx!({
|
||||||
@@ -27,23 +24,30 @@ impl atoms::Atoms for Ctx {
|
|||||||
&mut self,
|
&mut self,
|
||||||
an_int: u32,
|
an_int: u32,
|
||||||
) -> Result<types::AliasToFloat, types::Errno> {
|
) -> Result<types::AliasToFloat, types::Errno> {
|
||||||
|
// Do something inside this test that is Pending for a trivial amount of time,
|
||||||
|
// to make sure we are hooked up to the tokio executor properly.
|
||||||
|
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
|
||||||
Ok((an_int as f32) * 2.0)
|
Ok((an_int as f32) * 2.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[tokio::test]
|
||||||
fn test_sync_host_func() {
|
async fn test_sync_host_func() {
|
||||||
let mut store = async_store();
|
let mut store = async_store();
|
||||||
let mut linker = Linker::new(store.engine());
|
let mut linker = Linker::new(store.engine());
|
||||||
atoms::add_to_linker(&mut linker, |cx| cx).unwrap();
|
atoms::add_to_linker(&mut linker, |cx| cx).unwrap();
|
||||||
let shim_mod = shim_module(linker.engine());
|
let shim_mod = shim_module(linker.engine());
|
||||||
let shim_inst = run(linker.instantiate_async(&mut store, &shim_mod)).unwrap();
|
let shim_inst = linker
|
||||||
|
.instantiate_async(&mut store, &shim_mod)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let results = run(shim_inst
|
let results = shim_inst
|
||||||
.get_func(&mut store, "int_float_args_shim")
|
.get_func(&mut store, "int_float_args_shim")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.call_async(&mut store, &[0i32.into(), 123.45f32.into()]))
|
.call_async(&mut store, &[0i32.into(), 123.45f32.into()])
|
||||||
.unwrap();
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(results.len(), 1, "one return value");
|
assert_eq!(results.len(), 1, "one return value");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -53,23 +57,27 @@ fn test_sync_host_func() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[tokio::test]
|
||||||
fn test_async_host_func() {
|
async fn test_async_host_func() {
|
||||||
let mut store = async_store();
|
let mut store = async_store();
|
||||||
let mut linker = Linker::new(store.engine());
|
let mut linker = Linker::new(store.engine());
|
||||||
atoms::add_to_linker(&mut linker, |cx| cx).unwrap();
|
atoms::add_to_linker(&mut linker, |cx| cx).unwrap();
|
||||||
|
|
||||||
let shim_mod = shim_module(linker.engine());
|
let shim_mod = shim_module(linker.engine());
|
||||||
let shim_inst = run(linker.instantiate_async(&mut store, &shim_mod)).unwrap();
|
let shim_inst = linker
|
||||||
|
.instantiate_async(&mut store, &shim_mod)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let input: i32 = 123;
|
let input: i32 = 123;
|
||||||
let result_location: i32 = 0;
|
let result_location: i32 = 0;
|
||||||
|
|
||||||
let results = run(shim_inst
|
let results = shim_inst
|
||||||
.get_func(&mut store, "double_int_return_float_shim")
|
.get_func(&mut store, "double_int_return_float_shim")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.call_async(&mut store, &[input.into(), result_location.into()]))
|
.call_async(&mut store, &[input.into(), result_location.into()])
|
||||||
.unwrap();
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(results.len(), 1, "one return value");
|
assert_eq!(results.len(), 1, "one return value");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -87,40 +95,6 @@ fn test_async_host_func() {
|
|||||||
assert_eq!((input * 2) as f32, result);
|
assert_eq!((input * 2) as f32, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run<F: Future>(future: F) -> F::Output {
|
|
||||||
let mut f = Pin::from(Box::new(future));
|
|
||||||
let waker = dummy_waker();
|
|
||||||
let mut cx = Context::from_waker(&waker);
|
|
||||||
loop {
|
|
||||||
match f.as_mut().poll(&mut cx) {
|
|
||||||
Poll::Ready(val) => break val,
|
|
||||||
Poll::Pending => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dummy_waker() -> Waker {
|
|
||||||
return unsafe { Waker::from_raw(clone(5 as *const _)) };
|
|
||||||
|
|
||||||
unsafe fn clone(ptr: *const ()) -> RawWaker {
|
|
||||||
assert_eq!(ptr as usize, 5);
|
|
||||||
const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
|
|
||||||
RawWaker::new(ptr, &VTABLE)
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn wake(ptr: *const ()) {
|
|
||||||
assert_eq!(ptr as usize, 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn wake_by_ref(ptr: *const ()) {
|
|
||||||
assert_eq!(ptr as usize, 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn drop(ptr: *const ()) {
|
|
||||||
assert_eq!(ptr as usize, 5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn async_store() -> Store<Ctx> {
|
fn async_store() -> Store<Ctx> {
|
||||||
Store::new(
|
Store::new(
|
||||||
&Engine::new(Config::new().async_support(true)).unwrap(),
|
&Engine::new(Config::new().async_support(true)).unwrap(),
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ impl wiggle::GuestErrorType for types::Errno {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TRIGGER_PENDING: u32 = 0;
|
||||||
|
|
||||||
#[wiggle::async_trait]
|
#[wiggle::async_trait]
|
||||||
impl atoms::Atoms for Ctx {
|
impl atoms::Atoms for Ctx {
|
||||||
fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> {
|
fn int_float_args(&mut self, an_int: u32, an_float: f32) -> Result<(), types::Errno> {
|
||||||
@@ -24,6 +26,22 @@ impl atoms::Atoms for Ctx {
|
|||||||
&mut self,
|
&mut self,
|
||||||
an_int: u32,
|
an_int: u32,
|
||||||
) -> Result<types::AliasToFloat, types::Errno> {
|
) -> 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)
|
Ok((an_int as f32) * 2.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,6 +104,32 @@ fn test_async_host_func() {
|
|||||||
assert_eq!((input * 2) as f32, result);
|
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> {
|
fn store(engine: &Engine) -> Store<Ctx> {
|
||||||
Store::new(engine, Ctx)
|
Store::new(engine, Ctx)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user