1
1
//! Wasmtime test macro.
2
2
//!
3
3
//! This macro is a helper to define tests that exercise multiple configuration
4
- //! combinations for Wasmtime. Currently, only compiler strategies are
5
- //! supported.
4
+ //! combinations for Wasmtime. Currently compiler strategies and wasm features
5
+ //! are supported.
6
6
//!
7
7
//! Usage
8
8
//!
9
- //! #[wasmtime_test(strategies(Cranelift, Winch))]
9
+ //! To exclude a compiler strategy:
10
+ //!
11
+ //! ```rust
12
+ //! #[wasmtime_test(strategies(not(Winch)))]
10
13
//! fn my_test(config: &mut Config) -> Result<()> {
11
14
//! Ok(())
12
15
//! }
16
+ //! ```
17
+ //!
18
+ //! To explicitly indicate that a wasm features is needed
19
+ //! ```
20
+ //! #[wasmtime_test(wasm_features(gc))]
21
+ //! fn my_wasm_gc_test(config: &mut Config) -> Result<()> {
22
+ //! Ok(())
23
+ //! }
24
+ //! ```
25
+ //!
26
+ //! If the specified wasm feature is disabled by default, the macro will enable
27
+ //! the feature in the configuration passed to the test.
28
+ //!
29
+ //! If the wasm feature is not supported by any of the compiler strategies, no
30
+ //! tests will be generated for such strategy.
13
31
use proc_macro:: TokenStream ;
32
+ use proc_macro2:: Span ;
14
33
use quote:: { quote, ToTokens , TokenStreamExt } ;
15
34
use syn:: {
16
35
braced,
36
+ meta:: ParseNestedMeta ,
17
37
parse:: { Parse , ParseStream } ,
18
38
parse_macro_input, token, Attribute , Ident , Result , ReturnType , Signature , Visibility ,
19
39
} ;
20
40
21
41
/// Test configuration.
22
42
struct TestConfig {
23
43
/// Supported compiler strategies.
24
- strategies : Vec < ( String , Ident ) > ,
44
+ strategies : Vec < Ident > ,
45
+ /// Known WebAssembly features that will be turned on by default in the
46
+ /// resulting Config.
47
+ /// The identifiers in this list are features that are off by default in
48
+ /// Wasmtime's Config, which will be explicitly turned on for a given test.
49
+ wasm_features : Vec < Ident > ,
50
+ /// Flag to track if there are Wasm features not supported by Winch.
51
+ wasm_features_unsupported_by_winch : bool ,
25
52
}
26
53
27
54
impl TestConfig {
28
- /// Validate the test configuration.
29
- /// Only the number of strategies is validated, as this avoid expansions of
30
- /// empty strategies or more strategies than supported.
31
- ///
32
- /// The supported strategies are validated inline when parsing.
33
- fn validate ( & self ) -> anyhow:: Result < ( ) > {
34
- if self . strategies . len ( ) > 2 {
35
- Err ( anyhow:: anyhow!( "Expected at most 2 strategies" ) )
36
- } else if self . strategies . len ( ) == 0 {
37
- Err ( anyhow:: anyhow!( "Expected at least 1 strategy" ) )
55
+ fn strategies_from ( & mut self , meta : & ParseNestedMeta ) -> Result < ( ) > {
56
+ meta. parse_nested_meta ( |meta| {
57
+ if meta. path . is_ident ( "not" ) {
58
+ meta. parse_nested_meta ( |meta| {
59
+ if meta. path . is_ident ( "Winch" ) || meta. path . is_ident ( "Cranelift" ) {
60
+ let id = meta. path . require_ident ( ) ?. clone ( ) ;
61
+ self . strategies . retain ( |s| * s != id) ;
62
+ Ok ( ( ) )
63
+ } else {
64
+ Err ( meta. error ( "Unknown strategy" ) )
65
+ }
66
+ } )
67
+ } else {
68
+ Err ( meta. error ( "Unknown identifier" ) )
69
+ }
70
+ } ) ?;
71
+
72
+ if self . strategies . len ( ) == 0 {
73
+ Err ( meta. error ( "Expected at least one strategy" ) )
38
74
} else {
39
75
Ok ( ( ) )
40
76
}
41
77
}
78
+
79
+ fn wasm_features_from ( & mut self , meta : & ParseNestedMeta ) -> Result < ( ) > {
80
+ meta. parse_nested_meta ( |meta| {
81
+ if meta. path . is_ident ( "gc" ) || meta. path . is_ident ( "function_references" ) {
82
+ let feature = meta. path . require_ident ( ) ?. clone ( ) ;
83
+ self . wasm_features . push ( feature. clone ( ) ) ;
84
+ self . wasm_features_unsupported_by_winch = true ;
85
+ Ok ( ( ) )
86
+ } else if meta. path . is_ident ( "simd" )
87
+ || meta. path . is_ident ( "relaxed_simd" )
88
+ || meta. path . is_ident ( "reference_types" )
89
+ || meta. path . is_ident ( "tail_call" )
90
+ || meta. path . is_ident ( "threads" )
91
+ {
92
+ self . wasm_features_unsupported_by_winch = true ;
93
+ Ok ( ( ) )
94
+ } else {
95
+ Err ( meta. error ( "Unsupported wasm feature" ) )
96
+ }
97
+ } ) ?;
98
+
99
+ if self . wasm_features . len ( ) > 2 {
100
+ return Err ( meta. error ( "Expected at most 7 strategies" ) ) ;
101
+ }
102
+
103
+ if self . wasm_features_unsupported_by_winch {
104
+ self . strategies . retain ( |s| s. to_string ( ) != "Winch" ) ;
105
+ }
106
+
107
+ Ok ( ( ) )
108
+ }
42
109
}
43
110
44
111
impl Default for TestConfig {
45
112
fn default ( ) -> Self {
46
- Self { strategies : vec ! [ ] }
113
+ Self {
114
+ strategies : vec ! [
115
+ Ident :: new( "Cranelift" , Span :: call_site( ) ) ,
116
+ Ident :: new( "Winch" , Span :: call_site( ) ) ,
117
+ ] ,
118
+ wasm_features : vec ! [ ] ,
119
+ wasm_features_unsupported_by_winch : false ,
120
+ }
47
121
}
48
122
}
49
123
@@ -114,17 +188,9 @@ pub fn wasmtime_test(attrs: TokenStream, item: TokenStream) -> TokenStream {
114
188
115
189
let config_parser = syn:: meta:: parser ( |meta| {
116
190
if meta. path . is_ident ( "strategies" ) {
117
- meta. parse_nested_meta ( |meta| {
118
- if meta. path . is_ident ( "Winch" ) || meta. path . is_ident ( "Cranelift" ) {
119
- let id = meta. path . require_ident ( ) ?. clone ( ) ;
120
- test_config. strategies . push ( ( id. to_string ( ) , id) ) ;
121
- Ok ( ( ) )
122
- } else {
123
- Err ( meta. error ( "Unknown strategy" ) )
124
- }
125
- } ) ?;
126
-
127
- test_config. validate ( ) . map_err ( |e| meta. error ( e) )
191
+ test_config. strategies_from ( & meta)
192
+ } else if meta. path . is_ident ( "wasm_features" ) {
193
+ test_config. wasm_features_from ( & meta)
128
194
} else {
129
195
Err ( meta. error ( "Unsupported attributes" ) )
130
196
}
@@ -142,7 +208,8 @@ fn expand(test_config: &TestConfig, func: Fn) -> Result<TokenStream> {
142
208
let mut tests = vec ! [ quote! { #func } ] ;
143
209
let attrs = & func. attrs ;
144
210
145
- for ( strategy_name, ident) in & test_config. strategies {
211
+ for ident in & test_config. strategies {
212
+ let strategy_name = ident. to_string ( ) ;
146
213
// Winch currently only offers support for x64.
147
214
let target = if strategy_name == "Winch" {
148
215
quote ! { #[ cfg( target_arch = "x86_64" ) ] }
@@ -158,13 +225,22 @@ fn expand(test_config: &TestConfig, func: Fn) -> Result<TokenStream> {
158
225
& format ! ( "{}_{}" , strategy_name. to_lowercase( ) , func_name) ,
159
226
func_name. span ( ) ,
160
227
) ;
228
+
229
+ let config_setup = test_config. wasm_features . iter ( ) . map ( |f| {
230
+ let method_name = Ident :: new ( & format ! ( "wasm_{}" , f) , f. span ( ) ) ;
231
+ quote ! {
232
+ config. #method_name( true ) ;
233
+ }
234
+ } ) ;
235
+
161
236
let tok = quote ! {
162
237
#[ test]
163
238
#target
164
239
#( #attrs) *
165
240
fn #test_name( ) #ret {
166
241
let mut config = Config :: new( ) ;
167
242
config. strategy( Strategy :: #ident) ;
243
+ #( #config_setup) *
168
244
#func_name( & mut config)
169
245
}
170
246
} ;
0 commit comments