Skip to content
Open
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
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ exclude = ["tests/snapshots/**/*"]

[features]
with-regex = ["regex", "test-case-macros/with-regex"]
with-assert-override = ["test-case-macros/with-assert-override"]

[badges]
maintenance = { status = "actively-developed" }
Expand All @@ -29,10 +30,11 @@ regex = { version = "1.5", optional = true }
[dev-dependencies]
insta = "1.12"
itertools = "0.10"
regex = "1.5"

indexmap = "=1.8.2"
serde_yaml = "=0.8.25"
linked-hash-map = "=0.5.4"
regex = "1.5"
once_cell = "=1.13.0"

[workspace]
Expand Down
1 change: 1 addition & 0 deletions crates/test-case-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ exclude = ["tests/snapshots/**/*"]

[features]
with-regex = []
with-assert-override = []

[badges]
maintenance = { status = "actively-developed" }
Expand Down
112 changes: 112 additions & 0 deletions crates/test-case-macros/src/assert_override.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::{parse_quote, Token};

mod kw {
syn::custom_keyword!(assert_eq);
syn::custom_keyword!(assert);
}

#[derive(PartialEq, Debug)]
enum AssertOverrideKind {
AssertEq,
Assert,
}

impl Parse for AssertOverrideKind {
fn parse(input: ParseStream) -> syn::Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(kw::assert_eq) {
let _ = input.parse::<kw::assert_eq>()?;
Ok(AssertOverrideKind::AssertEq)
} else if lookahead.peek(kw::assert) {
let _ = input.parse::<kw::assert>()?;
Ok(AssertOverrideKind::Assert)
} else {
Err(lookahead.error())
}
}
}

struct AssertOverride {
override_kind: AssertOverrideKind,
path: syn::Path,
}

impl Parse for AssertOverride {
fn parse(input: ParseStream) -> syn::Result<Self> {
let override_kind = input.parse::<AssertOverrideKind>()?;
let _ = input.parse::<Token![:]>()?;
let path = input.parse::<syn::Path>()?;
Ok(Self {
override_kind,
path,
})
}
}

pub struct AssertOverrides {
overrides: Vec<AssertOverride>,
}

impl Parse for AssertOverrides {
fn parse(input: ParseStream) -> syn::Result<Self> {
let overrides = Punctuated::<AssertOverride, Token![,]>::parse_terminated(input)?;
Ok(Self {
overrides: overrides.into_iter().collect(),
})
}
}

pub fn assert_override_impl(items: AssertOverrides) -> TokenStream2 {
let mut assert_eq = None;
let mut assert = None;

for item in items.overrides {
match item.override_kind {
AssertOverrideKind::AssertEq => assert_eq = Some(item.path),
AssertOverrideKind::Assert => assert = Some(item.path),
}
}

let assert_eq = assert_eq.unwrap_or_else(|| parse_quote!(std::assert_eq));
let assert = assert.unwrap_or_else(|| parse_quote!(std::assert));

quote! {
mod __test_case_assert_override {
use super::*;

#[allow(unused_imports)]
pub use #assert_eq as assert_eq;

#[allow(unused_imports)]
pub use #assert as assert;
}
}
}

#[cfg(test)]
mod tests {
use quote::quote;

#[test]
fn can_parse_assert_override() {
let input = quote! {
assert_eq: my_assert_eq, assert: my_assert,
};
let items = syn::parse2::<super::AssertOverrides>(input).unwrap();
assert_eq!(items.overrides.len(), 2);
assert_eq!(
items.overrides[0].override_kind,
super::AssertOverrideKind::AssertEq
);
assert_eq!(items.overrides[0].path, syn::parse_quote!(my_assert_eq));
assert_eq!(
items.overrides[1].override_kind,
super::AssertOverrideKind::Assert
);
assert_eq!(items.overrides[1].path, syn::parse_quote!(my_assert));
}
}
7 changes: 6 additions & 1 deletion crates/test-case-macros/src/complex_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use proc_macro2::Group;
use proc_macro2::TokenStream;
use quote::{quote, TokenStreamExt};
use std::fmt::{Display, Formatter};
use std::str::FromStr;
use syn::__private::TokenStream2;
use syn::parse::{Parse, ParseStream};
use syn::{parse_quote, Expr};

Expand Down Expand Up @@ -209,7 +211,10 @@ impl ComplexTestCase {
pub fn assertion(&self) -> TokenStream {
let tokens = self.boolean_check();

quote! { assert!(#tokens) }
let assert_fn_ident =
TokenStream2::from_str(crate::assertions::ASSERT_OVERRIDE_ASSERT_PATH).unwrap();

quote! { #assert_fn_ident!(#tokens) }
}

fn boolean_check(&self) -> TokenStream {
Expand Down
6 changes: 5 additions & 1 deletion crates/test-case-macros/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::TokenStream2;
use quote::ToTokens;
use std::collections::HashSet;
use std::fmt::{Display, Formatter};
use std::str::FromStr;
use syn::parse::{Parse, ParseStream};
use syn::token::If;
use syn::{parse_quote, Attribute, Expr, Pat, Token};
Expand Down Expand Up @@ -118,8 +119,11 @@ impl Display for TestCaseResult {

impl TestCaseExpression {
pub fn assertion(&self) -> TokenStream2 {
let assert_fn_ident =
TokenStream2::from_str(crate::assertions::ASSERT_OVERRIDE_ASSERT_EQ_PATH).unwrap();

match &self.result {
TestCaseResult::Simple(expr) => parse_quote! { assert_eq!(#expr, _result) },
TestCaseResult::Simple(expr) => parse_quote! { #assert_fn_ident!(#expr, _result) },
TestCaseResult::Matching(pat, guard) => {
let pat_str = pat.to_token_stream().to_string();

Expand Down
27 changes: 24 additions & 3 deletions crates/test-case-macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,34 @@
extern crate proc_macro;

use proc_macro::TokenStream;

use syn::{parse_macro_input, ItemFn};

use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::parse_quote;
use syn::spanned::Spanned;
use syn::{parse_macro_input, ItemFn};
use test_case::TestCase;

#[cfg(feature = "with-assert-override")]
mod assert_override;
mod comment;
mod complex_expr;
mod expr;
mod modifier;
mod test_case;
mod utils;

#[cfg(feature = "with-assert-override")]
mod assertions {
pub const ASSERT_OVERRIDE_ASSERT_EQ_PATH: &str =
"crate::__test_case_assert_override::assert_eq";
pub const ASSERT_OVERRIDE_ASSERT_PATH: &str = "crate::__test_case_assert_override::assert";
}
#[cfg(not(feature = "with-assert-override"))]
mod assertions {
pub const ASSERT_OVERRIDE_ASSERT_EQ_PATH: &str = "core::assert_eq";
pub const ASSERT_OVERRIDE_ASSERT_PATH: &str = "core::assert";
}

/// Generates tests for given set of data
///
/// In general, test case consists of four elements:
Expand Down Expand Up @@ -68,6 +80,15 @@ pub fn test_case(args: TokenStream, input: TokenStream) -> TokenStream {
render_test_cases(&test_cases, item)
}

/// Prepares crate for use with `#[test_case]` macro and custom implementations of `assert_eq` and `assert_ne`.
#[cfg(feature = "with-assert-override")]
#[proc_macro]
pub fn test_case_assert_override(item: TokenStream) -> TokenStream {
let items = parse_macro_input!(item as crate::assert_override::AssertOverrides);

assert_override::assert_override_impl(items).into()
}

#[allow(unused_mut)]
fn render_test_cases(test_cases: &[TestCase], mut item: ItemFn) -> TokenStream {
let mut rendered_test_cases = vec![];
Expand Down
4 changes: 2 additions & 2 deletions scripts/test_all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ set -e

cargo clean

cargo +nightly clippy --all-targets --all-features -- -D warnings
cargo +nightly fmt --all
cargo clippy --all-targets --all-features -- -D warnings
cargo fmt --all
find . -name 'target' | xargs rm -rf
SNAPSHOT_DIR=rust-stable cargo +stable test --workspace --all-features
find . -name 'target' | xargs rm -rf
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,8 @@
pub use test_case_macros::test_case;
pub use test_case_macros::test_case as case;

#[cfg(feature = "with-assert-override")]
pub use test_case_macros::test_case_assert_override;

#[cfg(feature = "with-regex")]
pub use regex::*;
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "cases_can_override_default_assert_macros"
version = "0.1.0"
edition = "2018"

[lib]
name = "cases_can_panic"
path = "src/lib.rs"
doctest = false

[dev-dependencies]
test-case = { path = "../../../", features = ["with-assert-override"] }

[workspace]
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use test_case::test_case_assert_override;
use test_case::test_case;

test_case_assert_override!(
assert_eq: crate::assert_eq,
assert: std::assert,
);

pub mod testing_utils {
#[macro_export]
macro_rules! assert_eq {
($left:expr, $right:expr) => {
if $left != $right {
panic!("custom assertion failed: `(left == right)`\n left: `{:?}`,\n right: `{:?}`", $left, $right)
}
};
}
}

#[test_case(1 => 1)]
#[test_case(1 => 2)]
fn test(i: i32) -> i32 {
i
}

#[test_case(1 => is eq 1)]
#[test_case(1 => is eq 2)]
fn test_2(i: i32) -> i32 {
i
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "cases_can_override_default_assert_macros"
version = "0.1.0"
edition = "2018"

[lib]
name = "cases_can_panic"
path = "src/lib.rs"
doctest = false

[dev-dependencies]
test-case = { path = "../../../", features = ["with-assert-override"] }

[workspace]
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use test_case::test_case_assert_override;
use test_case::test_case;

test_case_assert_override!(
assert: std::assert,
);

#[test_case(1 => 1)]
#[test_case(1 => 2)]
fn test(i: i32) -> i32 {
i
}

#[test_case(1 => is eq 1)]
#[test_case(1 => is eq 2)]
fn test_2(i: i32) -> i32 {
i
}
3 changes: 0 additions & 3 deletions tests/acceptance_cases/cases_can_panic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ name = "cases_can_panic"
version = "0.1.0"
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
[lib]
name = "cases_can_panic"
path = "src/lib.rs"
Expand Down
10 changes: 10 additions & 0 deletions tests/acceptance_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,13 @@ fn cases_can_use_regex() {
fn features_produce_human_readable_errors() {
run_acceptance_test!("features_produce_human_readable_errors")
}

#[test]
fn cases_can_override_default_assert_macros() {
run_acceptance_test!("cases_can_override_default_assert_macros")
}

#[test]
fn cases_can_override_default_assert_macros_partial() {
run_acceptance_test!("cases_can_override_default_assert_macros_partial")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
source: tests/acceptance_tests.rs
assertion_line: 148
expression: output
---
test::_1_expects_2
test_2::_1_expects_complex_eq_2
left: `2`,
right: `1`', src/lib.rs:20:1
---- test::_1_expects_2 stdout ----
---- test_2::_1_expects_complex_eq_2 stdout ----
error: test failed, to rerun pass '--lib'
failures:
failures:
running 4 tests
test result: FAILED. 2 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out
test test::_1_expects_1 ... ok
test test::_1_expects_2 ... FAILED
test test_2::_1_expects_complex_eq_1 ... ok
test test_2::_1_expects_complex_eq_2 ... FAILED
thread 'test::_1_expects_2' panicked at 'custom assertion failed: `(left == right)`
thread 'test_2::_1_expects_complex_eq_2' panicked at 'assertion failed: _result == 2', src/lib.rs:26:1
Loading