Path: blob/main/crates/misc/component-async-tests/tests/scenario/round_trip_many.rs
3120 views
use std::iter;1use std::sync::atomic::{AtomicU32, Ordering::Relaxed};23use super::util::{config, make_component};4use component_async_tests::Ctx;5use component_async_tests::util::yield_times;6use futures::{7FutureExt,8stream::{FuturesUnordered, TryStreamExt},9};10use wasmtime::component::{Linker, ResourceTable, Val};11use wasmtime::{Engine, Result, Store, format_err};12use wasmtime_wasi::WasiCtxBuilder;1314#[tokio::test]15pub async fn async_round_trip_many_stackless() -> Result<()> {16test_round_trip_many_uncomposed(17test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,18)19.await20}2122#[tokio::test]23pub async fn async_round_trip_many_stackful() -> Result<()> {24test_round_trip_many_uncomposed(25test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,26)27.await28}2930#[tokio::test]31pub async fn async_round_trip_many_synchronous() -> Result<()> {32test_round_trip_many_uncomposed(33test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,34)35.await36}3738#[tokio::test]39pub async fn async_round_trip_many_wait() -> Result<()> {40test_round_trip_many_uncomposed(test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT)41.await42}4344#[tokio::test]45async fn async_round_trip_many_stackless_plus_stackless() -> Result<()> {46test_round_trip_many_composed(47test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,48test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,49)50.await51}5253#[tokio::test]54async fn async_round_trip_many_synchronous_plus_stackless() -> Result<()> {55test_round_trip_many_composed(56test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,57test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,58)59.await60}6162#[tokio::test]63async fn async_round_trip_many_stackless_plus_synchronous() -> Result<()> {64test_round_trip_many_composed(65test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,66test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,67)68.await69}7071#[tokio::test]72async fn async_round_trip_many_synchronous_plus_synchronous() -> Result<()> {73test_round_trip_many_composed(74test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,75test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,76)77.await78}7980#[tokio::test]81async fn async_round_trip_many_wait_plus_wait() -> Result<()> {82test_round_trip_many_composed(83test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,84test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,85)86.await87}8889#[tokio::test]90async fn async_round_trip_many_synchronous_plus_wait() -> Result<()> {91test_round_trip_many_composed(92test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,93test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,94)95.await96}9798#[tokio::test]99async fn async_round_trip_many_wait_plus_synchronous() -> Result<()> {100test_round_trip_many_composed(101test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,102test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,103)104.await105}106107#[tokio::test]108async fn async_round_trip_many_stackless_plus_wait() -> Result<()> {109test_round_trip_many_composed(110test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,111test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,112)113.await114}115116#[tokio::test]117async fn async_round_trip_many_wait_plus_stackless() -> Result<()> {118test_round_trip_many_composed(119test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,120test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,121)122.await123}124125#[tokio::test]126async fn async_round_trip_many_stackful_plus_stackful() -> Result<()> {127test_round_trip_many_composed(128test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,129test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,130)131.await132}133134#[tokio::test]135async fn async_round_trip_many_stackful_plus_stackless() -> Result<()> {136test_round_trip_many_composed(137test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,138test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,139)140.await141}142143#[tokio::test]144async fn async_round_trip_many_stackless_plus_stackful() -> Result<()> {145test_round_trip_many_composed(146test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,147test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,148)149.await150}151152#[tokio::test]153async fn async_round_trip_many_synchronous_plus_stackful() -> Result<()> {154test_round_trip_many_composed(155test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,156test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,157)158.await159}160161#[tokio::test]162async fn async_round_trip_many_stackful_plus_synchronous() -> Result<()> {163test_round_trip_many_composed(164test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,165test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,166)167.await168}169170async fn test_round_trip_many_uncomposed(component: &str) -> Result<()> {171test_round_trip_many(172&[component],173&[174(175"hello, world!",176"hello, world! - entered guest - entered host - exited host - exited guest",177),178(179"¡hola, mundo!",180"¡hola, mundo! - entered guest - entered host - exited host - exited guest",181),182(183"hi y'all!",184"hi y'all! - entered guest - entered host - exited host - exited guest",185),186],187)188.await189}190191async fn test_round_trip_many(192components: &[&str],193inputs_and_outputs: &[(&str, &str)],194) -> Result<()> {195use component_async_tests::round_trip_many::bindings::exports::local::local::many;196197let engine = Engine::new(&config())?;198199let make_store = || {200Store::new(201&engine,202Ctx {203wasi: WasiCtxBuilder::new().inherit_stdio().build(),204table: ResourceTable::default(),205continue_: false,206},207)208};209210let component = make_component(&engine, components).await?;211212let b = 42;213let c = vec![42u8; 42];214let d = (4242, 424242424242);215let e = many::Stuff {216a: vec![42i32; 42],217b: true,218c: 424242,219};220let f = Some(e.clone());221let g = Err(());222223// On miri, we only use one call style per test since they take so long to224// run. On non-miri, we use every call style for each test.225static CALL_STYLE_COUNTER: AtomicU32 = AtomicU32::new(0);226let call_style = CALL_STYLE_COUNTER.fetch_add(1, Relaxed) % 4;227228// First, test the `wasmtime-wit-bindgen` static API:229{230let mut linker = Linker::new(&engine);231232wasmtime_wasi::p2::add_to_linker_async(&mut linker)?;233component_async_tests::round_trip_many::bindings::local::local::many::add_to_linker::<234_,235Ctx,236>(&mut linker, |ctx| ctx)?;237238let mut store = make_store();239240let instance = linker.instantiate_async(&mut store, &component).await?;241let round_trip_many = component_async_tests::round_trip_many::bindings::RoundTripMany::new(242&mut store, &instance,243)?;244245if call_style == 0 {246store247.run_concurrent({248let c = c.clone();249let e = e.clone();250let f = f.clone();251let g = g.clone();252let inputs_and_outputs = inputs_and_outputs253.iter()254.map(|(a, b)| ((*a).to_owned(), (*b).to_owned()))255.collect::<Vec<_>>();256async move |accessor| {257// Start concurrent calls and then join them all:258let mut futures = FuturesUnordered::new();259for (input, output) in inputs_and_outputs {260futures.push(261round_trip_many262.local_local_many()263.call_foo(264accessor,265input,266b,267c.clone(),268d,269e.clone(),270f.clone(),271g.clone(),272)273.map(move |v| v.map(move |v| (v, output))),274);275}276277while let Some((actual, expected)) = futures.try_next().await? {278assert_eq!(279(expected, b, c.clone(), d, e.clone(), f.clone(), g.clone()),280actual281);282}283284wasmtime::error::Ok(())285}286})287.await??;288289store.assert_concurrent_state_empty();290}291292if call_style == 1 {293// Now do it again using `TypedFunc::call_async`-based bindings:294let e = component_async_tests::round_trip_many::non_concurrent_export_bindings::exports::local::local::many::Stuff {295a: vec![42i32; 42],296b: true,297c: 424242,298};299let f = Some(e.clone());300let g = Err(());301302let round_trip_many = component_async_tests::round_trip_many::non_concurrent_export_bindings::RoundTripMany::instantiate_async(303&mut store, &component, &linker,304)305.await?;306307for (input, expected) in inputs_and_outputs {308assert_eq!(309(310(*expected).to_owned(),311b,312c.clone(),313d,314e.clone(),315f.clone(),316g.clone()317),318round_trip_many319.local_local_many()320.call_foo(&mut store, input, b, &c, d, &e, f.as_ref(), Err(()))321.await?322);323}324325store.assert_concurrent_state_empty();326}327}328329// Now do it again using the dynamic API (except for WASI, where we stick with the static API):330{331let mut linker = Linker::new(&engine);332333wasmtime_wasi::p2::add_to_linker_async(&mut linker)?;334linker335.root()336.instance("local:local/many")?337.func_new_concurrent("foo", |_, _, params, results| {338Box::pin(async move {339yield_times(5).await;340let mut params = params.into_iter();341let Some(Val::String(s)) = params.next() else {342unreachable!()343};344results[0] = Val::Tuple(345iter::once(Val::String(format!("{s} - entered host - exited host")))346.chain(params.cloned())347.collect(),348);349Ok(())350})351})?;352353let mut store = make_store();354355let instance = linker.instantiate_async(&mut store, &component).await?;356let baz_instance = instance357.get_export_index(&mut store, None, "local:local/many")358.ok_or_else(|| format_err!("can't find `local:local/many` in instance"))?;359let foo_function = instance360.get_export_index(&mut store, Some(&baz_instance), "foo")361.ok_or_else(|| format_err!("can't find `foo` in instance"))?;362let foo_function = instance363.get_func(&mut store, foo_function)364.ok_or_else(|| format_err!("can't find `foo` in instance"))?;365366let make = |input: &str| {367let stuff = Val::Record(vec![368(369"a".into(),370Val::List(e.a.iter().map(|v| Val::S32(*v)).collect()),371),372("b".into(), Val::Bool(e.b)),373("c".into(), Val::U64(e.c)),374]);375vec![376Val::String(input.to_owned()),377Val::U32(b),378Val::List(c.iter().map(|v| Val::U8(*v)).collect()),379Val::Tuple(vec![Val::U64(d.0), Val::U64(d.1)]),380stuff.clone(),381Val::Option(Some(Box::new(stuff))),382Val::Result(Err(None)),383]384};385386if call_style == 2 {387store388.run_concurrent(async |store| -> wasmtime::Result<_> {389// Start three concurrent calls and then join them all:390let mut futures = FuturesUnordered::new();391for (input, output) in inputs_and_outputs {392let output = (*output).to_owned();393futures.push(async move {394let mut result = vec![Val::Bool(false)];395foo_function396.call_concurrent(store, &make(input), &mut result)397.await?;398wasmtime::error::Ok((result, output))399});400}401402while let Some((actual, expected)) = futures.try_next().await? {403let Some(Val::Tuple(actual)) = actual.into_iter().next() else {404unreachable!()405};406assert_eq!(make(&expected), actual);407}408Ok(())409})410.await??;411412store.assert_concurrent_state_empty();413}414415if call_style == 3 {416// Now do it again using `Func::call_async`:417for (input, expected) in inputs_and_outputs {418let mut results = [Val::Bool(false)];419foo_function420.call_async(&mut store, &make(input), &mut results)421.await?;422let Val::Tuple(actual) = &results[0] else {423unreachable!()424};425assert_eq!(&make(expected), actual);426}427428store.assert_concurrent_state_empty();429}430}431432Ok(())433}434435pub async fn test_round_trip_many_composed(a: &str, b: &str) -> Result<()> {436test_round_trip_many(437&[a, b],438&[439(440"hello, world!",441"hello, world! - entered guest - entered guest - entered host \442- exited host - exited guest - exited guest",443),444(445"¡hola, mundo!",446"¡hola, mundo! - entered guest - entered guest - entered host \447- exited host - exited guest - exited guest",448),449(450"hi y'all!",451"hi y'all! - entered guest - entered guest - entered host \452- exited host - exited guest - exited guest",453),454],455)456.await457}458459460