| 1 | use proc_macro::TokenStream; |
| 2 | use quote::{ToTokens, format_ident, quote}; |
| 3 | use syn::parse_macro_input; |
| 4 | |
| 5 | /// Attribute to use `wasm_bindgen_test` for wasm32 targets and `tokio::test` |
| 6 | /// for everything else with async-support and custom result-types |
| 7 | #[proc_macro_attribute] |
| 8 | pub fn async_test(_attr: TokenStream, item: TokenStream) -> TokenStream { |
| 9 | let fun = parse_macro_input!(item as syn::ItemFn); |
| 10 | |
| 11 | if !fun.sig.ident.to_string().starts_with("test_") { |
| 12 | panic!("test function names must start with test_"); |
| 13 | } |
| 14 | |
| 15 | // on the regular return-case, we can just use cfg_attr and quit early |
| 16 | if fun.sig.output == syn::ReturnType::Default { |
| 17 | let attrs = r#" |
| 18 | #[cfg_attr(not(target_family = "wasm"), tokio::test)] |
| 19 | #[cfg_attr(target_family = "wasm", wasm_bindgen_test::wasm_bindgen_test)] |
| 20 | "#; |
| 21 | |
| 22 | let mut out: TokenStream = attrs.parse().expect("Static works"); |
| 23 | let inner: TokenStream = fun.into_token_stream().into(); |
| 24 | out.extend(inner); |
| 25 | return out; |
| 26 | } |
| 27 | |
| 28 | // on the more complicated case, where we have some `->`-Result-Arrow |
| 29 | // `wasm_bindgen_test` doesn't yet support |
| 30 | // - see https://github.com/rustwasm/wasm-bindgen/issues/2565 - |
| 31 | // we split the attribution into to functions: one with the original |
| 32 | // to be attributed for non-wasm, and then a second outer-wrapper function |
| 33 | // that calls the first in wasm32 cases. |
| 34 | |
| 35 | let attrs = r#" |
| 36 | #[cfg_attr(not(target_family = "wasm"), tokio::test)] |
| 37 | "#; |
| 38 | |
| 39 | let mut out: TokenStream = attrs.parse().expect("Static works."); |
| 40 | |
| 41 | let mut outer = fun.clone(); |
| 42 | let fn_name = fun.sig.ident.clone(); |
| 43 | let fn_call: TokenStream = if fun.sig.asyncness.is_some() { |
| 44 | quote! { |
| 45 | { |
| 46 | let res = #fn_name().await; |
| 47 | assert!(res.is_ok(), "{:?}", res); |
| 48 | } |
| 49 | } |
| 50 | } else { |
| 51 | quote! { |
| 52 | { |
| 53 | let res = #fn_name(); |
| 54 | assert!(res.is_ok(), "{:?}", res); |
| 55 | } |
| 56 | } |
| 57 | } |
| 58 | .into(); |
| 59 | outer.sig.output = syn::ReturnType::Default; |
| 60 | outer.sig.ident = format_ident!("{}_outer", fun.sig.ident); |
| 61 | outer.block = Box::new(parse_macro_input!(fn_call as syn::Block)); |
| 62 | |
| 63 | let inner: TokenStream = fun.into_token_stream().into(); |
| 64 | out.extend(inner); |
| 65 | |
| 66 | let attrs = r#" |
| 67 | #[cfg(target_family = "wasm")] |
| 68 | #[wasm_bindgen_test::wasm_bindgen_test] |
| 69 | "#; |
| 70 | let outer_attrs: TokenStream = attrs.parse().expect("Static works."); |
| 71 | let of: TokenStream = outer.into_token_stream().into(); |
| 72 | out.extend(outer_attrs); |
| 73 | out.extend(of); |
| 74 | |
| 75 | out |
| 76 | } |