near_prop_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::{format_ident, quote};
3use syn::{
4    parse_macro_input, punctuated::Punctuated, token::Comma, AttributeArgs, Error, ItemFn,
5    NestedMeta,
6};
7
8/// Mark an async function to be fuzz-tested using [near-prop], within a tokio
9/// executor.
10///
11/// # Usage
12///
13/// ```
14/// #[near_prop::test]
15/// async fn fuzz_me(_ctx: PropContext, fuzz_arg: String) -> bool {
16///     fuzz_arg != "fuzzed".to_owned()
17/// }
18/// ```
19///
20/// # Attribute arguments
21///
22/// Arguments to this attribute are passed through to [tokio::test].
23///
24/// ```
25/// #[near_prop::test(core_threads = 3)]
26/// async fn fuzz_me(_ctx: PropContext, fuzz_arg: String) -> bool {
27///     fuzz_arg != "fuzzed".to_owned()
28/// }
29/// ```
30/// [near-prop]: https://github.com/austinabell/near-prop
31/// [tokio::test]: https://docs.rs/tokio/latest/tokio/attr.test.html
32#[proc_macro_attribute]
33pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
34    let fn_item = parse_macro_input!(item as ItemFn);
35
36    for attr in &fn_item.attrs {
37        if attr.path.is_ident("test") {
38            return Error::new_spanned(&fn_item, "multiple #[test] attributes were supplied")
39                .to_compile_error()
40                .into();
41        }
42    }
43
44    if fn_item.sig.asyncness.is_none() {
45        return Error::new_spanned(&fn_item, "test fn must be async")
46            .to_compile_error()
47            .into();
48    }
49
50    let p_args = parse_macro_input!(args as AttributeArgs);
51    let attrib: Punctuated<NestedMeta, Comma> = p_args.into_iter().collect();
52
53    let call_by = format_ident!("{}", fn_item.sig.ident);
54
55    quote! (
56        #[::tokio::test(#attrib)]
57        async fn #call_by() {
58            #fn_item
59
60            ::near_prop::prop_test(#call_by as fn(PropContext, _) -> _).await
61        }
62    )
63    .into()
64}