-
Notifications
You must be signed in to change notification settings - Fork 42
/
Copy pathlib.rs
86 lines (79 loc) · 2.69 KB
/
lib.rs
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//! Macro to wrap a live test function that automatically creates and cleans up
//! the `LiveTestContext`
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ItemFn};
/// Define a test function that uses `LiveTestContext`
///
/// This is usable only within the `omicron-live-tests` crate.
///
/// Similar to `nexus_test`, this macro lets you define a test function that
/// behaves like `tokio::test` except that it accepts an argument of type
/// `&LiveTestContext`. The `LiveTestContext` is cleaned up on _successful_
/// return of the test function. On failure, debugging information is
/// deliberately left around.
///
/// Example usage:
///
/// ```ignore
/// #[live_test]
/// async fn test_my_test_case(lc: &LiveTestContext) {
/// assert!(true);
/// }
/// ```
///
/// We use this instead of implementing Drop on LiveTestContext because we want
/// the teardown to only happen when the test doesn't fail (which causes a panic
/// and unwind).
#[proc_macro_attribute]
pub fn live_test(_attrs: TokenStream, input: TokenStream) -> TokenStream {
let input_func = parse_macro_input!(input as ItemFn);
let mut correct_signature = true;
if input_func.sig.variadic.is_some()
|| input_func.sig.inputs.len() != 1
|| input_func.sig.asyncness.is_none()
{
correct_signature = false;
}
// Verify we're returning an empty tuple
correct_signature &= match input_func.sig.output {
syn::ReturnType::Default => true,
syn::ReturnType::Type(_, ref t) => {
if let syn::Type::Tuple(syn::TypeTuple { elems, .. }) = &**t {
elems.is_empty()
} else {
false
}
}
};
if !correct_signature {
panic!("func signature must be async fn(&LiveTestContext)");
}
let func_ident_string = input_func.sig.ident.to_string();
let func_ident = input_func.sig.ident.clone();
let new_block = quote! {
{
#input_func
let ctx = crate::common::LiveTestContext::new(
#func_ident_string
).await.expect("setting up LiveTestContext");
#func_ident(&ctx).await;
ctx.cleanup_successful().await;
}
};
let mut sig = input_func.sig.clone();
sig.inputs.clear();
let func = ItemFn {
attrs: input_func.attrs,
vis: input_func.vis,
sig,
block: Box::new(syn::parse2(new_block).unwrap()),
};
TokenStream::from(quote!(
#[::tokio::test]
#func
))
}