Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ env_logger = {version = "0.11", default-features = false, optional = true}

[dev-dependencies]
logging = {version = "0.4.8", package = "log"}
rstest = "0.25.0"
test-case = {version = "3.1"}
tokio = {version = "1.0", default-features = false, features = ["rt-multi-thread", "macros"]}
tokio = {version = "1.38", default-features = false, features = ["rt-multi-thread", "macros"]}
tracing = {version = "0.1.20"}
51 changes: 45 additions & 6 deletions macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,38 @@ fn parse_attrs(attrs: Vec<Attribute>) -> syn::Result<(AttributeArgs, Vec<Attribu
}
}

fn try_test(attr: TokenStream, input: ItemFn) -> syn::Result<Tokens> {
let inner_test = if attr.is_empty() {
quote! { ::core::prelude::v1::test }
} else {
attr.into()
// Check whether given attribute is a test attribute of forms:
// * `#[test]`
// * `#[core::prelude::*::test]` or `#[::core::prelude::*::test]`
// * `#[std::prelude::*::test]` or `#[::std::prelude::*::test]`
fn is_test_attribute(attr: &Attribute) -> bool {
let path = match &attr.meta {
syn::Meta::Path(path) => path,
_ => return false,
};
let candidates = [
["core", "prelude", "*", "test"],
["std", "prelude", "*", "test"],
];
if path.leading_colon.is_none()
&& path.segments.len() == 1
&& path.segments[0].arguments.is_none()
&& path.segments[0].ident == "test"
{
return true;
} else if path.segments.len() != candidates[0].len() {
return false;
}
candidates.into_iter().any(|segments| {
path
.segments
.iter()
.zip(segments)
.all(|(segment, path)| segment.arguments.is_none() && (path == "*" || segment.ident == path))
})
}

fn try_test(attr: TokenStream, input: ItemFn) -> syn::Result<Tokens> {
let ItemFn {
attrs,
vis,
Expand All @@ -63,9 +88,23 @@ fn try_test(attr: TokenStream, input: ItemFn) -> syn::Result<Tokens> {
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
Expand Down
44 changes: 44 additions & 0 deletions tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

use tokio::runtime::Builder;

use rstest::rstest;
use tracing::debug;
use tracing::error;
use tracing::info;
Expand Down Expand Up @@ -57,6 +58,49 @@ 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)
}

#[rstest]
#[case(-2, -4)]
#[case(-2, -5)]
#[test_log::test]
fn with_append_test_attribute_and_test_args(#[case] x: i8, #[case] _y: i8) {
assert_eq!(x, -2);
}

#[rstest]
#[case(-2, -4)]
#[case(-3, -4)]
#[test_log::test]
// Applied to all cases, must not come before `rstest`, see https://github.com/la10736/rstest/issues/210
#[should_panic] // https://docs.rs/rstest/0.25.0/rstest/attr.rstest.html#use-specific-case-attributes
fn with_append_test_attribute_and_test_args_and_panic(#[case] x: i8, #[case] _y: i8) {
assert_eq!(x, 0);
}

#[rstest]
#[case(-2, -4)]
#[case(-3, -4)]
#[tokio::test]
#[test_log::test]
// Applied to all cases, must not come before `rstest`, see https://github.com/la10736/rstest/issues/210
#[should_panic] // https://docs.rs/rstest/0.25.0/rstest/attr.rstest.html#use-specific-case-attributes
async fn with_append_test_attribute_and_test_args_and_panic_async(#[case] x: i8, #[case] _y: i8) {
assert_eq!(x, 0);
}

#[instrument]
async fn instrumented(input: usize) -> usize {
info!("input = {}", input);
Expand Down