Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/misc/component-async-tests/tests/scenario/round_trip_many.rs
3128 views
1
use std::iter;
2
use std::sync::atomic::{AtomicU32, Ordering::Relaxed};
3
4
use super::util::{config, make_component};
5
use component_async_tests::Ctx;
6
use component_async_tests::util::yield_times;
7
use futures::{
8
FutureExt,
9
stream::{FuturesUnordered, TryStreamExt},
10
};
11
use wasmtime::component::{Linker, ResourceTable, Val};
12
use wasmtime::{Engine, Result, Store, format_err};
13
use wasmtime_wasi::WasiCtxBuilder;
14
15
#[tokio::test]
16
pub async fn async_round_trip_many_stackless() -> Result<()> {
17
test_round_trip_many_uncomposed(
18
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,
19
)
20
.await
21
}
22
23
#[tokio::test]
24
pub async fn async_round_trip_many_stackful() -> Result<()> {
25
test_round_trip_many_uncomposed(
26
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,
27
)
28
.await
29
}
30
31
#[tokio::test]
32
pub async fn async_round_trip_many_synchronous() -> Result<()> {
33
test_round_trip_many_uncomposed(
34
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,
35
)
36
.await
37
}
38
39
#[tokio::test]
40
pub async fn async_round_trip_many_wait() -> Result<()> {
41
test_round_trip_many_uncomposed(test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT)
42
.await
43
}
44
45
#[tokio::test]
46
async fn async_round_trip_many_stackless_plus_stackless() -> Result<()> {
47
test_round_trip_many_composed(
48
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,
49
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,
50
)
51
.await
52
}
53
54
#[tokio::test]
55
async fn async_round_trip_many_synchronous_plus_stackless() -> Result<()> {
56
test_round_trip_many_composed(
57
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,
58
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,
59
)
60
.await
61
}
62
63
#[tokio::test]
64
async fn async_round_trip_many_stackless_plus_synchronous() -> Result<()> {
65
test_round_trip_many_composed(
66
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,
67
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,
68
)
69
.await
70
}
71
72
#[tokio::test]
73
async fn async_round_trip_many_synchronous_plus_synchronous() -> Result<()> {
74
test_round_trip_many_composed(
75
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,
76
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,
77
)
78
.await
79
}
80
81
#[tokio::test]
82
async fn async_round_trip_many_wait_plus_wait() -> Result<()> {
83
test_round_trip_many_composed(
84
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,
85
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,
86
)
87
.await
88
}
89
90
#[tokio::test]
91
async fn async_round_trip_many_synchronous_plus_wait() -> Result<()> {
92
test_round_trip_many_composed(
93
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,
94
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,
95
)
96
.await
97
}
98
99
#[tokio::test]
100
async fn async_round_trip_many_wait_plus_synchronous() -> Result<()> {
101
test_round_trip_many_composed(
102
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,
103
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,
104
)
105
.await
106
}
107
108
#[tokio::test]
109
async fn async_round_trip_many_stackless_plus_wait() -> Result<()> {
110
test_round_trip_many_composed(
111
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,
112
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,
113
)
114
.await
115
}
116
117
#[tokio::test]
118
async fn async_round_trip_many_wait_plus_stackless() -> Result<()> {
119
test_round_trip_many_composed(
120
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_WAIT_COMPONENT,
121
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,
122
)
123
.await
124
}
125
126
#[tokio::test]
127
async fn async_round_trip_many_stackful_plus_stackful() -> Result<()> {
128
test_round_trip_many_composed(
129
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,
130
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,
131
)
132
.await
133
}
134
135
#[tokio::test]
136
async fn async_round_trip_many_stackful_plus_stackless() -> Result<()> {
137
test_round_trip_many_composed(
138
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,
139
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,
140
)
141
.await
142
}
143
144
#[tokio::test]
145
async fn async_round_trip_many_stackless_plus_stackful() -> Result<()> {
146
test_round_trip_many_composed(
147
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKLESS_COMPONENT,
148
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,
149
)
150
.await
151
}
152
153
#[tokio::test]
154
async fn async_round_trip_many_synchronous_plus_stackful() -> Result<()> {
155
test_round_trip_many_composed(
156
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,
157
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,
158
)
159
.await
160
}
161
162
#[tokio::test]
163
async fn async_round_trip_many_stackful_plus_synchronous() -> Result<()> {
164
test_round_trip_many_composed(
165
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_STACKFUL_COMPONENT,
166
test_programs_artifacts::ASYNC_ROUND_TRIP_MANY_SYNCHRONOUS_COMPONENT,
167
)
168
.await
169
}
170
171
async fn test_round_trip_many_uncomposed(component: &str) -> Result<()> {
172
test_round_trip_many(
173
&[component],
174
&[
175
(
176
"hello, world!",
177
"hello, world! - entered guest - entered host - exited host - exited guest",
178
),
179
(
180
"¡hola, mundo!",
181
"¡hola, mundo! - entered guest - entered host - exited host - exited guest",
182
),
183
(
184
"hi y'all!",
185
"hi y'all! - entered guest - entered host - exited host - exited guest",
186
),
187
],
188
)
189
.await
190
}
191
192
async fn test_round_trip_many(
193
components: &[&str],
194
inputs_and_outputs: &[(&str, &str)],
195
) -> Result<()> {
196
use component_async_tests::round_trip_many::bindings::exports::local::local::many;
197
198
let engine = Engine::new(&config())?;
199
200
let make_store = || {
201
Store::new(
202
&engine,
203
Ctx {
204
wasi: WasiCtxBuilder::new().inherit_stdio().build(),
205
table: ResourceTable::default(),
206
continue_: false,
207
},
208
)
209
};
210
211
let component = make_component(&engine, components).await?;
212
213
let b = 42;
214
let c = vec![42u8; 42];
215
let d = (4242, 424242424242);
216
let e = many::Stuff {
217
a: vec![42i32; 42],
218
b: true,
219
c: 424242,
220
};
221
let f = Some(e.clone());
222
let g = Err(());
223
224
// On miri, we only use one call style per test since they take so long to
225
// run. On non-miri, we use every call style for each test.
226
static CALL_STYLE_COUNTER: AtomicU32 = AtomicU32::new(0);
227
let call_style = CALL_STYLE_COUNTER.fetch_add(1, Relaxed) % 4;
228
229
// First, test the `wasmtime-wit-bindgen` static API:
230
{
231
let mut linker = Linker::new(&engine);
232
233
wasmtime_wasi::p2::add_to_linker_async(&mut linker)?;
234
component_async_tests::round_trip_many::bindings::local::local::many::add_to_linker::<
235
_,
236
Ctx,
237
>(&mut linker, |ctx| ctx)?;
238
239
let mut store = make_store();
240
241
let instance = linker.instantiate_async(&mut store, &component).await?;
242
let round_trip_many = component_async_tests::round_trip_many::bindings::RoundTripMany::new(
243
&mut store, &instance,
244
)?;
245
246
if call_style == 0 {
247
store
248
.run_concurrent({
249
let c = c.clone();
250
let e = e.clone();
251
let f = f.clone();
252
let g = g.clone();
253
let inputs_and_outputs = inputs_and_outputs
254
.iter()
255
.map(|(a, b)| ((*a).to_owned(), (*b).to_owned()))
256
.collect::<Vec<_>>();
257
async move |accessor| {
258
// Start concurrent calls and then join them all:
259
let mut futures = FuturesUnordered::new();
260
for (input, output) in inputs_and_outputs {
261
futures.push(
262
round_trip_many
263
.local_local_many()
264
.call_foo(
265
accessor,
266
input,
267
b,
268
c.clone(),
269
d,
270
e.clone(),
271
f.clone(),
272
g.clone(),
273
)
274
.map(move |v| v.map(move |v| (v, output))),
275
);
276
}
277
278
while let Some((actual, expected)) = futures.try_next().await? {
279
assert_eq!(
280
(expected, b, c.clone(), d, e.clone(), f.clone(), g.clone()),
281
actual
282
);
283
}
284
285
wasmtime::error::Ok(())
286
}
287
})
288
.await??;
289
290
store.assert_concurrent_state_empty();
291
}
292
293
if call_style == 1 {
294
// Now do it again using `TypedFunc::call_async`-based bindings:
295
let e = component_async_tests::round_trip_many::non_concurrent_export_bindings::exports::local::local::many::Stuff {
296
a: vec![42i32; 42],
297
b: true,
298
c: 424242,
299
};
300
let f = Some(e.clone());
301
let g = Err(());
302
303
let round_trip_many = component_async_tests::round_trip_many::non_concurrent_export_bindings::RoundTripMany::instantiate_async(
304
&mut store, &component, &linker,
305
)
306
.await?;
307
308
for (input, expected) in inputs_and_outputs {
309
assert_eq!(
310
(
311
(*expected).to_owned(),
312
b,
313
c.clone(),
314
d,
315
e.clone(),
316
f.clone(),
317
g.clone()
318
),
319
round_trip_many
320
.local_local_many()
321
.call_foo(&mut store, input, b, &c, d, &e, f.as_ref(), Err(()))
322
.await?
323
);
324
}
325
326
store.assert_concurrent_state_empty();
327
}
328
}
329
330
// Now do it again using the dynamic API (except for WASI, where we stick with the static API):
331
{
332
let mut linker = Linker::new(&engine);
333
334
wasmtime_wasi::p2::add_to_linker_async(&mut linker)?;
335
linker
336
.root()
337
.instance("local:local/many")?
338
.func_new_concurrent("foo", |_, _, params, results| {
339
Box::pin(async move {
340
yield_times(5).await;
341
let mut params = params.into_iter();
342
let Some(Val::String(s)) = params.next() else {
343
unreachable!()
344
};
345
results[0] = Val::Tuple(
346
iter::once(Val::String(format!("{s} - entered host - exited host")))
347
.chain(params.cloned())
348
.collect(),
349
);
350
Ok(())
351
})
352
})?;
353
354
let mut store = make_store();
355
356
let instance = linker.instantiate_async(&mut store, &component).await?;
357
let baz_instance = instance
358
.get_export_index(&mut store, None, "local:local/many")
359
.ok_or_else(|| format_err!("can't find `local:local/many` in instance"))?;
360
let foo_function = instance
361
.get_export_index(&mut store, Some(&baz_instance), "foo")
362
.ok_or_else(|| format_err!("can't find `foo` in instance"))?;
363
let foo_function = instance
364
.get_func(&mut store, foo_function)
365
.ok_or_else(|| format_err!("can't find `foo` in instance"))?;
366
367
let make = |input: &str| {
368
let stuff = Val::Record(vec![
369
(
370
"a".into(),
371
Val::List(e.a.iter().map(|v| Val::S32(*v)).collect()),
372
),
373
("b".into(), Val::Bool(e.b)),
374
("c".into(), Val::U64(e.c)),
375
]);
376
vec![
377
Val::String(input.to_owned()),
378
Val::U32(b),
379
Val::List(c.iter().map(|v| Val::U8(*v)).collect()),
380
Val::Tuple(vec![Val::U64(d.0), Val::U64(d.1)]),
381
stuff.clone(),
382
Val::Option(Some(Box::new(stuff))),
383
Val::Result(Err(None)),
384
]
385
};
386
387
if call_style == 2 {
388
store
389
.run_concurrent(async |store| -> wasmtime::Result<_> {
390
// Start three concurrent calls and then join them all:
391
let mut futures = FuturesUnordered::new();
392
for (input, output) in inputs_and_outputs {
393
let output = (*output).to_owned();
394
futures.push(async move {
395
let mut result = vec![Val::Bool(false)];
396
foo_function
397
.call_concurrent(store, &make(input), &mut result)
398
.await?;
399
wasmtime::error::Ok((result, output))
400
});
401
}
402
403
while let Some((actual, expected)) = futures.try_next().await? {
404
let Some(Val::Tuple(actual)) = actual.into_iter().next() else {
405
unreachable!()
406
};
407
assert_eq!(make(&expected), actual);
408
}
409
Ok(())
410
})
411
.await??;
412
413
store.assert_concurrent_state_empty();
414
}
415
416
if call_style == 3 {
417
// Now do it again using `Func::call_async`:
418
for (input, expected) in inputs_and_outputs {
419
let mut results = [Val::Bool(false)];
420
foo_function
421
.call_async(&mut store, &make(input), &mut results)
422
.await?;
423
let Val::Tuple(actual) = &results[0] else {
424
unreachable!()
425
};
426
assert_eq!(&make(expected), actual);
427
}
428
429
store.assert_concurrent_state_empty();
430
}
431
}
432
433
Ok(())
434
}
435
436
pub async fn test_round_trip_many_composed(a: &str, b: &str) -> Result<()> {
437
test_round_trip_many(
438
&[a, b],
439
&[
440
(
441
"hello, world!",
442
"hello, world! - entered guest - entered guest - entered host \
443
- exited host - exited guest - exited guest",
444
),
445
(
446
"¡hola, mundo!",
447
"¡hola, mundo! - entered guest - entered guest - entered host \
448
- exited host - exited guest - exited guest",
449
),
450
(
451
"hi y'all!",
452
"hi y'all! - entered guest - entered guest - entered host \
453
- exited host - exited guest - exited guest",
454
),
455
],
456
)
457
.await
458
}
459
460