Skip to content

Commit e020341

Browse files
committed
make the pattern arg optional
Use a tt-muncher to make the "pattern" argument optional. I tried adding support for reordering arguments, but the macro got pretty complicated and I had trouble handling the optional trailing comma. Reordering arguments would be much easier with a proc macro.
1 parent 59bb4c5 commit e020341

32 files changed

+360
-21
lines changed

README.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,20 @@ datatest_stable::harness! {
6969

7070
`pattern` is an arbitrary expression that implements [`Display`](https://doc.rust-lang.org/nightly/core/fmt/trait.Display.html), such as
7171
`&str`, or a function call that returns a `String`.
72+
73+
`pattern` is optional, and defaults to `r".*"` (match all files).
7274

7375
The three parameters can be repeated if you have multiple sets of data-driven tests to be run:
7476

7577
````rust,ignore
7678
datatest_stable::harness! {
7779
{ test = testfn1, root = root1, pattern = pattern1 },
78-
{ test = testfn2, root = root2, pattern = pattern2 },
80+
{ test = testfn2, root = root2 },
7981
}
8082
````
8183

84+
Trailing commas are optional.
85+
8286
### Relative and absolute paths
8387

8488
The `pattern` argument is tested against the **relative** path of each file,
@@ -115,8 +119,12 @@ fn my_test_utf8(path: &Utf8Path, contents: String) -> datatest_stable::Result<()
115119
}
116120

117121
datatest_stable::harness! {
118-
{ test = my_test, root = "path/to/fixtures", pattern = r"^.*\.txt$" },
119-
{ test = my_test_utf8, root = "path/to/fixtures", pattern = r"^.*\.txt$" },
122+
{ test = my_test, root = "path/to/fixtures" },
123+
{
124+
test = my_test_utf8,
125+
root = "path/to/fixtures",
126+
pattern = r"^.*\.txt$",
127+
},
120128
}
121129
````
122130

@@ -168,7 +176,7 @@ fn my_test(path: &Utf8Path, contents: String) -> datatest_stable::Result<()> {
168176
}
169177

170178
datatest_stable::harness! {
171-
{ test = my_test, root = &FIXTURES, pattern = r"^.*\.json$" },
179+
{ test = my_test, root = &FIXTURES },
172180
}
173181
````
174182

src/lib.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,19 @@
6464
//! `pattern` is an arbitrary expression that implements [`Display`](std::fmt::Display), such as
6565
//! `&str`, or a function call that returns a `String`.
6666
//!
67+
//! `pattern` is optional, and defaults to `r".*"` (match all files).
68+
//!
6769
//! The three parameters can be repeated if you have multiple sets of data-driven tests to be run:
6870
//!
6971
//! ```rust,ignore
7072
//! datatest_stable::harness! {
7173
//! { test = testfn1, root = root1, pattern = pattern1 },
72-
//! { test = testfn2, root = root2, pattern = pattern2 },
74+
//! { test = testfn2, root = root2 },
7375
//! }
7476
//! ```
7577
//!
78+
//! Trailing commas are optional.
79+
//!
7680
//! ## Relative and absolute paths
7781
//!
7882
//! The `pattern` argument is tested against the **relative** path of each file,
@@ -109,8 +113,12 @@
109113
//! }
110114
//!
111115
//! datatest_stable::harness! {
112-
//! { test = my_test, root = "path/to/fixtures", pattern = r"^.*\.txt$" },
113-
//! { test = my_test_utf8, root = "path/to/fixtures", pattern = r"^.*\.txt$" },
116+
//! { test = my_test, root = "path/to/fixtures" },
117+
//! {
118+
//! test = my_test_utf8,
119+
//! root = "path/to/fixtures",
120+
//! pattern = r"^.*\.txt$",
121+
//! },
114122
//! }
115123
//! ```
116124
//!
@@ -164,7 +172,7 @@
164172
//! }
165173
//!
166174
//! datatest_stable::harness! {
167-
//! { test = my_test, root = &FIXTURES, pattern = r"^.*\.json$" },
175+
//! { test = my_test, root = &FIXTURES },
168176
//! }
169177
//! ```
170178
//!

src/macros.rs

Lines changed: 156 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,14 @@
77
/// a target](https://doc.rust-lang.org/cargo/reference/manifest.html#configuring-a-target)).
88
#[macro_export]
99
macro_rules! harness {
10-
( $( { test = $name:path, root = $root:expr, pattern = $pattern:expr $(,)* } ),+ $(,)* ) => {
10+
( $( { $($args:tt)* } ),+ $(,)* ) => {
1111
fn main() -> ::std::process::ExitCode {
1212
let mut requirements = Vec::new();
1313
use $crate::data_source_kinds::*;
1414
use $crate::test_kinds::*;
1515

1616
$(
17-
requirements.push(
18-
$crate::Requirements::new(
19-
$name.kind().resolve($name),
20-
stringify!($name).to_string(),
21-
$root.resolve_data_source(),
22-
$pattern.to_string()
23-
)
24-
);
17+
$crate::harness_collect!(@gather_test requirements, { $($args)*, } => { });
2518
)+
2619

2720
$crate::runner(&requirements)
@@ -44,3 +37,157 @@ note: patterns are now evaluated relative to the provided root, not to the crate
4437
};
4538
}
4639
}
40+
41+
#[macro_export]
42+
#[doc(hidden)]
43+
macro_rules! harness_collect {
44+
// Gather `test`
45+
(@gather_test
46+
$requirements:expr,
47+
// Note: here and below, rest always ends with at least 1 comma
48+
{ test = $test:path, $($rest:tt)* } =>
49+
{ }
50+
) => {
51+
$crate::harness_collect!(@gather_root
52+
$requirements,
53+
{ $($rest)* } =>
54+
{ test = $test, }
55+
);
56+
};
57+
58+
// `test` not found
59+
(@gather_test
60+
$requirements:expr,
61+
{ $key:ident $($rest:tt)* } =>
62+
{ }
63+
) => {
64+
compile_error!(concat!("expected `test`, found `", stringify!($key), "`"));
65+
};
66+
67+
// No remaining arguments
68+
(@gather_test
69+
$requirements:expr,
70+
{ $(,)* } =>
71+
{ }
72+
) => {
73+
compile_error!("expected `test`, but ran out of arguments");
74+
};
75+
76+
// Something that isn't an identifier
77+
(@gather_test
78+
$requirements:expr,
79+
{ $($rest:tt)* } =>
80+
{ }
81+
) => {
82+
compile_error!(concat!("expected `test`, found non-identifier token: (rest: ", stringify!($($rest)*), ")"));
83+
};
84+
85+
// Gather `root`
86+
(@gather_root
87+
$requirements:expr,
88+
{ root = $root:expr, $($rest:tt)* } =>
89+
{ $($collected:tt)* }
90+
) => {
91+
$crate::harness_collect!(@gather_pattern
92+
$requirements,
93+
{ $($rest)* } =>
94+
{ $($collected)* root = $root, }
95+
);
96+
};
97+
98+
// `root` not found
99+
(@gather_root
100+
$requirements:expr,
101+
{ $key:ident $($rest:tt)* } =>
102+
{ $($collected:tt)* }
103+
) => {
104+
compile_error!(concat!("expected `root`, found `", stringify!($key), "`"));
105+
};
106+
107+
// No remaining arguments
108+
(@gather_root
109+
$requirements:expr,
110+
{ $(,)* } =>
111+
{ $($collected:tt)* }
112+
) => {
113+
compile_error!(concat!("expected `root`, but ran out of arguments (collected: ", stringify!($($collected)*), ")"));
114+
};
115+
116+
// Something that isn't an identifier
117+
(@gather_root
118+
$requirements:expr,
119+
{ $($rest:tt)* } =>
120+
{ $($collected:tt)* }
121+
) => {
122+
compile_error!(concat!("expected `root`, found non-identifier token (rest: ", stringify!($($rest)*), ")"));
123+
};
124+
125+
// Gather pattern
126+
(@gather_pattern
127+
$requirements:expr,
128+
{ pattern = $pattern:expr, $($rest:tt)* } =>
129+
{ $($collected:tt)* }
130+
) => {
131+
$crate::harness_collect!(@finish
132+
$requirements,
133+
{ $($rest)* } =>
134+
{ $($collected)* pattern = $pattern, }
135+
);
136+
};
137+
138+
// `pattern` not found
139+
(@gather_pattern
140+
$requirements:expr,
141+
{ $key:ident $($rest:tt)* } =>
142+
{ $($collected:tt)* }
143+
) => {
144+
compile_error!(concat!("expected `pattern`, found `", stringify!($key), "`"));
145+
};
146+
147+
// `pattern` not found: no remaining arguments
148+
(@gather_pattern
149+
$requirements:expr,
150+
{ $(,)* } =>
151+
{ $($collected:tt)* }
152+
) => {
153+
$crate::harness_collect!(@finish
154+
$requirements,
155+
{ } =>
156+
{ $($collected)* pattern = ".*", }
157+
);
158+
};
159+
160+
// Something that isn't an identifier
161+
(@gather_pattern
162+
$requirements:expr,
163+
{ $($rest:tt)* } =>
164+
{ $($collected:tt)* }
165+
) => {
166+
compile_error!(concat!("expected `pattern`, found non-identifier token (rest: ", stringify!($($rest)*), ")"));
167+
};
168+
169+
// Finish - no more arguments allowed
170+
(@finish
171+
$requirements:expr,
172+
{ $(,)* } =>
173+
{ test = $test:path, root = $root:expr, pattern = $pattern:expr, }
174+
) => {
175+
$requirements.push(
176+
$crate::Requirements::new(
177+
$test.kind().resolve($test),
178+
stringify!($test).to_string(),
179+
$root.resolve_data_source(),
180+
$pattern.to_string()
181+
)
182+
);
183+
};
184+
185+
// Finish - unexpected extra arguments
186+
(@finish
187+
$requirements:expr,
188+
{ $($unexpected:tt)+ } =>
189+
{ $($collected:tt)* }
190+
) => {
191+
compile_error!(concat!("unexpected extra arguments: ", stringify!($($unexpected)+)));
192+
};
193+
}

tests/compile-fail/empty-arg-list.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
datatest_stable::harness! {
2+
{ }
3+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error: expected `test`, but ran out of arguments
2+
--> tests/compile-fail/empty-arg-list.rs:1:1
3+
|
4+
1 | / datatest_stable::harness! {
5+
2 | | { }
6+
3 | | }
7+
| |_^
8+
|
9+
= note: this error originates in the macro `$crate::harness_collect` which comes from the expansion of the macro `datatest_stable::harness` (in Nightly builds, run with -Z macro-backtrace for more info)

tests/compile-fail/extra-args.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
datatest_stable::harness! {
2+
{ test = my_test, root = "abc", pattern = ".*", extra = "xyz" }
3+
}

tests/compile-fail/extra-args.stderr

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error: unexpected extra arguments: extra = "xyz",
2+
--> tests/compile-fail/extra-args.rs:1:1
3+
|
4+
1 | / datatest_stable::harness! {
5+
2 | | { test = my_test, root = "abc", pattern = ".*", extra = "xyz" }
6+
3 | | }
7+
| |_^
8+
|
9+
= note: this error originates in the macro `$crate::harness_collect` which comes from the expansion of the macro `datatest_stable::harness` (in Nightly builds, run with -Z macro-backtrace for more info)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
datatest_stable::harness! {
2+
{ test = my_test, root = "tests/files", foo = "bar", }
3+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error: expected `pattern`, found `foo`
2+
--> tests/compile-fail/incorrect-arg-in-pattern.rs:1:1
3+
|
4+
1 | / datatest_stable::harness! {
5+
2 | | { test = my_test, root = "tests/files", foo = "bar", }
6+
3 | | }
7+
| |_^
8+
|
9+
= note: this error originates in the macro `$crate::harness_collect` which comes from the expansion of the macro `datatest_stable::harness` (in Nightly builds, run with -Z macro-backtrace for more info)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
datatest_stable::harness! {
2+
{
3+
test = my_test,
4+
pattern = r"^.*(?<!\.skip)\.txt$",
5+
},
6+
}

0 commit comments

Comments
 (0)