-
Notifications
You must be signed in to change notification settings - Fork 835
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
Simplify event assertion with predicate-based check #7734
base: master
Are you sure you want to change the base?
Conversation
/cmd prdoc --audience runtime_dev --bump patch |
…_dev --bump patch'
…y-add-events-contains
All GitHub workflows were cancelled due to failure one of the required jobs. |
/cmd fmt |
substrate/frame/system/src/lib.rs
Outdated
/// Similar to `System::assert_has_event`, but allows checking for an event without needing to | ||
/// specify the exact details of its inner fields. | ||
#[cfg(any(feature = "std", feature = "runtime-benchmarks", test))] | ||
#[track_caller] |
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've never used this, but from what I’m reading, #[track_caller]
is used for functions that can panic or assert, allowing you to see the line where they were called rather than the line where the panic or assertion occurred.
So, this contains_event
returns bool, so I think it shouldn't fail.
#[track_caller] |
If we use this all the time as assert!(System::contains_event(
, maybe we could just change existing:
- pub fn assert_has_event(event: T::RuntimeEvent)`
+ pub fn assert_has_event(predicate: F)`
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.
Actually if we look above, we already have assert_has_event
. So this can just be used here already and we don't need contains_event
.
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, I agree that we already have a similar function, assert_has_event()
. Refactoring it to accept a predicate could be an option. The goal here is to simplify event assertions and provide an alternative way to check for events. Here's a comparison of assert_has_event()
vs contains_event()
:
relay_chain::System::assert_has_event(relay_chain::RuntimeEvent::XcmPallet(
pallet_xcm::Event::Attempted {
outcome: Outcome::Complete { used: Weight::from_parts(1_000, 1_000) },
},
));
assert!(relay_chain::System::contains_event(|event| {
matches!(
event,
relay_chain::RuntimeEvent::XcmPallet(pallet_xcm::Event::Attempted { .. })
)
}));
My original intent was to keep this as a lightweight PR. Refactoring assert_has_event()
would require more extensive changes. If that's the preferred approach, I’ll skip adding contains_event()
to frame
and instead refactor it as a local helper within the XCM scope.
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.
@bkchr Removed contains_event()
from frame
, please check.
substrate/frame/system/src/lib.rs
Outdated
/// Similar to `System::assert_has_event`, but allows checking for an event without needing to | ||
/// specify the exact details of its inner fields. | ||
#[cfg(any(feature = "std", feature = "runtime-benchmarks", test))] | ||
#[track_caller] |
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.
Actually if we look above, we already have assert_has_event
. So this can just be used here already and we don't need contains_event
.
Co-authored-by: Branislav Kontur <[email protected]>
@@ -26,6 +26,20 @@ fn buy_execution<C>(fees: impl Into<Asset>) -> Instruction<C> { | |||
BuyExecution { fees: fees.into(), weight_limit: Unlimited } | |||
} | |||
|
|||
// Helper function for checking if an XCM event matching the given predicate exists. | |||
fn contains_event<F>(predicate: F) -> bool |
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.
Nit: I would rename this function since it's not clear who should contain the event.
Also could we make it a macro ? I think it would be easier to use. At least for the use cases that I see in this PR. Something like assert_system_event!(event)
.
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.
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.
No, no, I mean something like:
macro_rules! assert_system_event {
( $expected_event:pat ) => {
relay_chain::System::events().iter().any(|e| matches!(e.event, $expected_event))
};
}
And then we can just do
assert_system_event!(relay_chain::RuntimeEvent::XcmPallet(pallet_xcm::Event::Attempted { .. }))
Or maybe we should provide relay_chain::System
also as a parameter, but anyway, something like 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.
Renamed
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.
Okay, will do.
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.
Sorry, my code is incomplete. You also have to check that relay_chain::System::events().iter().any(|e| matches!(e.event, $expected_event))
actually returns true
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.
Instead of assert_system_event
, I changed the logic a bit to system_contains_event
, which returns a bool
. This allows tests to check for both the presence and absence of events. It also works for both parachain and relay chain events.
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.
Looks good ! I just left one more comment, but it's mostly a nit.
$module::System::events().iter().any(|e| { | ||
matches!(e.event, $module::RuntimeEvent::$variant($($pattern)*)) |
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.
RuntimeEvent
is hardcoded and it's the same for all runtimes. But I think System
is not. The runtimes can use any name when adding the system pallet:
construct_runtime! {
pub enum Runtime
{
System: frame_system,
...
}
}
So if we need to use this method in more tests in the future, we might need to add a parameter to the macro for providing the name of the system pallet. But here it works like this.
Also I would rename module
to runtime
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.
Renamed. Will change later if we need more parameters.
A follow-up PR to simplify event assertions by introducing
contains_event
, allowing event checks without needing exact field matches. This reduces redundancy and makes tests more flexible.Partially addresses #6119 by providing an alternative way to assert events.
Reference: PR #7594 - Discussion