1727 lines
		
	
	
		
			54 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			1727 lines
		
	
	
		
			54 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| #![warn(clippy::undocumented_unsafe_blocks)]
 | |
| #![warn(missing_docs)]
 | |
| #![allow(clippy::type_complexity)]
 | |
| #![doc = include_str!("../README.md")]
 | |
| 
 | |
| #[cfg(target_pointer_width = "16")]
 | |
| compile_error!("bevy_ecs cannot safely compile for a 16-bit platform.");
 | |
| 
 | |
| pub mod archetype;
 | |
| pub mod bundle;
 | |
| pub mod change_detection;
 | |
| pub mod component;
 | |
| pub mod entity;
 | |
| pub mod event;
 | |
| pub mod query;
 | |
| #[cfg(feature = "bevy_reflect")]
 | |
| pub mod reflect;
 | |
| pub mod removal_detection;
 | |
| pub mod schedule;
 | |
| pub mod storage;
 | |
| pub mod system;
 | |
| pub mod world;
 | |
| 
 | |
| use std::any::TypeId;
 | |
| 
 | |
| pub use bevy_ptr as ptr;
 | |
| 
 | |
| /// Most commonly used re-exported types.
 | |
| pub mod prelude {
 | |
|     #[doc(hidden)]
 | |
|     #[cfg(feature = "bevy_reflect")]
 | |
|     pub use crate::reflect::{AppTypeRegistry, ReflectComponent, ReflectResource};
 | |
|     #[doc(hidden)]
 | |
|     pub use crate::{
 | |
|         bundle::Bundle,
 | |
|         change_detection::{DetectChanges, DetectChangesMut, Mut, Ref},
 | |
|         component::Component,
 | |
|         entity::Entity,
 | |
|         event::{Event, EventReader, EventWriter, Events},
 | |
|         query::{Added, AnyOf, Changed, Has, Or, QueryState, With, Without},
 | |
|         removal_detection::RemovedComponents,
 | |
|         schedule::{
 | |
|             apply_deferred, apply_state_transition, common_conditions::*, Condition,
 | |
|             IntoSystemConfigs, IntoSystemSet, IntoSystemSetConfig, IntoSystemSetConfigs, NextState,
 | |
|             OnEnter, OnExit, OnTransition, Schedule, Schedules, State, States, SystemSet,
 | |
|         },
 | |
|         system::{
 | |
|             adapter as system_adapter,
 | |
|             adapter::{dbg, error, ignore, info, unwrap, warn},
 | |
|             Commands, Deferred, In, IntoSystem, Local, NonSend, NonSendMut, ParallelCommands,
 | |
|             ParamSet, Query, ReadOnlySystem, Res, ResMut, Resource, System, SystemParamFunction,
 | |
|         },
 | |
|         world::{EntityRef, FromWorld, World},
 | |
|     };
 | |
| }
 | |
| 
 | |
| pub use bevy_utils::all_tuples;
 | |
| 
 | |
| /// A specialized hashmap type with Key of [`TypeId`]
 | |
| type TypeIdMap<V> = rustc_hash::FxHashMap<TypeId, V>;
 | |
| 
 | |
| #[cfg(test)]
 | |
| mod tests {
 | |
|     use crate as bevy_ecs;
 | |
|     use crate::prelude::Or;
 | |
|     use crate::{
 | |
|         bundle::Bundle,
 | |
|         change_detection::Ref,
 | |
|         component::{Component, ComponentId},
 | |
|         entity::Entity,
 | |
|         query::{Added, Changed, FilteredAccess, ReadOnlyWorldQuery, With, Without},
 | |
|         system::Resource,
 | |
|         world::{EntityRef, Mut, World},
 | |
|     };
 | |
|     use bevy_tasks::{ComputeTaskPool, TaskPool};
 | |
|     use std::{
 | |
|         any::TypeId,
 | |
|         marker::PhantomData,
 | |
|         sync::{
 | |
|             atomic::{AtomicUsize, Ordering},
 | |
|             Arc, Mutex,
 | |
|         },
 | |
|     };
 | |
| 
 | |
|     #[derive(Component, Resource, Debug, PartialEq, Eq, Clone, Copy)]
 | |
|     struct A(usize);
 | |
|     #[derive(Component, Debug, PartialEq, Eq, Clone, Copy)]
 | |
|     struct B(usize);
 | |
|     #[derive(Component, Debug, PartialEq, Eq, Clone, Copy)]
 | |
|     struct C;
 | |
| 
 | |
|     #[derive(Default)]
 | |
|     struct NonSendA(usize, PhantomData<*mut ()>);
 | |
| 
 | |
|     #[derive(Component, Clone, Debug)]
 | |
|     struct DropCk(Arc<AtomicUsize>);
 | |
|     impl DropCk {
 | |
|         fn new_pair() -> (Self, Arc<AtomicUsize>) {
 | |
|             let atomic = Arc::new(AtomicUsize::new(0));
 | |
|             (DropCk(atomic.clone()), atomic)
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     impl Drop for DropCk {
 | |
|         fn drop(&mut self) {
 | |
|             self.0.as_ref().fetch_add(1, Ordering::Relaxed);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     #[derive(Component, Clone, Debug)]
 | |
|     #[component(storage = "SparseSet")]
 | |
|     struct DropCkSparse(DropCk);
 | |
| 
 | |
|     #[derive(Component, Copy, Clone, PartialEq, Eq, Debug)]
 | |
|     #[component(storage = "Table")]
 | |
|     struct TableStored(&'static str);
 | |
|     #[derive(Component, Copy, Clone, PartialEq, Eq, Debug)]
 | |
|     #[component(storage = "SparseSet")]
 | |
|     struct SparseStored(u32);
 | |
| 
 | |
|     #[test]
 | |
|     fn random_access() {
 | |
|         let mut world = World::new();
 | |
| 
 | |
|         let e = world.spawn((TableStored("abc"), SparseStored(123))).id();
 | |
|         let f = world
 | |
|             .spawn((TableStored("def"), SparseStored(456), A(1)))
 | |
|             .id();
 | |
|         assert_eq!(world.get::<TableStored>(e).unwrap().0, "abc");
 | |
|         assert_eq!(world.get::<SparseStored>(e).unwrap().0, 123);
 | |
|         assert_eq!(world.get::<TableStored>(f).unwrap().0, "def");
 | |
|         assert_eq!(world.get::<SparseStored>(f).unwrap().0, 456);
 | |
| 
 | |
|         // test archetype get_mut()
 | |
|         world.get_mut::<TableStored>(e).unwrap().0 = "xyz";
 | |
|         assert_eq!(world.get::<TableStored>(e).unwrap().0, "xyz");
 | |
| 
 | |
|         // test sparse set get_mut()
 | |
|         world.get_mut::<SparseStored>(f).unwrap().0 = 42;
 | |
|         assert_eq!(world.get::<SparseStored>(f).unwrap().0, 42);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn bundle_derive() {
 | |
|         let mut world = World::new();
 | |
| 
 | |
|         #[derive(Bundle, PartialEq, Debug)]
 | |
|         struct FooBundle {
 | |
|             x: TableStored,
 | |
|             y: SparseStored,
 | |
|         }
 | |
|         let mut ids = Vec::new();
 | |
|         <FooBundle as Bundle>::component_ids(
 | |
|             &mut world.components,
 | |
|             &mut world.storages,
 | |
|             &mut |id| {
 | |
|                 ids.push(id);
 | |
|             },
 | |
|         );
 | |
| 
 | |
|         assert_eq!(
 | |
|             ids,
 | |
|             &[
 | |
|                 world.init_component::<TableStored>(),
 | |
|                 world.init_component::<SparseStored>(),
 | |
|             ]
 | |
|         );
 | |
| 
 | |
|         let e1 = world
 | |
|             .spawn(FooBundle {
 | |
|                 x: TableStored("abc"),
 | |
|                 y: SparseStored(123),
 | |
|             })
 | |
|             .id();
 | |
|         let e2 = world
 | |
|             .spawn((TableStored("def"), SparseStored(456), A(1)))
 | |
|             .id();
 | |
|         assert_eq!(world.get::<TableStored>(e1).unwrap().0, "abc");
 | |
|         assert_eq!(world.get::<SparseStored>(e1).unwrap().0, 123);
 | |
|         assert_eq!(world.get::<TableStored>(e2).unwrap().0, "def");
 | |
|         assert_eq!(world.get::<SparseStored>(e2).unwrap().0, 456);
 | |
| 
 | |
|         // test archetype get_mut()
 | |
|         world.get_mut::<TableStored>(e1).unwrap().0 = "xyz";
 | |
|         assert_eq!(world.get::<TableStored>(e1).unwrap().0, "xyz");
 | |
| 
 | |
|         // test sparse set get_mut()
 | |
|         world.get_mut::<SparseStored>(e2).unwrap().0 = 42;
 | |
|         assert_eq!(world.get::<SparseStored>(e2).unwrap().0, 42);
 | |
| 
 | |
|         assert_eq!(
 | |
|             world.entity_mut(e1).take::<FooBundle>().unwrap(),
 | |
|             FooBundle {
 | |
|                 x: TableStored("xyz"),
 | |
|                 y: SparseStored(123),
 | |
|             }
 | |
|         );
 | |
| 
 | |
|         #[derive(Bundle, PartialEq, Debug)]
 | |
|         struct NestedBundle {
 | |
|             a: A,
 | |
|             foo: FooBundle,
 | |
|             b: B,
 | |
|         }
 | |
| 
 | |
|         let mut ids = Vec::new();
 | |
|         <NestedBundle as Bundle>::component_ids(
 | |
|             &mut world.components,
 | |
|             &mut world.storages,
 | |
|             &mut |id| {
 | |
|                 ids.push(id);
 | |
|             },
 | |
|         );
 | |
| 
 | |
|         assert_eq!(
 | |
|             ids,
 | |
|             &[
 | |
|                 world.init_component::<A>(),
 | |
|                 world.init_component::<TableStored>(),
 | |
|                 world.init_component::<SparseStored>(),
 | |
|                 world.init_component::<B>(),
 | |
|             ]
 | |
|         );
 | |
| 
 | |
|         let e3 = world
 | |
|             .spawn(NestedBundle {
 | |
|                 a: A(1),
 | |
|                 foo: FooBundle {
 | |
|                     x: TableStored("ghi"),
 | |
|                     y: SparseStored(789),
 | |
|                 },
 | |
|                 b: B(2),
 | |
|             })
 | |
|             .id();
 | |
| 
 | |
|         assert_eq!(world.get::<TableStored>(e3).unwrap().0, "ghi");
 | |
|         assert_eq!(world.get::<SparseStored>(e3).unwrap().0, 789);
 | |
|         assert_eq!(world.get::<A>(e3).unwrap().0, 1);
 | |
|         assert_eq!(world.get::<B>(e3).unwrap().0, 2);
 | |
|         assert_eq!(
 | |
|             world.entity_mut(e3).take::<NestedBundle>().unwrap(),
 | |
|             NestedBundle {
 | |
|                 a: A(1),
 | |
|                 foo: FooBundle {
 | |
|                     x: TableStored("ghi"),
 | |
|                     y: SparseStored(789),
 | |
|                 },
 | |
|                 b: B(2),
 | |
|             }
 | |
|         );
 | |
| 
 | |
|         #[derive(Default, Component, PartialEq, Debug)]
 | |
|         struct Ignored;
 | |
| 
 | |
|         #[derive(Bundle, PartialEq, Debug)]
 | |
|         struct BundleWithIgnored {
 | |
|             c: C,
 | |
|             #[bundle(ignore)]
 | |
|             ignored: Ignored,
 | |
|         }
 | |
| 
 | |
|         let mut ids = Vec::new();
 | |
|         <BundleWithIgnored as Bundle>::component_ids(
 | |
|             &mut world.components,
 | |
|             &mut world.storages,
 | |
|             &mut |id| {
 | |
|                 ids.push(id);
 | |
|             },
 | |
|         );
 | |
| 
 | |
|         assert_eq!(ids, &[world.init_component::<C>(),]);
 | |
| 
 | |
|         let e4 = world
 | |
|             .spawn(BundleWithIgnored {
 | |
|                 c: C,
 | |
|                 ignored: Ignored,
 | |
|             })
 | |
|             .id();
 | |
| 
 | |
|         assert_eq!(world.get::<C>(e4).unwrap(), &C);
 | |
|         assert_eq!(world.get::<Ignored>(e4), None);
 | |
| 
 | |
|         assert_eq!(
 | |
|             world.entity_mut(e4).take::<BundleWithIgnored>().unwrap(),
 | |
|             BundleWithIgnored {
 | |
|                 c: C,
 | |
|                 ignored: Ignored,
 | |
|             }
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn despawn_table_storage() {
 | |
|         let mut world = World::new();
 | |
|         let e = world.spawn((TableStored("abc"), A(123))).id();
 | |
|         let f = world.spawn((TableStored("def"), A(456))).id();
 | |
|         assert_eq!(world.entities.len(), 2);
 | |
|         assert!(world.despawn(e));
 | |
|         assert_eq!(world.entities.len(), 1);
 | |
|         assert!(world.get::<TableStored>(e).is_none());
 | |
|         assert!(world.get::<A>(e).is_none());
 | |
|         assert_eq!(world.get::<TableStored>(f).unwrap().0, "def");
 | |
|         assert_eq!(world.get::<A>(f).unwrap().0, 456);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn despawn_mixed_storage() {
 | |
|         let mut world = World::new();
 | |
| 
 | |
|         let e = world.spawn((TableStored("abc"), SparseStored(123))).id();
 | |
|         let f = world.spawn((TableStored("def"), SparseStored(456))).id();
 | |
|         assert_eq!(world.entities.len(), 2);
 | |
|         assert!(world.despawn(e));
 | |
|         assert_eq!(world.entities.len(), 1);
 | |
|         assert!(world.get::<TableStored>(e).is_none());
 | |
|         assert!(world.get::<SparseStored>(e).is_none());
 | |
|         assert_eq!(world.get::<TableStored>(f).unwrap().0, "def");
 | |
|         assert_eq!(world.get::<SparseStored>(f).unwrap().0, 456);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn query_all() {
 | |
|         let mut world = World::new();
 | |
|         let e = world.spawn((TableStored("abc"), A(123))).id();
 | |
|         let f = world.spawn((TableStored("def"), A(456))).id();
 | |
| 
 | |
|         let ents = world
 | |
|             .query::<(Entity, &A, &TableStored)>()
 | |
|             .iter(&world)
 | |
|             .map(|(e, &i, &s)| (e, i, s))
 | |
|             .collect::<Vec<_>>();
 | |
|         assert_eq!(
 | |
|             ents,
 | |
|             &[
 | |
|                 (e, A(123), TableStored("abc")),
 | |
|                 (f, A(456), TableStored("def"))
 | |
|             ]
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn query_all_for_each() {
 | |
|         let mut world = World::new();
 | |
|         let e = world.spawn((TableStored("abc"), A(123))).id();
 | |
|         let f = world.spawn((TableStored("def"), A(456))).id();
 | |
| 
 | |
|         let mut results = Vec::new();
 | |
|         world
 | |
|             .query::<(Entity, &A, &TableStored)>()
 | |
|             .for_each(&world, |(e, &i, &s)| results.push((e, i, s)));
 | |
|         assert_eq!(
 | |
|             results,
 | |
|             &[
 | |
|                 (e, A(123), TableStored("abc")),
 | |
|                 (f, A(456), TableStored("def"))
 | |
|             ]
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn query_single_component() {
 | |
|         let mut world = World::new();
 | |
|         let e = world.spawn((TableStored("abc"), A(123))).id();
 | |
|         let f = world.spawn((TableStored("def"), A(456), B(1))).id();
 | |
|         let ents = world
 | |
|             .query::<(Entity, &A)>()
 | |
|             .iter(&world)
 | |
|             .map(|(e, &i)| (e, i))
 | |
|             .collect::<Vec<_>>();
 | |
|         assert_eq!(ents, &[(e, A(123)), (f, A(456))]);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn stateful_query_handles_new_archetype() {
 | |
|         let mut world = World::new();
 | |
|         let e = world.spawn((TableStored("abc"), A(123))).id();
 | |
|         let mut query = world.query::<(Entity, &A)>();
 | |
| 
 | |
|         let ents = query.iter(&world).map(|(e, &i)| (e, i)).collect::<Vec<_>>();
 | |
|         assert_eq!(ents, &[(e, A(123))]);
 | |
| 
 | |
|         let f = world.spawn((TableStored("def"), A(456), B(1))).id();
 | |
|         let ents = query.iter(&world).map(|(e, &i)| (e, i)).collect::<Vec<_>>();
 | |
|         assert_eq!(ents, &[(e, A(123)), (f, A(456))]);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn query_single_component_for_each() {
 | |
|         let mut world = World::new();
 | |
|         let e = world.spawn((TableStored("abc"), A(123))).id();
 | |
|         let f = world.spawn((TableStored("def"), A(456), B(1))).id();
 | |
|         let mut results = Vec::new();
 | |
|         world
 | |
|             .query::<(Entity, &A)>()
 | |
|             .for_each(&world, |(e, &i)| results.push((e, i)));
 | |
|         assert_eq!(results, &[(e, A(123)), (f, A(456))]);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn par_for_each_dense() {
 | |
|         ComputeTaskPool::init(TaskPool::default);
 | |
|         let mut world = World::new();
 | |
|         let e1 = world.spawn(A(1)).id();
 | |
|         let e2 = world.spawn(A(2)).id();
 | |
|         let e3 = world.spawn(A(3)).id();
 | |
|         let e4 = world.spawn((A(4), B(1))).id();
 | |
|         let e5 = world.spawn((A(5), B(1))).id();
 | |
|         let results = Arc::new(Mutex::new(Vec::new()));
 | |
|         world
 | |
|             .query::<(Entity, &A)>()
 | |
|             .par_iter(&world)
 | |
|             .for_each(|(e, &A(i))| {
 | |
|                 results.lock().unwrap().push((e, i));
 | |
|             });
 | |
|         results.lock().unwrap().sort();
 | |
|         assert_eq!(
 | |
|             &*results.lock().unwrap(),
 | |
|             &[(e1, 1), (e2, 2), (e3, 3), (e4, 4), (e5, 5)]
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn par_for_each_sparse() {
 | |
|         ComputeTaskPool::init(TaskPool::default);
 | |
|         let mut world = World::new();
 | |
|         let e1 = world.spawn(SparseStored(1)).id();
 | |
|         let e2 = world.spawn(SparseStored(2)).id();
 | |
|         let e3 = world.spawn(SparseStored(3)).id();
 | |
|         let e4 = world.spawn((SparseStored(4), A(1))).id();
 | |
|         let e5 = world.spawn((SparseStored(5), A(1))).id();
 | |
|         let results = Arc::new(Mutex::new(Vec::new()));
 | |
|         world
 | |
|             .query::<(Entity, &SparseStored)>()
 | |
|             .par_iter(&world)
 | |
|             .for_each(|(e, &SparseStored(i))| results.lock().unwrap().push((e, i)));
 | |
|         results.lock().unwrap().sort();
 | |
|         assert_eq!(
 | |
|             &*results.lock().unwrap(),
 | |
|             &[(e1, 1), (e2, 2), (e3, 3), (e4, 4), (e5, 5)]
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn query_missing_component() {
 | |
|         let mut world = World::new();
 | |
|         world.spawn((TableStored("abc"), A(123)));
 | |
|         world.spawn((TableStored("def"), A(456)));
 | |
|         assert!(world.query::<(&B, &A)>().iter(&world).next().is_none());
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn query_sparse_component() {
 | |
|         let mut world = World::new();
 | |
|         world.spawn((TableStored("abc"), A(123)));
 | |
|         let f = world.spawn((TableStored("def"), A(456), B(1))).id();
 | |
|         let ents = world
 | |
|             .query::<(Entity, &B)>()
 | |
|             .iter(&world)
 | |
|             .map(|(e, &b)| (e, b))
 | |
|             .collect::<Vec<_>>();
 | |
|         assert_eq!(ents, &[(f, B(1))]);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn query_filter_with() {
 | |
|         let mut world = World::new();
 | |
|         world.spawn((A(123), B(1)));
 | |
|         world.spawn(A(456));
 | |
|         let result = world
 | |
|             .query_filtered::<&A, With<B>>()
 | |
|             .iter(&world)
 | |
|             .cloned()
 | |
|             .collect::<Vec<_>>();
 | |
|         assert_eq!(result, vec![A(123)]);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn query_filter_with_for_each() {
 | |
|         let mut world = World::new();
 | |
|         world.spawn((A(123), B(1)));
 | |
|         world.spawn(A(456));
 | |
| 
 | |
|         let mut results = Vec::new();
 | |
|         world
 | |
|             .query_filtered::<&A, With<B>>()
 | |
|             .for_each(&world, |i| results.push(*i));
 | |
|         assert_eq!(results, vec![A(123)]);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn query_filter_with_sparse() {
 | |
|         let mut world = World::new();
 | |
| 
 | |
|         world.spawn((A(123), SparseStored(321)));
 | |
|         world.spawn(A(456));
 | |
|         let result = world
 | |
|             .query_filtered::<&A, With<SparseStored>>()
 | |
|             .iter(&world)
 | |
|             .cloned()
 | |
|             .collect::<Vec<_>>();
 | |
|         assert_eq!(result, vec![A(123)]);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn query_filter_with_sparse_for_each() {
 | |
|         let mut world = World::new();
 | |
| 
 | |
|         world.spawn((A(123), SparseStored(321)));
 | |
|         world.spawn(A(456));
 | |
|         let mut results = Vec::new();
 | |
|         world
 | |
|             .query_filtered::<&A, With<SparseStored>>()
 | |
|             .for_each(&world, |i| results.push(*i));
 | |
|         assert_eq!(results, vec![A(123)]);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn query_filter_without() {
 | |
|         let mut world = World::new();
 | |
|         world.spawn((A(123), B(321)));
 | |
|         world.spawn(A(456));
 | |
|         let result = world
 | |
|             .query_filtered::<&A, Without<B>>()
 | |
|             .iter(&world)
 | |
|             .cloned()
 | |
|             .collect::<Vec<_>>();
 | |
|         assert_eq!(result, vec![A(456)]);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn query_optional_component_table() {
 | |
|         let mut world = World::new();
 | |
|         let e = world.spawn((TableStored("abc"), A(123))).id();
 | |
|         let f = world.spawn((TableStored("def"), A(456), B(1))).id();
 | |
|         // this should be skipped
 | |
|         world.spawn(TableStored("abc"));
 | |
|         let ents = world
 | |
|             .query::<(Entity, Option<&B>, &A)>()
 | |
|             .iter(&world)
 | |
|             .map(|(e, b, &i)| (e, b.copied(), i))
 | |
|             .collect::<Vec<_>>();
 | |
|         assert_eq!(ents, &[(e, None, A(123)), (f, Some(B(1)), A(456))]);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn query_optional_component_sparse() {
 | |
|         let mut world = World::new();
 | |
| 
 | |
|         let e = world.spawn((TableStored("abc"), A(123))).id();
 | |
|         let f = world
 | |
|             .spawn((TableStored("def"), A(456), SparseStored(1)))
 | |
|             .id();
 | |
|         // this should be skipped
 | |
|         // world.spawn(SparseStored(1));
 | |
|         let ents = world
 | |
|             .query::<(Entity, Option<&SparseStored>, &A)>()
 | |
|             .iter(&world)
 | |
|             .map(|(e, b, &i)| (e, b.copied(), i))
 | |
|             .collect::<Vec<_>>();
 | |
|         assert_eq!(
 | |
|             ents,
 | |
|             &[(e, None, A(123)), (f, Some(SparseStored(1)), A(456))]
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn query_optional_component_sparse_no_match() {
 | |
|         let mut world = World::new();
 | |
| 
 | |
|         let e = world.spawn((TableStored("abc"), A(123))).id();
 | |
|         let f = world.spawn((TableStored("def"), A(456))).id();
 | |
|         // // this should be skipped
 | |
|         world.spawn(TableStored("abc"));
 | |
|         let ents = world
 | |
|             .query::<(Entity, Option<&SparseStored>, &A)>()
 | |
|             .iter(&world)
 | |
|             .map(|(e, b, &i)| (e, b.copied(), i))
 | |
|             .collect::<Vec<_>>();
 | |
|         assert_eq!(ents, &[(e, None, A(123)), (f, None, A(456))]);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn add_remove_components() {
 | |
|         let mut world = World::new();
 | |
|         let e1 = world.spawn((A(1), B(3), TableStored("abc"))).id();
 | |
|         let e2 = world.spawn((A(2), B(4), TableStored("xyz"))).id();
 | |
| 
 | |
|         assert_eq!(
 | |
|             world
 | |
|                 .query::<(Entity, &A, &B)>()
 | |
|                 .iter(&world)
 | |
|                 .map(|(e, &i, &b)| (e, i, b))
 | |
|                 .collect::<Vec<_>>(),
 | |
|             &[(e1, A(1), B(3)), (e2, A(2), B(4))]
 | |
|         );
 | |
| 
 | |
|         assert_eq!(world.entity_mut(e1).take::<A>(), Some(A(1)));
 | |
|         assert_eq!(
 | |
|             world
 | |
|                 .query::<(Entity, &A, &B)>()
 | |
|                 .iter(&world)
 | |
|                 .map(|(e, &i, &b)| (e, i, b))
 | |
|                 .collect::<Vec<_>>(),
 | |
|             &[(e2, A(2), B(4))]
 | |
|         );
 | |
|         assert_eq!(
 | |
|             world
 | |
|                 .query::<(Entity, &B, &TableStored)>()
 | |
|                 .iter(&world)
 | |
|                 .map(|(e, &B(b), &TableStored(s))| (e, b, s))
 | |
|                 .collect::<Vec<_>>(),
 | |
|             &[(e2, 4, "xyz"), (e1, 3, "abc")]
 | |
|         );
 | |
|         world.entity_mut(e1).insert(A(43));
 | |
|         assert_eq!(
 | |
|             world
 | |
|                 .query::<(Entity, &A, &B)>()
 | |
|                 .iter(&world)
 | |
|                 .map(|(e, &i, &b)| (e, i, b))
 | |
|                 .collect::<Vec<_>>(),
 | |
|             &[(e2, A(2), B(4)), (e1, A(43), B(3))]
 | |
|         );
 | |
|         world.entity_mut(e1).insert(C);
 | |
|         assert_eq!(
 | |
|             world
 | |
|                 .query::<(Entity, &C)>()
 | |
|                 .iter(&world)
 | |
|                 .map(|(e, &f)| (e, f))
 | |
|                 .collect::<Vec<_>>(),
 | |
|             &[(e1, C)]
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn table_add_remove_many() {
 | |
|         let mut world = World::default();
 | |
|         #[cfg(miri)]
 | |
|         let (mut entities, to) = {
 | |
|             let to = 10;
 | |
|             (Vec::with_capacity(to), to)
 | |
|         };
 | |
|         #[cfg(not(miri))]
 | |
|         let (mut entities, to) = {
 | |
|             let to = 10_000;
 | |
|             (Vec::with_capacity(to), to)
 | |
|         };
 | |
| 
 | |
|         for _ in 0..to {
 | |
|             entities.push(world.spawn(B(0)).id());
 | |
|         }
 | |
| 
 | |
|         for (i, entity) in entities.iter().cloned().enumerate() {
 | |
|             world.entity_mut(entity).insert(A(i));
 | |
|         }
 | |
| 
 | |
|         for (i, entity) in entities.iter().cloned().enumerate() {
 | |
|             assert_eq!(world.entity_mut(entity).take::<A>(), Some(A(i)));
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn sparse_set_add_remove_many() {
 | |
|         let mut world = World::default();
 | |
| 
 | |
|         let mut entities = Vec::with_capacity(1000);
 | |
|         for _ in 0..4 {
 | |
|             entities.push(world.spawn(A(2)).id());
 | |
|         }
 | |
| 
 | |
|         for (i, entity) in entities.iter().cloned().enumerate() {
 | |
|             world.entity_mut(entity).insert(SparseStored(i as u32));
 | |
|         }
 | |
| 
 | |
|         for (i, entity) in entities.iter().cloned().enumerate() {
 | |
|             assert_eq!(
 | |
|                 world.entity_mut(entity).take::<SparseStored>(),
 | |
|                 Some(SparseStored(i as u32))
 | |
|             );
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn remove_missing() {
 | |
|         let mut world = World::new();
 | |
|         let e = world.spawn((TableStored("abc"), A(123))).id();
 | |
|         assert!(world.entity_mut(e).take::<B>().is_none());
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn spawn_batch() {
 | |
|         let mut world = World::new();
 | |
|         world.spawn_batch((0..100).map(|x| (A(x), TableStored("abc"))));
 | |
|         let values = world
 | |
|             .query::<&A>()
 | |
|             .iter(&world)
 | |
|             .map(|v| v.0)
 | |
|             .collect::<Vec<_>>();
 | |
|         let expected = (0..100).collect::<Vec<_>>();
 | |
|         assert_eq!(values, expected);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn query_get() {
 | |
|         let mut world = World::new();
 | |
|         let a = world.spawn((TableStored("abc"), A(123))).id();
 | |
|         let b = world.spawn((TableStored("def"), A(456))).id();
 | |
|         let c = world.spawn((TableStored("ghi"), A(789), B(1))).id();
 | |
| 
 | |
|         let mut i32_query = world.query::<&A>();
 | |
|         assert_eq!(i32_query.get(&world, a).unwrap().0, 123);
 | |
|         assert_eq!(i32_query.get(&world, b).unwrap().0, 456);
 | |
| 
 | |
|         let mut i32_bool_query = world.query::<(&A, &B)>();
 | |
|         assert!(i32_bool_query.get(&world, a).is_err());
 | |
|         assert_eq!(i32_bool_query.get(&world, c).unwrap(), (&A(789), &B(1)));
 | |
|         assert!(world.despawn(a));
 | |
|         assert!(i32_query.get(&world, a).is_err());
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn query_get_works_across_sparse_removal() {
 | |
|         // Regression test for: https://github.com/bevyengine/bevy/issues/6623
 | |
|         let mut world = World::new();
 | |
|         let a = world.spawn((TableStored("abc"), SparseStored(123))).id();
 | |
|         let b = world.spawn((TableStored("def"), SparseStored(456))).id();
 | |
|         let c = world
 | |
|             .spawn((TableStored("ghi"), SparseStored(789), B(1)))
 | |
|             .id();
 | |
| 
 | |
|         let mut query = world.query::<&TableStored>();
 | |
|         assert_eq!(query.get(&world, a).unwrap(), &TableStored("abc"));
 | |
|         assert_eq!(query.get(&world, b).unwrap(), &TableStored("def"));
 | |
|         assert_eq!(query.get(&world, c).unwrap(), &TableStored("ghi"));
 | |
| 
 | |
|         world.entity_mut(b).remove::<SparseStored>();
 | |
|         world.entity_mut(c).remove::<SparseStored>();
 | |
| 
 | |
|         assert_eq!(query.get(&world, a).unwrap(), &TableStored("abc"));
 | |
|         assert_eq!(query.get(&world, b).unwrap(), &TableStored("def"));
 | |
|         assert_eq!(query.get(&world, c).unwrap(), &TableStored("ghi"));
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn remove_tracking() {
 | |
|         let mut world = World::new();
 | |
| 
 | |
|         let a = world.spawn((SparseStored(0), A(123))).id();
 | |
|         let b = world.spawn((SparseStored(1), A(123))).id();
 | |
| 
 | |
|         world.entity_mut(a).despawn();
 | |
|         assert_eq!(
 | |
|             world.removed::<A>().collect::<Vec<_>>(),
 | |
|             &[a],
 | |
|             "despawning results in 'removed component' state for table components"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             world.removed::<SparseStored>().collect::<Vec<_>>(),
 | |
|             &[a],
 | |
|             "despawning results in 'removed component' state for sparse set components"
 | |
|         );
 | |
| 
 | |
|         world.entity_mut(b).insert(B(1));
 | |
|         assert_eq!(
 | |
|             world.removed::<A>().collect::<Vec<_>>(),
 | |
|             &[a],
 | |
|             "archetype moves does not result in 'removed component' state"
 | |
|         );
 | |
| 
 | |
|         world.entity_mut(b).remove::<A>();
 | |
|         assert_eq!(
 | |
|             world.removed::<A>().collect::<Vec<_>>(),
 | |
|             &[a, b],
 | |
|             "removing a component results in a 'removed component' state"
 | |
|         );
 | |
| 
 | |
|         world.clear_trackers();
 | |
|         assert_eq!(
 | |
|             world.removed::<A>().collect::<Vec<_>>(),
 | |
|             &[],
 | |
|             "clearing trackers clears removals"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             world.removed::<SparseStored>().collect::<Vec<_>>(),
 | |
|             &[],
 | |
|             "clearing trackers clears removals"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             world.removed::<B>().collect::<Vec<_>>(),
 | |
|             &[],
 | |
|             "clearing trackers clears removals"
 | |
|         );
 | |
| 
 | |
|         // TODO: uncomment when world.clear() is implemented
 | |
|         // let c = world.spawn(("abc", 123)).id();
 | |
|         // let d = world.spawn(("abc", 123)).id();
 | |
|         // world.clear();
 | |
|         // assert_eq!(
 | |
|         //     world.removed::<i32>(),
 | |
|         //     &[c, d],
 | |
|         //     "world clears result in 'removed component' states"
 | |
|         // );
 | |
|         // assert_eq!(
 | |
|         //     world.removed::<&'static str>(),
 | |
|         //     &[c, d, b],
 | |
|         //     "world clears result in 'removed component' states"
 | |
|         // );
 | |
|         // assert_eq!(
 | |
|         //     world.removed::<f64>(),
 | |
|         //     &[b],
 | |
|         //     "world clears result in 'removed component' states"
 | |
|         // );
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn added_tracking() {
 | |
|         let mut world = World::new();
 | |
|         let a = world.spawn(A(123)).id();
 | |
| 
 | |
|         assert_eq!(world.query::<&A>().iter(&world).count(), 1);
 | |
|         assert_eq!(
 | |
|             world.query_filtered::<(), Added<A>>().iter(&world).count(),
 | |
|             1
 | |
|         );
 | |
|         assert_eq!(world.query::<&A>().iter(&world).count(), 1);
 | |
|         assert_eq!(
 | |
|             world.query_filtered::<(), Added<A>>().iter(&world).count(),
 | |
|             1
 | |
|         );
 | |
|         assert!(world.query::<&A>().get(&world, a).is_ok());
 | |
|         assert!(world
 | |
|             .query_filtered::<(), Added<A>>()
 | |
|             .get(&world, a)
 | |
|             .is_ok());
 | |
|         assert!(world.query::<&A>().get(&world, a).is_ok());
 | |
|         assert!(world
 | |
|             .query_filtered::<(), Added<A>>()
 | |
|             .get(&world, a)
 | |
|             .is_ok());
 | |
| 
 | |
|         world.clear_trackers();
 | |
| 
 | |
|         assert_eq!(world.query::<&A>().iter(&world).count(), 1);
 | |
|         assert_eq!(
 | |
|             world.query_filtered::<(), Added<A>>().iter(&world).count(),
 | |
|             0
 | |
|         );
 | |
|         assert_eq!(world.query::<&A>().iter(&world).count(), 1);
 | |
|         assert_eq!(
 | |
|             world.query_filtered::<(), Added<A>>().iter(&world).count(),
 | |
|             0
 | |
|         );
 | |
|         assert!(world.query::<&A>().get(&world, a).is_ok());
 | |
|         assert!(world
 | |
|             .query_filtered::<(), Added<A>>()
 | |
|             .get(&world, a)
 | |
|             .is_err());
 | |
|         assert!(world.query::<&A>().get(&world, a).is_ok());
 | |
|         assert!(world
 | |
|             .query_filtered::<(), Added<A>>()
 | |
|             .get(&world, a)
 | |
|             .is_err());
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn added_queries() {
 | |
|         let mut world = World::default();
 | |
|         let e1 = world.spawn(A(0)).id();
 | |
| 
 | |
|         fn get_added<Com: Component>(world: &mut World) -> Vec<Entity> {
 | |
|             world
 | |
|                 .query_filtered::<Entity, Added<Com>>()
 | |
|                 .iter(world)
 | |
|                 .collect::<Vec<Entity>>()
 | |
|         }
 | |
| 
 | |
|         assert_eq!(get_added::<A>(&mut world), vec![e1]);
 | |
|         world.entity_mut(e1).insert(B(0));
 | |
|         assert_eq!(get_added::<A>(&mut world), vec![e1]);
 | |
|         assert_eq!(get_added::<B>(&mut world), vec![e1]);
 | |
| 
 | |
|         world.clear_trackers();
 | |
|         assert!(get_added::<A>(&mut world).is_empty());
 | |
|         let e2 = world.spawn((A(1), B(1))).id();
 | |
|         assert_eq!(get_added::<A>(&mut world), vec![e2]);
 | |
|         assert_eq!(get_added::<B>(&mut world), vec![e2]);
 | |
| 
 | |
|         let added = world
 | |
|             .query_filtered::<Entity, (Added<A>, Added<B>)>()
 | |
|             .iter(&world)
 | |
|             .collect::<Vec<Entity>>();
 | |
|         assert_eq!(added, vec![e2]);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn changed_trackers() {
 | |
|         let mut world = World::default();
 | |
|         let e1 = world.spawn((A(0), B(0))).id();
 | |
|         let e2 = world.spawn((A(0), B(0))).id();
 | |
|         let e3 = world.spawn((A(0), B(0))).id();
 | |
|         world.spawn((A(0), B(0)));
 | |
| 
 | |
|         world.clear_trackers();
 | |
| 
 | |
|         for (i, mut a) in world.query::<&mut A>().iter_mut(&mut world).enumerate() {
 | |
|             if i % 2 == 0 {
 | |
|                 a.0 += 1;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         fn get_filtered<F: ReadOnlyWorldQuery>(world: &mut World) -> Vec<Entity> {
 | |
|             world
 | |
|                 .query_filtered::<Entity, F>()
 | |
|                 .iter(world)
 | |
|                 .collect::<Vec<Entity>>()
 | |
|         }
 | |
| 
 | |
|         assert_eq!(get_filtered::<Changed<A>>(&mut world), vec![e1, e3]);
 | |
| 
 | |
|         // ensure changing an entity's archetypes also moves its changed state
 | |
|         world.entity_mut(e1).insert(C);
 | |
| 
 | |
|         assert_eq!(get_filtered::<Changed<A>>(&mut world), vec![e3, e1], "changed entities list should not change (although the order will due to archetype moves)");
 | |
| 
 | |
|         // spawning a new A entity should not change existing changed state
 | |
|         world.entity_mut(e1).insert((A(0), B(0)));
 | |
|         assert_eq!(
 | |
|             get_filtered::<Changed<A>>(&mut world),
 | |
|             vec![e3, e1],
 | |
|             "changed entities list should not change"
 | |
|         );
 | |
| 
 | |
|         // removing an unchanged entity should not change changed state
 | |
|         assert!(world.despawn(e2));
 | |
|         assert_eq!(
 | |
|             get_filtered::<Changed<A>>(&mut world),
 | |
|             vec![e3, e1],
 | |
|             "changed entities list should not change"
 | |
|         );
 | |
| 
 | |
|         // removing a changed entity should remove it from enumeration
 | |
|         assert!(world.despawn(e1));
 | |
|         assert_eq!(
 | |
|             get_filtered::<Changed<A>>(&mut world),
 | |
|             vec![e3],
 | |
|             "e1 should no longer be returned"
 | |
|         );
 | |
| 
 | |
|         world.clear_trackers();
 | |
| 
 | |
|         assert!(get_filtered::<Changed<A>>(&mut world).is_empty());
 | |
| 
 | |
|         let e4 = world.spawn_empty().id();
 | |
| 
 | |
|         world.entity_mut(e4).insert(A(0));
 | |
|         assert_eq!(get_filtered::<Changed<A>>(&mut world), vec![e4]);
 | |
|         assert_eq!(get_filtered::<Added<A>>(&mut world), vec![e4]);
 | |
| 
 | |
|         world.entity_mut(e4).insert(A(1));
 | |
|         assert_eq!(get_filtered::<Changed<A>>(&mut world), vec![e4]);
 | |
| 
 | |
|         world.clear_trackers();
 | |
| 
 | |
|         // ensure inserting multiple components set changed state for all components and set added
 | |
|         // state for non existing components even when changing archetype.
 | |
|         world.entity_mut(e4).insert((A(0), B(0)));
 | |
| 
 | |
|         assert!(get_filtered::<Added<A>>(&mut world).is_empty());
 | |
|         assert_eq!(get_filtered::<Changed<A>>(&mut world), vec![e4]);
 | |
|         assert_eq!(get_filtered::<Added<B>>(&mut world), vec![e4]);
 | |
|         assert_eq!(get_filtered::<Changed<B>>(&mut world), vec![e4]);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn changed_trackers_sparse() {
 | |
|         let mut world = World::default();
 | |
|         let e1 = world.spawn(SparseStored(0)).id();
 | |
|         let e2 = world.spawn(SparseStored(0)).id();
 | |
|         let e3 = world.spawn(SparseStored(0)).id();
 | |
|         world.spawn(SparseStored(0));
 | |
| 
 | |
|         world.clear_trackers();
 | |
| 
 | |
|         for (i, mut a) in world
 | |
|             .query::<&mut SparseStored>()
 | |
|             .iter_mut(&mut world)
 | |
|             .enumerate()
 | |
|         {
 | |
|             if i % 2 == 0 {
 | |
|                 a.0 += 1;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         fn get_filtered<F: ReadOnlyWorldQuery>(world: &mut World) -> Vec<Entity> {
 | |
|             world
 | |
|                 .query_filtered::<Entity, F>()
 | |
|                 .iter(world)
 | |
|                 .collect::<Vec<Entity>>()
 | |
|         }
 | |
| 
 | |
|         assert_eq!(
 | |
|             get_filtered::<Changed<SparseStored>>(&mut world),
 | |
|             vec![e1, e3]
 | |
|         );
 | |
| 
 | |
|         // ensure changing an entity's archetypes also moves its changed state
 | |
|         world.entity_mut(e1).insert(C);
 | |
| 
 | |
|         assert_eq!(get_filtered::<Changed<SparseStored>>(&mut world), vec![e3, e1], "changed entities list should not change (although the order will due to archetype moves)");
 | |
| 
 | |
|         // spawning a new SparseStored entity should not change existing changed state
 | |
|         world.entity_mut(e1).insert(SparseStored(0));
 | |
|         assert_eq!(
 | |
|             get_filtered::<Changed<SparseStored>>(&mut world),
 | |
|             vec![e3, e1],
 | |
|             "changed entities list should not change"
 | |
|         );
 | |
| 
 | |
|         // removing an unchanged entity should not change changed state
 | |
|         assert!(world.despawn(e2));
 | |
|         assert_eq!(
 | |
|             get_filtered::<Changed<SparseStored>>(&mut world),
 | |
|             vec![e3, e1],
 | |
|             "changed entities list should not change"
 | |
|         );
 | |
| 
 | |
|         // removing a changed entity should remove it from enumeration
 | |
|         assert!(world.despawn(e1));
 | |
|         assert_eq!(
 | |
|             get_filtered::<Changed<SparseStored>>(&mut world),
 | |
|             vec![e3],
 | |
|             "e1 should no longer be returned"
 | |
|         );
 | |
| 
 | |
|         world.clear_trackers();
 | |
| 
 | |
|         assert!(get_filtered::<Changed<SparseStored>>(&mut world).is_empty());
 | |
| 
 | |
|         let e4 = world.spawn_empty().id();
 | |
| 
 | |
|         world.entity_mut(e4).insert(SparseStored(0));
 | |
|         assert_eq!(get_filtered::<Changed<SparseStored>>(&mut world), vec![e4]);
 | |
|         assert_eq!(get_filtered::<Added<SparseStored>>(&mut world), vec![e4]);
 | |
| 
 | |
|         world.entity_mut(e4).insert(A(1));
 | |
|         assert_eq!(get_filtered::<Changed<SparseStored>>(&mut world), vec![e4]);
 | |
| 
 | |
|         world.clear_trackers();
 | |
| 
 | |
|         // ensure inserting multiple components set changed state for all components and set added
 | |
|         // state for non existing components even when changing archetype.
 | |
|         world.entity_mut(e4).insert(SparseStored(0));
 | |
| 
 | |
|         assert!(get_filtered::<Added<SparseStored>>(&mut world).is_empty());
 | |
|         assert_eq!(get_filtered::<Changed<SparseStored>>(&mut world), vec![e4]);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn empty_spawn() {
 | |
|         let mut world = World::default();
 | |
|         let e = world.spawn_empty().id();
 | |
|         let mut e_mut = world.entity_mut(e);
 | |
|         e_mut.insert(A(0));
 | |
|         assert_eq!(e_mut.get::<A>().unwrap(), &A(0));
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn reserve_and_spawn() {
 | |
|         let mut world = World::default();
 | |
|         let e = world.entities().reserve_entity();
 | |
|         world.flush();
 | |
|         let mut e_mut = world.entity_mut(e);
 | |
|         e_mut.insert(A(0));
 | |
|         assert_eq!(e_mut.get::<A>().unwrap(), &A(0));
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn changed_query() {
 | |
|         let mut world = World::default();
 | |
|         let e1 = world.spawn((A(0), B(0))).id();
 | |
| 
 | |
|         fn get_changed(world: &mut World) -> Vec<Entity> {
 | |
|             world
 | |
|                 .query_filtered::<Entity, Changed<A>>()
 | |
|                 .iter(world)
 | |
|                 .collect::<Vec<Entity>>()
 | |
|         }
 | |
|         assert_eq!(get_changed(&mut world), vec![e1]);
 | |
|         world.clear_trackers();
 | |
|         assert_eq!(get_changed(&mut world), vec![]);
 | |
|         *world.get_mut(e1).unwrap() = A(1);
 | |
|         assert_eq!(get_changed(&mut world), vec![e1]);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn resource() {
 | |
|         use crate::system::Resource;
 | |
| 
 | |
|         #[derive(Resource, PartialEq, Debug)]
 | |
|         struct Num(i32);
 | |
| 
 | |
|         #[derive(Resource, PartialEq, Debug)]
 | |
|         struct BigNum(u64);
 | |
| 
 | |
|         let mut world = World::default();
 | |
|         assert!(world.get_resource::<Num>().is_none());
 | |
|         assert!(!world.contains_resource::<Num>());
 | |
|         assert!(!world.is_resource_added::<Num>());
 | |
|         assert!(!world.is_resource_changed::<Num>());
 | |
| 
 | |
|         world.insert_resource(Num(123));
 | |
|         let resource_id = world
 | |
|             .components()
 | |
|             .get_resource_id(TypeId::of::<Num>())
 | |
|             .unwrap();
 | |
|         let archetype_component_id = world.storages().resources.get(resource_id).unwrap().id();
 | |
| 
 | |
|         assert_eq!(world.resource::<Num>().0, 123);
 | |
|         assert!(world.contains_resource::<Num>());
 | |
|         assert!(world.is_resource_added::<Num>());
 | |
|         assert!(world.is_resource_changed::<Num>());
 | |
| 
 | |
|         world.insert_resource(BigNum(456));
 | |
|         assert_eq!(world.resource::<BigNum>().0, 456u64);
 | |
| 
 | |
|         world.insert_resource(BigNum(789));
 | |
|         assert_eq!(world.resource::<BigNum>().0, 789);
 | |
| 
 | |
|         {
 | |
|             let mut value = world.resource_mut::<BigNum>();
 | |
|             assert_eq!(value.0, 789);
 | |
|             value.0 = 10;
 | |
|         }
 | |
| 
 | |
|         assert_eq!(
 | |
|             world.resource::<BigNum>().0,
 | |
|             10,
 | |
|             "resource changes are preserved"
 | |
|         );
 | |
| 
 | |
|         assert_eq!(
 | |
|             world.remove_resource::<BigNum>(),
 | |
|             Some(BigNum(10)),
 | |
|             "removed resource has the correct value"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             world.get_resource::<BigNum>(),
 | |
|             None,
 | |
|             "removed resource no longer exists"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             world.remove_resource::<BigNum>(),
 | |
|             None,
 | |
|             "double remove returns nothing"
 | |
|         );
 | |
| 
 | |
|         world.insert_resource(BigNum(1));
 | |
|         assert_eq!(
 | |
|             world.get_resource::<BigNum>(),
 | |
|             Some(&BigNum(1)),
 | |
|             "re-inserting resources works"
 | |
|         );
 | |
| 
 | |
|         assert_eq!(
 | |
|             world.get_resource::<Num>(),
 | |
|             Some(&Num(123)),
 | |
|             "other resources are unaffected"
 | |
|         );
 | |
| 
 | |
|         let current_resource_id = world
 | |
|             .components()
 | |
|             .get_resource_id(TypeId::of::<Num>())
 | |
|             .unwrap();
 | |
|         assert_eq!(
 | |
|             resource_id, current_resource_id,
 | |
|             "resource id does not change after removing / re-adding"
 | |
|         );
 | |
| 
 | |
|         let current_archetype_component_id =
 | |
|             world.storages().resources.get(resource_id).unwrap().id();
 | |
| 
 | |
|         assert_eq!(
 | |
|             archetype_component_id, current_archetype_component_id,
 | |
|             "resource archetype component id does not change after removing / re-adding"
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn remove() {
 | |
|         let mut world = World::default();
 | |
|         let e1 = world.spawn((A(1), B(1), TableStored("a"))).id();
 | |
| 
 | |
|         let mut e = world.entity_mut(e1);
 | |
|         assert_eq!(e.get::<TableStored>(), Some(&TableStored("a")));
 | |
|         assert_eq!(e.get::<A>(), Some(&A(1)));
 | |
|         assert_eq!(e.get::<B>(), Some(&B(1)));
 | |
|         assert_eq!(
 | |
|             e.get::<C>(),
 | |
|             None,
 | |
|             "C is not in the entity, so it should not exist"
 | |
|         );
 | |
| 
 | |
|         e.remove::<(A, B, C)>();
 | |
|         assert_eq!(
 | |
|             e.get::<TableStored>(),
 | |
|             Some(&TableStored("a")),
 | |
|             "TableStored is not in the removed bundle, so it should exist"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             e.get::<A>(),
 | |
|             None,
 | |
|             "Num is in the removed bundle, so it should not exist"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             e.get::<B>(),
 | |
|             None,
 | |
|             "f64 is in the removed bundle, so it should not exist"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             e.get::<C>(),
 | |
|             None,
 | |
|             "usize is in the removed bundle, so it should not exist"
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn take() {
 | |
|         let mut world = World::default();
 | |
|         world.spawn((A(1), B(1), TableStored("1")));
 | |
|         let e2 = world.spawn((A(2), B(2), TableStored("2"))).id();
 | |
|         world.spawn((A(3), B(3), TableStored("3")));
 | |
| 
 | |
|         let mut query = world.query::<(&B, &TableStored)>();
 | |
|         let results = query
 | |
|             .iter(&world)
 | |
|             .map(|(a, b)| (a.0, b.0))
 | |
|             .collect::<Vec<_>>();
 | |
|         assert_eq!(results, vec![(1, "1"), (2, "2"), (3, "3"),]);
 | |
| 
 | |
|         let removed_bundle = world.entity_mut(e2).take::<(B, TableStored)>().unwrap();
 | |
|         assert_eq!(removed_bundle, (B(2), TableStored("2")));
 | |
| 
 | |
|         let results = query
 | |
|             .iter(&world)
 | |
|             .map(|(a, b)| (a.0, b.0))
 | |
|             .collect::<Vec<_>>();
 | |
|         assert_eq!(results, vec![(1, "1"), (3, "3"),]);
 | |
| 
 | |
|         let mut a_query = world.query::<&A>();
 | |
|         let results = a_query.iter(&world).map(|a| a.0).collect::<Vec<_>>();
 | |
|         assert_eq!(results, vec![1, 3, 2]);
 | |
| 
 | |
|         let entity_ref = world.entity(e2);
 | |
|         assert_eq!(
 | |
|             entity_ref.get::<A>(),
 | |
|             Some(&A(2)),
 | |
|             "A is not in the removed bundle, so it should exist"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             entity_ref.get::<B>(),
 | |
|             None,
 | |
|             "B is in the removed bundle, so it should not exist"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             entity_ref.get::<TableStored>(),
 | |
|             None,
 | |
|             "TableStored is in the removed bundle, so it should not exist"
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn non_send_resource() {
 | |
|         let mut world = World::default();
 | |
|         world.insert_non_send_resource(123i32);
 | |
|         world.insert_non_send_resource(456i64);
 | |
|         assert_eq!(*world.non_send_resource::<i32>(), 123);
 | |
|         assert_eq!(*world.non_send_resource_mut::<i64>(), 456);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn non_send_resource_points_to_distinct_data() {
 | |
|         let mut world = World::default();
 | |
|         world.insert_resource(A(123));
 | |
|         world.insert_non_send_resource(A(456));
 | |
|         assert_eq!(*world.resource::<A>(), A(123));
 | |
|         assert_eq!(*world.non_send_resource::<A>(), A(456));
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     #[should_panic]
 | |
|     fn non_send_resource_panic() {
 | |
|         let mut world = World::default();
 | |
|         world.insert_non_send_resource(0i32);
 | |
|         std::thread::spawn(move || {
 | |
|             let _ = world.non_send_resource_mut::<i32>();
 | |
|         })
 | |
|         .join()
 | |
|         .unwrap();
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn exact_size_query() {
 | |
|         let mut world = World::default();
 | |
|         world.spawn((A(0), B(0)));
 | |
|         world.spawn((A(0), B(0)));
 | |
|         world.spawn((A(0), B(0), C));
 | |
|         world.spawn(C);
 | |
| 
 | |
|         let mut query = world.query::<(&A, &B)>();
 | |
|         assert_eq!(query.iter(&world).len(), 3);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     #[should_panic]
 | |
|     fn duplicate_components_panic() {
 | |
|         let mut world = World::new();
 | |
|         world.spawn((A(1), A(2)));
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     #[should_panic]
 | |
|     fn ref_and_mut_query_panic() {
 | |
|         let mut world = World::new();
 | |
|         world.query::<(&A, &mut A)>();
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     #[should_panic]
 | |
|     fn entity_ref_and_mut_query_panic() {
 | |
|         let mut world = World::new();
 | |
|         world.query::<(EntityRef, &mut A)>();
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     #[should_panic]
 | |
|     fn mut_and_ref_query_panic() {
 | |
|         let mut world = World::new();
 | |
|         world.query::<(&mut A, &A)>();
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     #[should_panic]
 | |
|     fn mut_and_entity_ref_query_panic() {
 | |
|         let mut world = World::new();
 | |
|         world.query::<(&mut A, EntityRef)>();
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     #[should_panic]
 | |
|     fn mut_and_mut_query_panic() {
 | |
|         let mut world = World::new();
 | |
|         world.query::<(&mut A, &mut A)>();
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     #[should_panic]
 | |
|     fn multiple_worlds_same_query_iter() {
 | |
|         let mut world_a = World::new();
 | |
|         let world_b = World::new();
 | |
|         let mut query = world_a.query::<&A>();
 | |
|         query.iter(&world_a);
 | |
|         query.iter(&world_b);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn query_filters_dont_collide_with_fetches() {
 | |
|         let mut world = World::new();
 | |
|         world.query_filtered::<&mut A, Changed<A>>();
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn filtered_query_access() {
 | |
|         let mut world = World::new();
 | |
|         let query = world.query_filtered::<&mut A, Changed<B>>();
 | |
| 
 | |
|         let mut expected = FilteredAccess::<ComponentId>::default();
 | |
|         let a_id = world.components.get_id(TypeId::of::<A>()).unwrap();
 | |
|         let b_id = world.components.get_id(TypeId::of::<B>()).unwrap();
 | |
|         expected.add_write(a_id);
 | |
|         expected.add_read(b_id);
 | |
|         assert!(
 | |
|             query.component_access.eq(&expected),
 | |
|             "ComponentId access from query fetch and query filter should be combined"
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     #[should_panic]
 | |
|     fn multiple_worlds_same_query_get() {
 | |
|         let mut world_a = World::new();
 | |
|         let world_b = World::new();
 | |
|         let mut query = world_a.query::<&A>();
 | |
|         let _ = query.get(&world_a, Entity::from_raw(0));
 | |
|         let _ = query.get(&world_b, Entity::from_raw(0));
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     #[should_panic]
 | |
|     fn multiple_worlds_same_query_for_each() {
 | |
|         let mut world_a = World::new();
 | |
|         let world_b = World::new();
 | |
|         let mut query = world_a.query::<&A>();
 | |
|         query.for_each(&world_a, |_| {});
 | |
|         query.for_each(&world_b, |_| {});
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn resource_scope() {
 | |
|         let mut world = World::default();
 | |
|         world.insert_resource(A(0));
 | |
|         world.resource_scope(|world: &mut World, mut value: Mut<A>| {
 | |
|             value.0 += 1;
 | |
|             assert!(!world.contains_resource::<A>());
 | |
|         });
 | |
|         assert_eq!(world.resource::<A>().0, 1);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     #[should_panic(
 | |
|         expected = "Attempted to access or drop non-send resource bevy_ecs::tests::NonSendA from thread"
 | |
|     )]
 | |
|     fn non_send_resource_drop_from_different_thread() {
 | |
|         let mut world = World::default();
 | |
|         world.insert_non_send_resource(NonSendA::default());
 | |
| 
 | |
|         let thread = std::thread::spawn(move || {
 | |
|             // Dropping the non-send resource on a different thread
 | |
|             // Should result in a panic
 | |
|             drop(world);
 | |
|         });
 | |
| 
 | |
|         if let Err(err) = thread.join() {
 | |
|             std::panic::resume_unwind(err);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn non_send_resource_drop_from_same_thread() {
 | |
|         let mut world = World::default();
 | |
|         world.insert_non_send_resource(NonSendA::default());
 | |
|         drop(world);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn insert_overwrite_drop() {
 | |
|         let (dropck1, dropped1) = DropCk::new_pair();
 | |
|         let (dropck2, dropped2) = DropCk::new_pair();
 | |
|         let mut world = World::default();
 | |
|         world.spawn(dropck1).insert(dropck2);
 | |
|         assert_eq!(dropped1.load(Ordering::Relaxed), 1);
 | |
|         assert_eq!(dropped2.load(Ordering::Relaxed), 0);
 | |
|         drop(world);
 | |
|         assert_eq!(dropped1.load(Ordering::Relaxed), 1);
 | |
|         assert_eq!(dropped2.load(Ordering::Relaxed), 1);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn insert_overwrite_drop_sparse() {
 | |
|         let (dropck1, dropped1) = DropCk::new_pair();
 | |
|         let (dropck2, dropped2) = DropCk::new_pair();
 | |
|         let mut world = World::default();
 | |
| 
 | |
|         world
 | |
|             .spawn(DropCkSparse(dropck1))
 | |
|             .insert(DropCkSparse(dropck2));
 | |
|         assert_eq!(dropped1.load(Ordering::Relaxed), 1);
 | |
|         assert_eq!(dropped2.load(Ordering::Relaxed), 0);
 | |
|         drop(world);
 | |
|         assert_eq!(dropped1.load(Ordering::Relaxed), 1);
 | |
|         assert_eq!(dropped2.load(Ordering::Relaxed), 1);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn clear_entities() {
 | |
|         let mut world = World::default();
 | |
| 
 | |
|         world.insert_resource(A(0));
 | |
|         world.spawn(A(1));
 | |
|         world.spawn(SparseStored(1));
 | |
| 
 | |
|         let mut q1 = world.query::<&A>();
 | |
|         let mut q2 = world.query::<&SparseStored>();
 | |
| 
 | |
|         assert_eq!(q1.iter(&world).len(), 1);
 | |
|         assert_eq!(q2.iter(&world).len(), 1);
 | |
|         assert_eq!(world.entities().len(), 2);
 | |
| 
 | |
|         world.clear_entities();
 | |
| 
 | |
|         assert_eq!(
 | |
|             q1.iter(&world).len(),
 | |
|             0,
 | |
|             "world should not contain table components"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             q2.iter(&world).len(),
 | |
|             0,
 | |
|             "world should not contain sparse set components"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             world.entities().len(),
 | |
|             0,
 | |
|             "world should not have any entities"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             world.resource::<A>().0,
 | |
|             0,
 | |
|             "world should still contain resources"
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn test_is_archetypal_size_hints() {
 | |
|         let mut world = World::default();
 | |
|         macro_rules! query_min_size {
 | |
|             ($query:ty, $filter:ty) => {
 | |
|                 world
 | |
|                     .query_filtered::<$query, $filter>()
 | |
|                     .iter(&world)
 | |
|                     .size_hint()
 | |
|                     .0
 | |
|             };
 | |
|         }
 | |
| 
 | |
|         world.spawn((A(1), B(1), C));
 | |
|         world.spawn((A(1), C));
 | |
|         world.spawn((A(1), B(1)));
 | |
|         world.spawn((B(1), C));
 | |
|         world.spawn(A(1));
 | |
|         world.spawn(C);
 | |
|         assert_eq!(2, query_min_size![(), (With<A>, Without<B>)],);
 | |
|         assert_eq!(3, query_min_size![&B, Or<(With<A>, With<C>)>],);
 | |
|         assert_eq!(1, query_min_size![&B, (With<A>, With<C>)],);
 | |
|         assert_eq!(1, query_min_size![(&A, &B), With<C>],);
 | |
|         assert_eq!(4, query_min_size![&A, ()], "Simple Archetypal");
 | |
|         assert_eq!(4, query_min_size![Ref<A>, ()],);
 | |
|         // All the following should set minimum size to 0, as it's impossible to predict
 | |
|         // how many entities the filters will trim.
 | |
|         assert_eq!(0, query_min_size![(), Added<A>], "Simple Added");
 | |
|         assert_eq!(0, query_min_size![(), Changed<A>], "Simple Changed");
 | |
|         assert_eq!(0, query_min_size![(&A, &B), Changed<A>],);
 | |
|         assert_eq!(0, query_min_size![&A, (Changed<A>, With<B>)],);
 | |
|         assert_eq!(0, query_min_size![(&A, &B), Or<(Changed<A>, Changed<B>)>],);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn reserve_entities_across_worlds() {
 | |
|         let mut world_a = World::default();
 | |
|         let mut world_b = World::default();
 | |
| 
 | |
|         let e1 = world_a.spawn(A(1)).id();
 | |
|         let e2 = world_a.spawn(A(2)).id();
 | |
|         let e3 = world_a.entities().reserve_entity();
 | |
|         world_a.flush();
 | |
| 
 | |
|         let world_a_max_entities = world_a.entities().len();
 | |
|         world_b.entities.reserve_entities(world_a_max_entities);
 | |
|         world_b.entities.flush_as_invalid();
 | |
| 
 | |
|         let e4 = world_b.spawn(A(4)).id();
 | |
|         assert_eq!(
 | |
|             e4,
 | |
|             Entity::new(3, 0),
 | |
|             "new entity is created immediately after world_a's max entity"
 | |
|         );
 | |
|         assert!(world_b.get::<A>(e1).is_none());
 | |
|         assert!(world_b.get_entity(e1).is_none());
 | |
| 
 | |
|         assert!(world_b.get::<A>(e2).is_none());
 | |
|         assert!(world_b.get_entity(e2).is_none());
 | |
| 
 | |
|         assert!(world_b.get::<A>(e3).is_none());
 | |
|         assert!(world_b.get_entity(e3).is_none());
 | |
| 
 | |
|         world_b.get_or_spawn(e1).unwrap().insert(B(1));
 | |
|         assert_eq!(
 | |
|             world_b.get::<B>(e1),
 | |
|             Some(&B(1)),
 | |
|             "spawning into 'world_a' entities works"
 | |
|         );
 | |
| 
 | |
|         world_b.get_or_spawn(e4).unwrap().insert(B(4));
 | |
|         assert_eq!(
 | |
|             world_b.get::<B>(e4),
 | |
|             Some(&B(4)),
 | |
|             "spawning into existing `world_b` entities works"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             world_b.get::<A>(e4),
 | |
|             Some(&A(4)),
 | |
|             "spawning into existing `world_b` entities works"
 | |
|         );
 | |
| 
 | |
|         let e4_mismatched_generation = Entity::new(3, 1);
 | |
|         assert!(
 | |
|             world_b.get_or_spawn(e4_mismatched_generation).is_none(),
 | |
|             "attempting to spawn on top of an entity with a mismatched entity generation fails"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             world_b.get::<B>(e4),
 | |
|             Some(&B(4)),
 | |
|             "failed mismatched spawn doesn't change existing entity"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             world_b.get::<A>(e4),
 | |
|             Some(&A(4)),
 | |
|             "failed mismatched spawn doesn't change existing entity"
 | |
|         );
 | |
| 
 | |
|         let high_non_existent_entity = Entity::new(6, 0);
 | |
|         world_b
 | |
|             .get_or_spawn(high_non_existent_entity)
 | |
|             .unwrap()
 | |
|             .insert(B(10));
 | |
|         assert_eq!(
 | |
|             world_b.get::<B>(high_non_existent_entity),
 | |
|             Some(&B(10)),
 | |
|             "inserting into newly allocated high / non-continuous entity id works"
 | |
|         );
 | |
| 
 | |
|         let high_non_existent_but_reserved_entity = Entity::new(5, 0);
 | |
|         assert!(
 | |
|             world_b.get_entity(high_non_existent_but_reserved_entity).is_none(),
 | |
|             "entities between high-newly allocated entity and continuous block of existing entities don't exist"
 | |
|         );
 | |
| 
 | |
|         let reserved_entities = vec![
 | |
|             world_b.entities().reserve_entity(),
 | |
|             world_b.entities().reserve_entity(),
 | |
|             world_b.entities().reserve_entity(),
 | |
|             world_b.entities().reserve_entity(),
 | |
|         ];
 | |
| 
 | |
|         assert_eq!(
 | |
|             reserved_entities,
 | |
|             vec![
 | |
|                 Entity::new(5, 0),
 | |
|                 Entity::new(4, 0),
 | |
|                 Entity::new(7, 0),
 | |
|                 Entity::new(8, 0),
 | |
|             ],
 | |
|             "space between original entities and high entities is used for new entity ids"
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn insert_or_spawn_batch() {
 | |
|         let mut world = World::default();
 | |
|         let e0 = world.spawn(A(0)).id();
 | |
|         let e1 = Entity::from_raw(1);
 | |
| 
 | |
|         let values = vec![(e0, (B(0), C)), (e1, (B(1), C))];
 | |
| 
 | |
|         world.insert_or_spawn_batch(values).unwrap();
 | |
| 
 | |
|         assert_eq!(
 | |
|             world.get::<A>(e0),
 | |
|             Some(&A(0)),
 | |
|             "existing component was preserved"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             world.get::<B>(e0),
 | |
|             Some(&B(0)),
 | |
|             "pre-existing entity received correct B component"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             world.get::<B>(e1),
 | |
|             Some(&B(1)),
 | |
|             "new entity was spawned and received correct B component"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             world.get::<C>(e0),
 | |
|             Some(&C),
 | |
|             "pre-existing entity received C component"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             world.get::<C>(e1),
 | |
|             Some(&C),
 | |
|             "new entity was spawned and received C component"
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn insert_or_spawn_batch_invalid() {
 | |
|         let mut world = World::default();
 | |
|         let e0 = world.spawn(A(0)).id();
 | |
|         let e1 = Entity::from_raw(1);
 | |
|         let e2 = world.spawn_empty().id();
 | |
|         let invalid_e2 = Entity::new(e2.index(), 1);
 | |
| 
 | |
|         let values = vec![(e0, (B(0), C)), (e1, (B(1), C)), (invalid_e2, (B(2), C))];
 | |
| 
 | |
|         let result = world.insert_or_spawn_batch(values);
 | |
| 
 | |
|         assert_eq!(
 | |
|             result,
 | |
|             Err(vec![invalid_e2]),
 | |
|             "e2 failed to be spawned or inserted into"
 | |
|         );
 | |
| 
 | |
|         assert_eq!(
 | |
|             world.get::<A>(e0),
 | |
|             Some(&A(0)),
 | |
|             "existing component was preserved"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             world.get::<B>(e0),
 | |
|             Some(&B(0)),
 | |
|             "pre-existing entity received correct B component"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             world.get::<B>(e1),
 | |
|             Some(&B(1)),
 | |
|             "new entity was spawned and received correct B component"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             world.get::<C>(e0),
 | |
|             Some(&C),
 | |
|             "pre-existing entity received C component"
 | |
|         );
 | |
|         assert_eq!(
 | |
|             world.get::<C>(e1),
 | |
|             Some(&C),
 | |
|             "new entity was spawned and received C component"
 | |
|         );
 | |
|     }
 | |
| }
 | 
