Skip to content

Commit

Permalink
fix: improve compatibility among test proc macros
Browse files Browse the repository at this point in the history
This pr proposes a generic mechanism among different test proc macros to
avoid to generate multiple `[::core::prelude::v1::test]` on test method.

`proc_macro_attribute` function is fed with tokens after its attribute
and no tokens before it.

Give the above, this pr proposes test proc macros to append newly
generated macros after existing ones. This way, proc macros processed
later can read all macros including generated and handwritten and make
further decisions. Specifically, proc macros can append
`#[::core::prelude::v1::test]` only if it does not exist.

Macros that transform test method signature can append
`#[::core::prelude::v1::test]` directly without checking its existence
once they generate valid signature for test method.

Closes frondeus#101, frondeus#146.
  • Loading branch information
kezhuw committed Nov 20, 2024
1 parent 39526d4 commit ffa1f6f
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 7 deletions.
40 changes: 38 additions & 2 deletions crates/test-case-core/src/test_case.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,38 @@ use proc_macro2::{Span as Span2, TokenStream as TokenStream2};
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::{parse_quote, Error, Expr, Ident, ItemFn, ReturnType, Token};
use syn::{parse_quote, Attribute, Error, Expr, Ident, ItemFn, ReturnType, Token};

// 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,
};
const CANDIDATES_LEN: usize = 4;

let candidates: [[&str; CANDIDATES_LEN]; 2] = [
["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)
})
})
}

#[derive(Debug)]
pub struct TestCase {
Expand Down Expand Up @@ -99,7 +130,12 @@ impl TestCase {
quote! { let _result = super::#item_name(#(#arg_values),*).await; },
)
} else {
attrs.insert(0, parse_quote! { #[::core::prelude::v1::test] });
let qualified_test_attr = parse_quote! { #[::core::prelude::v1::test] };
if let Some(attr) = attrs.iter().find(|attr| is_test_attribute(attr)) {
let msg = "second test attribute is supplied, consider removing or changing the order of your test attributes";
return Error::new_spanned(attr, msg).into_compile_error();
}
attrs.push(qualified_test_attr);
(
TokenStream2::new(),
quote! { let _result = super::#item_name(#(#arg_values),*); },
Expand Down
2 changes: 1 addition & 1 deletion crates/test-case-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ fn expand_additional_test_case_macros(item: &mut ItemFn) -> syn::Result<Vec<(Tes
}

for i in attrs_to_remove.into_iter().rev() {
item.attrs.swap_remove(i);
item.attrs.remove(i);
}

Ok(additional_cases)
Expand Down
2 changes: 1 addition & 1 deletion tests/acceptance_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ macro_rules! run_acceptance_test {

let output = sanitize_lines(output);

insta::assert_display_snapshot!(output);
insta::assert_snapshot!(output);
})
};
($case_name:expr) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ expression: output
error: All literal values must be of the same type
error: Range bounds can only be an integer literal
error: Unbounded ranges are not supported
error: could not compile `matrices_compilation_errors` (lib test) due to 5 previous errors
error: could not compile `matrices_compilation_errors` (lib test) due to 5 previous errors; 1 warning emitted
error: number too large to fit in target type
error[E0308]: mismatched types
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ source: tests/acceptance_tests.rs
expression: output
---
error: 'with-regex' feature is required to use 'matches-regex' keyword
error: could not compile `features_produce_human_readable_errors` (lib test) due to previous error
error: could not compile `features_produce_human_readable_errors` (lib test) due to 1 previous error
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ expression: output
error: All literal values must be of the same type
error: Range bounds can only be an integer literal
error: Unbounded ranges are not supported
error: could not compile `matrices_compilation_errors` (lib test) due to 5 previous errors
error: could not compile `matrices_compilation_errors` (lib test) due to 5 previous errors; 1 warning emitted
error: number too large to fit in target type
error[E0308]: mismatched types

0 comments on commit ffa1f6f

Please sign in to comment.