Skip to content

Commit 1dae3b5

Browse files
committed
feat: Add support for '#[should_panic]' macro
1 parent 94e0434 commit 1dae3b5

File tree

2 files changed

+54
-6
lines changed

2 files changed

+54
-6
lines changed

crates/libtest2/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ pub mod _private {
6363

6464
pub use crate::_main_parse as main_parse;
6565
pub use crate::_parse_ignore as parse_ignore;
66+
pub use crate::_run_test as run_test;
6667
pub use crate::_test_parse as test_parse;
6768
pub use crate::case::DynCase;
6869
}

crates/libtest2/src/macros.rs

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ macro_rules! _main_parse {
1414
}
1515

1616
#[macro_export]
17-
#[allow(clippy::crate_in_macro_def)] // accessing item defined by `_main_parse`/`_parse_ignore`, and recursively calling the macro itself
17+
#[allow(clippy::crate_in_macro_def)] // accessing item defined by `_main_parse`/`_parse_ignore`/`_run_test`, and recursively calling the macro itself
1818
macro_rules! _test_parse {
1919
// Entry point
2020
(#[test] $(#[$($attr:tt)+])* fn $name:ident $($item:tt)*) => {
@@ -28,20 +28,22 @@ macro_rules! _test_parse {
2828
// Recursively handle attributes:
2929

3030
// Edge condition (no more attributes to parse)
31-
(continue: name=$name:ident body=[$($item:tt)*] attrs=[] $(ignore=$ignore:tt)?) => {
31+
(continue: name=$name:ident body=[$($item:tt)*] attrs=[] $(ignore=$ignore:tt)? $(should_panic=$should_panic:tt)?) => {
3232
$crate::_private::test_parse!(break:
3333
name=$name
3434
body=[$($item)*]
3535
$(ignore=$ignore)?
36+
$(should_panic=$should_panic)?
3637
);
3738
};
3839
// Process `#[ignore]`/`#[ignore = ".."]` (NOTE: This will only match if an ignore macro has not already been parsed)
39-
(continue: name=$name:ident body=[$($item:tt)*] attrs=[#[ignore $(= $reason:literal)?] $(#[$($attr:tt)+])*]) => {
40+
(continue: name=$name:ident body=[$($item:tt)*] attrs=[#[ignore $(= $reason:literal)?] $(#[$($attr:tt)+])*] $(should_panic=$should_panic:tt)?) => {
4041
$crate::_private::test_parse!(continue:
4142
name=$name
4243
body=[$($item)*]
4344
attrs=[$(#[$($attr)*])*]
4445
ignore=[$($reason)?]
46+
$(should_panic=$should_panic)?
4547
);
4648
};
4749
// Ignore subsequent calls to `#[ignore]`/`#[ignore = ".."]`
@@ -53,19 +55,51 @@ macro_rules! _test_parse {
5355
ignore=$ignore
5456
);
5557
};
58+
// Process `#[should_panic]`/`#[should_panic = ".."]` (NOTE: This will only match if a should_panic macro has not already been parsed)
59+
(continue: name=$name:ident body=[$($item:tt)*] attrs=[#[should_panic $(= $expected:literal)?] $(#[$($attr:tt)+])*] $(ignore=$ignore:tt)?) => {
60+
$crate::_private::test_parse!(continue:
61+
name=$name
62+
body=[$($item)*]
63+
attrs=[$(#[$($attr)*])*]
64+
$(ignore=$ignore)?
65+
should_panic=[$($expected)?]
66+
);
67+
};
68+
// Process `#[should_panic(expected = "..")]` (NOTE: Same as branch above)
69+
(continue: name=$name:ident body=[$($item:tt)*] attrs=[#[should_panic(expected = $expected:literal)] $(#[$($attr:tt)+])*] $(ignore=$ignore:tt)?) => {
70+
$crate::_private::test_parse!(continue:
71+
name=$name
72+
body=[$($item)*]
73+
attrs=[$(#[$($attr)*])*]
74+
$(ignore=$ignore)?
75+
should_panic=[$expected]
76+
);
77+
};
78+
// Emit an error for subsequent calls to `#[should_panic]`/`#[should_panic = ".."]`/`#[should_panic(expected = "..")]` (but continue parsing)
79+
(continue: name=$name:ident body=[$($item:tt)*] attrs=[#[should_panic $($unused:tt)*] $(#[$($attr:tt)+])*] $(ignore=$ignore:tt)? should_panic=$should_panic:tt) => {
80+
compile_error!("annotating a test with multiple 'should_panic' attributes is not allowed");
81+
$crate::_private::test_parse!(continue:
82+
name=$name
83+
body=[$($item)*]
84+
attrs=[$(#[$($attr)*])*]
85+
$(ignore=$ignore)?
86+
should_panic=$should_panic
87+
);
88+
};
5689
// Emit error on unknown attributes (but continue parsing)
57-
(continue: name=$name:ident body=[$($item:tt)*] attrs=[#[$($unknown_attr:tt)+] $(#[$($attr:tt)+])*] $(ignore=$ignore:tt)?) => {
90+
(continue: name=$name:ident body=[$($item:tt)*] attrs=[#[$($unknown_attr:tt)+] $(#[$($attr:tt)+])*] $(ignore=$ignore:tt)? $(should_panic=$should_panic:tt)?) => {
5891
compile_error!(concat!("unknown attribute '", stringify!($($unknown_attr)+), "'"));
5992
$crate::_private::test_parse!(continue:
6093
name=$name
6194
body=[$($item)*]
6295
attrs=[$(#[$($attr)*])*]
6396
$(ignore=$ignore)?
97+
$(should_panic=$should_panic)?
6498
);
6599
};
66100

67101
// End result
68-
(break: name=$name:ident body=[$($item:tt)*] $(ignore=$ignore:tt)?) => {
102+
(break: name=$name:ident body=[$($item:tt)*] $(ignore=$ignore:tt)? $(should_panic=$should_panic:tt)?) => {
69103
#[allow(non_camel_case_types)]
70104
struct $name;
71105

@@ -91,7 +125,7 @@ macro_rules! _test_parse {
91125
$crate::_private::parse_ignore!(context, $($ignore)?);
92126

93127
use $crate::IntoRunResult;
94-
let result = run(context);
128+
let result = $crate::_private::run_test!(context, $($should_panic)?);
95129
IntoRunResult::into_run_result(result)
96130
}
97131
}
@@ -108,3 +142,16 @@ macro_rules! _parse_ignore {
108142
};
109143
($context:expr $(,)?) => {};
110144
}
145+
146+
#[macro_export]
147+
macro_rules! _run_test {
148+
($context:expr, [$expected:literal]) => {
149+
$crate::panic::assert_panic_contains(|| run($context), $expected)
150+
};
151+
($context:expr, []) => {
152+
$crate::panic::assert_panic(|| run($context))
153+
};
154+
($context:expr $(,)?) => {{
155+
run($context)
156+
}};
157+
}

0 commit comments

Comments
 (0)