@@ -6,14 +6,17 @@ use std::ffi::OsString;
66use std:: path:: { Path , PathBuf } ;
77use syn:: parse:: { Parse , ParseStream } ;
88use syn:: punctuated:: Punctuated ;
9- use syn:: { bracketed, parse_macro_input, Expr , LitStr , Token } ;
9+ use syn:: token:: Async ;
10+ use syn:: { bracketed, parse_macro_input, Expr , LitStr , Meta , Token } ;
1011use unicode_ident:: { is_xid_continue, is_xid_start} ;
1112
1213struct TestEachArgs {
1314 path : LitStr ,
1415 module : Option < Ident > ,
1516 function : Expr ,
1617 extensions : Vec < String > ,
18+ attributes : Vec < Meta > ,
19+ async_fn : Option < Async > ,
1720}
1821
1922macro_rules! abort {
@@ -30,6 +33,33 @@ macro_rules! abort_token_stream {
3033
3134impl Parse for TestEachArgs {
3235 fn parse ( input : ParseStream ) -> syn:: Result < Self > {
36+ // Optionally parse attributes if `#` is used. Aborts if none are given.
37+ let attributes: Vec < Meta > = input
38+ . parse :: < Token ! [ #] > ( )
39+ . and_then ( |_| {
40+ let content;
41+ bracketed ! ( content in input) ;
42+
43+ match Punctuated :: < Meta , Token ! [ , ] > :: parse_separated_nonempty ( & content) {
44+ Ok ( attributes) => Ok ( attributes. into_iter ( ) . collect ( ) ) ,
45+ Err ( e) => abort ! ( e. span( ) , "Expected at least one attribute to be given." ) ,
46+ }
47+ } )
48+ . unwrap_or_default ( ) ;
49+
50+ // Optionally mark as async.
51+ // The async keyword is the error span if we did not specify an attribute.
52+ let async_span = input. span ( ) ;
53+ let async_fn = match input. parse :: < Token ! [ async ] > ( ) {
54+ Ok ( token) => {
55+ if attributes. is_empty ( ) {
56+ abort ! ( async_span, "Expected at least one attribute (e.g., `#[tokio::test]`) when `async` is given." ) ;
57+ }
58+ Some ( token)
59+ }
60+ Err ( _) => None ,
61+ } ;
62+
3363 // Optionally parse extensions if the keyword `for` is used. Aborts if none are given.
3464 let extensions = input
3565 . parse :: < Token ! [ for ] > ( )
@@ -81,6 +111,8 @@ impl Parse for TestEachArgs {
81111 module,
82112 function,
83113 extensions,
114+ attributes,
115+ async_fn,
84116 } )
85117 }
86118}
@@ -231,12 +263,29 @@ fn generate_from_tree(
231263 quote ! ( [ #arguments] )
232264 } ;
233265
234- stream. extend ( quote ! {
235- #[ test]
236- fn #file_name( ) {
237- ( #function) ( #arguments)
238- }
239- } ) ;
266+ for attribute in & parsed. attributes {
267+ stream. extend ( quote ! {
268+ #[ #attribute]
269+ } ) ;
270+ }
271+
272+ if let Some ( async_keyword) = & parsed. async_fn {
273+ // For async functions, we'd need something like `#[tokio::test]` instead of `#[test]`.
274+ // Here we assume the user will have already provided that in the list of attributes.
275+ stream. extend ( quote ! {
276+ #async_keyword fn #file_name( ) {
277+ ( #function) ( #arguments) . await
278+ }
279+ } ) ;
280+ } else {
281+ // Default, non-async test.
282+ stream. extend ( quote ! {
283+ #[ test]
284+ fn #file_name( ) {
285+ ( #function) ( #arguments)
286+ }
287+ } ) ;
288+ }
240289 }
241290
242291 Ok ( ( ) )
0 commit comments