Skip to content

Commit

Permalink
Do not submit!
Browse files Browse the repository at this point in the history
Macro for logging with test_case. Might be needed once this bug is fixed:
frondeus/test-case#101
See here:
https://docs.google.com/document/d/1zAbVqlSNeM5zaOqt4b-hFISlKy6ua9bh0mm99SHrb4E/edit#
  • Loading branch information
yuvalsw committed Nov 21, 2022
1 parent fe68d2b commit d695187
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 0 deletions.
22 changes: 22 additions & 0 deletions crates/test_utils_macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "test_utils_macros"
version.workspace = true
edition.workspace = true

[lib]
proc-macro = true

[features]
testing = []

[dependencies]
env_logger.workspace = true
indexmap.workspace = true
itertools.workspace = true
syn.workspace = true
quote.workspace = true

[dev-dependencies]
proc-macro2 = "1.0"
test-case-macros = "2.2.1"
test-case.workspace = true
81 changes: 81 additions & 0 deletions crates/test_utils_macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use proc_macro::TokenStream;
use quote::__private::TokenStream as TokenStream2;
use quote::{quote, ToTokens};
use syn::parse::{Parse, ParseStream, Parser};
use syn::punctuated::Punctuated;
use syn::{parenthesized, token, Error, Expr, LitStr, Token};

// A macro to replace #[test], which allows logging.
#[proc_macro_attribute]
pub fn test(_attr: TokenStream, item: TokenStream) -> TokenStream {
let item_fn = syn::parse_macro_input!(item as syn::ItemFn);
proc_macro::TokenStream::from(quote! {
#[test_log::test]
#item_fn
})
}

/// Represents a single test-case of the form: `(args...; "name")`.
struct WrappedTestCase {
_paren_token: token::Paren,
test_case: Punctuated<Expr, Token![,]>,
_semi: Token![;],
name: LitStr,
}
impl Parse for WrappedTestCase {
fn parse(input: ParseStream<'_>) -> Result<Self, Error> {
let content;
Ok(WrappedTestCase {
_paren_token: parenthesized!(content in input),
test_case: Punctuated::parse_separated_nonempty_with(&content, Expr::parse)?,
_semi: content.parse()?,
name: content.parse()?,
})
}
}
impl ToTokens for WrappedTestCase {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let test_case = &self.test_case;
let name = &self.name;
let all = quote! { (#test_case; #name) };
tokens.extend(all);
}
}

/// A comma-separated array of `WrappedTestCase`s. e.g.: `(1, "case1"), (2, "case2")`.
struct TestCaseArray {
pub test_cases: Punctuated<WrappedTestCase, Token![,]>,
}
impl Parse for TestCaseArray {
fn parse(input: ParseStream<'_>) -> Result<Self, Error> {
Ok(TestCaseArray {
test_cases: Punctuated::parse_separated_nonempty_with(input, WrappedTestCase::parse)?,
})
}
}

/// A macro to replace #[test_case], which allows logging.
/// The syntax is similar but not the same: instead of having multiple `#[test_case(...)]`
/// attributes, use a single `#[test_case((1, "case1"), (2, "case2"))]` attribute.
#[proc_macro_attribute]
pub fn test_case(attr: TokenStream, item: TokenStream) -> TokenStream {
let item_fn = syn::parse_macro_input!(item as syn::ItemFn);

let mut all_test_cases = quote! {};
let parser = TestCaseArray::parse;
let test_case_arr = parser.parse(attr).expect("unexpected macro failure");

for wrapped_test_case in test_case_arr.test_cases {
let test_case = wrapped_test_case.test_case;
all_test_cases = quote! {
#all_test_cases
#[test_case::test_case(#test_case)]
};
}

proc_macro::TokenStream::from(quote! {
#all_test_cases
#[test_log::test]
#item_fn
})
}

0 comments on commit d695187

Please sign in to comment.