Path: blob/main/crates/bevy_ecs/src/world/entity_access/mod.rs
7224 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::{26change_detection::{MaybeLocation, MutUntyped},27component::ComponentId,28prelude::*,29system::{assert_is_system, RunSystemOnce as _},30world::{error::EntityComponentError, DeferredWorld, FilteredEntityMut, FilteredEntityRef},31};3233use super::{EntityMutExcept, EntityRefExcept};3435#[derive(Component, Clone, Copy, Debug, PartialEq)]36struct TestComponent(u32);3738#[derive(Component, Clone, Copy, Debug, PartialEq)]39#[component(storage = "SparseSet")]40struct TestComponent2(u32);4142#[test]43fn entity_ref_get_by_id() {44let mut world = World::new();45let entity = world.spawn(TestComponent(42)).id();46let component_id = world47.components()48.get_valid_id(core::any::TypeId::of::<TestComponent>())49.unwrap();5051let entity = world.entity(entity);52let test_component = entity.get_by_id(component_id).unwrap();53// SAFETY: points to a valid `TestComponent`54let test_component = unsafe { test_component.deref::<TestComponent>() };5556assert_eq!(test_component.0, 42);57}5859#[test]60fn entity_mut_get_by_id() {61let mut world = World::new();62let entity = world.spawn(TestComponent(42)).id();63let component_id = world64.components()65.get_valid_id(core::any::TypeId::of::<TestComponent>())66.unwrap();6768let mut entity_mut = world.entity_mut(entity);69let mut test_component = entity_mut.get_mut_by_id(component_id).unwrap();70{71test_component.set_changed();72let test_component =73// SAFETY: `test_component` has unique access of the `EntityWorldMut` and is not used afterwards74unsafe { test_component.into_inner().deref_mut::<TestComponent>() };75test_component.0 = 43;76}7778let entity = world.entity(entity);79let test_component = entity.get_by_id(component_id).unwrap();80// SAFETY: `TestComponent` is the correct component type81let test_component = unsafe { test_component.deref::<TestComponent>() };8283assert_eq!(test_component.0, 43);84}8586#[test]87fn entity_ref_get_by_id_invalid_component_id() {88let invalid_component_id = ComponentId::new(usize::MAX);8990let mut world = World::new();91let entity = world.spawn_empty().id();92let entity = world.entity(entity);93assert!(entity.get_by_id(invalid_component_id).is_err());94}9596#[test]97fn entity_mut_get_by_id_invalid_component_id() {98let invalid_component_id = ComponentId::new(usize::MAX);99100let mut world = World::new();101let mut entity = world.spawn_empty();102assert!(entity.get_by_id(invalid_component_id).is_err());103assert!(entity.get_mut_by_id(invalid_component_id).is_err());104}105106#[derive(Resource)]107struct R(usize);108109#[test]110fn entity_mut_resource_scope() {111// Keep in sync with the `resource_scope` test in lib.rs112let mut world = World::new();113let mut entity = world.spawn_empty();114115assert!(entity.try_resource_scope::<R, _>(|_, _| {}).is_none());116entity.world_scope(|world| world.insert_resource(R(0)));117entity.resource_scope(|entity: &mut EntityWorldMut, mut value: Mut<R>| {118value.0 += 1;119assert!(!entity.world().contains_resource::<R>());120});121assert_eq!(entity.resource::<R>().0, 1);122}123124#[test]125fn entity_mut_resource_scope_panic() {126let mut world = World::new();127world.insert_resource(R(0));128129let mut entity = world.spawn_empty();130let old_location = entity.location();131let result = std::panic::catch_unwind(AssertUnwindSafe(|| {132entity.resource_scope(|entity: &mut EntityWorldMut, _: Mut<R>| {133// Change the entity's `EntityLocation`.134entity.insert(TestComponent(0));135136// Ensure that the entity location still gets updated even in case of a panic.137panic!("this should get caught by the outer scope")138});139}));140assert!(result.is_err());141142// Ensure that the location has been properly updated.143assert_ne!(entity.location(), old_location);144}145146// regression test for https://github.com/bevyengine/bevy/pull/7387147#[test]148fn entity_mut_world_scope_panic() {149let mut world = World::new();150151let mut entity = world.spawn_empty();152let old_location = entity.location();153let id = entity.id();154let res = std::panic::catch_unwind(AssertUnwindSafe(|| {155entity.world_scope(|w| {156// Change the entity's `EntityLocation`, which invalidates the original `EntityWorldMut`.157// This will get updated at the end of the scope.158w.entity_mut(id).insert(TestComponent(0));159160// Ensure that the entity location still gets updated even in case of a panic.161panic!("this should get caught by the outer scope")162});163}));164assert!(res.is_err());165166// Ensure that the location has been properly updated.167assert_ne!(entity.location(), old_location);168}169170#[test]171fn entity_mut_reborrow_scope_panic() {172let mut world = World::new();173174let mut entity = world.spawn_empty();175let old_location = entity.location();176let res = std::panic::catch_unwind(AssertUnwindSafe(|| {177entity.reborrow_scope(|mut entity| {178// Change the entity's `EntityLocation`, which invalidates the original `EntityWorldMut`.179// This will get updated at the end of the scope.180entity.insert(TestComponent(0));181182// Ensure that the entity location still gets updated even in case of a panic.183panic!("this should get caught by the outer scope")184});185}));186assert!(res.is_err());187188// Ensure that the location has been properly updated.189assert_ne!(entity.location(), old_location);190}191192// regression test for https://github.com/bevyengine/bevy/pull/7805193#[test]194fn removing_sparse_updates_archetype_row() {195#[derive(Component, PartialEq, Debug)]196struct Dense(u8);197198#[derive(Component)]199#[component(storage = "SparseSet")]200struct Sparse;201202let mut world = World::new();203let e1 = world.spawn((Dense(0), Sparse)).id();204let e2 = world.spawn((Dense(1), Sparse)).id();205206world.entity_mut(e1).remove::<Sparse>();207assert_eq!(world.entity(e2).get::<Dense>().unwrap(), &Dense(1));208}209210// regression test for https://github.com/bevyengine/bevy/pull/7805211#[test]212fn removing_dense_updates_table_row() {213#[derive(Component, PartialEq, Debug)]214struct Dense(u8);215216#[derive(Component)]217#[component(storage = "SparseSet")]218struct Sparse;219220let mut world = World::new();221let e1 = world.spawn((Dense(0), Sparse)).id();222let e2 = world.spawn((Dense(1), Sparse)).id();223224world.entity_mut(e1).remove::<Dense>();225assert_eq!(world.entity(e2).get::<Dense>().unwrap(), &Dense(1));226}227228// Test that calling retain with `()` removes all components.229#[test]230fn retain_nothing() {231#[derive(Component)]232struct Marker<const N: usize>;233234let mut world = World::new();235let ent = world.spawn((Marker::<1>, Marker::<2>, Marker::<3>)).id();236237world.entity_mut(ent).retain::<()>();238assert_eq!(world.entity(ent).archetype().components().len(), 0);239}240241// Test removing some components with `retain`, including components not on the entity.242#[test]243fn retain_some_components() {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::<(Marker<2>, Marker<4>)>();251// Check that marker 2 was retained.252assert!(world.entity(ent).get::<Marker<2>>().is_some());253// Check that only marker 2 was retained.254assert_eq!(world.entity(ent).archetype().components().len(), 1);255}256257// regression test for https://github.com/bevyengine/bevy/pull/7805258#[test]259fn inserting_sparse_updates_archetype_row() {260#[derive(Component, PartialEq, Debug)]261struct Dense(u8);262263#[derive(Component)]264#[component(storage = "SparseSet")]265struct Sparse;266267let mut world = World::new();268let e1 = world.spawn(Dense(0)).id();269let e2 = world.spawn(Dense(1)).id();270271world.entity_mut(e1).insert(Sparse);272assert_eq!(world.entity(e2).get::<Dense>().unwrap(), &Dense(1));273}274275// regression test for https://github.com/bevyengine/bevy/pull/7805276#[test]277fn inserting_dense_updates_archetype_row() {278#[derive(Component, PartialEq, Debug)]279struct Dense(u8);280281#[derive(Component)]282struct Dense2;283284#[derive(Component)]285#[component(storage = "SparseSet")]286struct Sparse;287288let mut world = World::new();289let e1 = world.spawn(Dense(0)).id();290let e2 = world.spawn(Dense(1)).id();291292world.entity_mut(e1).insert(Sparse).remove::<Sparse>();293294// archetype with [e2, e1]295// table with [e1, e2]296297world.entity_mut(e2).insert(Dense2);298299assert_eq!(world.entity(e1).get::<Dense>().unwrap(), &Dense(0));300}301302#[test]303fn inserting_dense_updates_table_row() {304#[derive(Component, PartialEq, Debug)]305struct Dense(u8);306307#[derive(Component)]308struct Dense2;309310#[derive(Component)]311#[component(storage = "SparseSet")]312struct Sparse;313314let mut world = World::new();315let e1 = world.spawn(Dense(0)).id();316let e2 = world.spawn(Dense(1)).id();317318world.entity_mut(e1).insert(Sparse).remove::<Sparse>();319320// archetype with [e2, e1]321// table with [e1, e2]322323world.entity_mut(e1).insert(Dense2);324325assert_eq!(world.entity(e2).get::<Dense>().unwrap(), &Dense(1));326}327328// regression test for https://github.com/bevyengine/bevy/pull/7805329#[test]330fn despawning_entity_updates_archetype_row() {331#[derive(Component, PartialEq, Debug)]332struct Dense(u8);333334#[derive(Component)]335#[component(storage = "SparseSet")]336struct Sparse;337338let mut world = World::new();339let e1 = world.spawn(Dense(0)).id();340let e2 = world.spawn(Dense(1)).id();341342world.entity_mut(e1).insert(Sparse).remove::<Sparse>();343344// archetype with [e2, e1]345// table with [e1, e2]346347world.entity_mut(e2).despawn();348349assert_eq!(world.entity(e1).get::<Dense>().unwrap(), &Dense(0));350}351352// regression test for https://github.com/bevyengine/bevy/pull/7805353#[test]354fn despawning_entity_updates_table_row() {355#[derive(Component, PartialEq, Debug)]356struct Dense(u8);357358#[derive(Component)]359#[component(storage = "SparseSet")]360struct Sparse;361362let mut world = World::new();363let e1 = world.spawn(Dense(0)).id();364let e2 = world.spawn(Dense(1)).id();365366world.entity_mut(e1).insert(Sparse).remove::<Sparse>();367368// archetype with [e2, e1]369// table with [e1, e2]370371world.entity_mut(e1).despawn();372373assert_eq!(world.entity(e2).get::<Dense>().unwrap(), &Dense(1));374}375376#[test]377fn entity_mut_insert_by_id() {378let mut world = World::new();379let test_component_id = world.register_component::<TestComponent>();380381let mut entity = world.spawn_empty();382OwningPtr::make(TestComponent(42), |ptr| {383// SAFETY: `ptr` matches the component id384unsafe { entity.insert_by_id(test_component_id, ptr) };385});386387let components: Vec<_> = world.query::<&TestComponent>().iter(&world).collect();388389assert_eq!(components, vec![&TestComponent(42)]);390391// Compare with `insert_bundle_by_id`392393let mut entity = world.spawn_empty();394OwningPtr::make(TestComponent(84), |ptr| {395// SAFETY: `ptr` matches the component id396unsafe { entity.insert_by_ids(&[test_component_id], vec![ptr].into_iter()) };397});398399let components: Vec<_> = world.query::<&TestComponent>().iter(&world).collect();400401assert_eq!(components, vec![&TestComponent(42), &TestComponent(84)]);402}403404#[test]405fn entity_mut_insert_bundle_by_id() {406let mut world = World::new();407let test_component_id = world.register_component::<TestComponent>();408let test_component_2_id = world.register_component::<TestComponent2>();409410let component_ids = [test_component_id, test_component_2_id];411let test_component_value = TestComponent(42);412let test_component_2_value = TestComponent2(84);413414let mut entity = world.spawn_empty();415OwningPtr::make(test_component_value, |ptr1| {416OwningPtr::make(test_component_2_value, |ptr2| {417// SAFETY: `ptr1` and `ptr2` match the component ids418unsafe { entity.insert_by_ids(&component_ids, vec![ptr1, ptr2].into_iter()) };419});420});421422let dynamic_components: Vec<_> = world423.query::<(&TestComponent, &TestComponent2)>()424.iter(&world)425.collect();426427assert_eq!(428dynamic_components,429vec![(&TestComponent(42), &TestComponent2(84))]430);431432// Compare with `World` generated using static type equivalents433let mut static_world = World::new();434435static_world.spawn((test_component_value, test_component_2_value));436let static_components: Vec<_> = static_world437.query::<(&TestComponent, &TestComponent2)>()438.iter(&static_world)439.collect();440441assert_eq!(dynamic_components, static_components);442}443444#[test]445fn entity_mut_remove_by_id() {446let mut world = World::new();447let test_component_id = world.register_component::<TestComponent>();448449let mut entity = world.spawn(TestComponent(42));450entity.remove_by_id(test_component_id);451452let components: Vec<_> = world.query::<&TestComponent>().iter(&world).collect();453454assert_eq!(components, vec![] as Vec<&TestComponent>);455456// remove non-existent component does not panic457world.spawn_empty().remove_by_id(test_component_id);458}459460/// Tests that components can be accessed through an `EntityRefExcept`.461#[test]462fn entity_ref_except() {463let mut world = World::new();464world.register_component::<TestComponent>();465world.register_component::<TestComponent2>();466467world.spawn(TestComponent(0)).insert(TestComponent2(0));468469let mut query = world.query::<EntityRefExcept<TestComponent>>();470471let mut found = false;472for entity_ref in query.iter_mut(&mut world) {473found = true;474assert!(entity_ref.get::<TestComponent>().is_none());475assert!(entity_ref.get_ref::<TestComponent>().is_none());476assert!(matches!(477entity_ref.get::<TestComponent2>(),478Some(TestComponent2(0))479));480}481482assert!(found);483}484485// Test that a single query can't both contain a mutable reference to a486// component C and an `EntityRefExcept` that doesn't include C among its487// exclusions.488#[test]489#[should_panic]490fn entity_ref_except_conflicts_with_self() {491let mut world = World::new();492world.spawn(TestComponent(0)).insert(TestComponent2(0));493494// This should panic, because we have a mutable borrow on495// `TestComponent` but have a simultaneous indirect immutable borrow on496// that component via `EntityRefExcept`.497world.run_system_once(system).unwrap();498499fn system(_: Query<(&mut TestComponent, EntityRefExcept<TestComponent2>)>) {}500}501502// Test that an `EntityRefExcept` that doesn't include a component C among503// its exclusions can't coexist with a mutable query for that component.504#[test]505#[should_panic]506fn entity_ref_except_conflicts_with_other() {507let mut world = World::new();508world.spawn(TestComponent(0)).insert(TestComponent2(0));509510// This should panic, because we have a mutable borrow on511// `TestComponent` but have a simultaneous indirect immutable borrow on512// that component via `EntityRefExcept`.513world.run_system_once(system).unwrap();514515fn system(_: Query<&mut TestComponent>, _: Query<EntityRefExcept<TestComponent2>>) {}516}517518// Test that an `EntityRefExcept` with an exception for some component C can519// coexist with a query for that component C.520#[test]521fn entity_ref_except_doesnt_conflict() {522let mut world = World::new();523world.spawn(TestComponent(0)).insert(TestComponent2(0));524525world.run_system_once(system).unwrap();526527fn system(_: Query<&mut TestComponent>, query: Query<EntityRefExcept<TestComponent>>) {528for entity_ref in query.iter() {529assert!(matches!(530entity_ref.get::<TestComponent2>(),531Some(TestComponent2(0))532));533}534}535}536537/// Tests that components can be mutably accessed through an538/// `EntityMutExcept`.539#[test]540fn entity_mut_except() {541let mut world = World::new();542world.spawn(TestComponent(0)).insert(TestComponent2(0));543544let mut query = world.query::<EntityMutExcept<TestComponent>>();545546let mut found = false;547for mut entity_mut in query.iter_mut(&mut world) {548found = true;549assert!(entity_mut.get::<TestComponent>().is_none());550assert!(entity_mut.get_ref::<TestComponent>().is_none());551assert!(entity_mut.get_mut::<TestComponent>().is_none());552assert!(matches!(553entity_mut.get::<TestComponent2>(),554Some(TestComponent2(0))555));556}557558assert!(found);559}560561// Test that a single query can't both contain a mutable reference to a562// component C and an `EntityMutExcept` that doesn't include C among its563// exclusions.564#[test]565#[should_panic]566fn entity_mut_except_conflicts_with_self() {567let mut world = World::new();568world.spawn(TestComponent(0)).insert(TestComponent2(0));569570// This should panic, because we have a mutable borrow on571// `TestComponent` but have a simultaneous indirect immutable borrow on572// that component via `EntityRefExcept`.573world.run_system_once(system).unwrap();574575fn system(_: Query<(&mut TestComponent, EntityMutExcept<TestComponent2>)>) {}576}577578// Test that an `EntityMutExcept` that doesn't include a component C among579// its exclusions can't coexist with a query for that component.580#[test]581#[should_panic]582fn entity_mut_except_conflicts_with_other() {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>, mut query: Query<EntityMutExcept<TestComponent2>>) {592for mut entity_mut in query.iter_mut() {593assert!(entity_mut594.get_mut::<TestComponent2>()595.is_some_and(|component| component.0 == 0));596}597}598}599600// Test that an `EntityMutExcept` with an exception for some component C can601// coexist with a query for that component C.602#[test]603fn entity_mut_except_doesnt_conflict() {604let mut world = World::new();605world.spawn(TestComponent(0)).insert(TestComponent2(0));606607world.run_system_once(system).unwrap();608609fn system(_: Query<&mut TestComponent>, mut query: Query<EntityMutExcept<TestComponent>>) {610for mut entity_mut in query.iter_mut() {611assert!(entity_mut612.get_mut::<TestComponent2>()613.is_some_and(|component| component.0 == 0));614}615}616}617618#[test]619fn entity_mut_except_registers_components() {620// Checks for a bug where `EntityMutExcept` would not register the component and621// would therefore not include an exception, causing it to conflict with the later query.622fn system1(_query: Query<EntityMutExcept<TestComponent>>, _: Query<&mut TestComponent>) {}623let mut world = World::new();624world.run_system_once(system1).unwrap();625626fn system2(_: Query<&mut TestComponent>, _query: Query<EntityMutExcept<TestComponent>>) {}627let mut world = World::new();628world.run_system_once(system2).unwrap();629}630631#[derive(Component)]632struct A;633634#[test]635fn disjoint_access() {636fn disjoint_readonly(_: Query<EntityMut, With<A>>, _: Query<EntityRef, Without<A>>) {}637638fn disjoint_mutable(_: Query<EntityMut, With<A>>, _: Query<EntityMut, Without<A>>) {}639640assert_is_system(disjoint_readonly);641assert_is_system(disjoint_mutable);642}643644#[test]645fn ref_compatible() {646fn borrow_system(_: Query<(EntityRef, &A)>, _: Query<&A>) {}647648assert_is_system(borrow_system);649}650651#[test]652fn ref_compatible_with_resource() {653fn borrow_system(_: Query<EntityRef>, _: Res<R>) {}654655assert_is_system(borrow_system);656}657658#[test]659fn ref_compatible_with_resource_mut() {660fn borrow_system(_: Query<EntityRef>, _: ResMut<R>) {}661662assert_is_system(borrow_system);663}664665#[test]666#[should_panic]667fn ref_incompatible_with_mutable_component() {668fn incompatible_system(_: Query<(EntityRef, &mut A)>) {}669670assert_is_system(incompatible_system);671}672673#[test]674#[should_panic]675fn ref_incompatible_with_mutable_query() {676fn incompatible_system(_: Query<EntityRef>, _: Query<&mut A>) {}677678assert_is_system(incompatible_system);679}680681#[test]682fn mut_compatible_with_entity() {683fn borrow_mut_system(_: Query<(Entity, EntityMut)>) {}684685assert_is_system(borrow_mut_system);686}687688#[test]689fn mut_compatible_with_resource() {690fn borrow_mut_system(_: Res<R>, _: Query<EntityMut>) {}691692assert_is_system(borrow_mut_system);693}694695#[test]696fn mut_compatible_with_resource_mut() {697fn borrow_mut_system(_: ResMut<R>, _: Query<EntityMut>) {}698699assert_is_system(borrow_mut_system);700}701702#[test]703#[should_panic]704fn mut_incompatible_with_read_only_component() {705fn incompatible_system(_: Query<(EntityMut, &A)>) {}706707assert_is_system(incompatible_system);708}709710#[test]711#[should_panic]712fn mut_incompatible_with_mutable_component() {713fn incompatible_system(_: Query<(EntityMut, &mut A)>) {}714715assert_is_system(incompatible_system);716}717718#[test]719#[should_panic]720fn mut_incompatible_with_read_only_query() {721fn incompatible_system(_: Query<EntityMut>, _: Query<&A>) {}722723assert_is_system(incompatible_system);724}725726#[test]727#[should_panic]728fn mut_incompatible_with_mutable_query() {729fn incompatible_system(_: Query<EntityMut>, _: Query<&mut A>) {}730731assert_is_system(incompatible_system);732}733734#[test]735fn filtered_entity_ref_normal() {736let mut world = World::new();737let a_id = world.register_component::<A>();738739let e: FilteredEntityRef = world.spawn(A).into();740741assert!(e.get::<A>().is_some());742assert!(e.get_ref::<A>().is_some());743assert!(e.get_change_ticks::<A>().is_some());744assert!(e.get_by_id(a_id).is_some());745assert!(e.get_change_ticks_by_id(a_id).is_some());746}747748#[test]749fn filtered_entity_ref_missing() {750let mut world = World::new();751let a_id = world.register_component::<A>();752753let e: FilteredEntityRef = world.spawn(()).into();754755assert!(e.get::<A>().is_none());756assert!(e.get_ref::<A>().is_none());757assert!(e.get_change_ticks::<A>().is_none());758assert!(e.get_by_id(a_id).is_none());759assert!(e.get_change_ticks_by_id(a_id).is_none());760}761762#[test]763fn filtered_entity_mut_normal() {764let mut world = World::new();765let a_id = world.register_component::<A>();766767let mut e: FilteredEntityMut = world.spawn(A).into();768769assert!(e.get::<A>().is_some());770assert!(e.get_ref::<A>().is_some());771assert!(e.get_mut::<A>().is_some());772assert!(e.get_change_ticks::<A>().is_some());773assert!(e.get_by_id(a_id).is_some());774assert!(e.get_mut_by_id(a_id).is_some());775assert!(e.get_change_ticks_by_id(a_id).is_some());776}777778#[test]779fn filtered_entity_mut_missing() {780let mut world = World::new();781let a_id = world.register_component::<A>();782783let mut e: FilteredEntityMut = world.spawn(()).into();784785assert!(e.get::<A>().is_none());786assert!(e.get_ref::<A>().is_none());787assert!(e.get_mut::<A>().is_none());788assert!(e.get_change_ticks::<A>().is_none());789assert!(e.get_by_id(a_id).is_none());790assert!(e.get_mut_by_id(a_id).is_none());791assert!(e.get_change_ticks_by_id(a_id).is_none());792}793794#[derive(Component, PartialEq, Eq, Debug)]795struct X(usize);796797#[derive(Component, PartialEq, Eq, Debug)]798struct Y(usize);799800#[test]801fn get_components() {802let mut world = World::default();803let e1 = world.spawn((X(7), Y(10))).id();804let e2 = world.spawn(X(8)).id();805let e3 = world.spawn_empty().id();806807assert_eq!(808Some((&X(7), &Y(10))),809world.entity(e1).get_components::<(&X, &Y)>()810);811assert_eq!(None, world.entity(e2).get_components::<(&X, &Y)>());812assert_eq!(None, world.entity(e3).get_components::<(&X, &Y)>());813}814815#[test]816fn get_by_id_array() {817let mut world = World::default();818let e1 = world.spawn((X(7), Y(10))).id();819let e2 = world.spawn(X(8)).id();820let e3 = world.spawn_empty().id();821822let x_id = world.register_component::<X>();823let y_id = world.register_component::<Y>();824825assert_eq!(826Ok((&X(7), &Y(10))),827world828.entity(e1)829.get_by_id([x_id, y_id])830.map(|[x_ptr, y_ptr]| {831// SAFETY: components match the id they were fetched with832(unsafe { x_ptr.deref::<X>() }, unsafe { y_ptr.deref::<Y>() })833})834);835assert_eq!(836Err(EntityComponentError::MissingComponent(y_id)),837world838.entity(e2)839.get_by_id([x_id, y_id])840.map(|[x_ptr, y_ptr]| {841// SAFETY: components match the id they were fetched with842(unsafe { x_ptr.deref::<X>() }, unsafe { y_ptr.deref::<Y>() })843})844);845assert_eq!(846Err(EntityComponentError::MissingComponent(x_id)),847world848.entity(e3)849.get_by_id([x_id, y_id])850.map(|[x_ptr, y_ptr]| {851// SAFETY: components match the id they were fetched with852(unsafe { x_ptr.deref::<X>() }, unsafe { y_ptr.deref::<Y>() })853})854);855}856857#[test]858fn get_by_id_vec() {859let mut world = World::default();860let e1 = world.spawn((X(7), Y(10))).id();861let e2 = world.spawn(X(8)).id();862let e3 = world.spawn_empty().id();863864let x_id = world.register_component::<X>();865let y_id = world.register_component::<Y>();866867assert_eq!(868Ok((&X(7), &Y(10))),869world870.entity(e1)871.get_by_id(&[x_id, y_id] as &[ComponentId])872.map(|ptrs| {873let Ok([x_ptr, y_ptr]): Result<[Ptr; 2], _> = ptrs.try_into() else {874panic!("get_by_id(slice) didn't return 2 elements")875};876877// SAFETY: components match the id they were fetched with878(unsafe { x_ptr.deref::<X>() }, unsafe { y_ptr.deref::<Y>() })879})880);881assert_eq!(882Err(EntityComponentError::MissingComponent(y_id)),883world884.entity(e2)885.get_by_id(&[x_id, y_id] as &[ComponentId])886.map(|ptrs| {887let Ok([x_ptr, y_ptr]): Result<[Ptr; 2], _> = ptrs.try_into() else {888panic!("get_by_id(slice) didn't return 2 elements")889};890891// SAFETY: components match the id they were fetched with892(unsafe { x_ptr.deref::<X>() }, unsafe { y_ptr.deref::<Y>() })893})894);895assert_eq!(896Err(EntityComponentError::MissingComponent(x_id)),897world898.entity(e3)899.get_by_id(&[x_id, y_id] as &[ComponentId])900.map(|ptrs| {901let Ok([x_ptr, y_ptr]): Result<[Ptr; 2], _> = ptrs.try_into() else {902panic!("get_by_id(slice) didn't return 2 elements")903};904905// SAFETY: components match the id they were fetched with906(unsafe { x_ptr.deref::<X>() }, unsafe { y_ptr.deref::<Y>() })907})908);909}910911#[test]912fn get_mut_by_id_array() {913let mut world = World::default();914let e1 = world.spawn((X(7), Y(10))).id();915let e2 = world.spawn(X(8)).id();916let e3 = world.spawn_empty().id();917918let x_id = world.register_component::<X>();919let y_id = world.register_component::<Y>();920921assert_eq!(922Ok((&mut X(7), &mut Y(10))),923world924.entity_mut(e1)925.get_mut_by_id([x_id, y_id])926.map(|[x_ptr, y_ptr]| {927// SAFETY: components match the id they were fetched with928(unsafe { x_ptr.into_inner().deref_mut::<X>() }, unsafe {929y_ptr.into_inner().deref_mut::<Y>()930})931})932);933assert_eq!(934Err(EntityComponentError::MissingComponent(y_id)),935world936.entity_mut(e2)937.get_mut_by_id([x_id, y_id])938.map(|[x_ptr, y_ptr]| {939// SAFETY: components match the id they were fetched with940(unsafe { x_ptr.into_inner().deref_mut::<X>() }, unsafe {941y_ptr.into_inner().deref_mut::<Y>()942})943})944);945assert_eq!(946Err(EntityComponentError::MissingComponent(x_id)),947world948.entity_mut(e3)949.get_mut_by_id([x_id, y_id])950.map(|[x_ptr, y_ptr]| {951// SAFETY: components match the id they were fetched with952(unsafe { x_ptr.into_inner().deref_mut::<X>() }, unsafe {953y_ptr.into_inner().deref_mut::<Y>()954})955})956);957958assert_eq!(959Err(EntityComponentError::AliasedMutability(x_id)),960world961.entity_mut(e1)962.get_mut_by_id([x_id, x_id])963.map(|_| { unreachable!() })964);965assert_eq!(966Err(EntityComponentError::AliasedMutability(x_id)),967world968.entity_mut(e3)969.get_mut_by_id([x_id, x_id])970.map(|_| { unreachable!() })971);972}973974#[test]975fn get_mut_by_id_vec() {976let mut world = World::default();977let e1 = world.spawn((X(7), Y(10))).id();978let e2 = world.spawn(X(8)).id();979let e3 = world.spawn_empty().id();980981let x_id = world.register_component::<X>();982let y_id = world.register_component::<Y>();983984assert_eq!(985Ok((&mut X(7), &mut Y(10))),986world987.entity_mut(e1)988.get_mut_by_id(&[x_id, y_id] as &[ComponentId])989.map(|ptrs| {990let Ok([x_ptr, y_ptr]): Result<[MutUntyped; 2], _> = ptrs.try_into() else {991panic!("get_mut_by_id(slice) didn't return 2 elements")992};993994// 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] as &[ComponentId])1005.map(|ptrs| {1006let Ok([x_ptr, y_ptr]): Result<[MutUntyped; 2], _> = ptrs.try_into() else {1007panic!("get_mut_by_id(slice) didn't return 2 elements")1008};10091010// SAFETY: components match the id they were fetched with1011(unsafe { x_ptr.into_inner().deref_mut::<X>() }, unsafe {1012y_ptr.into_inner().deref_mut::<Y>()1013})1014})1015);1016assert_eq!(1017Err(EntityComponentError::MissingComponent(x_id)),1018world1019.entity_mut(e3)1020.get_mut_by_id(&[x_id, y_id] as &[ComponentId])1021.map(|ptrs| {1022let Ok([x_ptr, y_ptr]): Result<[MutUntyped; 2], _> = ptrs.try_into() else {1023panic!("get_mut_by_id(slice) didn't return 2 elements")1024};10251026// SAFETY: components match the id they were fetched with1027(unsafe { x_ptr.into_inner().deref_mut::<X>() }, unsafe {1028y_ptr.into_inner().deref_mut::<Y>()1029})1030})1031);10321033assert_eq!(1034Err(EntityComponentError::AliasedMutability(x_id)),1035world1036.entity_mut(e1)1037.get_mut_by_id(&[x_id, x_id])1038.map(|_| { unreachable!() })1039);1040assert_eq!(1041Err(EntityComponentError::AliasedMutability(x_id)),1042world1043.entity_mut(e3)1044.get_mut_by_id(&[x_id, x_id])1045.map(|_| { unreachable!() })1046);1047}10481049#[test]1050fn get_mut_by_id_unchecked() {1051let mut world = World::default();1052let e1 = world.spawn((X(7), Y(10))).id();1053let x_id = world.register_component::<X>();1054let y_id = world.register_component::<Y>();10551056let e1_mut = &world.get_entity_mut([e1]).unwrap()[0];1057// SAFETY: The entity e1 contains component X.1058let x_ptr = unsafe { e1_mut.get_mut_by_id_unchecked(x_id) }.unwrap();1059// SAFETY: The entity e1 contains component Y, with components X and Y being mutually independent.1060let y_ptr = unsafe { e1_mut.get_mut_by_id_unchecked(y_id) }.unwrap();10611062// SAFETY: components match the id they were fetched with1063let x_component = unsafe { x_ptr.into_inner().deref_mut::<X>() };1064x_component.0 += 1;1065// SAFETY: components match the id they were fetched with1066let y_component = unsafe { y_ptr.into_inner().deref_mut::<Y>() };1067y_component.0 -= 1;10681069assert_eq!((&mut X(8), &mut Y(9)), (x_component, y_component));1070}10711072#[derive(EntityEvent)]1073struct TestEvent(Entity);10741075#[test]1076fn adding_observer_updates_location() {1077let mut world = World::new();1078let entity = world1079.spawn_empty()1080.observe(|event: On<TestEvent>, mut commands: Commands| {1081commands1082.entity(event.event_target())1083.insert(TestComponent(0));1084})1085.id();10861087// this should not be needed, but is currently required to tease out the bug1088world.flush();10891090let mut a = world.entity_mut(entity);1091// SAFETY: this _intentionally_ doesn't update the location, to ensure that we're actually testing1092// that observe() updates location1093unsafe { a.world_mut().trigger(TestEvent(entity)) }1094a.observe(|_: On<TestEvent>| {}); // this flushes commands implicitly by spawning1095let location = a.location();1096assert_eq!(world.entities().get(entity), Some(location));1097}10981099#[test]1100#[should_panic]1101fn location_on_despawned_entity_panics() {1102let mut world = World::new();1103world.add_observer(|add: On<Add, TestComponent>, mut commands: Commands| {1104commands.entity(add.entity).despawn();1105});1106let entity = world.spawn_empty().id();1107let mut a = world.entity_mut(entity);1108a.insert(TestComponent(0));1109a.location();1110}11111112#[derive(Resource)]1113struct TestFlush(usize);11141115fn count_flush(world: &mut World) {1116world.resource_mut::<TestFlush>().0 += 1;1117}11181119#[test]1120fn archetype_modifications_trigger_flush() {1121let mut world = World::new();1122world.insert_resource(TestFlush(0));1123world.add_observer(|_: On<Add, TestComponent>, mut commands: Commands| {1124commands.queue(count_flush);1125});1126world.add_observer(|_: On<Remove, TestComponent>, mut commands: Commands| {1127commands.queue(count_flush);1128});1129world.commands().queue(count_flush);1130let entity = world.spawn_empty().id();1131assert_eq!(world.resource::<TestFlush>().0, 1);1132world.commands().queue(count_flush);1133world.flush_commands();1134let mut a = world.entity_mut(entity);1135assert_eq!(a.world().resource::<TestFlush>().0, 2);1136a.insert(TestComponent(0));1137assert_eq!(a.world().resource::<TestFlush>().0, 3);1138a.remove::<TestComponent>();1139assert_eq!(a.world().resource::<TestFlush>().0, 4);1140a.insert(TestComponent(0));1141assert_eq!(a.world().resource::<TestFlush>().0, 5);1142let _ = a.take::<TestComponent>();1143assert_eq!(a.world().resource::<TestFlush>().0, 6);1144a.insert(TestComponent(0));1145assert_eq!(a.world().resource::<TestFlush>().0, 7);1146a.retain::<()>();1147assert_eq!(a.world().resource::<TestFlush>().0, 8);1148a.insert(TestComponent(0));1149assert_eq!(a.world().resource::<TestFlush>().0, 9);1150a.clear();1151assert_eq!(a.world().resource::<TestFlush>().0, 10);1152a.insert(TestComponent(0));1153assert_eq!(a.world().resource::<TestFlush>().0, 11);1154a.despawn();1155assert_eq!(world.resource::<TestFlush>().0, 12);1156}11571158#[derive(Resource)]1159struct TestVec(Vec<&'static str>);11601161#[derive(Component)]1162#[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)]1163struct OrdA;11641165fn ord_a_hook_on_add(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {1166world.resource_mut::<TestVec>().0.push("OrdA hook on_add");1167world.commands().entity(entity).insert(OrdB);1168}11691170fn ord_a_hook_on_insert(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {1171world1172.resource_mut::<TestVec>()1173.01174.push("OrdA hook on_insert");1175world.commands().entity(entity).remove::<OrdA>();1176world.commands().entity(entity).remove::<OrdB>();1177}11781179fn ord_a_hook_on_replace(mut world: DeferredWorld, _: HookContext) {1180world1181.resource_mut::<TestVec>()1182.01183.push("OrdA hook on_replace");1184}11851186fn ord_a_hook_on_remove(mut world: DeferredWorld, _: HookContext) {1187world1188.resource_mut::<TestVec>()1189.01190.push("OrdA hook on_remove");1191}11921193fn ord_a_observer_on_add(_event: On<Add, OrdA>, mut res: ResMut<TestVec>) {1194res.0.push("OrdA observer on_add");1195}11961197fn ord_a_observer_on_insert(_event: On<Insert, OrdA>, mut res: ResMut<TestVec>) {1198res.0.push("OrdA observer on_insert");1199}12001201fn ord_a_observer_on_replace(_event: On<Replace, OrdA>, mut res: ResMut<TestVec>) {1202res.0.push("OrdA observer on_replace");1203}12041205fn ord_a_observer_on_remove(_event: On<Remove, OrdA>, mut res: ResMut<TestVec>) {1206res.0.push("OrdA observer on_remove");1207}12081209#[derive(Component)]1210#[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)]1211struct OrdB;12121213fn ord_b_hook_on_add(mut world: DeferredWorld, _: HookContext) {1214world.resource_mut::<TestVec>().0.push("OrdB hook on_add");1215world.commands().queue(|world: &mut World| {1216world1217.resource_mut::<TestVec>()1218.01219.push("OrdB command on_add");1220});1221}12221223fn ord_b_hook_on_insert(mut world: DeferredWorld, _: HookContext) {1224world1225.resource_mut::<TestVec>()1226.01227.push("OrdB hook on_insert");1228}12291230fn ord_b_hook_on_replace(mut world: DeferredWorld, _: HookContext) {1231world1232.resource_mut::<TestVec>()1233.01234.push("OrdB hook on_replace");1235}12361237fn ord_b_hook_on_remove(mut world: DeferredWorld, _: HookContext) {1238world1239.resource_mut::<TestVec>()1240.01241.push("OrdB hook on_remove");1242}12431244fn ord_b_observer_on_add(_event: On<Add, OrdB>, mut res: ResMut<TestVec>) {1245res.0.push("OrdB observer on_add");1246}12471248fn ord_b_observer_on_insert(_event: On<Insert, OrdB>, mut res: ResMut<TestVec>) {1249res.0.push("OrdB observer on_insert");1250}12511252fn ord_b_observer_on_replace(_event: On<Replace, OrdB>, mut res: ResMut<TestVec>) {1253res.0.push("OrdB observer on_replace");1254}12551256fn ord_b_observer_on_remove(_event: On<Remove, OrdB>, mut res: ResMut<TestVec>) {1257res.0.push("OrdB observer on_remove");1258}12591260#[test]1261fn command_ordering_is_correct() {1262let mut world = World::new();1263world.insert_resource(TestVec(Vec::new()));1264world.add_observer(ord_a_observer_on_add);1265world.add_observer(ord_a_observer_on_insert);1266world.add_observer(ord_a_observer_on_replace);1267world.add_observer(ord_a_observer_on_remove);1268world.add_observer(ord_b_observer_on_add);1269world.add_observer(ord_b_observer_on_insert);1270world.add_observer(ord_b_observer_on_replace);1271world.add_observer(ord_b_observer_on_remove);1272let _entity = world.spawn(OrdA).id();1273let expected = [1274"OrdA hook on_add", // adds command to insert OrdB1275"OrdA observer on_add",1276"OrdA hook on_insert", // adds command to despawn entity1277"OrdA observer on_insert",1278"OrdB hook on_add", // adds command to just add to this log1279"OrdB observer on_add",1280"OrdB hook on_insert",1281"OrdB observer on_insert",1282"OrdB command on_add", // command added by OrdB hook on_add, needs to run before despawn command1283"OrdA observer on_replace", // start of despawn1284"OrdA hook on_replace",1285"OrdA observer on_remove",1286"OrdA hook on_remove",1287"OrdB observer on_replace",1288"OrdB hook on_replace",1289"OrdB observer on_remove",1290"OrdB hook on_remove",1291];1292world.flush();1293assert_eq!(world.resource_mut::<TestVec>().0.as_slice(), &expected[..]);1294}12951296#[test]1297fn entity_world_mut_clone_and_move_components() {1298#[derive(Component, Clone, PartialEq, Debug)]1299struct A;13001301#[derive(Component, Clone, PartialEq, Debug)]1302struct B;13031304#[derive(Component, Clone, PartialEq, Debug)]1305struct C(u32);13061307let mut world = World::new();1308let entity_a = world.spawn((A, B, C(5))).id();1309let entity_b = world.spawn((A, C(4))).id();13101311world.entity_mut(entity_a).clone_components::<B>(entity_b);1312assert_eq!(world.entity(entity_a).get::<B>(), Some(&B));1313assert_eq!(world.entity(entity_b).get::<B>(), Some(&B));13141315world.entity_mut(entity_a).move_components::<C>(entity_b);1316assert_eq!(world.entity(entity_a).get::<C>(), None);1317assert_eq!(world.entity(entity_b).get::<C>(), Some(&C(5)));13181319assert_eq!(world.entity(entity_a).get::<A>(), Some(&A));1320assert_eq!(world.entity(entity_b).get::<A>(), Some(&A));1321}13221323#[test]1324fn entity_world_mut_clone_with_move_and_require() {1325#[derive(Component, Clone, PartialEq, Debug)]1326#[require(B(3))]1327struct A;13281329#[derive(Component, Clone, PartialEq, Debug, Default)]1330#[require(C(3))]1331struct B(u32);13321333#[derive(Component, Clone, PartialEq, Debug, Default)]1334#[require(D)]1335struct C(u32);13361337#[derive(Component, Clone, PartialEq, Debug, Default)]1338struct D;13391340let mut world = World::new();1341let entity_a = world.spawn((A, B(5))).id();1342let entity_b = world.spawn_empty().id();13431344world1345.entity_mut(entity_a)1346.clone_with_opt_in(entity_b, |builder| {1347builder1348.move_components(true)1349.allow::<C>()1350.without_required_components(|builder| {1351builder.allow::<A>();1352});1353});13541355assert_eq!(world.entity(entity_a).get::<A>(), None);1356assert_eq!(world.entity(entity_b).get::<A>(), Some(&A));13571358assert_eq!(world.entity(entity_a).get::<B>(), Some(&B(5)));1359assert_eq!(world.entity(entity_b).get::<B>(), Some(&B(3)));13601361assert_eq!(world.entity(entity_a).get::<C>(), None);1362assert_eq!(world.entity(entity_b).get::<C>(), Some(&C(3)));13631364assert_eq!(world.entity(entity_a).get::<D>(), None);1365assert_eq!(world.entity(entity_b).get::<D>(), Some(&D));1366}13671368#[test]1369fn update_despawned_by_after_observers() {1370let mut world = World::new();13711372#[derive(Component)]1373#[component(on_remove = get_tracked)]1374struct C;13751376static TRACKED: OnceLock<(MaybeLocation, Tick)> = OnceLock::new();1377fn get_tracked(world: DeferredWorld, HookContext { entity, .. }: HookContext) {1378TRACKED.get_or_init(|| {1379let by = world1380.entities1381.entity_get_spawned_or_despawned_by(entity)1382.map(|l| l.unwrap());1383let at = world1384.entities1385.entity_get_spawn_or_despawn_tick(entity)1386.unwrap();1387(by, at)1388});1389}13901391#[track_caller]1392fn caller_spawn(world: &mut World) -> (Entity, MaybeLocation, Tick) {1393let caller = MaybeLocation::caller();1394(world.spawn(C).id(), caller, world.change_tick())1395}1396let (entity, spawner, spawn_tick) = caller_spawn(&mut world);13971398assert_eq!(1399spawner,1400world1401.entities()1402.entity_get_spawned_or_despawned_by(entity)1403.map(|l| l.unwrap())1404);14051406#[track_caller]1407fn caller_despawn(world: &mut World, entity: Entity) -> (MaybeLocation, Tick) {1408world.despawn(entity);1409(MaybeLocation::caller(), world.change_tick())1410}1411let (despawner, despawn_tick) = caller_despawn(&mut world, entity);14121413assert_eq!((spawner, spawn_tick), *TRACKED.get().unwrap());1414assert_eq!(1415despawner,1416world1417.entities()1418.entity_get_spawned_or_despawned_by(entity)1419.map(|l| l.unwrap())1420);1421assert_eq!(1422despawn_tick,1423world1424.entities()1425.entity_get_spawn_or_despawn_tick(entity)1426.unwrap()1427);1428}14291430#[test]1431fn with_component_activates_hooks() {1432use core::sync::atomic::{AtomicBool, AtomicU8, Ordering};14331434#[derive(Component, PartialEq, Eq, Debug)]1435#[component(immutable)]1436struct Foo(bool);14371438static EXPECTED_VALUE: AtomicBool = AtomicBool::new(false);14391440static ADD_COUNT: AtomicU8 = AtomicU8::new(0);1441static REMOVE_COUNT: AtomicU8 = AtomicU8::new(0);1442static REPLACE_COUNT: AtomicU8 = AtomicU8::new(0);1443static INSERT_COUNT: AtomicU8 = AtomicU8::new(0);14441445let mut world = World::default();14461447world.register_component::<Foo>();1448world1449.register_component_hooks::<Foo>()1450.on_add(|world, context| {1451ADD_COUNT.fetch_add(1, Ordering::Relaxed);14521453assert_eq!(1454world.get(context.entity),1455Some(&Foo(EXPECTED_VALUE.load(Ordering::Relaxed)))1456);1457})1458.on_remove(|world, context| {1459REMOVE_COUNT.fetch_add(1, Ordering::Relaxed);14601461assert_eq!(1462world.get(context.entity),1463Some(&Foo(EXPECTED_VALUE.load(Ordering::Relaxed)))1464);1465})1466.on_replace(|world, context| {1467REPLACE_COUNT.fetch_add(1, Ordering::Relaxed);14681469assert_eq!(1470world.get(context.entity),1471Some(&Foo(EXPECTED_VALUE.load(Ordering::Relaxed)))1472);1473})1474.on_insert(|world, context| {1475INSERT_COUNT.fetch_add(1, Ordering::Relaxed);14761477assert_eq!(1478world.get(context.entity),1479Some(&Foo(EXPECTED_VALUE.load(Ordering::Relaxed)))1480);1481});14821483let entity = world.spawn(Foo(false)).id();14841485assert_eq!(ADD_COUNT.load(Ordering::Relaxed), 1);1486assert_eq!(REMOVE_COUNT.load(Ordering::Relaxed), 0);1487assert_eq!(REPLACE_COUNT.load(Ordering::Relaxed), 0);1488assert_eq!(INSERT_COUNT.load(Ordering::Relaxed), 1);14891490let mut entity = world.entity_mut(entity);14911492let archetype_pointer_before = &raw const *entity.archetype();14931494assert_eq!(entity.get::<Foo>(), Some(&Foo(false)));14951496entity.modify_component(|foo: &mut Foo| {1497foo.0 = true;1498EXPECTED_VALUE.store(foo.0, Ordering::Relaxed);1499});15001501let archetype_pointer_after = &raw const *entity.archetype();15021503assert_eq!(entity.get::<Foo>(), Some(&Foo(true)));15041505assert_eq!(ADD_COUNT.load(Ordering::Relaxed), 1);1506assert_eq!(REMOVE_COUNT.load(Ordering::Relaxed), 0);1507assert_eq!(REPLACE_COUNT.load(Ordering::Relaxed), 1);1508assert_eq!(INSERT_COUNT.load(Ordering::Relaxed), 2);15091510assert_eq!(archetype_pointer_before, archetype_pointer_after);1511}15121513#[test]1514fn bundle_remove_only_triggers_for_present_components() {1515let mut world = World::default();15161517#[derive(Component)]1518struct A;15191520#[derive(Component)]1521struct B;15221523#[derive(Resource, PartialEq, Eq, Debug)]1524struct Tracker {1525a: bool,1526b: bool,1527}15281529world.insert_resource(Tracker { a: false, b: false });1530let entity = world.spawn(A).id();15311532world.add_observer(|_: On<Remove, A>, mut tracker: ResMut<Tracker>| {1533tracker.a = true;1534});1535world.add_observer(|_: On<Remove, B>, mut tracker: ResMut<Tracker>| {1536tracker.b = true;1537});15381539world.entity_mut(entity).remove::<(A, B)>();15401541assert_eq!(1542world.resource::<Tracker>(),1543&Tracker {1544a: true,1545// The entity didn't have a B component, so it should not have been triggered.1546b: false,1547}1548);1549}15501551#[test]1552fn spawned_after_swap_remove() {1553#[derive(Component)]1554struct Marker;15551556let mut world = World::new();1557let id1 = world.spawn(Marker).id();1558let _id2 = world.spawn(Marker).id();1559let id3 = world.spawn(Marker).id();15601561let e1_spawned = world.entity(id1).spawned_by();15621563let spawn = world.entity(id3).spawned_by();1564world.entity_mut(id1).despawn();1565let e1_despawned = world.entities().entity_get_spawned_or_despawned_by(id1);15661567// These assertions are only possible if the `track_location` feature is enabled1568if let (Some(e1_spawned), Some(e1_despawned)) =1569(e1_spawned.into_option(), e1_despawned.into_option())1570{1571assert!(e1_despawned.is_some());1572assert_ne!(Some(e1_spawned), e1_despawned);1573}15741575let spawn_after = world.entity(id3).spawned_by();1576assert_eq!(spawn, spawn_after);1577}15781579#[test]1580fn spawned_by_set_before_flush() {1581#[derive(Component)]1582#[component(on_despawn = on_despawn)]1583struct C;15841585fn on_despawn(mut world: DeferredWorld, context: HookContext) {1586let spawned = world.entity(context.entity).spawned_by();1587world.commands().queue(move |world: &mut World| {1588// The entity has finished despawning...1589assert!(world.get_entity(context.entity).is_err());1590let despawned = world1591.entities()1592.entity_get_spawned_or_despawned_by(context.entity);1593// These assertions are only possible if the `track_location` feature is enabled1594if let (Some(spawned), Some(despawned)) =1595(spawned.into_option(), despawned.into_option())1596{1597// ... so ensure that `despawned_by` has been written1598assert!(despawned.is_some());1599assert_ne!(Some(spawned), despawned);1600}1601});1602}16031604let mut world = World::new();1605let original = world.spawn(C).id();1606world.despawn(original);1607}1608}160916101611