Added ordering information to observer tests (#14332) (#15178)

Fixes #14332 by recording the order in which the events occur.
This commit is contained in:
Wybe Westra 2024-09-14 16:08:49 +02:00 committed by GitHub
parent b36443b6ed
commit 70808af776
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -480,6 +480,8 @@ impl World {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::vec;
use bevy_ptr::OwningPtr; use bevy_ptr::OwningPtr;
use crate as bevy_ecs; use crate as bevy_ecs;
@ -506,13 +508,12 @@ mod tests {
struct EventA; struct EventA;
#[derive(Resource, Default)] #[derive(Resource, Default)]
struct R(usize); struct Order(Vec<&'static str>);
impl R { impl Order {
#[track_caller] #[track_caller]
fn assert_order(&mut self, count: usize) { fn observed(&mut self, name: &'static str) {
assert_eq!(count, self.0); self.0.push(name);
self.0 += 1;
} }
} }
@ -537,61 +538,72 @@ mod tests {
#[test] #[test]
fn observer_order_spawn_despawn() { fn observer_order_spawn_despawn() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>(); world.init_resource::<Order>();
world.observe(|_: Trigger<OnAdd, A>, mut res: ResMut<R>| res.assert_order(0)); world.observe(|_: Trigger<OnAdd, A>, mut res: ResMut<Order>| res.observed("add"));
world.observe(|_: Trigger<OnInsert, A>, mut res: ResMut<R>| res.assert_order(1)); world.observe(|_: Trigger<OnInsert, A>, mut res: ResMut<Order>| res.observed("insert"));
world.observe(|_: Trigger<OnReplace, A>, mut res: ResMut<R>| res.assert_order(2)); world.observe(|_: Trigger<OnReplace, A>, mut res: ResMut<Order>| res.observed("replace"));
world.observe(|_: Trigger<OnRemove, A>, mut res: ResMut<R>| res.assert_order(3)); world.observe(|_: Trigger<OnRemove, A>, mut res: ResMut<Order>| res.observed("remove"));
let entity = world.spawn(A).id(); let entity = world.spawn(A).id();
world.despawn(entity); world.despawn(entity);
assert_eq!(4, world.resource::<R>().0); assert_eq!(
vec!["add", "insert", "replace", "remove"],
world.resource::<Order>().0
);
} }
#[test] #[test]
fn observer_order_insert_remove() { fn observer_order_insert_remove() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>(); world.init_resource::<Order>();
world.observe(|_: Trigger<OnAdd, A>, mut res: ResMut<R>| res.assert_order(0)); world.observe(|_: Trigger<OnAdd, A>, mut res: ResMut<Order>| res.observed("add"));
world.observe(|_: Trigger<OnInsert, A>, mut res: ResMut<R>| res.assert_order(1)); world.observe(|_: Trigger<OnInsert, A>, mut res: ResMut<Order>| res.observed("insert"));
world.observe(|_: Trigger<OnReplace, A>, mut res: ResMut<R>| res.assert_order(2)); world.observe(|_: Trigger<OnReplace, A>, mut res: ResMut<Order>| res.observed("replace"));
world.observe(|_: Trigger<OnRemove, A>, mut res: ResMut<R>| res.assert_order(3)); world.observe(|_: Trigger<OnRemove, A>, mut res: ResMut<Order>| res.observed("remove"));
let mut entity = world.spawn_empty(); let mut entity = world.spawn_empty();
entity.insert(A); entity.insert(A);
entity.remove::<A>(); entity.remove::<A>();
entity.flush(); entity.flush();
assert_eq!(4, world.resource::<R>().0); assert_eq!(
vec!["add", "insert", "replace", "remove"],
world.resource::<Order>().0
);
} }
#[test] #[test]
fn observer_order_insert_remove_sparse() { fn observer_order_insert_remove_sparse() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>(); world.init_resource::<Order>();
world.observe(|_: Trigger<OnAdd, S>, mut res: ResMut<R>| res.assert_order(0)); world.observe(|_: Trigger<OnAdd, S>, mut res: ResMut<Order>| res.observed("add"));
world.observe(|_: Trigger<OnInsert, S>, mut res: ResMut<R>| res.assert_order(1)); world.observe(|_: Trigger<OnInsert, S>, mut res: ResMut<Order>| res.observed("insert"));
world.observe(|_: Trigger<OnReplace, S>, mut res: ResMut<R>| res.assert_order(2)); world.observe(|_: Trigger<OnReplace, S>, mut res: ResMut<Order>| res.observed("replace"));
world.observe(|_: Trigger<OnRemove, S>, mut res: ResMut<R>| res.assert_order(3)); world.observe(|_: Trigger<OnRemove, S>, mut res: ResMut<Order>| res.observed("remove"));
let mut entity = world.spawn_empty(); let mut entity = world.spawn_empty();
entity.insert(S); entity.insert(S);
entity.remove::<S>(); entity.remove::<S>();
entity.flush(); entity.flush();
assert_eq!(4, world.resource::<R>().0); assert_eq!(
vec!["add", "insert", "replace", "remove"],
world.resource::<Order>().0
);
} }
#[test] #[test]
fn observer_order_replace() { fn observer_order_replace() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>(); world.init_resource::<Order>();
let entity = world.spawn(A).id(); let entity = world.spawn(A).id();
world.observe(|_: Trigger<OnReplace, A>, mut res: ResMut<R>| res.assert_order(0)); world.observe(|_: Trigger<OnAdd, A>, mut res: ResMut<Order>| res.observed("add"));
world.observe(|_: Trigger<OnInsert, A>, mut res: ResMut<R>| res.assert_order(1)); world.observe(|_: Trigger<OnInsert, A>, mut res: ResMut<Order>| res.observed("insert"));
world.observe(|_: Trigger<OnReplace, A>, mut res: ResMut<Order>| res.observed("replace"));
world.observe(|_: Trigger<OnRemove, A>, mut res: ResMut<Order>| res.observed("remove"));
// TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut // TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut
// and therefore does not automatically flush. // and therefore does not automatically flush.
@ -600,53 +612,56 @@ mod tests {
let mut entity = world.entity_mut(entity); let mut entity = world.entity_mut(entity);
entity.insert(A); entity.insert(A);
entity.flush(); entity.flush();
assert_eq!(2, world.resource::<R>().0); assert_eq!(vec!["replace", "insert"], world.resource::<Order>().0);
} }
#[test] #[test]
fn observer_order_recursive() { fn observer_order_recursive() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>(); world.init_resource::<Order>();
world.observe( world.observe(
|obs: Trigger<OnAdd, A>, mut res: ResMut<R>, mut commands: Commands| { |obs: Trigger<OnAdd, A>, mut res: ResMut<Order>, mut commands: Commands| {
res.assert_order(0); res.observed("add_a");
commands.entity(obs.entity()).insert(B); commands.entity(obs.entity()).insert(B);
}, },
); );
world.observe( world.observe(
|obs: Trigger<OnRemove, A>, mut res: ResMut<R>, mut commands: Commands| { |obs: Trigger<OnRemove, A>, mut res: ResMut<Order>, mut commands: Commands| {
res.assert_order(2); res.observed("remove_a");
commands.entity(obs.entity()).remove::<B>(); commands.entity(obs.entity()).remove::<B>();
}, },
); );
world.observe( world.observe(
|obs: Trigger<OnAdd, B>, mut res: ResMut<R>, mut commands: Commands| { |obs: Trigger<OnAdd, B>, mut res: ResMut<Order>, mut commands: Commands| {
res.assert_order(1); res.observed("add_b");
commands.entity(obs.entity()).remove::<A>(); commands.entity(obs.entity()).remove::<A>();
}, },
); );
world.observe(|_: Trigger<OnRemove, B>, mut res: ResMut<R>| { world.observe(|_: Trigger<OnRemove, B>, mut res: ResMut<Order>| {
res.assert_order(3); res.observed("remove_b");
}); });
let entity = world.spawn(A).flush(); let entity = world.spawn(A).flush();
let entity = world.get_entity(entity).unwrap(); let entity = world.get_entity(entity).unwrap();
assert!(!entity.contains::<A>()); assert!(!entity.contains::<A>());
assert!(!entity.contains::<B>()); assert!(!entity.contains::<B>());
assert_eq!(4, world.resource::<R>().0); assert_eq!(
vec!["add_a", "add_b", "remove_a", "remove_b"],
world.resource::<Order>().0
);
} }
#[test] #[test]
fn observer_multiple_listeners() { fn observer_multiple_listeners() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>(); world.init_resource::<Order>();
world.observe(|_: Trigger<OnAdd, A>, mut res: ResMut<R>| res.0 += 1); world.observe(|_: Trigger<OnAdd, A>, mut res: ResMut<Order>| res.observed("add_1"));
world.observe(|_: Trigger<OnAdd, A>, mut res: ResMut<R>| res.0 += 1); world.observe(|_: Trigger<OnAdd, A>, mut res: ResMut<Order>| res.observed("add_2"));
world.spawn(A).flush(); world.spawn(A).flush();
assert_eq!(2, world.resource::<R>().0); assert_eq!(vec!["add_1", "add_2"], world.resource::<Order>().0);
// Our A entity plus our two observers // Our A entity plus our two observers
assert_eq!(world.entities().len(), 3); assert_eq!(world.entities().len(), 3);
} }
@ -654,40 +669,44 @@ mod tests {
#[test] #[test]
fn observer_multiple_events() { fn observer_multiple_events() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>(); world.init_resource::<Order>();
let on_remove = world.init_component::<OnRemove>(); let on_remove = world.init_component::<OnRemove>();
world.spawn( world.spawn(
// SAFETY: OnAdd and OnRemove are both unit types, so this is safe // SAFETY: OnAdd and OnRemove are both unit types, so this is safe
unsafe { unsafe {
Observer::new(|_: Trigger<OnAdd, A>, mut res: ResMut<R>| res.0 += 1) Observer::new(|_: Trigger<OnAdd, A>, mut res: ResMut<Order>| {
.with_event(on_remove) res.observed("add/remove");
})
.with_event(on_remove)
}, },
); );
let entity = world.spawn(A).id(); let entity = world.spawn(A).id();
world.despawn(entity); world.despawn(entity);
assert_eq!(2, world.resource::<R>().0); assert_eq!(
vec!["add/remove", "add/remove"],
world.resource::<Order>().0
);
} }
#[test] #[test]
fn observer_multiple_components() { fn observer_multiple_components() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>(); world.init_resource::<Order>();
world.init_component::<A>(); world.init_component::<A>();
world.init_component::<B>(); world.init_component::<B>();
world.observe(|_: Trigger<OnAdd, (A, B)>, mut res: ResMut<R>| res.0 += 1); world.observe(|_: Trigger<OnAdd, (A, B)>, mut res: ResMut<Order>| res.observed("add_ab"));
let entity = world.spawn(A).id(); let entity = world.spawn(A).id();
world.entity_mut(entity).insert(B); world.entity_mut(entity).insert(B);
world.flush(); world.flush();
assert_eq!(2, world.resource::<R>().0); assert_eq!(vec!["add_ab", "add_ab"], world.resource::<Order>().0);
} }
#[test] #[test]
fn observer_despawn() { fn observer_despawn() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>();
let observer = world let observer = world
.observe(|_: Trigger<OnAdd, A>| panic!("Observer triggered after being despawned.")) .observe(|_: Trigger<OnAdd, A>| panic!("Observer triggered after being despawned."))
@ -700,11 +719,11 @@ mod tests {
#[test] #[test]
fn observer_despawn_archetype_flags() { fn observer_despawn_archetype_flags() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>(); world.init_resource::<Order>();
let entity = world.spawn((A, B)).flush(); let entity = world.spawn((A, B)).flush();
world.observe(|_: Trigger<OnRemove, A>, mut res: ResMut<R>| res.0 += 1); world.observe(|_: Trigger<OnRemove, A>, mut res: ResMut<Order>| res.observed("remove_a"));
let observer = world let observer = world
.observe(|_: Trigger<OnRemove, B>| panic!("Observer triggered after being despawned.")) .observe(|_: Trigger<OnRemove, B>| panic!("Observer triggered after being despawned."))
@ -713,31 +732,31 @@ mod tests {
world.despawn(entity); world.despawn(entity);
assert_eq!(1, world.resource::<R>().0); assert_eq!(vec!["remove_a"], world.resource::<Order>().0);
} }
#[test] #[test]
fn observer_multiple_matches() { fn observer_multiple_matches() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>(); world.init_resource::<Order>();
world.observe(|_: Trigger<OnAdd, (A, B)>, mut res: ResMut<R>| res.0 += 1); world.observe(|_: Trigger<OnAdd, (A, B)>, mut res: ResMut<Order>| res.observed("add_ab"));
world.spawn((A, B)).flush(); world.spawn((A, B)).flush();
assert_eq!(1, world.resource::<R>().0); assert_eq!(vec!["add_ab"], world.resource::<Order>().0);
} }
#[test] #[test]
fn observer_no_target() { fn observer_no_target() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>(); world.init_resource::<Order>();
world world
.spawn_empty() .spawn_empty()
.observe(|_: Trigger<EventA>| panic!("Trigger routed to non-targeted entity.")); .observe(|_: Trigger<EventA>| panic!("Trigger routed to non-targeted entity."));
world.observe(move |obs: Trigger<EventA>, mut res: ResMut<R>| { world.observe(move |obs: Trigger<EventA>, mut res: ResMut<Order>| {
assert_eq!(obs.entity(), Entity::PLACEHOLDER); assert_eq!(obs.entity(), Entity::PLACEHOLDER);
res.0 += 1; res.observed("event_a");
}); });
// TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut // TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut
@ -745,24 +764,24 @@ mod tests {
world.flush(); world.flush();
world.trigger(EventA); world.trigger(EventA);
world.flush(); world.flush();
assert_eq!(1, world.resource::<R>().0); assert_eq!(vec!["event_a"], world.resource::<Order>().0);
} }
#[test] #[test]
fn observer_entity_routing() { fn observer_entity_routing() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>(); world.init_resource::<Order>();
world world
.spawn_empty() .spawn_empty()
.observe(|_: Trigger<EventA>| panic!("Trigger routed to non-targeted entity.")); .observe(|_: Trigger<EventA>| panic!("Trigger routed to non-targeted entity."));
let entity = world let entity = world
.spawn_empty() .spawn_empty()
.observe(|_: Trigger<EventA>, mut res: ResMut<R>| res.0 += 1) .observe(|_: Trigger<EventA>, mut res: ResMut<Order>| res.observed("a_1"))
.id(); .id();
world.observe(move |obs: Trigger<EventA>, mut res: ResMut<R>| { world.observe(move |obs: Trigger<EventA>, mut res: ResMut<Order>| {
assert_eq!(obs.entity(), entity); assert_eq!(obs.entity(), entity);
res.0 += 1; res.observed("a_2");
}); });
// TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut // TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut
@ -770,17 +789,17 @@ mod tests {
world.flush(); world.flush();
world.trigger_targets(EventA, entity); world.trigger_targets(EventA, entity);
world.flush(); world.flush();
assert_eq!(2, world.resource::<R>().0); assert_eq!(vec!["a_2", "a_1"], world.resource::<Order>().0);
} }
#[test] #[test]
fn observer_dynamic_component() { fn observer_dynamic_component() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>(); world.init_resource::<Order>();
let component_id = world.init_component::<A>(); let component_id = world.init_component::<A>();
world.spawn( world.spawn(
Observer::new(|_: Trigger<OnAdd>, mut res: ResMut<R>| res.0 += 1) Observer::new(|_: Trigger<OnAdd>, mut res: ResMut<Order>| res.observed("event_a"))
.with_component(component_id), .with_component(component_id),
); );
@ -793,20 +812,20 @@ mod tests {
world.trigger_targets(EventA, entity); world.trigger_targets(EventA, entity);
world.flush(); world.flush();
assert_eq!(1, world.resource::<R>().0); assert_eq!(vec!["event_a"], world.resource::<Order>().0);
} }
#[test] #[test]
fn observer_dynamic_trigger() { fn observer_dynamic_trigger() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>(); world.init_resource::<Order>();
let event_a = world.init_component::<EventA>(); let event_a = world.init_component::<EventA>();
world.spawn(ObserverState { world.spawn(ObserverState {
// SAFETY: we registered `event_a` above and it matches the type of TriggerA // SAFETY: we registered `event_a` above and it matches the type of TriggerA
descriptor: unsafe { ObserverDescriptor::default().with_events(vec![event_a]) }, descriptor: unsafe { ObserverDescriptor::default().with_events(vec![event_a]) },
runner: |mut world, _trigger, _ptr, _propagate| { runner: |mut world, _trigger, _ptr, _propagate| {
world.resource_mut::<R>().0 += 1; world.resource_mut::<Order>().observed("event_a");
}, },
..Default::default() ..Default::default()
}); });
@ -816,22 +835,22 @@ mod tests {
unsafe { EmitDynamicTrigger::new_with_id(event_a, EventA, ()) }, unsafe { EmitDynamicTrigger::new_with_id(event_a, EventA, ()) },
); );
world.flush(); world.flush();
assert_eq!(1, world.resource::<R>().0); assert_eq!(vec!["event_a"], world.resource::<Order>().0);
} }
#[test] #[test]
fn observer_propagating() { fn observer_propagating() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>(); world.init_resource::<Order>();
let parent = world let parent = world
.spawn_empty() .spawn_empty()
.observe(|_: Trigger<EventPropagating>, mut res: ResMut<R>| res.0 += 1) .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| res.observed("parent"))
.id(); .id();
let child = world let child = world
.spawn(Parent(parent)) .spawn(Parent(parent))
.observe(|_: Trigger<EventPropagating>, mut res: ResMut<R>| res.0 += 1) .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| res.observed("child"))
.id(); .id();
// TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut // TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut
@ -839,22 +858,22 @@ mod tests {
world.flush(); world.flush();
world.trigger_targets(EventPropagating, child); world.trigger_targets(EventPropagating, child);
world.flush(); world.flush();
assert_eq!(2, world.resource::<R>().0); assert_eq!(vec!["child", "parent"], world.resource::<Order>().0);
} }
#[test] #[test]
fn observer_propagating_redundant_dispatch_same_entity() { fn observer_propagating_redundant_dispatch_same_entity() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>(); world.init_resource::<Order>();
let parent = world let parent = world
.spawn_empty() .spawn_empty()
.observe(|_: Trigger<EventPropagating>, mut res: ResMut<R>| res.0 += 1) .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| res.observed("parent"))
.id(); .id();
let child = world let child = world
.spawn(Parent(parent)) .spawn(Parent(parent))
.observe(|_: Trigger<EventPropagating>, mut res: ResMut<R>| res.0 += 1) .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| res.observed("child"))
.id(); .id();
// TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut // TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut
@ -862,22 +881,25 @@ mod tests {
world.flush(); world.flush();
world.trigger_targets(EventPropagating, [child, child]); world.trigger_targets(EventPropagating, [child, child]);
world.flush(); world.flush();
assert_eq!(4, world.resource::<R>().0); assert_eq!(
vec!["child", "parent", "child", "parent"],
world.resource::<Order>().0
);
} }
#[test] #[test]
fn observer_propagating_redundant_dispatch_parent_child() { fn observer_propagating_redundant_dispatch_parent_child() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>(); world.init_resource::<Order>();
let parent = world let parent = world
.spawn_empty() .spawn_empty()
.observe(|_: Trigger<EventPropagating>, mut res: ResMut<R>| res.0 += 1) .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| res.observed("parent"))
.id(); .id();
let child = world let child = world
.spawn(Parent(parent)) .spawn(Parent(parent))
.observe(|_: Trigger<EventPropagating>, mut res: ResMut<R>| res.0 += 1) .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| res.observed("child"))
.id(); .id();
// TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut // TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut
@ -885,24 +907,27 @@ mod tests {
world.flush(); world.flush();
world.trigger_targets(EventPropagating, [child, parent]); world.trigger_targets(EventPropagating, [child, parent]);
world.flush(); world.flush();
assert_eq!(3, world.resource::<R>().0); assert_eq!(
vec!["child", "parent", "parent"],
world.resource::<Order>().0
);
} }
#[test] #[test]
fn observer_propagating_halt() { fn observer_propagating_halt() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>(); world.init_resource::<Order>();
let parent = world let parent = world
.spawn_empty() .spawn_empty()
.observe(|_: Trigger<EventPropagating>, mut res: ResMut<R>| res.0 += 1) .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| res.observed("parent"))
.id(); .id();
let child = world let child = world
.spawn(Parent(parent)) .spawn(Parent(parent))
.observe( .observe(
|mut trigger: Trigger<EventPropagating>, mut res: ResMut<R>| { |mut trigger: Trigger<EventPropagating>, mut res: ResMut<Order>| {
res.0 += 1; res.observed("child");
trigger.propagate(false); trigger.propagate(false);
}, },
) )
@ -913,30 +938,30 @@ mod tests {
world.flush(); world.flush();
world.trigger_targets(EventPropagating, child); world.trigger_targets(EventPropagating, child);
world.flush(); world.flush();
assert_eq!(1, world.resource::<R>().0); assert_eq!(vec!["child"], world.resource::<Order>().0);
} }
#[test] #[test]
fn observer_propagating_join() { fn observer_propagating_join() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>(); world.init_resource::<Order>();
let parent = world let parent = world
.spawn_empty() .spawn_empty()
.observe(|_: Trigger<EventPropagating>, mut res: ResMut<R>| res.0 += 1) .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| res.observed("parent"))
.id(); .id();
let child_a = world let child_a = world
.spawn(Parent(parent)) .spawn(Parent(parent))
.observe(|_: Trigger<EventPropagating>, mut res: ResMut<R>| { .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| {
res.0 += 1; res.observed("child_a");
}) })
.id(); .id();
let child_b = world let child_b = world
.spawn(Parent(parent)) .spawn(Parent(parent))
.observe(|_: Trigger<EventPropagating>, mut res: ResMut<R>| { .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| {
res.0 += 1; res.observed("child_b");
}) })
.id(); .id();
@ -945,17 +970,20 @@ mod tests {
world.flush(); world.flush();
world.trigger_targets(EventPropagating, [child_a, child_b]); world.trigger_targets(EventPropagating, [child_a, child_b]);
world.flush(); world.flush();
assert_eq!(4, world.resource::<R>().0); assert_eq!(
vec!["child_a", "parent", "child_b", "parent"],
world.resource::<Order>().0
);
} }
#[test] #[test]
fn observer_propagating_no_next() { fn observer_propagating_no_next() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>(); world.init_resource::<Order>();
let entity = world let entity = world
.spawn_empty() .spawn_empty()
.observe(|_: Trigger<EventPropagating>, mut res: ResMut<R>| res.0 += 1) .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| res.observed("event"))
.id(); .id();
// TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut // TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut
@ -963,24 +991,26 @@ mod tests {
world.flush(); world.flush();
world.trigger_targets(EventPropagating, entity); world.trigger_targets(EventPropagating, entity);
world.flush(); world.flush();
assert_eq!(1, world.resource::<R>().0); assert_eq!(vec!["event"], world.resource::<Order>().0);
} }
#[test] #[test]
fn observer_propagating_parallel_propagation() { fn observer_propagating_parallel_propagation() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>(); world.init_resource::<Order>();
let parent_a = world let parent_a = world
.spawn_empty() .spawn_empty()
.observe(|_: Trigger<EventPropagating>, mut res: ResMut<R>| res.0 += 1) .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| {
res.observed("parent_a");
})
.id(); .id();
let child_a = world let child_a = world
.spawn(Parent(parent_a)) .spawn(Parent(parent_a))
.observe( .observe(
|mut trigger: Trigger<EventPropagating>, mut res: ResMut<R>| { |mut trigger: Trigger<EventPropagating>, mut res: ResMut<Order>| {
res.0 += 1; res.observed("child_a");
trigger.propagate(false); trigger.propagate(false);
}, },
) )
@ -988,12 +1018,14 @@ mod tests {
let parent_b = world let parent_b = world
.spawn_empty() .spawn_empty()
.observe(|_: Trigger<EventPropagating>, mut res: ResMut<R>| res.0 += 1) .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| {
res.observed("parent_b");
})
.id(); .id();
let child_b = world let child_b = world
.spawn(Parent(parent_b)) .spawn(Parent(parent_b))
.observe(|_: Trigger<EventPropagating>, mut res: ResMut<R>| res.0 += 1) .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| res.observed("child_b"))
.id(); .id();
// TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut // TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut
@ -1001,15 +1033,18 @@ mod tests {
world.flush(); world.flush();
world.trigger_targets(EventPropagating, [child_a, child_b]); world.trigger_targets(EventPropagating, [child_a, child_b]);
world.flush(); world.flush();
assert_eq!(3, world.resource::<R>().0); assert_eq!(
vec!["child_a", "child_b", "parent_b"],
world.resource::<Order>().0
);
} }
#[test] #[test]
fn observer_propagating_world() { fn observer_propagating_world() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>(); world.init_resource::<Order>();
world.observe(|_: Trigger<EventPropagating>, mut res: ResMut<R>| res.0 += 1); world.observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| res.observed("event"));
let grandparent = world.spawn_empty().id(); let grandparent = world.spawn_empty().id();
let parent = world.spawn(Parent(grandparent)).id(); let parent = world.spawn(Parent(grandparent)).id();
@ -1020,18 +1055,18 @@ mod tests {
world.flush(); world.flush();
world.trigger_targets(EventPropagating, child); world.trigger_targets(EventPropagating, child);
world.flush(); world.flush();
assert_eq!(3, world.resource::<R>().0); assert_eq!(vec!["event", "event", "event"], world.resource::<Order>().0);
} }
#[test] #[test]
fn observer_propagating_world_skipping() { fn observer_propagating_world_skipping() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<R>(); world.init_resource::<Order>();
world.observe( world.observe(
|trigger: Trigger<EventPropagating>, query: Query<&A>, mut res: ResMut<R>| { |trigger: Trigger<EventPropagating>, query: Query<&A>, mut res: ResMut<Order>| {
if query.get(trigger.entity()).is_ok() { if query.get(trigger.entity()).is_ok() {
res.0 += 1; res.observed("event");
} }
}, },
); );
@ -1045,6 +1080,6 @@ mod tests {
world.flush(); world.flush();
world.trigger_targets(EventPropagating, child); world.trigger_targets(EventPropagating, child);
world.flush(); world.flush();
assert_eq!(2, world.resource::<R>().0); assert_eq!(vec!["event", "event"], world.resource::<Order>().0);
} }
} }