-
Notifications
You must be signed in to change notification settings - Fork 7
Add support for #[should_panic] macro
#151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
0ff7c92 to
1cf8554
Compare
Pull Request Test Coverage Report for Build 19069497492Details
💛 - Coveralls |
| $( | ||
| match $crate::_private::parse_ignore!($($attr)*) { | ||
| ::std::option::Option::None => context.ignore()?, | ||
| ::std::option::Option::Some(reason) => context.ignore_for(reason)?, | ||
| } | ||
| )* | ||
| $crate::_private::parse_ignore!(context, $ignore)?; | ||
| )? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we move the conditional nature of $ignore into _parse_ignore, simplifying this?
crates/libtest2/src/macros.rs
Outdated
| // Discard unknown attributes | ||
| (continue: name=$name:ident body=[$($item:tt)*] attrs=[#[$($unknown_attr:tt)+] $(#[$($attr:tt)+])*] $(ignore=$ignore:tt)?) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason we aren't doing compile errors for them anymore?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Side note: we need to add trybuild to check the error reporting of our macros
crates/libtest2/src/macros.rs
Outdated
| #[macro_export] | ||
| macro_rules! assert_panic { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should add documentation and tests for this
crates/libtest2/src/macros.rs
Outdated
| #[macro_export] | ||
| macro_rules! assert_panic { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How much of this can live in a function? This is a lot of code to put in a macro which can negatively impact compile times
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can put everything in a function: assert_panic(f: impl FnOnce(), panic_message: Option<&str>). That's a pretty decent API, but it has the slight disadvantage of the Option being there at runtime, even though it should always be known at compile time whether or not there is a panic message. Normally that seems fine to me, but since this is "test code" most people won't have optimizations on, so I'm less happy about that.
We can also do two functions: assert_panic(f: impl FnOnce()) & assert_panic_with_message(f: impl FnOnce(), panic_message: &str). It's also possible to then hide those behind an assert_panic macro similar to the one implemented here, which delegates to the correct function call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If assert_panic is an implementation detail of the macro, then I don't mind there being one variant. The runtime overhead of Option should be minimal.
If it's not, then I would go with assert_panic and assert_panic_message (don't need with because we're asserting on the message, not panicking itself) / assert_panic_contains (express what message operation we do).
One thing in favor of a macro is adding a display message like other assert macros ... but that would be confusing with the panic message being optional.
crates/libtest2/src/macros.rs
Outdated
| match ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(|| $f)) { | ||
| // The test should have panicked, but didn't. | ||
| ::std::result::Result::Ok(_) => { | ||
| // TODO: Rust includes the source file location here, consider doing the same? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should resolve TODOs or track them in issues before merging, rather than having a second backlog
crates/libtest2/src/macros.rs
Outdated
| ($f:expr $(,)?) => { | ||
| match ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(|| $f)) { | ||
| // The test should have panicked, but didn't. | ||
| ::std::result::Result::Ok(_) => { | ||
| // TODO: Rust includes the source file location here, consider doing the same? | ||
| ::std::result::Result::Err($crate::RunError::fail("test did not panic as expected")) | ||
| } | ||
|
|
||
| // The test panicked, as expected. | ||
| ::std::result::Result::Err(_) => Ok(()), | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would there be a good way to unify these code paths, like turning expected into an Option<&str>?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, definitely, absolutely. If it's okay to merge these paths then I think swapping to an assert_panic function (as mentioned in another comment) would be best.
| #[should_panic = "very long and specific message"] | ||
| fn passes_because_subsequent_panics_are_ignored(_context: &libtest2::TestContext) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we should error instead. We could always defer this question to another issue.
| ---- panics_with_the_wrong_message ---- | ||
| panic did not contain expected string | ||
| panic message: "with the wrong message" | ||
| expected substring: "in a controlled manner" | ||
| failures: | ||
| accidentally_panics | ||
| panics_with_the_wrong_message | ||
| test result: FAILED. 3 passed; 2 failed; 0 ignored; 0 filtered out; finished in [..]s |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does libtest show the backtrace for the panic? Are we able to do that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you update the documentation in src/lib.rs with any deviations from libtest?
The `test_parse` macro is changed to recursively parse attributes, filtering out the `#[ignore]` macro. This should also make it fairly trivial to add additional macros, such as `#[should_ignore]`. There is a fair amount of complexity added to the macro with this change, but on the other hand, it also succeeds in generating only the necessary code. You win some, you lose some.
1cf8554 to
b69f17b
Compare
b69f17b to
dea0e5f
Compare
This is done by recursively parsing attributes following the
#[libtest2::test]macro, and then parsing the first#[should_panic]macro encountered. If a macro is encountered then the test will be run within acatch_unwind, which is done by wrapping it in theassert_panicmacro (function style).Potentially supporting multiple
#[should_panic]macros is left for the future.Fixes #15