target_test_dir_support/
procmacro.rs

1use proc_macro2::TokenStream;
2
3/// Transform a token stream of a test function definition into a proper `#[test]` fn
4///
5/// This requires the input to be a fn item just like a standard `#[test]` function _except_ it
6/// takes a single [std::path::PathBuf] argument to an empty test-specific directory. Any return
7/// type is propagated, although any errors during setting up the test directory cause panics.
8///
9/// Almost every use of this function would be via the `test_with_dir` macro within the
10/// `target-test-dir` crate; see that crate for examples.
11pub fn transform_test_with_dir<TS>(input: TS) -> TS
12where
13    TokenStream: From<TS>,
14    TS: From<TokenStream>,
15{
16    let input = TokenStream::from(input);
17
18    TS::from(transform_test_with_dir_inner(input).unwrap_or_else(syn::Error::into_compile_error))
19}
20
21fn transform_test_with_dir_inner(input: TokenStream) -> Result<TokenStream, syn::parse::Error> {
22    use quote::quote;
23    use syn::{parse2, Ident, ItemFn};
24
25    let mut implfn: ItemFn = parse2(input)?;
26
27    // Save the textual name for a generated wrapper function so that the actual #[test] has the
28    // user-specified name:
29    let testname = implfn.sig.ident;
30    let testnamestr = testname.to_string();
31
32    // Rename the user-provided implementation function to be wrapped:
33    let implname = Ident::new(&format!("{}_impl", &testnamestr), testname.span());
34    implfn.sig.ident = implname.clone();
35
36    // Propagate the return type:
37    let output = implfn.sig.output.clone();
38
39    // TODO: propagate the user test return type.
40    Ok(quote! {
41        #[test]
42        fn #testname() #output {
43            let testdir =
44            ::target_test_dir_support::get_base_test_dir()
45                .join(format!("{}-{}", module_path!().replace("::", "-"), #testnamestr));
46
47            match std::fs::create_dir(&testdir) {
48                Ok(()) => {}
49                Err(e) => {
50                    panic!("Could not create test dir {:?}: {}", testdir.display(), e);
51                }
52            }
53
54            #implname (testdir)
55        }
56
57        #implfn
58    })
59}
60
61#[cfg(test)]
62mod tests;