Path: blob/main/crates/bevy_ecs/src/world/entity_access/mod.rs
9367 views
mod component_fetch;1mod entity_mut;2mod entity_ref;3mod entry;4mod except;5mod filtered;6mod world_mut;78pub use component_fetch::*;9pub use entity_mut::*;10pub use entity_ref::*;11pub use entry::*;12pub use except::*;13pub use filtered::*;14pub use world_mut::*;1516#[cfg(test)]17mod tests {18use alloc::{vec, vec::Vec};19use bevy_ptr::{OwningPtr, Ptr};20use core::panic::AssertUnwindSafe;21use std::sync::OnceLock;2223use crate::change_detection::Tick;24use crate::lifecycle::HookContext;25use crate::query::QueryAccessError;26use crate::{27change_detection::{MaybeLocation, MutUntyped},28component::ComponentId,29prelude::*,30resource::IsResource,31system::{assert_is_system, RunSystemOnce as _},32world::{error::EntityComponentError, DeferredWorld, FilteredEntityMut, FilteredEntityRef},33};3435use super::{EntityMutExcept, EntityRefExcept};3637#[derive(Component, Clone, Copy, Debug, PartialEq)]38struct TestComponent(u32);3940#[derive(Component, Clone, Copy, Debug, PartialEq)]41#[component(storage = "SparseSet")]42struct TestComponent2(u32);4344#[derive(Component)]45struct Marker;4647#[derive(Component)]48#[component(on_add = despawn_on_add)]49struct DespawnOnAdd;5051fn despawn_on_add(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {52world.commands().entity(entity).despawn();53}5455#[test]56fn entity_ref_get_by_id() {57let mut world = World::new();58let entity = world.spawn(TestComponent(42)).id();59let component_id = world60.components()61.get_valid_id(core::any::TypeId::of::<TestComponent>())62.unwrap();6364let entity = world.entity(entity);65let test_component = entity.get_by_id(component_id).unwrap();66// SAFETY: points to a valid `TestComponent`67let test_component = unsafe { test_component.deref::<TestComponent>() };6869assert_eq!(test_component.0, 42);70}7172#[test]73fn entity_mut_get_by_id() {74let mut world = World::new();75let entity = world.spawn(TestComponent(42)).id();76let component_id = world77.components()78.get_valid_id(core::any::TypeId::of::<TestComponent>())79.unwrap();8081let mut entity_mut = world.entity_mut(entity);82let mut test_component = entity_mut.get_mut_by_id(component_id).unwrap();83{84test_component.set_changed();85let test_component =86// SAFETY: `test_component` has unique access of the `EntityWorldMut` and is not used afterwards87unsafe { test_component.into_inner().deref_mut::<TestComponent>() };88test_component.0 = 43;89}9091let entity = world.entity(entity);92let test_component = entity.get_by_id(component_id).unwrap();93// SAFETY: `TestComponent` is the correct component type94let test_component = unsafe { test_component.deref::<TestComponent>() };9596assert_eq!(test_component.0, 43);97}9899#[test]100fn entity_ref_get_by_id_invalid_component_id() {101let invalid_component_id = ComponentId::new(usize::MAX);102103let mut world = World::new();104let entity = world.spawn_empty().id();105let entity = world.entity(entity);106assert!(entity.get_by_id(invalid_component_id).is_err());107}108109#[test]110fn entity_mut_get_by_id_invalid_component_id() {111let invalid_component_id = ComponentId::new(usize::MAX);112113let mut world = World::new();114let mut entity = world.spawn_empty();115assert!(entity.get_by_id(invalid_component_id).is_err());116assert!(entity.get_mut_by_id(invalid_component_id).is_err());117}118119#[derive(Resource)]120struct R(usize);121122#[test]123fn entity_mut_resource_scope() {124// Keep in sync with the `resource_scope` test in lib.rs125let mut world = World::new();126let mut entity = world.spawn_empty();127128assert!(entity.try_resource_scope::<R, _>(|_, _| {}).is_none());129entity.world_scope(|world| world.insert_resource(R(0)));130entity.resource_scope(|entity: &mut EntityWorldMut, mut value: Mut<R>| {131value.0 += 1;132assert!(!entity.world().contains_resource::<R>());133});134assert_eq!(entity.resource::<R>().0, 1);135}136137#[test]138fn entity_mut_resource_scope_panic() {139let mut world = World::new();140world.insert_resource(R(0));141142let mut entity = world.spawn_empty();143let old_location = entity.location();144let result = std::panic::catch_unwind(AssertUnwindSafe(|| {145entity.resource_scope(|entity: &mut EntityWorldMut, _: Mut<R>| {146// Change the entity's `EntityLocation`.147entity.insert(TestComponent(0));148149// Ensure that the entity location still gets updated even in case of a panic.150panic!("this should get caught by the outer scope")151});152}));153assert!(result.is_err());154155// Ensure that the location has been properly updated.156assert_ne!(entity.location(), old_location);157}158159// regression test for https://github.com/bevyengine/bevy/pull/7387160#[test]161fn entity_mut_world_scope_panic() {162let mut world = World::new();163164let mut entity = world.spawn_empty();165let old_location = entity.location();166let id = entity.id();167let res = std::panic::catch_unwind(AssertUnwindSafe(|| {168entity.world_scope(|w| {169// Change the entity's `EntityLocation`, which invalidates the original `EntityWorldMut`.170// This will get updated at the end of the scope.171w.entity_mut(id).insert(TestComponent(0));172173// Ensure that the entity location still gets updated even in case of a panic.174panic!("this should get caught by the outer scope")175});176}));177assert!(res.is_err());178179// Ensure that the location has been properly updated.180assert_ne!(entity.location(), old_location);181}182183#[test]184fn entity_mut_reborrow_scope_panic() {185let mut world = World::new();186187let mut entity = world.spawn_empty();188let old_location = entity.location();189let res = std::panic::catch_unwind(AssertUnwindSafe(|| {190entity.reborrow_scope(|mut entity| {191// Change the entity's `EntityLocation`, which invalidates the original `EntityWorldMut`.192// This will get updated at the end of the scope.193entity.insert(TestComponent(0));194195// Ensure that the entity location still gets updated even in case of a panic.196panic!("this should get caught by the outer scope")197});198}));199assert!(res.is_err());200201// Ensure that the location has been properly updated.202assert_ne!(entity.location(), old_location);203}204205// regression test for https://github.com/bevyengine/bevy/pull/7805206#[test]207fn removing_sparse_updates_archetype_row() {208#[derive(Component, PartialEq, Debug)]209struct Dense(u8);210211#[derive(Component)]212#[component(storage = "SparseSet")]213struct Sparse;214215let mut world = World::new();216let e1 = world.spawn((Dense(0), Sparse)).id();217let e2 = world.spawn((Dense(1), Sparse)).id();218219world.entity_mut(e1).remove::<Sparse>();220assert_eq!(world.entity(e2).get::<Dense>().unwrap(), &Dense(1));221}222223// regression test for https://github.com/bevyengine/bevy/pull/7805224#[test]225fn removing_dense_updates_table_row() {226#[derive(Component, PartialEq, Debug)]227struct Dense(u8);228229#[derive(Component)]230#[component(storage = "SparseSet")]231struct Sparse;232233let mut world = World::new();234let e1 = world.spawn((Dense(0), Sparse)).id();235let e2 = world.spawn((Dense(1), Sparse)).id();236237world.entity_mut(e1).remove::<Dense>();238assert_eq!(world.entity(e2).get::<Dense>().unwrap(), &Dense(1));239}240241// Test that calling retain with `()` removes all components.242#[test]243fn retain_nothing() {244#[derive(Component)]245struct Marker<const N: usize>;246247let mut world = World::new();248let ent = world.spawn((Marker::<1>, Marker::<2>, Marker::<3>)).id();249250world.entity_mut(ent).retain::<()>();251assert_eq!(world.entity(ent).archetype().components().len(), 0);252}253254// Test removing some components with `retain`, including components not on the entity.255#[test]256fn retain_some_components() {257#[derive(Component)]258struct Marker<const N: usize>;259260let mut world = World::new();261let ent = world.spawn((Marker::<1>, Marker::<2>, Marker::<3>)).id();262263world.entity_mut(ent).retain::<(Marker<2>, Marker<4>)>();264// Check that marker 2 was retained.265assert!(world.entity(ent).get::<Marker<2>>().is_some());266// Check that only marker 2 was retained.267assert_eq!(world.entity(ent).archetype().components().len(), 1);268}269270// regression test for https://github.com/bevyengine/bevy/pull/7805271#[test]272fn inserting_sparse_updates_archetype_row() {273#[derive(Component, PartialEq, Debug)]274struct Dense(u8);275276#[derive(Component)]277#[component(storage = "SparseSet")]278struct Sparse;279280let mut world = World::new();281let e1 = world.spawn(Dense(0)).id();282let e2 = world.spawn(Dense(1)).id();283284world.entity_mut(e1).insert(Sparse);285assert_eq!(world.entity(e2).get::<Dense>().unwrap(), &Dense(1));286}287288// regression test for https://github.com/bevyengine/bevy/pull/7805289#[test]290fn inserting_dense_updates_archetype_row() {291#[derive(Component, PartialEq, Debug)]292struct Dense(u8);293294#[derive(Component)]295struct Dense2;296297#[derive(Component)]298#[component(storage = "SparseSet")]299struct Sparse;300301let mut world = World::new();302let e1 = world.spawn(Dense(0)).id();303let e2 = world.spawn(Dense(1)).id();304305world.entity_mut(e1).insert(Sparse).remove::<Sparse>();306307// archetype with [e2, e1]308// table with [e1, e2]309310world.entity_mut(e2).insert(Dense2);311312assert_eq!(world.entity(e1).get::<Dense>().unwrap(), &Dense(0));313}314315#[test]316fn inserting_dense_updates_table_row() {317#[derive(Component, PartialEq, Debug)]318struct Dense(u8);319320#[derive(Component)]321struct Dense2;322323#[derive(Component)]324#[component(storage = "SparseSet")]325struct Sparse;326327let mut world = World::new();328let e1 = world.spawn(Dense(0)).id();329let e2 = world.spawn(Dense(1)).id();330331world.entity_mut(e1).insert(Sparse).remove::<Sparse>();332333// archetype with [e2, e1]334// table with [e1, e2]335336world.entity_mut(e1).insert(Dense2);337338assert_eq!(world.entity(e2).get::<Dense>().unwrap(), &Dense(1));339}340341// regression test for https://github.com/bevyengine/bevy/pull/7805342#[test]343fn despawning_entity_updates_archetype_row() {344#[derive(Component, PartialEq, Debug)]345struct Dense(u8);346347#[derive(Component)]348#[component(storage = "SparseSet")]349struct Sparse;350351let mut world = World::new();352let e1 = world.spawn(Dense(0)).id();353let e2 = world.spawn(Dense(1)).id();354355world.entity_mut(e1).insert(Sparse).remove::<Sparse>();356357// archetype with [e2, e1]358// table with [e1, e2]359360world.entity_mut(e2).despawn();361362assert_eq!(world.entity(e1).get::<Dense>().unwrap(), &Dense(0));363}364365// regression test for https://github.com/bevyengine/bevy/pull/7805366#[test]367fn despawning_entity_updates_table_row() {368#[derive(Component, PartialEq, Debug)]369struct Dense(u8);370371#[derive(Component)]372#[component(storage = "SparseSet")]373struct Sparse;374375let mut world = World::new();376let e1 = world.spawn(Dense(0)).id();377let e2 = world.spawn(Dense(1)).id();378379world.entity_mut(e1).insert(Sparse).remove::<Sparse>();380381// archetype with [e2, e1]382// table with [e1, e2]383384world.entity_mut(e1).despawn();385386assert_eq!(world.entity(e2).get::<Dense>().unwrap(), &Dense(1));387}388389#[test]390fn entity_mut_insert_by_id() {391let mut world = World::new();392let test_component_id = world.register_component::<TestComponent>();393394let mut entity = world.spawn_empty();395OwningPtr::make(TestComponent(42), |ptr| {396// SAFETY: `ptr` matches the component id397unsafe { entity.insert_by_id(test_component_id, ptr) };398});399400let components: Vec<_> = world.query::<&TestComponent>().iter(&world).collect();401402assert_eq!(components, vec![&TestComponent(42)]);403404// Compare with `insert_bundle_by_id`405406let mut entity = world.spawn_empty();407OwningPtr::make(TestComponent(84), |ptr| {408// SAFETY: `ptr` matches the component id409unsafe { entity.insert_by_ids(&[test_component_id], vec![ptr].into_iter()) };410});411412let components: Vec<_> = world.query::<&TestComponent>().iter(&world).collect();413414assert_eq!(components, vec![&TestComponent(42), &TestComponent(84)]);415}416417#[test]418fn entity_mut_insert_bundle_by_id() {419let mut world = World::new();420let test_component_id = world.register_component::<TestComponent>();421let test_component_2_id = world.register_component::<TestComponent2>();422423let component_ids = [test_component_id, test_component_2_id];424let test_component_value = TestComponent(42);425let test_component_2_value = TestComponent2(84);426427let mut entity = world.spawn_empty();428OwningPtr::make(test_component_value, |ptr1| {429OwningPtr::make(test_component_2_value, |ptr2| {430// SAFETY: `ptr1` and `ptr2` match the component ids431unsafe { entity.insert_by_ids(&component_ids, vec![ptr1, ptr2].into_iter()) };432});433});434435let dynamic_components: Vec<_> = world436.query::<(&TestComponent, &TestComponent2)>()437.iter(&world)438.collect();439440assert_eq!(441dynamic_components,442vec![(&TestComponent(42), &TestComponent2(84))]443);444445// Compare with `World` generated using static type equivalents446let mut static_world = World::new();447448static_world.spawn((test_component_value, test_component_2_value));449let static_components: Vec<_> = static_world450.query::<(&TestComponent, &TestComponent2)>()451.iter(&static_world)452.collect();453454assert_eq!(dynamic_components, static_components);455}456457#[test]458fn entity_mut_remove_by_id() {459let mut world = World::new();460let test_component_id = world.register_component::<TestComponent>();461462let mut entity = world.spawn(TestComponent(42));463entity.remove_by_id(test_component_id);464465let components: Vec<_> = world.query::<&TestComponent>().iter(&world).collect();466467assert_eq!(components, vec![] as Vec<&TestComponent>);468469// remove non-existent component does not panic470world.spawn_empty().remove_by_id(test_component_id);471}472473/// Tests that components can be accessed through an `EntityRefExcept`.474#[test]475fn entity_ref_except() {476let mut world = World::new();477world.register_component::<TestComponent>();478world.register_component::<TestComponent2>();479480world.spawn((TestComponent(0), TestComponent2(0), Marker));481482let mut query = world.query_filtered::<EntityRefExcept<TestComponent>, With<Marker>>();483484let mut found = false;485for entity_ref in query.iter_mut(&mut world) {486found = true;487assert!(entity_ref.get::<TestComponent>().is_none());488assert!(entity_ref.get_ref::<TestComponent>().is_none());489assert!(matches!(490entity_ref.get::<TestComponent2>(),491Some(TestComponent2(0))492));493}494495assert!(found);496}497498// Test that a single query can't both contain a mutable reference to a499// component C and an `EntityRefExcept` that doesn't include C among its500// exclusions.501#[test]502#[should_panic]503fn entity_ref_except_conflicts_with_self() {504let mut world = World::new();505world.spawn(TestComponent(0)).insert(TestComponent2(0));506507// This should panic, because we have a mutable borrow on508// `TestComponent` but have a simultaneous indirect immutable borrow on509// that component via `EntityRefExcept`.510world.run_system_once(system).unwrap();511512fn system(_: Query<(&mut TestComponent, EntityRefExcept<TestComponent2>)>) {}513}514515// Test that an `EntityRefExcept` that doesn't include a component C among516// its exclusions can't coexist with a mutable query for that component.517#[test]518#[should_panic]519fn entity_ref_except_conflicts_with_other() {520let mut world = World::new();521world.spawn(TestComponent(0)).insert(TestComponent2(0));522523// This should panic, because we have a mutable borrow on524// `TestComponent` but have a simultaneous indirect immutable borrow on525// that component via `EntityRefExcept`.526world.run_system_once(system).unwrap();527528fn system(_: Query<&mut TestComponent>, _: Query<EntityRefExcept<TestComponent2>>) {}529}530531// Test that an `EntityRefExcept` with an exception for some component C can532// coexist with a query for that component C.533#[test]534fn entity_ref_except_doesnt_conflict() {535let mut world = World::new();536world.spawn((TestComponent(0), TestComponent2(0), Marker));537538world.run_system_once(system).unwrap();539540fn system(541_: Query<&mut TestComponent, With<Marker>>,542query: Query<EntityRefExcept<TestComponent>, With<Marker>>,543) {544for entity_ref in query.iter() {545assert!(matches!(546entity_ref.get::<TestComponent2>(),547Some(TestComponent2(0))548));549}550}551}552553/// Tests that components can be mutably accessed through an554/// `EntityMutExcept`.555#[test]556fn entity_mut_except() {557let mut world = World::new();558world.spawn((TestComponent(0), TestComponent2(0), Marker));559560let mut query = world.query_filtered::<EntityMutExcept<TestComponent>, With<Marker>>();561562let mut found = false;563for mut entity_mut in query.iter_mut(&mut world) {564found = true;565assert!(entity_mut.get::<TestComponent>().is_none());566assert!(entity_mut.get_ref::<TestComponent>().is_none());567assert!(entity_mut.get_mut::<TestComponent>().is_none());568assert!(matches!(569entity_mut.get::<TestComponent2>(),570Some(TestComponent2(0))571));572}573574assert!(found);575}576577// Test that a single query can't both contain a mutable reference to a578// component C and an `EntityMutExcept` that doesn't include C among its579// exclusions.580#[test]581#[should_panic]582fn entity_mut_except_conflicts_with_self() {583let mut world = World::new();584world.spawn(TestComponent(0)).insert(TestComponent2(0));585586// This should panic, because we have a mutable borrow on587// `TestComponent` but have a simultaneous indirect immutable borrow on588// that component via `EntityRefExcept`.589world.run_system_once(system).unwrap();590591fn system(_: Query<(&mut TestComponent, EntityMutExcept<TestComponent2>)>) {}592}593594// Test that an `EntityMutExcept` that doesn't include a component C among595// its exclusions can't coexist with a query for that component.596#[test]597#[should_panic]598fn entity_mut_except_conflicts_with_other() {599let mut world = World::new();600world.spawn(TestComponent(0)).insert(TestComponent2(0));601602// This should panic, because we have a mutable borrow on603// `TestComponent` but have a simultaneous indirect immutable borrow on604// that component via `EntityRefExcept`.605world.run_system_once(system).unwrap();606607fn system(_: Query<&mut TestComponent>, mut query: Query<EntityMutExcept<TestComponent2>>) {608for mut entity_mut in query.iter_mut() {609assert!(entity_mut610.get_mut::<TestComponent2>()611.is_some_and(|component| component.0 == 0));612}613}614}615616// Test that an `EntityMutExcept` with an exception for some component C can617// coexist with a query for that component C.618#[test]619fn entity_mut_except_doesnt_conflict() {620let mut world = World::new();621world.spawn((TestComponent(0), TestComponent2(0), Marker));622623world.run_system_once(system).unwrap();624625fn system(626_: Query<&mut TestComponent, With<Marker>>,627mut query: Query<EntityMutExcept<TestComponent>, With<Marker>>,628) {629for mut entity_mut in query.iter_mut() {630assert!(entity_mut631.get_mut::<TestComponent2>()632.is_some_and(|component| component.0 == 0));633}634}635}636637#[test]638fn entity_mut_except_registers_components() {639// Checks for a bug where `EntityMutExcept` would not register the component and640// would therefore not include an exception, causing it to conflict with the later query.641fn system1(_query: Query<EntityMutExcept<TestComponent>>, _: Query<&mut TestComponent>) {}642let mut world = World::new();643world.run_system_once(system1).unwrap();644645fn system2(_: Query<&mut TestComponent>, _query: Query<EntityMutExcept<TestComponent>>) {}646let mut world = World::new();647world.run_system_once(system2).unwrap();648}649650#[derive(Component)]651struct A;652653#[test]654fn disjoint_access() {655fn disjoint_readonly(_: Query<EntityMut, With<A>>, _: Query<EntityRef, Without<A>>) {}656657fn disjoint_mutable(_: Query<EntityMut, With<A>>, _: Query<EntityMut, Without<A>>) {}658659assert_is_system(disjoint_readonly);660assert_is_system(disjoint_mutable);661}662663#[test]664fn ref_compatible() {665fn borrow_system(_: Query<(EntityRef, &A)>, _: Query<&A>) {}666667assert_is_system(borrow_system);668}669670#[test]671fn ref_compatible_with_resource() {672fn borrow_system(_: Query<EntityRef>, _: Res<R>) {}673674assert_is_system(borrow_system);675}676677#[test]678#[should_panic]679fn ref_incompatible_with_resource_mut() {680fn borrow_system(_: Query<EntityRef>, _: ResMut<R>) {}681682assert_is_system(borrow_system);683}684685#[test]686fn ref_compatible_with_resource_mut() {687fn borrow_system(_: Query<EntityRef, Without<IsResource>>, _: ResMut<R>) {}688689assert_is_system(borrow_system);690}691692#[test]693#[should_panic]694fn ref_incompatible_with_mutable_component() {695fn incompatible_system(_: Query<(EntityRef, &mut A)>) {}696697assert_is_system(incompatible_system);698}699700#[test]701#[should_panic]702fn ref_incompatible_with_mutable_query() {703fn incompatible_system(_: Query<EntityRef>, _: Query<&mut A>) {}704705assert_is_system(incompatible_system);706}707708#[test]709fn mut_compatible_with_entity() {710fn borrow_mut_system(_: Query<(Entity, EntityMut)>) {}711712assert_is_system(borrow_mut_system);713}714715#[test]716#[should_panic]717fn mut_incompatible_with_resource() {718fn borrow_mut_system(_: Res<R>, _: Query<EntityMut>) {}719720assert_is_system(borrow_mut_system);721}722723#[test]724#[should_panic]725fn mut_incompatible_with_resource_mut() {726fn borrow_mut_system(_: ResMut<R>, _: Query<EntityMut>) {}727728assert_is_system(borrow_mut_system);729}730731#[test]732fn mut_compatible_with_resource() {733fn borrow_mut_system(_: Res<R>, _: Query<EntityMut, Without<IsResource>>) {}734735assert_is_system(borrow_mut_system);736}737738#[test]739fn mut_compatible_with_resource_mut() {740fn borrow_mut_system(_: ResMut<R>, _: Query<EntityMut, Without<IsResource>>) {}741742assert_is_system(borrow_mut_system);743}744745#[test]746#[should_panic]747fn mut_incompatible_with_read_only_component() {748fn incompatible_system(_: Query<(EntityMut, &A)>) {}749750assert_is_system(incompatible_system);751}752753#[test]754#[should_panic]755fn mut_incompatible_with_mutable_component() {756fn incompatible_system(_: Query<(EntityMut, &mut A)>) {}757758assert_is_system(incompatible_system);759}760761#[test]762#[should_panic]763fn mut_incompatible_with_read_only_query() {764fn incompatible_system(_: Query<EntityMut>, _: Query<&A>) {}765766assert_is_system(incompatible_system);767}768769#[test]770#[should_panic]771fn mut_incompatible_with_mutable_query() {772fn incompatible_system(_: Query<EntityMut>, _: Query<&mut A>) {}773774assert_is_system(incompatible_system);775}776777#[test]778fn filtered_entity_ref_normal() {779let mut world = World::new();780let a_id = world.register_component::<A>();781782let e: FilteredEntityRef = world.spawn(A).into();783784assert!(e.get::<A>().is_some());785assert!(e.get_ref::<A>().is_some());786assert!(e.get_change_ticks::<A>().is_some());787assert!(e.get_by_id(a_id).is_some());788assert!(e.get_change_ticks_by_id(a_id).is_some());789}790791#[test]792fn filtered_entity_ref_missing() {793let mut world = World::new();794let a_id = world.register_component::<A>();795796let e: FilteredEntityRef = world.spawn(()).into();797798assert!(e.get::<A>().is_none());799assert!(e.get_ref::<A>().is_none());800assert!(e.get_change_ticks::<A>().is_none());801assert!(e.get_by_id(a_id).is_none());802assert!(e.get_change_ticks_by_id(a_id).is_none());803}804805#[test]806fn filtered_entity_mut_normal() {807let mut world = World::new();808let a_id = world.register_component::<A>();809810let mut e: FilteredEntityMut = world.spawn(A).into();811812assert!(e.get::<A>().is_some());813assert!(e.get_ref::<A>().is_some());814assert!(e.get_mut::<A>().is_some());815assert!(e.get_change_ticks::<A>().is_some());816assert!(e.get_by_id(a_id).is_some());817assert!(e.get_mut_by_id(a_id).is_some());818assert!(e.get_change_ticks_by_id(a_id).is_some());819}820821#[test]822fn filtered_entity_mut_missing() {823let mut world = World::new();824let a_id = world.register_component::<A>();825826let mut e: FilteredEntityMut = world.spawn(()).into();827828assert!(e.get::<A>().is_none());829assert!(e.get_ref::<A>().is_none());830assert!(e.get_mut::<A>().is_none());831assert!(e.get_change_ticks::<A>().is_none());832assert!(e.get_by_id(a_id).is_none());833assert!(e.get_mut_by_id(a_id).is_none());834assert!(e.get_change_ticks_by_id(a_id).is_none());835}836837#[derive(Component, PartialEq, Eq, Debug)]838struct X(usize);839840#[derive(Component, PartialEq, Eq, Debug)]841struct Y(usize);842843#[test]844fn get_components() {845let mut world = World::default();846let e1 = world.spawn((X(7), Y(10))).id();847let e2 = world.spawn(X(8)).id();848let e3 = world.spawn_empty().id();849850assert_eq!(851Ok((&X(7), &Y(10))),852world.entity(e1).get_components::<(&X, &Y)>()853);854assert_eq!(855Err(QueryAccessError::EntityDoesNotMatch),856world.entity(e2).get_components::<(&X, &Y)>()857);858assert_eq!(859Err(QueryAccessError::EntityDoesNotMatch),860world.entity(e3).get_components::<(&X, &Y)>()861);862}863864#[test]865fn get_components_mut() {866let mut world = World::default();867let e1 = world.spawn((X(7), Y(10))).id();868869let mut entity_mut_1 = world.entity_mut(e1);870let Ok((mut x, mut y)) = entity_mut_1.get_components_mut::<(&mut X, &mut Y)>() else {871panic!("could not get components");872};873x.0 += 1;874y.0 += 1;875876assert_eq!(877Ok((&X(8), &Y(11))),878world.entity(e1).get_components::<(&X, &Y)>()879);880}881882#[test]883fn get_by_id_array() {884let mut world = World::default();885let e1 = world.spawn((X(7), Y(10))).id();886let e2 = world.spawn(X(8)).id();887let e3 = world.spawn_empty().id();888889let x_id = world.register_component::<X>();890let y_id = world.register_component::<Y>();891892assert_eq!(893Ok((&X(7), &Y(10))),894world895.entity(e1)896.get_by_id([x_id, y_id])897.map(|[x_ptr, y_ptr]| {898// SAFETY: components match the id they were fetched with899(unsafe { x_ptr.deref::<X>() }, unsafe { y_ptr.deref::<Y>() })900})901);902assert_eq!(903Err(EntityComponentError::MissingComponent(y_id)),904world905.entity(e2)906.get_by_id([x_id, y_id])907.map(|[x_ptr, y_ptr]| {908// SAFETY: components match the id they were fetched with909(unsafe { x_ptr.deref::<X>() }, unsafe { y_ptr.deref::<Y>() })910})911);912assert_eq!(913Err(EntityComponentError::MissingComponent(x_id)),914world915.entity(e3)916.get_by_id([x_id, y_id])917.map(|[x_ptr, y_ptr]| {918// SAFETY: components match the id they were fetched with919(unsafe { x_ptr.deref::<X>() }, unsafe { y_ptr.deref::<Y>() })920})921);922}923924#[test]925fn get_by_id_vec() {926let mut world = World::default();927let e1 = world.spawn((X(7), Y(10))).id();928let e2 = world.spawn(X(8)).id();929let e3 = world.spawn_empty().id();930931let x_id = world.register_component::<X>();932let y_id = world.register_component::<Y>();933934assert_eq!(935Ok((&X(7), &Y(10))),936world937.entity(e1)938.get_by_id(&[x_id, y_id] as &[ComponentId])939.map(|ptrs| {940let Ok([x_ptr, y_ptr]): Result<[Ptr; 2], _> = ptrs.try_into() else {941panic!("get_by_id(slice) didn't return 2 elements")942};943944// SAFETY: components match the id they were fetched with945(unsafe { x_ptr.deref::<X>() }, unsafe { y_ptr.deref::<Y>() })946})947);948assert_eq!(949Err(EntityComponentError::MissingComponent(y_id)),950world951.entity(e2)952.get_by_id(&[x_id, y_id] as &[ComponentId])953.map(|ptrs| {954let Ok([x_ptr, y_ptr]): Result<[Ptr; 2], _> = ptrs.try_into() else {955panic!("get_by_id(slice) didn't return 2 elements")956};957958// SAFETY: components match the id they were fetched with959(unsafe { x_ptr.deref::<X>() }, unsafe { y_ptr.deref::<Y>() })960})961);962assert_eq!(963Err(EntityComponentError::MissingComponent(x_id)),964world965.entity(e3)966.get_by_id(&[x_id, y_id] as &[ComponentId])967.map(|ptrs| {968let Ok([x_ptr, y_ptr]): Result<[Ptr; 2], _> = ptrs.try_into() else {969panic!("get_by_id(slice) didn't return 2 elements")970};971972// SAFETY: components match the id they were fetched with973(unsafe { x_ptr.deref::<X>() }, unsafe { y_ptr.deref::<Y>() })974})975);976}977978#[test]979fn get_mut_by_id_array() {980let mut world = World::default();981let e1 = world.spawn((X(7), Y(10))).id();982let e2 = world.spawn(X(8)).id();983let e3 = world.spawn_empty().id();984985let x_id = world.register_component::<X>();986let y_id = world.register_component::<Y>();987988assert_eq!(989Ok((&mut X(7), &mut Y(10))),990world991.entity_mut(e1)992.get_mut_by_id([x_id, y_id])993.map(|[x_ptr, y_ptr]| {994// SAFETY: components match the id they were fetched with995(unsafe { x_ptr.into_inner().deref_mut::<X>() }, unsafe {996y_ptr.into_inner().deref_mut::<Y>()997})998})999);1000assert_eq!(1001Err(EntityComponentError::MissingComponent(y_id)),1002world1003.entity_mut(e2)1004.get_mut_by_id([x_id, y_id])1005.map(|[x_ptr, y_ptr]| {1006// SAFETY: components match the id they were fetched with1007(unsafe { x_ptr.into_inner().deref_mut::<X>() }, unsafe {1008y_ptr.into_inner().deref_mut::<Y>()1009})1010})1011);1012assert_eq!(1013Err(EntityComponentError::MissingComponent(x_id)),1014world1015.entity_mut(e3)1016.get_mut_by_id([x_id, y_id])1017.map(|[x_ptr, y_ptr]| {1018// SAFETY: components match the id they were fetched with1019(unsafe { x_ptr.into_inner().deref_mut::<X>() }, unsafe {1020y_ptr.into_inner().deref_mut::<Y>()1021})1022})1023);10241025assert_eq!(1026Err(EntityComponentError::AliasedMutability(x_id)),1027world1028.entity_mut(e1)1029.get_mut_by_id([x_id, x_id])1030.map(|_| { unreachable!() })1031);1032assert_eq!(1033Err(EntityComponentError::AliasedMutability(x_id)),1034world1035.entity_mut(e3)1036.get_mut_by_id([x_id, x_id])1037.map(|_| { unreachable!() })1038);1039}10401041#[test]1042fn get_mut_by_id_vec() {1043let mut world = World::default();1044let e1 = world.spawn((X(7), Y(10))).id();1045let e2 = world.spawn(X(8)).id();1046let e3 = world.spawn_empty().id();10471048let x_id = world.register_component::<X>();1049let y_id = world.register_component::<Y>();10501051assert_eq!(1052Ok((&mut X(7), &mut Y(10))),1053world1054.entity_mut(e1)1055.get_mut_by_id(&[x_id, y_id] as &[ComponentId])1056.map(|ptrs| {1057let Ok([x_ptr, y_ptr]): Result<[MutUntyped; 2], _> = ptrs.try_into() else {1058panic!("get_mut_by_id(slice) didn't return 2 elements")1059};10601061// SAFETY: components match the id they were fetched with1062(unsafe { x_ptr.into_inner().deref_mut::<X>() }, unsafe {1063y_ptr.into_inner().deref_mut::<Y>()1064})1065})1066);1067assert_eq!(1068Err(EntityComponentError::MissingComponent(y_id)),1069world1070.entity_mut(e2)1071.get_mut_by_id(&[x_id, y_id] as &[ComponentId])1072.map(|ptrs| {1073let Ok([x_ptr, y_ptr]): Result<[MutUntyped; 2], _> = ptrs.try_into() else {1074panic!("get_mut_by_id(slice) didn't return 2 elements")1075};10761077// SAFETY: components match the id they were fetched with1078(unsafe { x_ptr.into_inner().deref_mut::<X>() }, unsafe {1079y_ptr.into_inner().deref_mut::<Y>()1080})1081})1082);1083assert_eq!(1084Err(EntityComponentError::MissingComponent(x_id)),1085world1086.entity_mut(e3)1087.get_mut_by_id(&[x_id, y_id] as &[ComponentId])1088.map(|ptrs| {1089let Ok([x_ptr, y_ptr]): Result<[MutUntyped; 2], _> = ptrs.try_into() else {1090panic!("get_mut_by_id(slice) didn't return 2 elements")1091};10921093// SAFETY: components match the id they were fetched with1094(unsafe { x_ptr.into_inner().deref_mut::<X>() }, unsafe {1095y_ptr.into_inner().deref_mut::<Y>()1096})1097})1098);10991100assert_eq!(1101Err(EntityComponentError::AliasedMutability(x_id)),1102world1103.entity_mut(e1)1104.get_mut_by_id(&[x_id, x_id])1105.map(|_| { unreachable!() })1106);1107assert_eq!(1108Err(EntityComponentError::AliasedMutability(x_id)),1109world1110.entity_mut(e3)1111.get_mut_by_id(&[x_id, x_id])1112.map(|_| { unreachable!() })1113);1114}11151116#[test]1117fn get_mut_by_id_unchecked() {1118let mut world = World::default();1119let e1 = world.spawn((X(7), Y(10))).id();1120let x_id = world.register_component::<X>();1121let y_id = world.register_component::<Y>();11221123let e1_mut = &world.get_entity_mut([e1]).unwrap()[0];1124// SAFETY: The entity e1 contains component X.1125let x_ptr = unsafe { e1_mut.get_mut_by_id_unchecked(x_id) }.unwrap();1126// SAFETY: The entity e1 contains component Y, with components X and Y being mutually independent.1127let y_ptr = unsafe { e1_mut.get_mut_by_id_unchecked(y_id) }.unwrap();11281129// SAFETY: components match the id they were fetched with1130let x_component = unsafe { x_ptr.into_inner().deref_mut::<X>() };1131x_component.0 += 1;1132// SAFETY: components match the id they were fetched with1133let y_component = unsafe { y_ptr.into_inner().deref_mut::<Y>() };1134y_component.0 -= 1;11351136assert_eq!((&mut X(8), &mut Y(9)), (x_component, y_component));1137}11381139#[derive(EntityEvent)]1140struct TestEvent(Entity);11411142#[test]1143fn adding_observer_updates_location() {1144let mut world = World::new();1145let entity = world1146.spawn_empty()1147.observe(|event: On<TestEvent>, mut commands: Commands| {1148commands1149.entity(event.event_target())1150.insert(TestComponent(0));1151})1152.id();11531154// this should not be needed, but is currently required to tease out the bug1155world.flush();11561157let mut a = world.entity_mut(entity);1158// SAFETY: this _intentionally_ doesn't update the location, to ensure that we're actually testing1159// that observe() updates location1160unsafe { a.world_mut().trigger(TestEvent(entity)) }1161a.observe(|_: On<TestEvent>| {}); // this flushes commands implicitly by spawning1162let location = a.location();1163assert_eq!(world.entities().get(entity).unwrap(), Some(location));1164}11651166#[test]1167#[should_panic]1168fn location_on_despawned_entity_panics() {1169let mut world = World::new();1170world.add_observer(|add: On<Add, TestComponent>, mut commands: Commands| {1171commands.entity(add.entity).despawn();1172});1173let entity = world.spawn_empty().id();1174let mut a = world.entity_mut(entity);1175a.insert(TestComponent(0));1176a.location();1177}11781179#[derive(Resource)]1180struct TestFlush(usize);11811182fn count_flush(world: &mut World) {1183world.resource_mut::<TestFlush>().0 += 1;1184}11851186#[test]1187fn archetype_modifications_trigger_flush() {1188let mut world = World::new();1189world.insert_resource(TestFlush(0));1190world.add_observer(|_: On<Add, TestComponent>, mut commands: Commands| {1191commands.queue(count_flush);1192});1193world.add_observer(|_: On<Remove, TestComponent>, mut commands: Commands| {1194commands.queue(count_flush);1195});11961197// Spawning an empty should not flush.1198world.commands().queue(count_flush);1199let entity = world.spawn_empty().id();1200assert_eq!(world.resource::<TestFlush>().0, 0);12011202world.commands().queue(count_flush);1203world.flush_commands();12041205let mut a = world.entity_mut(entity);1206assert_eq!(a.world().resource::<TestFlush>().0, 2);1207a.insert(TestComponent(0));1208assert_eq!(a.world().resource::<TestFlush>().0, 3);1209a.remove::<TestComponent>();1210assert_eq!(a.world().resource::<TestFlush>().0, 4);1211a.insert(TestComponent(0));1212assert_eq!(a.world().resource::<TestFlush>().0, 5);1213let _ = a.take::<TestComponent>();1214assert_eq!(a.world().resource::<TestFlush>().0, 6);1215a.insert(TestComponent(0));1216assert_eq!(a.world().resource::<TestFlush>().0, 7);1217a.retain::<()>();1218assert_eq!(a.world().resource::<TestFlush>().0, 8);1219a.insert(TestComponent(0));1220assert_eq!(a.world().resource::<TestFlush>().0, 9);1221a.clear();1222assert_eq!(a.world().resource::<TestFlush>().0, 10);1223a.insert(TestComponent(0));1224assert_eq!(a.world().resource::<TestFlush>().0, 11);1225a.despawn();1226assert_eq!(world.resource::<TestFlush>().0, 12);1227}12281229#[derive(Resource)]1230struct TestVec(Vec<&'static str>);12311232#[derive(Component)]1233#[component(on_add = ord_a_hook_on_add, on_insert = ord_a_hook_on_insert, on_replace = ord_a_hook_on_replace, on_remove = ord_a_hook_on_remove)]1234struct OrdA;12351236fn ord_a_hook_on_add(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {1237world.resource_mut::<TestVec>().0.push("OrdA hook on_add");1238world.commands().entity(entity).insert(OrdB);1239}12401241fn ord_a_hook_on_insert(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {1242world1243.resource_mut::<TestVec>()1244.01245.push("OrdA hook on_insert");1246world.commands().entity(entity).remove::<OrdA>();1247world.commands().entity(entity).remove::<OrdB>();1248}12491250fn ord_a_hook_on_replace(mut world: DeferredWorld, _: HookContext) {1251world1252.resource_mut::<TestVec>()1253.01254.push("OrdA hook on_replace");1255}12561257fn ord_a_hook_on_remove(mut world: DeferredWorld, _: HookContext) {1258world1259.resource_mut::<TestVec>()1260.01261.push("OrdA hook on_remove");1262}12631264fn ord_a_observer_on_add(_event: On<Add, OrdA>, mut res: ResMut<TestVec>) {1265res.0.push("OrdA observer on_add");1266}12671268fn ord_a_observer_on_insert(_event: On<Insert, OrdA>, mut res: ResMut<TestVec>) {1269res.0.push("OrdA observer on_insert");1270}12711272fn ord_a_observer_on_replace(_event: On<Replace, OrdA>, mut res: ResMut<TestVec>) {1273res.0.push("OrdA observer on_replace");1274}12751276fn ord_a_observer_on_remove(_event: On<Remove, OrdA>, mut res: ResMut<TestVec>) {1277res.0.push("OrdA observer on_remove");1278}12791280#[derive(Component)]1281#[component(on_add = ord_b_hook_on_add, on_insert = ord_b_hook_on_insert, on_replace = ord_b_hook_on_replace, on_remove = ord_b_hook_on_remove)]1282struct OrdB;12831284fn ord_b_hook_on_add(mut world: DeferredWorld, _: HookContext) {1285world.resource_mut::<TestVec>().0.push("OrdB hook on_add");1286world.commands().queue(|world: &mut World| {1287world1288.resource_mut::<TestVec>()1289.01290.push("OrdB command on_add");1291});1292}12931294fn ord_b_hook_on_insert(mut world: DeferredWorld, _: HookContext) {1295world1296.resource_mut::<TestVec>()1297.01298.push("OrdB hook on_insert");1299}13001301fn ord_b_hook_on_replace(mut world: DeferredWorld, _: HookContext) {1302world1303.resource_mut::<TestVec>()1304.01305.push("OrdB hook on_replace");1306}13071308fn ord_b_hook_on_remove(mut world: DeferredWorld, _: HookContext) {1309world1310.resource_mut::<TestVec>()1311.01312.push("OrdB hook on_remove");1313}13141315fn ord_b_observer_on_add(_event: On<Add, OrdB>, mut res: ResMut<TestVec>) {1316res.0.push("OrdB observer on_add");1317}13181319fn ord_b_observer_on_insert(_event: On<Insert, OrdB>, mut res: ResMut<TestVec>) {1320res.0.push("OrdB observer on_insert");1321}13221323fn ord_b_observer_on_replace(_event: On<Replace, OrdB>, mut res: ResMut<TestVec>) {1324res.0.push("OrdB observer on_replace");1325}13261327fn ord_b_observer_on_remove(_event: On<Remove, OrdB>, mut res: ResMut<TestVec>) {1328res.0.push("OrdB observer on_remove");1329}13301331#[test]1332fn command_ordering_is_correct() {1333let mut world = World::new();1334world.insert_resource(TestVec(Vec::new()));1335world.add_observer(ord_a_observer_on_add);1336world.add_observer(ord_a_observer_on_insert);1337world.add_observer(ord_a_observer_on_replace);1338world.add_observer(ord_a_observer_on_remove);1339world.add_observer(ord_b_observer_on_add);1340world.add_observer(ord_b_observer_on_insert);1341world.add_observer(ord_b_observer_on_replace);1342world.add_observer(ord_b_observer_on_remove);1343let _entity = world.spawn(OrdA).id();1344let expected = [1345"OrdA hook on_add", // adds command to insert OrdB1346"OrdA observer on_add",1347"OrdA hook on_insert", // adds command to despawn entity1348"OrdA observer on_insert",1349"OrdB hook on_add", // adds command to just add to this log1350"OrdB observer on_add",1351"OrdB hook on_insert",1352"OrdB observer on_insert",1353"OrdB command on_add", // command added by OrdB hook on_add, needs to run before despawn command1354"OrdA observer on_replace", // start of despawn1355"OrdA hook on_replace",1356"OrdA observer on_remove",1357"OrdA hook on_remove",1358"OrdB observer on_replace",1359"OrdB hook on_replace",1360"OrdB observer on_remove",1361"OrdB hook on_remove",1362];1363world.flush();1364assert_eq!(world.resource_mut::<TestVec>().0.as_slice(), &expected[..]);1365}13661367#[test]1368fn entity_world_mut_clone_and_move_components() {1369#[derive(Component, Clone, PartialEq, Debug)]1370struct A;13711372#[derive(Component, Clone, PartialEq, Debug)]1373struct B;13741375#[derive(Component, Clone, PartialEq, Debug)]1376struct C(u32);13771378let mut world = World::new();1379let entity_a = world.spawn((A, B, C(5))).id();1380let entity_b = world.spawn((A, C(4))).id();13811382world.entity_mut(entity_a).clone_components::<B>(entity_b);1383assert_eq!(world.entity(entity_a).get::<B>(), Some(&B));1384assert_eq!(world.entity(entity_b).get::<B>(), Some(&B));13851386world.entity_mut(entity_a).move_components::<C>(entity_b);1387assert_eq!(world.entity(entity_a).get::<C>(), None);1388assert_eq!(world.entity(entity_b).get::<C>(), Some(&C(5)));13891390assert_eq!(world.entity(entity_a).get::<A>(), Some(&A));1391assert_eq!(world.entity(entity_b).get::<A>(), Some(&A));1392}13931394#[test]1395fn entity_world_mut_clone_with_move_and_require() {1396#[derive(Component, Clone, PartialEq, Debug)]1397#[require(B(3))]1398struct A;13991400#[derive(Component, Clone, PartialEq, Debug, Default)]1401#[require(C(3))]1402struct B(u32);14031404#[derive(Component, Clone, PartialEq, Debug, Default)]1405#[require(D)]1406struct C(u32);14071408#[derive(Component, Clone, PartialEq, Debug, Default)]1409struct D;14101411let mut world = World::new();1412let entity_a = world.spawn((A, B(5))).id();1413let entity_b = world.spawn_empty().id();14141415world1416.entity_mut(entity_a)1417.clone_with_opt_in(entity_b, |builder| {1418builder1419.move_components(true)1420.allow::<C>()1421.without_required_components(|builder| {1422builder.allow::<A>();1423});1424});14251426assert_eq!(world.entity(entity_a).get::<A>(), None);1427assert_eq!(world.entity(entity_b).get::<A>(), Some(&A));14281429assert_eq!(world.entity(entity_a).get::<B>(), Some(&B(5)));1430assert_eq!(world.entity(entity_b).get::<B>(), Some(&B(3)));14311432assert_eq!(world.entity(entity_a).get::<C>(), None);1433assert_eq!(world.entity(entity_b).get::<C>(), Some(&C(3)));14341435assert_eq!(world.entity(entity_a).get::<D>(), None);1436assert_eq!(world.entity(entity_b).get::<D>(), Some(&D));1437}14381439#[test]1440fn command_despawns_dont_invalidate_entity_world_muts() {1441let mut world = World::new();14421443let mut entity = world.spawn(TestComponent(1));1444entity.insert(DespawnOnAdd);1445assert!(entity.is_despawned());1446}14471448#[test]1449#[should_panic]1450fn using_despawned_entity_world_mut_panics() {1451let mut world = World::new();14521453let mut entity = world.spawn(TestComponent(1));1454entity.insert(DespawnOnAdd);1455assert!(entity.is_despawned());1456entity.insert(TestComponent2(2));1457}14581459#[test]1460fn update_despawned_by_after_observers() {1461let mut world = World::new();14621463#[derive(Component)]1464#[component(on_remove = get_tracked)]1465struct C;14661467static TRACKED: OnceLock<(MaybeLocation, Tick)> = OnceLock::new();1468fn get_tracked(world: DeferredWorld, HookContext { entity, .. }: HookContext) {1469TRACKED.get_or_init(|| {1470let by = world1471.entities1472.entity_get_spawned_or_despawned_by(entity)1473.map(|l| l.unwrap());1474let at = world1475.entities1476.entity_get_spawn_or_despawn_tick(entity)1477.unwrap();1478(by, at)1479});1480}14811482#[track_caller]1483fn caller_spawn(world: &mut World) -> (Entity, MaybeLocation, Tick) {1484let caller = MaybeLocation::caller();1485(world.spawn(C).id(), caller, world.change_tick())1486}1487let (entity, spawner, spawn_tick) = caller_spawn(&mut world);14881489assert_eq!(1490spawner,1491world1492.entities()1493.entity_get_spawned_or_despawned_by(entity)1494.map(|l| l.unwrap())1495);14961497#[track_caller]1498fn caller_despawn(world: &mut World, entity: Entity) -> (MaybeLocation, Tick) {1499world.despawn(entity);1500(MaybeLocation::caller(), world.change_tick())1501}1502let (despawner, despawn_tick) = caller_despawn(&mut world, entity);15031504assert_eq!((spawner, spawn_tick), *TRACKED.get().unwrap());1505assert_eq!(1506despawner,1507world1508.entities()1509.entity_get_spawned_or_despawned_by(entity)1510.map(|l| l.unwrap())1511);1512assert_eq!(1513despawn_tick,1514world1515.entities()1516.entity_get_spawn_or_despawn_tick(entity)1517.unwrap()1518);1519}15201521#[test]1522fn with_component_activates_hooks() {1523use core::sync::atomic::{AtomicBool, AtomicU8, Ordering};15241525#[derive(Component, PartialEq, Eq, Debug)]1526#[component(immutable)]1527struct Foo(bool);15281529static EXPECTED_VALUE: AtomicBool = AtomicBool::new(false);15301531static ADD_COUNT: AtomicU8 = AtomicU8::new(0);1532static REMOVE_COUNT: AtomicU8 = AtomicU8::new(0);1533static REPLACE_COUNT: AtomicU8 = AtomicU8::new(0);1534static INSERT_COUNT: AtomicU8 = AtomicU8::new(0);15351536let mut world = World::default();15371538world.register_component::<Foo>();1539world1540.register_component_hooks::<Foo>()1541.on_add(|world, context| {1542ADD_COUNT.fetch_add(1, Ordering::Relaxed);15431544assert_eq!(1545world.get(context.entity),1546Some(&Foo(EXPECTED_VALUE.load(Ordering::Relaxed)))1547);1548})1549.on_remove(|world, context| {1550REMOVE_COUNT.fetch_add(1, Ordering::Relaxed);15511552assert_eq!(1553world.get(context.entity),1554Some(&Foo(EXPECTED_VALUE.load(Ordering::Relaxed)))1555);1556})1557.on_replace(|world, context| {1558REPLACE_COUNT.fetch_add(1, Ordering::Relaxed);15591560assert_eq!(1561world.get(context.entity),1562Some(&Foo(EXPECTED_VALUE.load(Ordering::Relaxed)))1563);1564})1565.on_insert(|world, context| {1566INSERT_COUNT.fetch_add(1, Ordering::Relaxed);15671568assert_eq!(1569world.get(context.entity),1570Some(&Foo(EXPECTED_VALUE.load(Ordering::Relaxed)))1571);1572});15731574let entity = world.spawn(Foo(false)).id();15751576assert_eq!(ADD_COUNT.load(Ordering::Relaxed), 1);1577assert_eq!(REMOVE_COUNT.load(Ordering::Relaxed), 0);1578assert_eq!(REPLACE_COUNT.load(Ordering::Relaxed), 0);1579assert_eq!(INSERT_COUNT.load(Ordering::Relaxed), 1);15801581let mut entity = world.entity_mut(entity);15821583let archetype_pointer_before = &raw const *entity.archetype();15841585assert_eq!(entity.get::<Foo>(), Some(&Foo(false)));15861587entity.modify_component(|foo: &mut Foo| {1588foo.0 = true;1589EXPECTED_VALUE.store(foo.0, Ordering::Relaxed);1590});15911592let archetype_pointer_after = &raw const *entity.archetype();15931594assert_eq!(entity.get::<Foo>(), Some(&Foo(true)));15951596assert_eq!(ADD_COUNT.load(Ordering::Relaxed), 1);1597assert_eq!(REMOVE_COUNT.load(Ordering::Relaxed), 0);1598assert_eq!(REPLACE_COUNT.load(Ordering::Relaxed), 1);1599assert_eq!(INSERT_COUNT.load(Ordering::Relaxed), 2);16001601assert_eq!(archetype_pointer_before, archetype_pointer_after);1602}16031604#[test]1605fn bundle_remove_only_triggers_for_present_components() {1606let mut world = World::default();16071608#[derive(Component)]1609struct A;16101611#[derive(Component)]1612struct B;16131614#[derive(Resource, PartialEq, Eq, Debug)]1615struct Tracker {1616a: bool,1617b: bool,1618}16191620world.insert_resource(Tracker { a: false, b: false });1621let entity = world.spawn(A).id();16221623world.add_observer(|_: On<Remove, A>, mut tracker: ResMut<Tracker>| {1624tracker.a = true;1625});1626world.add_observer(|_: On<Remove, B>, mut tracker: ResMut<Tracker>| {1627tracker.b = true;1628});16291630world.entity_mut(entity).remove::<(A, B)>();16311632assert_eq!(1633world.resource::<Tracker>(),1634&Tracker {1635a: true,1636// The entity didn't have a B component, so it should not have been triggered.1637b: false,1638}1639);1640}16411642#[test]1643fn spawned_after_swap_remove() {1644#[derive(Component)]1645struct Marker;16461647let mut world = World::new();1648let id1 = world.spawn(Marker).id();1649let _id2 = world.spawn(Marker).id();1650let id3 = world.spawn(Marker).id();16511652let e1_spawned = world.entity(id1).spawned_by();16531654let spawn = world.entity(id3).spawned_by();1655world.entity_mut(id1).despawn();1656let e1_despawned = world.entities().entity_get_spawned_or_despawned_by(id1);16571658// These assertions are only possible if the `track_location` feature is enabled1659if let (Some(e1_spawned), Some(e1_despawned)) =1660(e1_spawned.into_option(), e1_despawned.into_option())1661{1662assert!(e1_despawned.is_some());1663assert_ne!(Some(e1_spawned), e1_despawned);1664}16651666let spawn_after = world.entity(id3).spawned_by();1667assert_eq!(spawn, spawn_after);1668}16691670#[test]1671fn spawned_by_set_before_flush() {1672#[derive(Component)]1673#[component(on_despawn = on_despawn)]1674struct C;16751676fn on_despawn(mut world: DeferredWorld, context: HookContext) {1677let spawned = world.entity(context.entity).spawned_by();1678world.commands().queue(move |world: &mut World| {1679// The entity has finished despawning...1680assert!(world.get_entity(context.entity).is_err());1681let despawned = world1682.entities()1683.entity_get_spawned_or_despawned_by(context.entity);1684// These assertions are only possible if the `track_location` feature is enabled1685if let (Some(spawned), Some(despawned)) =1686(spawned.into_option(), despawned.into_option())1687{1688// ... so ensure that `despawned_by` has been written1689assert!(despawned.is_some());1690assert_ne!(Some(spawned), despawned);1691}1692});1693}16941695let mut world = World::new();1696let original = world.spawn(C).id();1697world.despawn(original);1698}1699}170017011702