77//! lifting and lowering code and verify the values remain intact during both processes.
88
99use crate :: block_on;
10+ use crate :: generators:: { self , CompilerStrategy , InstanceAllocationStrategy } ;
11+ use crate :: oracles:: log_wasm;
1012use arbitrary:: { Arbitrary , Unstructured } ;
1113use std:: any:: Any ;
1214use std:: fmt:: Debug ;
1315use std:: ops:: ControlFlow ;
14- use wasmtime:: component:: { self , Component , ComponentNamedList , Lift , Linker , Lower , Val } ;
15- use wasmtime:: { AsContextMut , Config , Engine , Store , StoreContextMut } ;
16- use wasmtime_test_util:: component_fuzz:: { Declarations , EXPORT_FUNCTION , IMPORT_FUNCTION } ;
16+ use wasmtime:: component:: {
17+ self , Accessor , Component , ComponentNamedList , Lift , Linker , Lower , Val ,
18+ } ;
19+ use wasmtime:: { AsContextMut , Enabled , Engine , Result , Store , StoreContextMut } ;
20+ use wasmtime_test_util:: component_fuzz:: {
21+ Declarations , EXPORT_FUNCTION , IMPORT_FUNCTION , MAX_TYPE_DEPTH , TestCase , Type ,
22+ } ;
1723
1824/// Minimum length of an arbitrary list value generated for a test case
1925const MIN_LIST_LENGTH : u32 = 0 ;
@@ -22,7 +28,7 @@ const MIN_LIST_LENGTH: u32 = 0;
2228const MAX_LIST_LENGTH : u32 = 10 ;
2329
2430/// Generate an arbitrary instance of the specified type.
25- pub fn arbitrary_val ( ty : & component:: Type , input : & mut Unstructured ) -> arbitrary:: Result < Val > {
31+ fn arbitrary_val ( ty : & component:: Type , input : & mut Unstructured ) -> arbitrary:: Result < Val > {
2632 use component:: Type ;
2733
2834 Ok ( match ty {
@@ -116,6 +122,63 @@ pub fn arbitrary_val(ty: &component::Type, input: &mut Unstructured) -> arbitrar
116122 } )
117123}
118124
125+ fn store < T > ( input : & mut Unstructured < ' _ > , val : T ) -> arbitrary:: Result < Store < T > > {
126+ crate :: init_fuzzing ( ) ;
127+
128+ let mut config = input. arbitrary :: < generators:: Config > ( ) ?;
129+ config. enable_async ( input) ?;
130+ config. module_config . config . multi_value_enabled = true ;
131+ config. module_config . config . reference_types_enabled = true ;
132+ config. module_config . config . max_memories = 2 ;
133+ config. module_config . component_model_async = true ;
134+ config. module_config . component_model_async_stackful = true ;
135+ if config. wasmtime . compiler_strategy == CompilerStrategy :: Winch {
136+ config. wasmtime . compiler_strategy = CompilerStrategy :: CraneliftNative ;
137+ }
138+
139+ fn set_min < T > ( a : & mut T , min : T )
140+ where
141+ T : Ord + Copy ,
142+ {
143+ if * a < min {
144+ * a = min;
145+ }
146+ }
147+
148+ fn set_opt_min < T > ( a : & mut Option < T > , min : T )
149+ where
150+ T : Ord + Copy ,
151+ {
152+ if let Some ( a) = a {
153+ set_min ( a, min) ;
154+ }
155+ }
156+
157+ if let InstanceAllocationStrategy :: Pooling ( p) = & mut config. wasmtime . strategy {
158+ set_min ( & mut p. total_component_instances , 5 ) ;
159+ set_min ( & mut p. total_core_instances , 5 ) ;
160+ set_min ( & mut p. total_memories , 2 ) ;
161+ set_min ( & mut p. max_memories_per_component , 2 ) ;
162+ set_min ( & mut p. max_memories_per_module , 2 ) ;
163+ p. memory_protection_keys = Enabled :: No ;
164+ p. max_memory_size = 10 << 20 ; // 10 MiB
165+ }
166+ set_opt_min (
167+ & mut config. wasmtime . memory_config . memory_reservation ,
168+ 10 << 20 ,
169+ ) ;
170+
171+ let engine = Engine :: new (
172+ config
173+ . to_wasmtime ( )
174+ . debug_adapter_modules ( input. arbitrary ( ) ?) ,
175+ )
176+ . unwrap ( ) ;
177+ let mut store = Store :: new ( & engine, val) ;
178+ config. configure_store_epoch_and_fuel ( & mut store) ;
179+ Ok ( store)
180+ }
181+
119182/// Generate zero or more sets of arbitrary argument and result values and execute the test using those
120183/// values, asserting that they flow from host-to-guest and guest-to-host unchanged.
121184pub fn static_api_test < ' a , P , R > (
@@ -146,13 +209,8 @@ where
146209{
147210 crate :: init_fuzzing ( ) ;
148211
149- let mut config = Config :: new ( ) ;
150- config. wasm_component_model ( true ) ;
151- config. wasm_component_model_async ( true ) ;
152- config. wasm_component_model_async_stackful ( true ) ;
153- config. async_support ( true ) ;
154- config. debug_adapter_modules ( input. arbitrary ( ) ?) ;
155- let engine = Engine :: new ( & config) . unwrap ( ) ;
212+ let mut store = store :: < Box < dyn Any + Send > > ( input, Box :: new ( ( ) ) ) ?;
213+ let engine = store. engine ( ) ;
156214 let wat = declarations. make_component ( ) ;
157215 let wat = wat. as_bytes ( ) ;
158216 crate :: oracles:: log_wasm ( wat) ;
@@ -192,7 +250,6 @@ where
192250 } )
193251 . unwrap ( ) ;
194252 }
195- let mut store: Store < Box < dyn Any + Send > > = Store :: new ( & engine, Box :: new ( ( ) ) ) ;
196253
197254 block_on ( async {
198255 let instance = linker
@@ -226,12 +283,117 @@ where
226283 } )
227284}
228285
286+ /// Generate and execute a `crate::generators::component_types::TestCase` using the specified `input` to create
287+ /// arbitrary types and values.
288+ pub fn dynamic_component_api_target ( input : & mut arbitrary:: Unstructured ) -> arbitrary:: Result < ( ) > {
289+ crate :: init_fuzzing ( ) ;
290+
291+ let mut types = Vec :: new ( ) ;
292+ let mut type_fuel = 500 ;
293+
294+ for _ in 0 ..5 {
295+ types. push ( Type :: generate ( input, MAX_TYPE_DEPTH , & mut type_fuel) ?) ;
296+ }
297+
298+ let case = TestCase :: generate ( & types, input) ?;
299+
300+ let mut store = store ( input, ( Vec :: new ( ) , None ) ) ?;
301+ let engine = store. engine ( ) ;
302+ let wat = case. declarations ( ) . make_component ( ) ;
303+ let wat = wat. as_bytes ( ) ;
304+ log_wasm ( wat) ;
305+ let component = Component :: new ( & engine, wat) . unwrap ( ) ;
306+ let mut linker = Linker :: new ( & engine) ;
307+
308+ fn host_function (
309+ mut cx : StoreContextMut < ' _ , ( Vec < Val > , Option < Vec < Val > > ) > ,
310+ params : & [ Val ] ,
311+ results : & mut [ Val ] ,
312+ ) -> Result < ( ) > {
313+ log:: trace!( "received params {params:?}" ) ;
314+ let ( expected_args, expected_results) = cx. data_mut ( ) ;
315+ assert_eq ! ( params. len( ) , expected_args. len( ) ) ;
316+ for ( expected, actual) in expected_args. iter ( ) . zip ( params) {
317+ assert_eq ! ( expected, actual) ;
318+ }
319+ results. clone_from_slice ( & expected_results. take ( ) . unwrap ( ) ) ;
320+ log:: trace!( "returning results {results:?}" ) ;
321+ Ok ( ( ) )
322+ }
323+
324+ if case. options . host_async {
325+ linker
326+ . root ( )
327+ . func_new_concurrent ( IMPORT_FUNCTION , {
328+ move |cx : & Accessor < _ , _ > , _, params : & [ Val ] , results : & mut [ Val ] | {
329+ Box :: pin ( async move {
330+ cx. with ( |mut store| host_function ( store. as_context_mut ( ) , params, results) )
331+ } )
332+ }
333+ } )
334+ . unwrap ( ) ;
335+ } else {
336+ linker
337+ . root ( )
338+ . func_new ( IMPORT_FUNCTION , {
339+ move |cx, _, params, results| host_function ( cx, params, results)
340+ } )
341+ . unwrap ( ) ;
342+ }
343+
344+ block_on ( async {
345+ let instance = linker
346+ . instantiate_async ( & mut store, & component)
347+ . await
348+ . unwrap ( ) ;
349+ let func = instance. get_func ( & mut store, EXPORT_FUNCTION ) . unwrap ( ) ;
350+ let ty = func. ty ( & store) ;
351+
352+ while input. arbitrary ( ) ? {
353+ let params = ty
354+ . params ( )
355+ . map ( |( _, ty) | arbitrary_val ( & ty, input) )
356+ . collect :: < arbitrary:: Result < Vec < _ > > > ( ) ?;
357+ let results = ty
358+ . results ( )
359+ . map ( |ty| arbitrary_val ( & ty, input) )
360+ . collect :: < arbitrary:: Result < Vec < _ > > > ( ) ?;
361+
362+ * store. data_mut ( ) = ( params. clone ( ) , Some ( results. clone ( ) ) ) ;
363+
364+ log:: trace!( "passing params {params:?}" ) ;
365+ let mut actual = vec ! [ Val :: Bool ( false ) ; results. len( ) ] ;
366+ if case. options . guest_caller_async {
367+ store
368+ . run_concurrent ( async |a| {
369+ func. call_concurrent ( a, & params, & mut actual) . await . unwrap ( ) ;
370+ } )
371+ . await
372+ . unwrap ( ) ;
373+ } else {
374+ func. call_async ( & mut store, & params, & mut actual)
375+ . await
376+ . unwrap ( ) ;
377+ func. post_return_async ( & mut store) . await . unwrap ( ) ;
378+ }
379+ log:: trace!( "received results {actual:?}" ) ;
380+ assert_eq ! ( actual, results) ;
381+ }
382+ Ok ( ( ) )
383+ } )
384+ }
385+
229386#[ cfg( test) ]
230387mod tests {
231388 use super :: * ;
232389 use crate :: test:: test_n_times;
233390 use wasmtime_test_util:: component_fuzz:: { TestCase , Type } ;
234391
392+ #[ test]
393+ fn dynamic_component_api_smoke_test ( ) {
394+ test_n_times ( 50 , |( ) , u| super :: dynamic_component_api_target ( u) ) ;
395+ }
396+
235397 #[ test]
236398 fn static_api_smoke_test ( ) {
237399 test_n_times ( 10 , |( ) , u| {
0 commit comments