From 7ce34ca8a202b8f07fb481c637047f4c0c78cc16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Biel?= Date: Wed, 18 Oct 2023 09:42:03 +0200 Subject: [PATCH 1/8] tests: update to newest stable --- ...cceptance__cases_can_be_declared_on_non_test_items.snap | 2 +- .../snapshots/rust-stable/acceptance__cases_can_panic.snap | 7 ++++--- .../rust-stable/acceptance__cases_can_use_regex.snap | 6 +++--- ...ance__cases_declared_on_non_test_items_can_be_used.snap | 2 +- .../acceptance__cases_support_complex_assertions.snap | 2 +- .../acceptance__cases_support_pattern_matching.snap | 4 ++-- 6 files changed, 12 insertions(+), 11 deletions(-) diff --git a/tests/snapshots/rust-stable/acceptance__cases_can_be_declared_on_non_test_items.snap b/tests/snapshots/rust-stable/acceptance__cases_can_be_declared_on_non_test_items.snap index 755cf16..f5cf842 100644 --- a/tests/snapshots/rust-stable/acceptance__cases_can_be_declared_on_non_test_items.snap +++ b/tests/snapshots/rust-stable/acceptance__cases_can_be_declared_on_non_test_items.snap @@ -10,4 +10,4 @@ test internal_tested_function3::_1_expects_matching_3_ ... ok test internal_tested_function3::_2_expects_inconclusive6 ... ignored test internal_tested_function4::_2_expects_panicking_some_can_t_ - should panic ... ok test result: FAILED. 4 passed; 1 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.00s -thread 'internal_tested_function1::_3_expects_6' panicked at 'assertion failed: `(left == right)` +thread 'internal_tested_function1::_3_expects_6' panicked at src/lib.rs:8:1: diff --git a/tests/snapshots/rust-stable/acceptance__cases_can_panic.snap b/tests/snapshots/rust-stable/acceptance__cases_can_panic.snap index 5215572..c50ab6a 100644 --- a/tests/snapshots/rust-stable/acceptance__cases_can_panic.snap +++ b/tests/snapshots/rust-stable/acceptance__cases_can_panic.snap @@ -7,6 +7,7 @@ test panicking::_expects_panicking_some_it_has_to_panic_ - should panic ... ok test panicking::_expects_panicking_some_this_should_fail_ - should panic ... FAILED test panics_without_value::_expects_panicking_none - should panic ... ok test pattern_matching_result_fails::simpleenum_var1_expects_matching_simpleenum_var2_ - should panic ... ok -test result: FAILED. 4 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s -test result_which_panics::_2_2_expects_2_3 - should panic ... ok -thread 'panicking::_expects_panicking_some_this_should_fail_' panicked at 'It has to panic', src/lib.rs:20:5 +test result: FAILED. 3 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s +test result_which_panics::_2_2_expects_2_3 - should panic ... FAILED +thread 'panicking::_expects_panicking_some_this_should_fail_' panicked at src/lib.rs:20:5: +thread 'result_which_panics::_2_2_expects_2_3' panicked at src/lib.rs:28:1: diff --git a/tests/snapshots/rust-stable/acceptance__cases_can_use_regex.snap b/tests/snapshots/rust-stable/acceptance__cases_can_use_regex.snap index 555c99a..1c3a246 100644 --- a/tests/snapshots/rust-stable/acceptance__cases_can_use_regex.snap +++ b/tests/snapshots/rust-stable/acceptance__cases_can_use_regex.snap @@ -11,6 +11,6 @@ test regex_test::_kumkwat_expects_complex_regex_r_ ... FAILED test regex_test::_kumkwat_expects_complex_regex_r_abc_ ... FAILED test regex_test::_kumkwat_expects_complex_regex_r_kumkwat_ ... ok test result: FAILED. 3 passed; 3 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s -thread 'regex_test::_abcabc201_expects_complex_regex_r_d_4_' panicked at 'assertion failed: {/n let re = ::test_case::Regex::new(r#/"//d{4}/"#).expect(/"Regex::new/");/n re.is_match(_result)/n}', src/lib.rs:5:1 -thread 'regex_test::_kumkwat_expects_complex_regex_r_' panicked at 'Regex::new: Syntax( -thread 'regex_test::_kumkwat_expects_complex_regex_r_abc_' panicked at 'assertion failed: {/n let re = ::test_case::Regex::new(r#/"abc/"#).expect(/"Regex::new/");/n re.is_match(_result)/n}', src/lib.rs:5:1 +thread 'regex_test::_abcabc201_expects_complex_regex_r_d_4_' panicked at src/lib.rs:5:1: +thread 'regex_test::_kumkwat_expects_complex_regex_r_' panicked at src/lib.rs:5:1: +thread 'regex_test::_kumkwat_expects_complex_regex_r_abc_' panicked at src/lib.rs:5:1: diff --git a/tests/snapshots/rust-stable/acceptance__cases_declared_on_non_test_items_can_be_used.snap b/tests/snapshots/rust-stable/acceptance__cases_declared_on_non_test_items_can_be_used.snap index e593821..5e2f70e 100644 --- a/tests/snapshots/rust-stable/acceptance__cases_declared_on_non_test_items_can_be_used.snap +++ b/tests/snapshots/rust-stable/acceptance__cases_declared_on_non_test_items_can_be_used.snap @@ -2,4 +2,4 @@ source: tests/acceptance_tests.rs expression: output --- -thread 'main' panicked at 'Can't', src/lib.rs:33:5 +thread 'main' panicked at src/lib.rs:33:5: diff --git a/tests/snapshots/rust-stable/acceptance__cases_support_complex_assertions.snap b/tests/snapshots/rust-stable/acceptance__cases_support_complex_assertions.snap index 43d4632..c56b26a 100644 --- a/tests/snapshots/rust-stable/acceptance__cases_support_complex_assertions.snap +++ b/tests/snapshots/rust-stable/acceptance__cases_support_complex_assertions.snap @@ -58,4 +58,4 @@ test not_path::_cargo_toml_parse_unwrap_expects_complex_not_path_dir ... ok test not_path::_cargo_yaml_parse_unwrap_expects_complex_not_path_path ... ok test not_path::_src_parse_unwrap_expects_complex_not_path_file ... ok test result: FAILED. 53 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s -thread 'empty::vec_0_expects_complex_empty' panicked at 'assertion failed: _result.is_empty()', src/lib.rs:115:1 +thread 'empty::vec_0_expects_complex_empty' panicked at src/lib.rs:115:1: diff --git a/tests/snapshots/rust-stable/acceptance__cases_support_pattern_matching.snap b/tests/snapshots/rust-stable/acceptance__cases_support_pattern_matching.snap index 41483dd..4cd298a 100644 --- a/tests/snapshots/rust-stable/acceptance__cases_support_pattern_matching.snap +++ b/tests/snapshots/rust-stable/acceptance__cases_support_pattern_matching.snap @@ -9,5 +9,5 @@ test extended_pattern_matching_result::simpleenum_var1_expects_matching_ok_e_e_s test extended_pattern_matching_result::simpleenum_var2_expects_matching_err_e_e_var2_ ... ok test pattern_matching_result::simpleenum_var2_expects_matching_simpleenum_var2_ ... ok test result: FAILED. 3 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s -thread 'extended_pattern_matching_result::err_should_fail' panicked at 'Expected `Err(e) if e == "var1"` found Err("var2")', src/lib.rs:16:1 -thread 'extended_pattern_matching_result::ok_should_fail' panicked at 'Expected `Ok(e) if e == SimpleEnum :: Var2` found Ok(Var1)', src/lib.rs:16:1 +thread 'extended_pattern_matching_result::err_should_fail' panicked at src/lib.rs:16:1: +thread 'extended_pattern_matching_result::ok_should_fail' panicked at src/lib.rs:16:1: From bf3ee272741ef43ccac69bc2537d392316f7bc09 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Thu, 19 Oct 2023 08:26:09 -0400 Subject: [PATCH 2/8] deps: disable proc-macro-error default features (#134) * .github: add scheduled build This will help PR authors determine if CI failures are caused by their changes or by e.g. Rust releases. * deps: disable proc-macro-error default features proc-macro-error has an optional dependency on syn@1 whereas much of the ecosystem has migrated to syn@2, resulting in dependents of test-case having to compile syn twice. This avoids that by removing the optional dependency. --- .github/workflows/test.yml | 2 ++ crates/test-case-core/Cargo.toml | 2 +- crates/test-case-macros/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7dda8f9..b7c91c1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,6 +6,8 @@ on: branches: - master - develop + schedule: + - cron: "0 0 * * *" jobs: validate: diff --git a/crates/test-case-core/Cargo.toml b/crates/test-case-core/Cargo.toml index ba15c27..a6e1166 100644 --- a/crates/test-case-core/Cargo.toml +++ b/crates/test-case-core/Cargo.toml @@ -24,6 +24,6 @@ path = "src/lib.rs" [dependencies] cfg-if = "1.0" proc-macro2 = { version = "1.0", features = [] } -proc-macro-error = "1.0" +proc-macro-error = { version = "1.0", default-features = false } quote = "1.0" syn = { version = "2.0", features = ["full", "extra-traits"] } diff --git a/crates/test-case-macros/Cargo.toml b/crates/test-case-macros/Cargo.toml index 79a2513..14111cb 100644 --- a/crates/test-case-macros/Cargo.toml +++ b/crates/test-case-macros/Cargo.toml @@ -24,7 +24,7 @@ path = "src/lib.rs" [dependencies] proc-macro2 = { version = "1.0", features = [] } -proc-macro-error = "1.0" +proc-macro-error = { version = "1.0", default-features = false } quote = "1.0" syn = { version = "2.0", features = ["full", "extra-traits", "parsing"] } test-case-core = { version = "3.2.1", path = "../test-case-core", default-features = false } From 97bcfc095c292b8d8ede630ea9a877b809d19ca0 Mon Sep 17 00:00:00 2001 From: Nick Krichevsky Date: Thu, 19 Oct 2023 08:43:11 -0400 Subject: [PATCH 3/8] docs: test_matrix example that does not compile in README (#133) --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a73dada..d005aff 100644 --- a/README.md +++ b/README.md @@ -79,13 +79,13 @@ mod tests { use test_case::test_matrix; #[test_matrix( - [-2, 2], - [-4, 4] + [4, 6, 8], + [1, 3, 5] )] fn multiplication_tests(x: i8, y: i8) { - let actual = (x * y).abs(); + let product = x * y; - assert_eq!(8, actual) + assert_eq!(0, product % 2) } } ``` From 2fc16bf89730d6bfb1e01599ac0dbd224801b4ec Mon Sep 17 00:00:00 2001 From: Nick Krichevsky Date: Sun, 22 Oct 2023 12:14:15 -0400 Subject: [PATCH 4/8] feat: Add the ability to have comments in matrix tests (#132) --- crates/test-case-core/src/test_case.rs | 123 +++++++++++++----- crates/test-case-core/src/test_matrix/mod.rs | 22 ++-- crates/test-case-macros/src/lib.rs | 11 +- .../matrices_compilation_errors/src/lib.rs | 8 -- .../src/lib.rs | 28 ++++ ...ceptance__matrices_compilation_errors.snap | 3 +- ...ance__matrices_support_basic_features.snap | 18 ++- ...ceptance__matrices_compilation_errors.snap | 3 +- ...ance__matrices_support_basic_features.snap | 18 ++- 9 files changed, 169 insertions(+), 65 deletions(-) diff --git a/crates/test-case-core/src/test_case.rs b/crates/test-case-core/src/test_case.rs index 105d421..bb37311 100644 --- a/crates/test-case-core/src/test_case.rs +++ b/crates/test-case-core/src/test_case.rs @@ -10,53 +10,62 @@ use syn::{parse_quote, Error, Expr, Ident, ItemFn, ReturnType, Token}; #[derive(Debug)] pub struct TestCase { args: Punctuated, - pub(crate) expression: Option, - pub(crate) comment: Option, + expression: Option, + name: Ident, } impl Parse for TestCase { fn parse(input: ParseStream) -> Result { - Ok(Self { - args: Punctuated::parse_separated_nonempty_with(input, Expr::parse)?, - expression: input.parse().ok(), - comment: input.parse().ok(), - }) + let args = Punctuated::parse_separated_nonempty_with(input, Expr::parse)?; + let expression = input.parse::().ok(); + let comment = input.parse::().ok(); + + Ok(Self::new_from_parsed(args, expression, comment)) } } +impl TestCase { + pub(crate) fn new>( + args: I, + expression: Option, + comment: Option, + ) -> Self { + Self::new_from_parsed(args.into_iter().collect(), expression, comment) + } + + pub(crate) fn new_from_parsed( + args: Punctuated, + expression: Option, + comment: Option, + ) -> Self { + let name = Self::test_case_name_ident(args.iter(), expression.as_ref(), comment.as_ref()); -impl From for TestCase -where - I: IntoIterator, -{ - fn from(into_iter: I) -> Self { Self { - args: into_iter.into_iter().collect(), - expression: None, - comment: None, + args, + expression, + name, + } + } + + pub(crate) fn new_with_prefixed_name>( + args: I, + expression: Option, + prefix: &str, + ) -> Self { + let parsed_args = args.into_iter().collect::>(); + let name = Self::prefixed_test_case_name(parsed_args.iter(), expression.as_ref(), prefix); + + Self { + args: parsed_args, + expression, + name, } } -} -impl TestCase { pub fn test_case_name(&self) -> Ident { - let case_desc = self - .comment - .as_ref() - .map(|item| item.comment.value()) - .unwrap_or_else(|| { - let mut acc = String::new(); - for arg in &self.args { - acc.push_str(&fmt_syn(&arg)); - acc.push('_'); - } - acc.push_str("expects"); - if let Some(expression) = &self.expression { - acc.push(' '); - acc.push_str(&expression.to_string()) - } - acc - }); - crate::utils::escape_test_name(case_desc) + // The clone is kind of annoying here, but because this is behind a reference, we must clone + // to preserve the signature without a breaking change + // TODO: return a reference? + self.name.clone() } pub fn render(&self, mut item: ItemFn, origin_span: Span2) -> TokenStream2 { @@ -118,4 +127,48 @@ impl TestCase { } } } + + fn test_case_name_ident<'a, I: Iterator>( + args: I, + expression: Option<&TestCaseExpression>, + comment: Option<&TestCaseComment>, + ) -> Ident { + let desc = Self::test_case_name_string(args, expression, comment); + + crate::utils::escape_test_name(desc) + } + + fn prefixed_test_case_name<'a, I: Iterator>( + args: I, + expression: Option<&TestCaseExpression>, + prefix: &str, + ) -> Ident { + let generated_name = Self::test_case_name_string(args, expression, None); + let full_desc = format!("{prefix}_{generated_name}"); + + crate::utils::escape_test_name(full_desc) + } + + fn test_case_name_string<'a, I: Iterator>( + args: I, + expression: Option<&TestCaseExpression>, + comment: Option<&TestCaseComment>, + ) -> String { + comment + .as_ref() + .map(|item| item.comment.value()) + .unwrap_or_else(|| { + let mut acc = String::new(); + for arg in args { + acc.push_str(&fmt_syn(&arg)); + acc.push('_'); + } + acc.push_str("expects"); + if let Some(expression) = expression { + acc.push(' '); + acc.push_str(&expression.to_string()) + } + acc + }) + } } diff --git a/crates/test-case-core/src/test_matrix/mod.rs b/crates/test-case-core/src/test_matrix/mod.rs index 41051a5..889c3c3 100644 --- a/crates/test-case-core/src/test_matrix/mod.rs +++ b/crates/test-case-core/src/test_matrix/mod.rs @@ -16,6 +16,7 @@ mod matrix_product; pub struct TestMatrix { variables: Vec>, expression: Option, + comment: Option, } impl TestMatrix { @@ -25,11 +26,18 @@ impl TestMatrix { pub fn cases(&self) -> impl Iterator { let expression = self.expression.clone(); + let comment = self.comment.clone(); matrix_product::multi_cartesian_product(self.variables.iter().cloned()).map(move |v| { - let mut case = TestCase::from(v); - case.expression = expression.clone(); - case + if let Some(comment) = comment.clone() { + TestCase::new_with_prefixed_name( + v, + expression.clone(), + comment.comment.value().as_ref(), + ) + } else { + TestCase::new(v, expression.clone(), None) + } }) } } @@ -40,16 +48,10 @@ impl Parse for TestMatrix { let mut matrix = TestMatrix { expression: input.parse().ok(), + comment: input.parse().ok(), ..Default::default() }; - if let Ok(c) = input.parse::() { - return Err(syn::Error::new( - c.span(), - "Comments are not allowed in #[test_matrix]", - )); - } - for arg in args { let values: Vec = match &arg { Expr::Array(v) => v.elems.iter().cloned().collect(), diff --git a/crates/test-case-macros/src/lib.rs b/crates/test-case-macros/src/lib.rs index b598095..82561da 100644 --- a/crates/test-case-macros/src/lib.rs +++ b/crates/test-case-macros/src/lib.rs @@ -39,17 +39,16 @@ pub fn test_case(args: TokenStream, input: TokenStream) -> TokenStream { /// Generates tests for the cartesian product of a given set of data /// -/// A test matrix consists of three elements: +/// A test matrix consists of four elements: /// -/// 1. _(Required)_ Sets of values to combine; the nubmer of sets must be the same as the number of +/// 1. _(Required)_ Sets of values to combine; the number of sets must be the same as the number of /// arguments to the test body function /// 2. _(Optional)_ Expected result (for all combinations of values) -/// 3. _(Required)_ Test body +/// 3. _(Optional)_ Test case description (applied as a prefix the generated name of the test) +/// 4. _(Required)_ Test body /// /// _Expected result_ and _Test body_ are the same as they are for the singular `#[test_case(...)]` -/// macro but are applied to every case generated by `#[test_matrix(...)]`. `Test case description` -/// is not allowed for `test_matrix`, because test case names are auto-generated from the test body -/// function name and matrix values. +/// macro but are applied to every case generated by `#[test_matrix(...)]`. #[proc_macro_attribute] #[proc_macro_error::proc_macro_error] pub fn test_matrix(args: TokenStream, input: TokenStream) -> TokenStream { diff --git a/tests/acceptance_cases/matrices_compilation_errors/src/lib.rs b/tests/acceptance_cases/matrices_compilation_errors/src/lib.rs index 93337a0..805438a 100644 --- a/tests/acceptance_cases/matrices_compilation_errors/src/lib.rs +++ b/tests/acceptance_cases/matrices_compilation_errors/src/lib.rs @@ -25,14 +25,6 @@ fn unbounded_range(x: u32) { unreachable!("Should never compile") } -#[test_matrix( - [1, 2, 3] - ; "Illegal comment" -)] -fn illegal_comment(x: u32) { - unreachable!("Should never compile") -} - const USIZE_CONST: usize = 0; #[test_matrix(USIZE_CONST)] diff --git a/tests/acceptance_cases/matrices_support_basic_features/src/lib.rs b/tests/acceptance_cases/matrices_support_basic_features/src/lib.rs index 13b91c6..4724910 100644 --- a/tests/acceptance_cases/matrices_support_basic_features/src/lib.rs +++ b/tests/acceptance_cases/matrices_support_basic_features/src/lib.rs @@ -78,6 +78,34 @@ mod test_cases { assert!(x < 10); } + #[test_matrix( + [1, 2], + [11, 12] + )] + #[test_matrix( + [3, 4], + [13, 14] + )] + fn two_matrices(x: u32, y: u32) { + assert!(x < 10); + assert!(y > 10); + } + + #[test_matrix( + [1, 2], + [11, 12] + ; "one, two" + )] + #[test_matrix( + [3, 4], + [13, 14] + ; "three, four" + )] + fn two_matrices_with_comments(x: u32, y: u32) { + assert!(x < 10); + assert!(y > 10); + } + #[test_case(5)] #[test_matrix( [6, 7, 8] diff --git a/tests/snapshots/rust-nightly/acceptance__matrices_compilation_errors.snap b/tests/snapshots/rust-nightly/acceptance__matrices_compilation_errors.snap index 1d7a241..2617d4f 100644 --- a/tests/snapshots/rust-nightly/acceptance__matrices_compilation_errors.snap +++ b/tests/snapshots/rust-nightly/acceptance__matrices_compilation_errors.snap @@ -3,9 +3,8 @@ source: tests/acceptance_tests.rs expression: output --- error: All literal values must be of the same type -error: Comments are not allowed in #[test_matrix] 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 6 previous errors +error: could not compile `matrices_compilation_errors` (lib test) due to 5 previous errors error: number too large to fit in target type error[E0308]: mismatched types diff --git a/tests/snapshots/rust-nightly/acceptance__matrices_support_basic_features.snap b/tests/snapshots/rust-nightly/acceptance__matrices_support_basic_features.snap index be719b9..fa88fed 100644 --- a/tests/snapshots/rust-nightly/acceptance__matrices_support_basic_features.snap +++ b/tests/snapshots/rust-nightly/acceptance__matrices_support_basic_features.snap @@ -2,7 +2,7 @@ source: tests/acceptance_tests.rs expression: output --- -test result: ok. 54 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s +test result: ok. 70 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test test_cases::case_after_matrix::_1_expects ... ok test test_cases::case_after_matrix::_2_expects ... ok test test_cases::case_after_matrix::_3_expects ... ok @@ -57,3 +57,19 @@ test test_cases::str_values_tuple::_one_blue_expects ... ok test test_cases::str_values_tuple::_one_yellow_expects ... ok test test_cases::str_values_tuple::_two_blue_expects ... ok test test_cases::str_values_tuple::_two_yellow_expects ... ok +test test_cases::two_matrices::_1_11_expects ... ok +test test_cases::two_matrices::_1_12_expects ... ok +test test_cases::two_matrices::_2_11_expects ... ok +test test_cases::two_matrices::_2_12_expects ... ok +test test_cases::two_matrices::_3_13_expects ... ok +test test_cases::two_matrices::_3_14_expects ... ok +test test_cases::two_matrices::_4_13_expects ... ok +test test_cases::two_matrices::_4_14_expects ... ok +test test_cases::two_matrices_with_comments::one_two_1_11_expects ... ok +test test_cases::two_matrices_with_comments::one_two_1_12_expects ... ok +test test_cases::two_matrices_with_comments::one_two_2_11_expects ... ok +test test_cases::two_matrices_with_comments::one_two_2_12_expects ... ok +test test_cases::two_matrices_with_comments::three_four_3_13_expects ... ok +test test_cases::two_matrices_with_comments::three_four_3_14_expects ... ok +test test_cases::two_matrices_with_comments::three_four_4_13_expects ... ok +test test_cases::two_matrices_with_comments::three_four_4_14_expects ... ok diff --git a/tests/snapshots/rust-stable/acceptance__matrices_compilation_errors.snap b/tests/snapshots/rust-stable/acceptance__matrices_compilation_errors.snap index 1d7a241..2617d4f 100644 --- a/tests/snapshots/rust-stable/acceptance__matrices_compilation_errors.snap +++ b/tests/snapshots/rust-stable/acceptance__matrices_compilation_errors.snap @@ -3,9 +3,8 @@ source: tests/acceptance_tests.rs expression: output --- error: All literal values must be of the same type -error: Comments are not allowed in #[test_matrix] 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 6 previous errors +error: could not compile `matrices_compilation_errors` (lib test) due to 5 previous errors error: number too large to fit in target type error[E0308]: mismatched types diff --git a/tests/snapshots/rust-stable/acceptance__matrices_support_basic_features.snap b/tests/snapshots/rust-stable/acceptance__matrices_support_basic_features.snap index be719b9..fa88fed 100644 --- a/tests/snapshots/rust-stable/acceptance__matrices_support_basic_features.snap +++ b/tests/snapshots/rust-stable/acceptance__matrices_support_basic_features.snap @@ -2,7 +2,7 @@ source: tests/acceptance_tests.rs expression: output --- -test result: ok. 54 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s +test result: ok. 70 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s test test_cases::case_after_matrix::_1_expects ... ok test test_cases::case_after_matrix::_2_expects ... ok test test_cases::case_after_matrix::_3_expects ... ok @@ -57,3 +57,19 @@ test test_cases::str_values_tuple::_one_blue_expects ... ok test test_cases::str_values_tuple::_one_yellow_expects ... ok test test_cases::str_values_tuple::_two_blue_expects ... ok test test_cases::str_values_tuple::_two_yellow_expects ... ok +test test_cases::two_matrices::_1_11_expects ... ok +test test_cases::two_matrices::_1_12_expects ... ok +test test_cases::two_matrices::_2_11_expects ... ok +test test_cases::two_matrices::_2_12_expects ... ok +test test_cases::two_matrices::_3_13_expects ... ok +test test_cases::two_matrices::_3_14_expects ... ok +test test_cases::two_matrices::_4_13_expects ... ok +test test_cases::two_matrices::_4_14_expects ... ok +test test_cases::two_matrices_with_comments::one_two_1_11_expects ... ok +test test_cases::two_matrices_with_comments::one_two_1_12_expects ... ok +test test_cases::two_matrices_with_comments::one_two_2_11_expects ... ok +test test_cases::two_matrices_with_comments::one_two_2_12_expects ... ok +test test_cases::two_matrices_with_comments::three_four_3_13_expects ... ok +test test_cases::two_matrices_with_comments::three_four_3_14_expects ... ok +test test_cases::two_matrices_with_comments::three_four_4_13_expects ... ok +test test_cases::two_matrices_with_comments::three_four_4_14_expects ... ok From d32127bb5d1eb0143f2e902955f084552f2863d6 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Fri, 17 Nov 2023 03:11:49 -0500 Subject: [PATCH 5/8] Remove proc-macro-error dependency (#136) * Don't blindly discard errors This prevents syn::Error from propagating. Updates #135. * Use syn::ParseStream::error This fails tests. https://github.com/frondeus/test-case/issues/135 * Use proc-macro2-diagnostics Bump MSRV to 1.63 for the new yansi dependency (through proc-macro2-diagnostics). See https://github.com/SergioBenitez/yansi/blob/e8247a492e1b/Cargo.toml#L12. --- Cargo.toml | 2 +- crates/test-case-core/Cargo.toml | 10 +++++----- crates/test-case-core/src/complex_expr.rs | 11 ++++++++--- crates/test-case-core/src/expr.rs | 3 ++- crates/test-case-core/src/test_case.rs | 11 +++++++++-- crates/test-case-core/src/test_matrix/mod.rs | 14 ++++++++++++-- crates/test-case-macros/Cargo.toml | 9 ++++----- crates/test-case-macros/src/lib.rs | 2 -- 8 files changed, 41 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c3a17a9..c5d2cfc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ license = "MIT" repository = "https://github.com/frondeus/test-case" documentation = "https://docs.rs/test-case" exclude = ["tests/snapshots/**/*"] -rust-version = "1.58.1" +rust-version = "1.63" [features] with-regex = ["regex", "test-case-macros/with-regex"] diff --git a/crates/test-case-core/Cargo.toml b/crates/test-case-core/Cargo.toml index a6e1166..e314fe4 100644 --- a/crates/test-case-core/Cargo.toml +++ b/crates/test-case-core/Cargo.toml @@ -22,8 +22,8 @@ doctest = false path = "src/lib.rs" [dependencies] -cfg-if = "1.0" -proc-macro2 = { version = "1.0", features = [] } -proc-macro-error = { version = "1.0", default-features = false } -quote = "1.0" -syn = { version = "2.0", features = ["full", "extra-traits"] } +cfg-if = "1.0" +proc-macro2 = { version = "1.0", features = [] } +proc-macro2-diagnostics = "0.10" +quote = "1.0" +syn = { version = "2.0", features = ["full", "extra-traits"] } diff --git a/crates/test-case-core/src/complex_expr.rs b/crates/test-case-core/src/complex_expr.rs index e708f2e..08a7637 100644 --- a/crates/test-case-core/src/complex_expr.rs +++ b/crates/test-case-core/src/complex_expr.rs @@ -1,5 +1,6 @@ use crate::utils::fmt_syn; use proc_macro2::Group; +use proc_macro2::Span; use proc_macro2::TokenStream; use quote::{quote, TokenStreamExt}; use std::fmt::{Display, Formatter}; @@ -323,11 +324,11 @@ impl ComplexTestCase { expected_regex: input.parse()?, }) } else { - proc_macro_error::abort!(input.span(), "'with-regex' feature is required to use 'matches-regex' keyword"); + return Err(input.error("'with-regex' feature is required to use 'matches-regex' keyword")); } } } else { - proc_macro_error::abort!(input.span(), "cannot parse complex expression") + return Err(input.error("cannot parse complex expression")); }) } } @@ -450,7 +451,11 @@ fn regex_assertion(expected_regex: &Expr) -> TokenStream { fn not_assertion(not: &ComplexTestCase) -> TokenStream { match not { ComplexTestCase::Not(_) => { - proc_macro_error::abort_call_site!("multiple negations on single item are forbidden") + use proc_macro2_diagnostics::SpanDiagnosticExt as _; + + Span::call_site() + .error("multiple negations on single item are forbidden") + .emit_as_expr_tokens() } ComplexTestCase::And(cases) => negate(and_assertion(cases)), ComplexTestCase::Or(cases) => negate(or_assertion(cases)), diff --git a/crates/test-case-core/src/expr.rs b/crates/test-case-core/src/expr.rs index 41b1bcc..4e4cc2f 100644 --- a/crates/test-case-core/src/expr.rs +++ b/crates/test-case-core/src/expr.rs @@ -193,9 +193,10 @@ where Mapping: FnOnce(Option) -> TestCaseResult, Inner: Parse, { + let result = (!input.is_empty()).then(|| input.parse()).transpose()?; Ok(TestCaseExpression { _token: token, extra_keywords, - result: mapping(input.parse::().ok()), + result: mapping(result), }) } diff --git a/crates/test-case-core/src/test_case.rs b/crates/test-case-core/src/test_case.rs index bb37311..376b14e 100644 --- a/crates/test-case-core/src/test_case.rs +++ b/crates/test-case-core/src/test_case.rs @@ -17,8 +17,15 @@ pub struct TestCase { impl Parse for TestCase { fn parse(input: ParseStream) -> Result { let args = Punctuated::parse_separated_nonempty_with(input, Expr::parse)?; - let expression = input.parse::().ok(); - let comment = input.parse::().ok(); + let expression = (!input.is_empty()).then(|| input.parse()).transpose(); + let comment = (!input.is_empty()).then(|| input.parse()).transpose(); + // if both are errors, pick the expression error since it is more likely to be informative. + // + // TODO(https://github.com/frondeus/test-case/issues/135): avoid Result::ok entirely. + let (expression, comment) = match (expression, comment) { + (Err(expression), Err(_comment)) => return Err(expression), + (expression, comment) => (expression.ok().flatten(), comment.ok().flatten()), + }; Ok(Self::new_from_parsed(args, expression, comment)) } diff --git a/crates/test-case-core/src/test_matrix/mod.rs b/crates/test-case-core/src/test_matrix/mod.rs index 889c3c3..310c3c9 100644 --- a/crates/test-case-core/src/test_matrix/mod.rs +++ b/crates/test-case-core/src/test_matrix/mod.rs @@ -46,9 +46,19 @@ impl Parse for TestMatrix { fn parse(input: ParseStream) -> syn::Result { let args: Punctuated = Punctuated::parse_separated_nonempty(input)?; + let expression = (!input.is_empty()).then(|| input.parse()).transpose(); + let comment = (!input.is_empty()).then(|| input.parse()).transpose(); + // if both are errors, pick the expression error since it is more likely to be informative. + // + // TODO(https://github.com/frondeus/test-case/issues/135): avoid Result::ok entirely. + let (expression, comment) = match (expression, comment) { + (Err(expression), Err(_comment)) => return Err(expression), + (expression, comment) => (expression.ok().flatten(), comment.ok().flatten()), + }; + let mut matrix = TestMatrix { - expression: input.parse().ok(), - comment: input.parse().ok(), + expression, + comment, ..Default::default() }; diff --git a/crates/test-case-macros/Cargo.toml b/crates/test-case-macros/Cargo.toml index 14111cb..dd9bd4d 100644 --- a/crates/test-case-macros/Cargo.toml +++ b/crates/test-case-macros/Cargo.toml @@ -23,8 +23,7 @@ proc-macro = true path = "src/lib.rs" [dependencies] -proc-macro2 = { version = "1.0", features = [] } -proc-macro-error = { version = "1.0", default-features = false } -quote = "1.0" -syn = { version = "2.0", features = ["full", "extra-traits", "parsing"] } -test-case-core = { version = "3.2.1", path = "../test-case-core", default-features = false } +proc-macro2 = { version = "1.0", features = [] } +quote = "1.0" +syn = { version = "2.0", features = ["full", "extra-traits", "parsing"] } +test-case-core = { version = "3.2.1", path = "../test-case-core", default-features = false } diff --git a/crates/test-case-macros/src/lib.rs b/crates/test-case-macros/src/lib.rs index 82561da..90d4675 100644 --- a/crates/test-case-macros/src/lib.rs +++ b/crates/test-case-macros/src/lib.rs @@ -22,7 +22,6 @@ use test_case_core::{TestCase, TestMatrix}; /// When _expected result_ is provided, it is compared against the actual value generated with _test body_ using `assert_eq!`. /// _Test cases_ that don't provide _expected result_ should contain custom assertions within _test body_ or return `Result` similar to `#[test]` macro. #[proc_macro_attribute] -#[proc_macro_error::proc_macro_error] pub fn test_case(args: TokenStream, input: TokenStream) -> TokenStream { let test_case = parse_macro_input!(args as TestCase); let mut item = parse_macro_input!(input as ItemFn); @@ -50,7 +49,6 @@ pub fn test_case(args: TokenStream, input: TokenStream) -> TokenStream { /// _Expected result_ and _Test body_ are the same as they are for the singular `#[test_case(...)]` /// macro but are applied to every case generated by `#[test_matrix(...)]`. #[proc_macro_attribute] -#[proc_macro_error::proc_macro_error] pub fn test_matrix(args: TokenStream, input: TokenStream) -> TokenStream { let matrix = parse_macro_input!(args as TestMatrix); let mut item = parse_macro_input!(input as ItemFn); From b0a764f10f2332fb0aeca317c1427a80890b05dd Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Fri, 17 Nov 2023 03:12:44 -0500 Subject: [PATCH 6/8] .github: replace actions-rs with dtolnay/rust-toolchain, add caching (#137) * .github: bump actions/checkout to v4 * .github: do not fail fast * .github: replace actions-rs with dtolnay/rust-toolchain actions-rs is no longer maintained. * .github: add rust cache action This should reduce CI time. --- .github/workflows/test.yml | 59 +++++++++++++++------------------ scripts/force-msrv-toolchain.sh | 16 --------- 2 files changed, 26 insertions(+), 49 deletions(-) delete mode 100755 scripts/force-msrv-toolchain.sh diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b7c91c1..6ca68ad 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,38 +16,30 @@ jobs: steps: - name: Checkout source code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install Rust - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@stable with: - profile: minimal - toolchain: stable - override: true components: rustfmt, clippy + - name: Cache + uses: Swatinem/rust-cache@v2 + - name: Check - uses: actions-rs/cargo@v1 - with: - command: check - args: --all-features + run: cargo check --all-features --all-targets - name: Clippy - uses: actions-rs/cargo@v1 - with: - command: clippy - args: --all-features --tests -- -D warnings + run: cargo clippy --all-features --all-targets -- -D warnings - name: Fmt - uses: actions-rs/cargo@v1 - with: - command: fmt - args: -- --check + run: cargo fmt -- --check test: name: Test crate runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: os: - ubuntu-latest @@ -59,22 +51,21 @@ jobs: steps: - name: Checkout source code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install Rust - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@master with: - profile: minimal toolchain: ${{ matrix.toolchain }} - override: true - components: rustfmt, clippy + + - name: Cache + uses: Swatinem/rust-cache@v2 - name: Test - uses: actions-rs/cargo@v1 env: + CARGO_TERM_COLOR: never SNAPSHOT_DIR: rust-${{ matrix.toolchain }} - with: - command: test + run: cargo test msrv-build: name: Build crate with documented MSRV @@ -82,17 +73,19 @@ jobs: steps: - name: Checkout source code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - - name: Install Rust MSRV version - run: scripts/force-msrv-toolchain.sh + - name: Read crate metadata + id: metadata + run: echo "rust-version=$(sed -ne 's/rust-version *= *\"\(.*\)\"/\1/p' Cargo.toml)" >> $GITHUB_OUTPUT - name: Install Rust - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@master with: - profile: minimal + toolchain: ${{ steps.metadata.outputs.rust-version }} + + - name: Cache + uses: Swatinem/rust-cache@v2 - name: Build - uses: actions-rs/cargo@v1 - with: - command: build + run: cargo build diff --git a/scripts/force-msrv-toolchain.sh b/scripts/force-msrv-toolchain.sh deleted file mode 100755 index 25b279b..0000000 --- a/scripts/force-msrv-toolchain.sh +++ /dev/null @@ -1,16 +0,0 @@ -#! /bin/sh -# -# Force the use of the MSRV toolchain (for use with the CI). -# Since action-rs/toolchain@v1 uses rustup 1.21.x, only the -# toolchain name can be given in the file. -# -# If you call this script in your working directory, do not -# forget that it will create a "rust-toolchain" file there. - -set -e - -root=$(dirname "$0")/.. - -version=$(sed -ne 's/rust-version *= *"\(.*\)"/\1/p' "$root"/Cargo.toml) -echo $version > "$root"/rust-toolchain -echo "Rust $version installed as the forced toolchain" From 21212e2ba03bd9a5eb843d8aa8af362794a50275 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Biel?= Date: Fri, 17 Nov 2023 10:20:50 +0100 Subject: [PATCH 7/8] chore: Update crate publishing script --- scripts/publish.sh | 29 ++++++++++++++++++++++------- src/lib.rs | 2 +- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/scripts/publish.sh b/scripts/publish.sh index 2a10f6e..c95a2a7 100755 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -1,5 +1,11 @@ #!/usr/bin/env bash +# Publish a new version of the crate. +# +# Dependencies: +# - cargo-get +# - nvim + SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) REPO_DIR="${SCRIPT_DIR}/.." CURRENT_DIR=$(pwd) @@ -8,18 +14,27 @@ cd "${REPO_DIR}" set -eo xtrace -./scripts/test_all.sh +# Read current version from the Cargo.toml file +CURRENT_VERSION=$(cargo get package.version) +echo "Current version: ${CURRENT_VERSION}" +read -p 'New version: ' NEW_VERSION -nvim Cargo.toml -nvim crates/test-case-macros/Cargo.toml -nvim crates/test-case-core/Cargo.toml -cargo build +# Update version in Cargo.toml files +sed -i '' "s/version = \"${CURRENT_VERSION}\"/version = \"${NEW_VERSION}\"/g" Cargo.toml +sed -i '' "s/version = \"${CURRENT_VERSION}\"/version = \"${NEW_VERSION}\"/g" crates/test-case-macros/Cargo.toml +sed -i '' "s/version = \"${CURRENT_VERSION}\"/version = \"${NEW_VERSION}\"/g" crates/test-case-core/Cargo.toml -nvim CHANGELOG.md -nvim src/lib.rs +# Validate the release +rustup update +./scripts/test_all.sh +# Update README if needed cargo readme > README.md +# Add changelog entry +nvim CHANGELOG.md + +# Push to github git add . git commit git push origin diff --git a/src/lib.rs b/src/lib.rs index 97d9094..0a5769b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,7 @@ //! //! ```toml //! [dev-dependencies] -//! test-case = "3.2.1" +//! test-case = "*" //! ``` //! //! and imported to the scope of a block where it's being called From f7b669106f354513723064219fea3c6fdadb8e56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Biel?= Date: Fri, 17 Nov 2023 10:27:50 +0100 Subject: [PATCH 8/8] Bump version to 3.3.0 --- CHANGELOG.md | 7 +++++++ Cargo.toml | 2 +- README.md | 10 +++++----- crates/test-case-core/Cargo.toml | 2 +- crates/test-case-macros/Cargo.toml | 2 +- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb5d7c9..28e41c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 3.3.0 +### Features +* Allow comments in `test-matrix` macro (#132) + +### Changes +* Drop `proc-macro-error` dependency & improve error messages (#136) + ## 3.2.1 ### Changes * Update `syn` dependency to 2.0 diff --git a/Cargo.toml b/Cargo.toml index c5d2cfc..160f9b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "test-case" -version = "3.2.1" +version = "3.3.0" edition = "2021" authors = ["Marcin Sas-Szymanski ", "Wojciech Polak ", "Łukasz Biel "] description = "Provides #[test_case(...)] procedural macro attribute for generating parametrized test cases easily" diff --git a/README.md b/README.md index d005aff..84200e5 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Crate has to be added as a dependency to `Cargo.toml`: ```toml [dev-dependencies] -test-case = "3.2.1" +test-case = "*" ``` and imported to the scope of a block where it's being called @@ -79,13 +79,13 @@ mod tests { use test_case::test_matrix; #[test_matrix( - [4, 6, 8], - [1, 3, 5] + [-2, 2], + [-4, 4] )] fn multiplication_tests(x: i8, y: i8) { - let product = x * y; + let actual = (x * y).abs(); - assert_eq!(0, product % 2) + assert_eq!(8, actual) } } ``` diff --git a/crates/test-case-core/Cargo.toml b/crates/test-case-core/Cargo.toml index e314fe4..079a9d1 100644 --- a/crates/test-case-core/Cargo.toml +++ b/crates/test-case-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "test-case-core" -version = "3.2.1" +version = "3.3.0" edition = "2021" authors = ["Marcin Sas-Szymanski ", "Wojciech Polak ", "Łukasz Biel "] description = "Provides core functionality for parsing #[test_case(...)] procedural macro attribute for generating parametrized test cases easily" diff --git a/crates/test-case-macros/Cargo.toml b/crates/test-case-macros/Cargo.toml index dd9bd4d..e64f085 100644 --- a/crates/test-case-macros/Cargo.toml +++ b/crates/test-case-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "test-case-macros" -version = "3.2.1" +version = "3.3.0" edition = "2021" authors = ["Marcin Sas-Szymanski ", "Wojciech Polak ", "Łukasz Biel "] description = "Provides #[test_case(...)] procedural macro attribute for generating parametrized test cases easily"