Path: blob/main/crates/misc/component-async-tests/tests/scenario/round_trip_many.rs
1693 views
use std::iter;1use std::sync::{2Arc, Mutex,3atomic::{AtomicU32, Ordering::Relaxed},4};5use std::time::Duration;67use super::util::{config, make_component};8use anyhow::{Result, anyhow};9use component_async_tests::Ctx;10use component_async_tests::util::sleep;11use futures::{12FutureExt,13stream::{FuturesUnordered, TryStreamExt},14};15use wasmtime::component::{Linker, ResourceTable, Val};16use wasmtime::{Engine, Store};17use wasmtime_wasi::WasiCtxBuilder;1819#[tokio::test]20pub async fn async_round_trip_many_stackless() -> Result<()> {21test_round_trip_many_uncomposed(22test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,23)24.await25}2627#[tokio::test]28pub async fn async_round_trip_many_stackful() -> Result<()> {29test_round_trip_many_uncomposed(30test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,31)32.await33}3435#[tokio::test]36pub async fn async_round_trip_many_synchronous() -> Result<()> {37test_round_trip_many_uncomposed(38test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,39)40.await41}4243#[tokio::test]44pub async fn async_round_trip_many_wait() -> Result<()> {45test_round_trip_many_uncomposed(test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT)46.await47}4849#[tokio::test]50async fn async_round_trip_many_stackless_plus_stackless() -> Result<()> {51test_round_trip_many_composed(52test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,53test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,54)55.await56}5758#[tokio::test]59async fn async_round_trip_many_synchronous_plus_stackless() -> Result<()> {60test_round_trip_many_composed(61test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,62test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,63)64.await65}6667#[tokio::test]68async fn async_round_trip_many_stackless_plus_synchronous() -> Result<()> {69test_round_trip_many_composed(70test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,71test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,72)73.await74}7576#[tokio::test]77async fn async_round_trip_many_synchronous_plus_synchronous() -> Result<()> {78test_round_trip_many_composed(79test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,80test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,81)82.await83}8485#[tokio::test]86async fn async_round_trip_many_wait_plus_wait() -> Result<()> {87test_round_trip_many_composed(88test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,89test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,90)91.await92}9394#[tokio::test]95async fn async_round_trip_many_synchronous_plus_wait() -> Result<()> {96test_round_trip_many_composed(97test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,98test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,99)100.await101}102103#[tokio::test]104async fn async_round_trip_many_wait_plus_synchronous() -> Result<()> {105test_round_trip_many_composed(106test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,107test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,108)109.await110}111112#[tokio::test]113async fn async_round_trip_many_stackless_plus_wait() -> Result<()> {114test_round_trip_many_composed(115test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,116test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,117)118.await119}120121#[tokio::test]122async fn async_round_trip_many_wait_plus_stackless() -> Result<()> {123test_round_trip_many_composed(124test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,125test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,126)127.await128}129130#[tokio::test]131async fn async_round_trip_many_stackful_plus_stackful() -> Result<()> {132test_round_trip_many_composed(133test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,134test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,135)136.await137}138139#[tokio::test]140async fn async_round_trip_many_stackful_plus_stackless() -> Result<()> {141test_round_trip_many_composed(142test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,143test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,144)145.await146}147148#[tokio::test]149async fn async_round_trip_many_stackless_plus_stackful() -> Result<()> {150test_round_trip_many_composed(151test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,152test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,153)154.await155}156157#[tokio::test]158async fn async_round_trip_many_synchronous_plus_stackful() -> Result<()> {159test_round_trip_many_composed(160test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,161test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,162)163.await164}165166#[tokio::test]167async fn async_round_trip_many_stackful_plus_synchronous() -> Result<()> {168test_round_trip_many_composed(169test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,170test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,171)172.await173}174175async fn test_round_trip_many_uncomposed(component: &str) -> Result<()> {176test_round_trip_many(177&[component],178&[179(180"hello, world!",181"hello, world! - entered guest - entered host - exited host - exited guest",182),183(184"¡hola, mundo!",185"¡hola, mundo! - entered guest - entered host - exited host - exited guest",186),187(188"hi y'all!",189"hi y'all! - entered guest - entered host - exited host - exited guest",190),191],192)193.await194}195196async fn test_round_trip_many(197components: &[&str],198inputs_and_outputs: &[(&str, &str)],199) -> Result<()> {200use component_async_tests::round_trip_many::bindings::exports::local::local::many;201202let engine = Engine::new(&config())?;203204let make_store = || {205Store::new(206&engine,207Ctx {208wasi: WasiCtxBuilder::new().inherit_stdio().build(),209table: ResourceTable::default(),210continue_: false,211wakers: Arc::new(Mutex::new(None)),212},213)214};215216let component = make_component(&engine, components).await?;217218let b = 42;219let c = vec![42u8; 42];220let d = (4242, 424242424242);221let e = many::Stuff {222a: vec![42i32; 42],223b: true,224c: 424242,225};226let f = Some(e.clone());227let g = Err(());228229// On miri, we only use one call style per test since they take so long to230// run. On non-miri, we use every call style for each test.231static CALL_STYLE_COUNTER: AtomicU32 = AtomicU32::new(0);232let call_style = CALL_STYLE_COUNTER.fetch_add(1, Relaxed) % 4;233234// First, test the `wasmtime-wit-bindgen` static API:235{236let mut linker = Linker::new(&engine);237238wasmtime_wasi::p2::add_to_linker_async(&mut linker)?;239component_async_tests::round_trip_many::bindings::local::local::many::add_to_linker::<240_,241Ctx,242>(&mut linker, |ctx| ctx)?;243244let mut store = make_store();245246let instance = linker.instantiate_async(&mut store, &component).await?;247let round_trip_many = component_async_tests::round_trip_many::bindings::RoundTripMany::new(248&mut store, &instance,249)?;250251if call_style == 0 {252instance253.run_concurrent(&mut store, {254let c = c.clone();255let e = e.clone();256let f = f.clone();257let g = g.clone();258let inputs_and_outputs = inputs_and_outputs259.iter()260.map(|(a, b)| ((*a).to_owned(), (*b).to_owned()))261.collect::<Vec<_>>();262async move |accessor| {263// Start concurrent calls and then join them all:264let mut futures = FuturesUnordered::new();265for (input, output) in inputs_and_outputs {266futures.push(267round_trip_many268.local_local_many()269.call_foo(270accessor,271input,272b,273c.clone(),274d,275e.clone(),276f.clone(),277g.clone(),278)279.map(move |v| v.map(move |v| (v, output))),280);281}282283while let Some((actual, expected)) = futures.try_next().await? {284assert_eq!(285(expected, b, c.clone(), d, e.clone(), f.clone(), g.clone()),286actual287);288}289290anyhow::Ok(())291}292})293.await??;294295instance.assert_concurrent_state_empty(&mut store);296}297298if call_style == 1 {299// Now do it again using `TypedFunc::call_async`-based bindings:300let e = component_async_tests::round_trip_many::non_concurrent_export_bindings::exports::local::local::many::Stuff {301a: vec![42i32; 42],302b: true,303c: 424242,304};305let f = Some(e.clone());306let g = Err(());307308let round_trip_many = component_async_tests::round_trip_many::non_concurrent_export_bindings::RoundTripMany::instantiate_async(309&mut store, &component, &linker,310)311.await?;312313for (input, expected) in inputs_and_outputs {314assert_eq!(315(316(*expected).to_owned(),317b,318c.clone(),319d,320e.clone(),321f.clone(),322g.clone()323),324round_trip_many325.local_local_many()326.call_foo(&mut store, input, b, &c, d, &e, f.as_ref(), Err(()))327.await?328);329}330331instance.assert_concurrent_state_empty(&mut store);332}333}334335// Now do it again using the dynamic API (except for WASI, where we stick with the static API):336{337let mut linker = Linker::new(&engine);338339wasmtime_wasi::p2::add_to_linker_async(&mut linker)?;340linker341.root()342.instance("local:local/many")?343.func_new_concurrent("[async]foo", |_, params, results| {344Box::pin(async move {345sleep(Duration::from_millis(10)).await;346let mut params = params.into_iter();347let Some(Val::String(s)) = params.next() else {348unreachable!()349};350results[0] = Val::Tuple(351iter::once(Val::String(format!("{s} - entered host - exited host")))352.chain(params.cloned())353.collect(),354);355Ok(())356})357})?;358359let mut store = make_store();360361let instance = linker.instantiate_async(&mut store, &component).await?;362let baz_instance = instance363.get_export_index(&mut store, None, "local:local/many")364.ok_or_else(|| anyhow!("can't find `local:local/many` in instance"))?;365let foo_function = instance366.get_export_index(&mut store, Some(&baz_instance), "[async]foo")367.ok_or_else(|| anyhow!("can't find `foo` in instance"))?;368let foo_function = instance369.get_func(&mut store, foo_function)370.ok_or_else(|| anyhow!("can't find `foo` in instance"))?;371372let make = |input: &str| {373let stuff = Val::Record(vec![374(375"a".into(),376Val::List(e.a.iter().map(|v| Val::S32(*v)).collect()),377),378("b".into(), Val::Bool(e.b)),379("c".into(), Val::U64(e.c)),380]);381vec![382Val::String(input.to_owned()),383Val::U32(b),384Val::List(c.iter().map(|v| Val::U8(*v)).collect()),385Val::Tuple(vec![Val::U64(d.0), Val::U64(d.1)]),386stuff.clone(),387Val::Option(Some(Box::new(stuff))),388Val::Result(Err(None)),389]390};391392if call_style == 2 {393instance394.run_concurrent(&mut store, async |store| -> wasmtime::Result<_> {395// Start three concurrent calls and then join them all:396let mut futures = FuturesUnordered::new();397for (input, output) in inputs_and_outputs {398let output = (*output).to_owned();399futures.push(async move {400let mut result = vec![Val::Bool(false)];401foo_function402.call_concurrent(store, &make(input), &mut result)403.await?;404anyhow::Ok((result, output))405});406}407408while let Some((actual, expected)) = futures.try_next().await? {409let Some(Val::Tuple(actual)) = actual.into_iter().next() else {410unreachable!()411};412assert_eq!(make(&expected), actual);413}414Ok(())415})416.await??;417418instance.assert_concurrent_state_empty(&mut store);419}420421if call_style == 3 {422// Now do it again using `Func::call_async`:423for (input, expected) in inputs_and_outputs {424let mut results = [Val::Bool(false)];425foo_function426.call_async(&mut store, &make(input), &mut results)427.await?;428let Val::Tuple(actual) = &results[0] else {429unreachable!()430};431assert_eq!(&make(expected), actual);432foo_function.post_return_async(&mut store).await?;433}434435instance.assert_concurrent_state_empty(&mut store);436}437}438439Ok(())440}441442pub async fn test_round_trip_many_composed(a: &str, b: &str) -> Result<()> {443test_round_trip_many(444&[a, b],445&[446(447"hello, world!",448"hello, world! - entered guest - entered guest - entered host \449- exited host - exited guest - exited guest",450),451(452"¡hola, mundo!",453"¡hola, mundo! - entered guest - entered guest - entered host \454- exited host - exited guest - exited guest",455),456(457"hi y'all!",458"hi y'all! - entered guest - entered guest - entered host \459- exited host - exited guest - exited guest",460),461],462)463.await464}465466467