diff --git a/Cargo.toml b/Cargo.toml index 5ad7a68..7ee333d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,3 +70,7 @@ tracing = {version = "0.1.20"} # # Probably fixed by https://github.com/rust-lang-nursery/lazy-static.rs/pull/107. _lazy_static_unused = { package = "lazy_static", version = "1.0.2" } + +[patch.crates-io] +test-case = { git = "https://github.com/kezhuw/test-case.git", branch = "test-proc-macros-cooperation" } +tokio = { git = "https://github.com/kezhuw/tokio.git", branch = "test-proc-macros-cooperation" } diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 6d29b75..7093aaf 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -43,13 +43,28 @@ fn parse_attrs(attrs: Vec) -> syn::Result<(AttributeArgs, Vec syn::Result { - let inner_test = if attr.is_empty() { - quote! { ::core::prelude::v1::test } - } else { - attr.into() +// Check whether given attribute is `#[test]` or `#[::core::prelude::v1::test]`. +fn is_test_attribute(attr: &Attribute) -> bool { + let path = match &attr.meta { + syn::Meta::Path(path) => path, + _ => return false, }; + let segments = ["core", "prelude", "v1", "test"]; + if path.leading_colon.is_none() { + return path.segments.len() == 1 + && path.segments[0].arguments.is_none() + && path.segments[0].ident == "test"; + } else if path.segments.len() != segments.len() { + return false; + } + path + .segments + .iter() + .zip(segments) + .all(|(segment, path)| segment.arguments.is_none() && segment.ident == path) +} +fn try_test(attr: TokenStream, input: ItemFn) -> syn::Result { let ItemFn { attrs, vis, @@ -61,9 +76,23 @@ fn try_test(attr: TokenStream, input: ItemFn) -> syn::Result { let logging_init = expand_logging_init(&attribute_args); let tracing_init = expand_tracing_init(&attribute_args); + let (inner_test, generated_test) = if attr.is_empty() { + let has_test = ignored_attrs.iter().any(is_test_attribute); + let generated_test = if has_test { + quote! {} + } else { + quote! { #[::core::prelude::v1::test]} + }; + (quote! {}, generated_test) + } else { + let attr = Tokens::from(attr); + (quote! { #[#attr] }, quote! {}) + }; + let result = quote! { - #[#inner_test] + #inner_test #(#ignored_attrs)* + #generated_test #vis #sig { // We put all initialization code into a separate module here in // order to prevent potential ambiguities that could result in diff --git a/tests/mod.rs b/tests/mod.rs index 79b0bdc..3c6e5d4 100644 --- a/tests/mod.rs +++ b/tests/mod.rs @@ -57,6 +57,44 @@ fn with_inner_test_attribute_and_test_args_and_panic(x: i8, _y: i8) { assert_eq!(x, 0); } +#[test_log::test] +#[test] +fn with_existing_test_attribute() {} + +#[test_log::test] +#[::core::prelude::v1::test] +fn with_existing_generated_test_attribute() {} + +#[tokio::test] +#[test_log::test] +async fn with_append_test_attribute_and_async() { + assert_eq!(async { 42 }.await, 42) +} + +#[test_case::test_case(-2, -4)] +#[test_case::test_case(-2, -5)] +#[test_log::test] +fn with_append_test_attribute_and_test_args(x: i8, _y: i8) { + assert_eq!(x, -2); +} + +#[should_panic] +#[test_case::test_case(-2, -4)] +#[test_case::test_case(-3, -4)] +#[test_log::test] +fn with_append_test_attribute_and_test_args_and_panic(x: i8, _y: i8) { + assert_eq!(x, 0); +} + +#[should_panic] +#[test_case::test_case(-2, -4)] +#[test_case::test_case(-3, -4)] +#[tokio::test] +#[test_log::test] +async fn with_append_test_attribute_and_test_args_and_panic_async(x: i8, _y: i8) { + assert_eq!(x, 0); +} + #[instrument] async fn instrumented(input: usize) -> usize { info!("input = {}", input);