Skip to content

Commit dbe095f

Browse files
committed
feat: support outer doc comments for naming matrix tests
Adds support for using outer doc comments (`///`) instead of auto-generated tests names for parameterized matrix tests. e.g. legacy compact syntax: ```rust expected => [ /// forty_two 42, /// four 2*3-2, ], )] fn test(expected: usize) { ... } ``` e.g. attribute syntax: ```rust fn test( #[values( /// forty_two 42, /// four 2*3-2, )] expected: usize, ) { ... } ``` The generated test names will be `values_1_len_forty_two` and `values_2_len_four`. This implementation also changes the internal `sanitize_ident` function to allow sanitizing whitespaces as `_`. Fixes <#320> Signed-off-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
1 parent ff20f9a commit dbe095f

File tree

10 files changed

+293
-50
lines changed

10 files changed

+293
-50
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
- Bump msrv to 1.82.0
88
- Disabled default features of `futures-util`
9+
- Doc comments before `#[values(...)]` entries can be used to override the generated matrix
10+
test names (both for the legacy `arg => [..]` syntax and the new attribute form).
11+
See [#321](https://github.com/la10736/rstest/pull/321) thanks to @orhun.
912

1013
### Add
1114

rstest/src/lib.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -938,6 +938,28 @@ pub use rstest_macros::fixture;
938938
/// Note that the test names contains the given expression sanitized into
939939
/// a valid Rust identifier name. This should help to identify which case fails.
940940
///
941+
/// If you prefer to provide an explicit name you can use doc comments before
942+
/// each value inside the `#[values(...)]` attribute:
943+
///
944+
/// ```
945+
/// # use rstest::rstest;
946+
/// #[rstest]
947+
/// fn describe_values(
948+
/// #[values(
949+
/// /// short_value
950+
/// 42,
951+
/// /// long_value
952+
/// 100,
953+
/// )]
954+
/// value: u32,
955+
/// ) {
956+
/// assert!(value > 0);
957+
/// }
958+
/// ```
959+
///
960+
/// The generated tests will be named `value_1_short_value` and
961+
/// `value_2_long_value` instead of using the raw expression text.
962+
///
941963
///
942964
/// Also value list implements the magic conversion feature: every time the value type
943965
/// implements `FromStr` trait you can use a literal string to define it.

rstest/tests/resources/rstest/matrix/simple.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
use rstest::rstest;
22

33
#[rstest(
4-
expected => [4, 2*3-2],
5-
input => ["ciao", "buzz"],
4+
expected => [
5+
/// len_four
6+
4,
7+
/// len_four_alt
8+
2*3-2,
9+
],
10+
input => [
11+
/// greet_ciao
12+
"ciao",
13+
/// greet_buzz
14+
"buzz",
15+
],
616
)]
717
fn strlen_test(expected: usize, input: &str) {
818
assert_eq!(expected, input.len());
Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,53 @@
11
use rstest::rstest;
22

33
#[rstest]
4-
fn both(#[values(4, 2*3-2)] expected: usize, #[values("ciao", "buzz")] input: &str) {
4+
fn both(
5+
#[values(
6+
/// len_four
7+
4,
8+
/// len_four_alt
9+
2*3-2
10+
)]
11+
expected: usize,
12+
#[values(
13+
/// greet_ciao
14+
"ciao",
15+
/// greet_buzz
16+
"buzz"
17+
)]
18+
input: &str,
19+
) {
520
assert_eq!(expected, input.len());
621
}
722

823
#[rstest(
924
input => ["ciao", "buzz"]
1025
)]
11-
fn first(#[values(4, 2*3-2)] expected: usize, input: &str) {
26+
fn first(
27+
#[values(
28+
/// len_four
29+
4,
30+
/// len_four_alt
31+
2*3-2
32+
)]
33+
expected: usize,
34+
input: &str,
35+
) {
1236
assert_eq!(expected, input.len());
1337
}
1438

1539
#[rstest(
1640
expected => [4, 2*3-2]
1741
)]
18-
fn second(expected: usize, #[values("ciao", "buzz")] input: &str) {
42+
fn second(
43+
expected: usize,
44+
#[values(
45+
/// greet_ciao
46+
"ciao",
47+
/// greet_buzz
48+
"buzz"
49+
)]
50+
input: &str,
51+
) {
1952
assert_eq!(expected, input.len());
2053
}

rstest/tests/rstest/mod.rs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -903,10 +903,10 @@ mod matrix {
903903

904904
TestResults::new()
905905
.with_contains(true)
906-
.ok("strlen_test::expected_1_4::input_1___ciao__")
907-
.ok("strlen_test::expected_1_4::input_2___buzz__")
908-
.ok("strlen_test::expected_2_2_3_2::input_1___ciao__")
909-
.ok("strlen_test::expected_2_2_3_2::input_2___buzz__")
906+
.ok("strlen_test::expected_1_len_four::input_1_greet_ciao")
907+
.ok("strlen_test::expected_1_len_four::input_2_greet_buzz")
908+
.ok("strlen_test::expected_2_len_four_alt::input_1_greet_ciao")
909+
.ok("strlen_test::expected_2_len_four_alt::input_2_greet_buzz")
910910
.assert(output);
911911
}
912912

@@ -986,18 +986,18 @@ mod matrix {
986986
let (output, _) = run_test(res("use_attr.rs"));
987987

988988
TestResults::new()
989-
.ok("both::expected_1_4::input_1___ciao__")
990-
.ok("both::expected_1_4::input_2___buzz__")
991-
.ok("both::expected_2_2_3_2::input_1___ciao__")
992-
.ok("both::expected_2_2_3_2::input_2___buzz__")
993-
.ok("first::input_1___ciao__::expected_1_4")
994-
.ok("first::input_2___buzz__::expected_1_4")
995-
.ok("first::input_1___ciao__::expected_2_2_3_2")
996-
.ok("first::input_2___buzz__::expected_2_2_3_2")
997-
.ok("second::expected_1_4::input_1___ciao__")
998-
.ok("second::expected_1_4::input_2___buzz__")
999-
.ok("second::expected_2_2_3_2::input_1___ciao__")
1000-
.ok("second::expected_2_2_3_2::input_2___buzz__")
989+
.ok("both::expected_1_len_four::input_1_greet_ciao")
990+
.ok("both::expected_1_len_four::input_2_greet_buzz")
991+
.ok("both::expected_2_len_four_alt::input_1_greet_ciao")
992+
.ok("both::expected_2_len_four_alt::input_2_greet_buzz")
993+
.ok("first::input_1___ciao__::expected_1_len_four")
994+
.ok("first::input_2___buzz__::expected_1_len_four")
995+
.ok("first::input_1___ciao__::expected_2_len_four_alt")
996+
.ok("first::input_2___buzz__::expected_2_len_four_alt")
997+
.ok("second::expected_1_4::input_1_greet_ciao")
998+
.ok("second::expected_1_4::input_2_greet_buzz")
999+
.ok("second::expected_2_2_3_2::input_1_greet_ciao")
1000+
.ok("second::expected_2_2_3_2::input_2_greet_buzz")
10011001
.assert(output);
10021002
}
10031003
}

rstest_macros/src/parse/mod.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ use quote::ToTokens;
1919
use testcase::TestCase;
2020

2121
use self::{
22-
expressions::Expressions, just_once::JustOnceFnArgAttributeExtractor, vlist::ValueList,
22+
expressions::Expressions,
23+
just_once::JustOnceFnArgAttributeExtractor,
24+
vlist::{MatrixValues, ValueList},
2325
};
2426

2527
// To use the macros this should be the first one module
@@ -363,10 +365,11 @@ impl VisitMut for CasesFunctionExtractor {
363365
if attr_starts_with(&attr, &case) {
364366
match attr.parse_args::<Expressions>() {
365367
Ok(expressions) => {
368+
let args = expressions.take();
366369
let description =
367370
attr.path().segments.iter().nth(1).map(|p| p.ident.clone());
368371
self.0.push(TestCase {
369-
args: expressions.into(),
372+
args,
370373
attrs: std::mem::take(&mut attrs_buffer),
371374
description,
372375
});
@@ -398,9 +401,9 @@ pub(crate) fn extract_value_list(item_fn: &mut ItemFn) -> Result<Vec<ValueList>,
398401
type Out = ValueList;
399402

400403
fn build(attr: syn::Attribute, extra: &Pat) -> syn::Result<Self::Out> {
401-
attr.parse_args::<Expressions>().map(|v| ValueList {
404+
attr.parse_args::<MatrixValues>().map(|v| ValueList {
402405
arg: extra.clone(),
403-
values: v.take().into_iter().map(|e| e.into()).collect(),
406+
values: v.into_values(),
404407
})
405408
}
406409
}

rstest_macros/src/parse/rstest.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,6 +1057,44 @@ mod test {
10571057
&pat("__destruct_2")
10581058
);
10591059
}
1060+
1061+
#[test]
1062+
fn doc_comment_in_values_attribute_used_as_description() {
1063+
let mut item_fn = r#"
1064+
fn test_fn(
1065+
#[values(
1066+
/// the answer
1067+
42,
1068+
/// bigger entry
1069+
100,
1070+
2
1071+
)]
1072+
arg: u32
1073+
) {
1074+
}
1075+
"#
1076+
.ast();
1077+
1078+
let mut info = RsTestInfo::default();
1079+
1080+
info.extend_with_function_attrs(&mut item_fn).unwrap();
1081+
1082+
let list_values = info.data.list_values().cloned().collect::<Vec<_>>();
1083+
1084+
assert_eq!(1, list_values.len());
1085+
let values = &list_values[0].values;
1086+
1087+
assert_eq!(3, values.len());
1088+
1089+
assert_eq!("the_answer", values[0].description());
1090+
assert_eq!("42", values[0].expr.to_token_stream().to_string());
1091+
1092+
assert_eq!("bigger_entry", values[1].description());
1093+
assert_eq!("100", values[1].expr.to_token_stream().to_string());
1094+
1095+
assert_eq!("2", values[2].description());
1096+
assert_eq!("2", values[2].expr.to_token_stream().to_string());
1097+
}
10601098
}
10611099

10621100
#[test]

0 commit comments

Comments
 (0)