Skip to content

Commit 04416e4

Browse files
authored
Initial migration to wasmtime_test (#8789)
* Initial migration to `wasmtime_test` This commit introduces the initial migration to the `wasmtime_test` macro. This change starts by migrating all the applicable `func.rs` integration tests and at the same time it removes all the duplicated integration tests in `winch.rs`. Additionally, this change introduces a slight change to how the macro works. Inspired by #8622 (review) it defaults to including all the known configuration combinations and allows the macro user to opt-out where applicable. This makes the usage of the macro less verbose. The intention is to follow-up with subsequent PRs to migrate the all the applicable tests. * Remove unused `bail` import * Add the ability to specify `wasm_features` This commit adds the ability to specify `wasm_features` when invoking the macro. If the feature is off by default, the macro will ensure that the feature is enabled in the resulting config. If the feature is not supported by any of the compiler strategies, no tests will be generated for such strategy.
1 parent e93dfbd commit 04416e4

File tree

3 files changed

+205
-373
lines changed

3 files changed

+205
-373
lines changed

crates/test-macros/src/lib.rs

Lines changed: 103 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,123 @@
11
//! Wasmtime test macro.
22
//!
33
//! 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.
66
//!
77
//! Usage
88
//!
9-
//! #[wasmtime_test(strategies(Cranelift, Winch))]
9+
//! To exclude a compiler strategy:
10+
//!
11+
//! ```rust
12+
//! #[wasmtime_test(strategies(not(Winch)))]
1013
//! fn my_test(config: &mut Config) -> Result<()> {
1114
//! Ok(())
1215
//! }
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.
1331
use proc_macro::TokenStream;
32+
use proc_macro2::Span;
1433
use quote::{quote, ToTokens, TokenStreamExt};
1534
use syn::{
1635
braced,
36+
meta::ParseNestedMeta,
1737
parse::{Parse, ParseStream},
1838
parse_macro_input, token, Attribute, Ident, Result, ReturnType, Signature, Visibility,
1939
};
2040

2141
/// Test configuration.
2242
struct TestConfig {
2343
/// 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,
2552
}
2653

2754
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"))
3874
} else {
3975
Ok(())
4076
}
4177
}
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+
}
42109
}
43110

44111
impl Default for TestConfig {
45112
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+
}
47121
}
48122
}
49123

@@ -114,17 +188,9 @@ pub fn wasmtime_test(attrs: TokenStream, item: TokenStream) -> TokenStream {
114188

115189
let config_parser = syn::meta::parser(|meta| {
116190
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)
128194
} else {
129195
Err(meta.error("Unsupported attributes"))
130196
}
@@ -142,7 +208,8 @@ fn expand(test_config: &TestConfig, func: Fn) -> Result<TokenStream> {
142208
let mut tests = vec![quote! { #func }];
143209
let attrs = &func.attrs;
144210

145-
for (strategy_name, ident) in &test_config.strategies {
211+
for ident in &test_config.strategies {
212+
let strategy_name = ident.to_string();
146213
// Winch currently only offers support for x64.
147214
let target = if strategy_name == "Winch" {
148215
quote! { #[cfg(target_arch = "x86_64")] }
@@ -158,13 +225,22 @@ fn expand(test_config: &TestConfig, func: Fn) -> Result<TokenStream> {
158225
&format!("{}_{}", strategy_name.to_lowercase(), func_name),
159226
func_name.span(),
160227
);
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+
161236
let tok = quote! {
162237
#[test]
163238
#target
164239
#(#attrs)*
165240
fn #test_name() #ret {
166241
let mut config = Config::new();
167242
config.strategy(Strategy::#ident);
243+
#(#config_setup)*
168244
#func_name(&mut config)
169245
}
170246
};

0 commit comments

Comments
 (0)