1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
use quote::quote;
use syn::{
    parse_macro_input, parse_quote, AttributeArgs, ItemFn, Lit, Meta, MetaNameValue, NestedMeta,
    Path,
};

#[proc_macro_attribute]
pub fn festive(
    args: proc_macro::TokenStream,
    input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
    let args = parse_macro_input!(args as AttributeArgs);
    let timeout: syn::Expr = syn::parse_str(&format!("{:?}", parse_args(&args))).unwrap();

    let ItemFn {
        attrs,
        vis,
        sig,
        block,
    } = parse_macro_input!(input as ItemFn);
    let fun_name = sig.ident.to_string();

    (quote! {
        #(
            #attrs
        )*
        #[test]
        #vis #sig {
            fn inner() #block

            struct _Anon;
            let fork_id = ::festive::ForkId::of(::std::any::TypeId::of::<_Anon>());

            let path = format!("{}::{}", ::std::module_path!(), #fun_name);

            let res = ::festive::fork(
                &path,
                fork_id,
                #timeout,
                inner,
            ).expect("forking test failed");

            let stringout = format!(
                "Child stdout:: {}\nChild stderr: {}",
                String::from_utf8_lossy(&res.stdout),
                String::from_utf8_lossy(&res.stderr)
            );

            assert!(res.status.success(), stringout);
        }
    })
    .into()
}

fn parse_args(args: &[NestedMeta]) -> Option<u64> {
    let timeout_path: Path = parse_quote!(timeout_ms);
    for a in args {
        match a {
            NestedMeta::Meta(Meta::NameValue(MetaNameValue { path, lit, .. }))
                if path == &timeout_path =>
            {
                if let Lit::Int(li) = lit {
                    return li.base10_parse().ok();
                } else {
                    panic!("timeout_ms should have an int value")
                }
            }
            _ => {}
        }
    }
    None
}