testing_macros/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
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::ItemFn;
mod fixture;
/// Create tests from files.
///
/// **NOTE: Path should be relative to the directory of `Cargo.toml` file**.
/// This is limitation of current proc macro api.
///
/// # Why
///
/// If you create test dynamically, running a specific test become very
/// cumbersome.
///
/// For example, if you use `test` crate with nightly, you can't use `cargo test
/// foo` to run tests with name containing `foo`. Instead, you have to implement
/// your own ignoring logic
///
///
/// # Usage
///
/// If you want to load all typescript files from `pass`
///
/// ```rust,ignore
///
/// #[fixture("pass/**/*.{ts,tsx}")]
/// fn pass(file: PathBuf) {
/// // test by reading file
/// }
/// ```
///
/// # Return value
///
/// The function is allowed to return `Result` on error. If it's the case
///
///
/// ## Ignoring a test
///
/// If the path to the file contains a component starts with `.` (dot), it will
/// be ignore. This convention is widely used in many projects (including other
/// languages), as a file or a directory starting with `.` means hidden file in
/// unix system.
///
/// Note that they are added as a test `#[ignore]`, so you can use
/// `cargo test -- --ignored` or `cargo test -- --include-ignored` to run those
/// tests.
///
///
/// # Roadmap
///
/// - Support async function
#[proc_macro_attribute]
pub fn fixture(attr: TokenStream, item: TokenStream) -> TokenStream {
let item: ItemFn = syn::parse(item).expect("failed to parse input as a function item");
if cfg!(feature = "rust-analyzer") {
return quote!(
#[allow(unused)]
#item
)
.into();
}
let config: self::fixture::Config =
syn::parse(attr).expect("failed to parse input passed to #[fixture]");
let cases = self::fixture::expand(&item.sig.ident, config).unwrap();
let mut output = proc_macro2::TokenStream::new();
for case in cases {
case.to_tokens(&mut output);
}
item.to_tokens(&mut output);
output.into()
}