Proof of Concept: custom scope for check!#39
Conversation
…he scope after which check! should panic
|
Hey! Thanks for the PR. I had been thinking about a mechanism like this as a solution too. The main reason I didn't implement it is because I was still holding out for something like In a way, this PR would make it more difficult to switch because we're giving more control over when the panic happens. So changing the macro to not panic at all would be more of a breaking change than it is now (the macro documentation explicitly states that the function might stop panicking at all in the future). So I'm a bit torn: on the one hand I want to have a solution, and I think the one in this PR is currently the only viable one. On the other hand, I don't really want |
|
Sry that it's been taking me so long to respond... Couldn't Am I missing something? |
|
Hello, friendly bump to ask, I've been testing @felixwrt branch for a bit, and the drop solution seems to work well. This solution is especially great to create multiple tests cases. const INPUTS: [&str; ...] = ["aaa", "bbb", ...];
const EXPECTED: [&str; ...] = ["1", "2", ...];
let ctx = CheckContext::new();
INPUTS.iter()
.interleave(EXPECTED.iter())
.for_each(|(input, expected)| {
let actual = fn_to_test(input);
check_ctx!(ctx, expected == actual);
})This is a super nice API, instead of having to do macro shenanigans to generate tests case, or using something like I understand not wanting to add something that could be deprecated in the future, but I don't really see a way that this could be done in stable until custom tests framework is merged, which seems pretty far away, and this is a really useful feature to have. |
|
Ok, I'm convinced that we should have something that can work short term. I think I want to call it just a I'm not 100% sure about the exact API for this though. It will probably have to be macro based too (atleast for capturing variables) because captured variables also need to be un-registered when the scope closes where they were captured. I'm thinking something along these lines: let scope = assert2::Scope::new();
assert2::capture!(scope, $expression);And this would expand to something like: let scope = assert2::Scope::new();
let scope = scope.capture(
stringify!($expression),
$expression,
);And probably this would internally be backed by an The difficult part: what syntax will the |
|
Anyway, there's also the question of syntax. We could go with a new macro name, or some special syntax. I don't really like weird syntax, but I also don't feel great about adding more macros either. Some options: assert_scoped!(scope, $expression, $msg...);
assert!(@my_scope, $expression, $msg...);
assert!(scope = my_scope, $expression, $msg...); // Maybe this is ambiguous? Also somewhat consfusing considering `$msg...` can have named placeholders too.
assert!(@scope = my_scope, $expression, $msg...);
assert!(#[scope = my_scope] $expression, $msg...); // Ewww?
assert!(
#[scope = my_scope]
$expression,
$msg...
); // Still ewww?I think looking at all of these a new macro seems the cleanest 😅 |
|
Hi! I really love this crate. I use it on every project now. And I’d also love to see this feature get merged, so here are my two cents on this :D I think the best option could be a new macro, because:
So IMO, a new macro But again, I’m not a library author myself and don’t know much about macros, so take my words with a grain of salt :) |
|
Regarding whether or not But I want to re-use the same scope object for adding captures/diagnostics. For example, I would like to support something like: use assert2::{assert_scoped, capture};
let mut scope = assert2::scope();
for i in 0..10 {
capture!(scope, i);
assert_scoped!(scope, difficult_math(i) == 2 * i);
}And this would show the value of Right now you have to do this: use assert2::assert;
for i in 0..10 {
assert!(difficult_math(i) == 2 * i, "i = {i}");
}But that gets cumbersome for more complex assertions and it allows for typos. I would also like to let them automatically get removed from the "scope" object when they go out of scope: use assert2::{assert_scoped, capture};
let mut scope = assert2::scope();
for i in 0..10 {
capture!(scope, i);
assert!(scope, difficult_math(i) == 2 * i);
}
assert!(false); // Doesn't show `i` because it's out of scope already.But that's kinda contrary to how the scope is supposed to work for delaying checks... But it's probably necessary if we're going to keep around a |
I use
assert2::check!a lot in my tests, but was missing some flexibility. Especially when usingcheck!in a loop, there's currently no way to prevent panicking after the first iteration in which the check fails. I read #23 and had a slightly different idea that I successfully tried out:This PR contains the proof of concept of that idea. I've introduced a
CheckContexttype that tracks whether a check failed and panics when it goes out of scope. This context can be used in the new macrocheck_ctx!. I've also written two tests that show how these two items can be used. This is the output generated by these tests:I quite like the approach and I think that it's even possible to add support for passing a context to the existing
check!macro.What do you think? Could you see something like this in
assert2?