1#](https://github.com/bevyengine/bevy#license)\n[](https://crates.io/crates/bevy_ecs)\n[](https://crates.io/crates/bevy_ecs)\n[](https://docs.rs/bevy_ecs/latest/bevy_ecs/)\n[](https://discord.gg/bevy)\n\n## What is Bevy ECS?\n\nBevy ECS is an Entity Component System custom-built for the [Bevy][bevy] game engine.\nIt aims to be simple to use, ergonomic, fast, massively parallel, opinionated, and featureful.\nIt was created specifically for Bevy\'s needs, but it can easily be used as a standalone crate in other projects.\n\n## ECS\n\nAll app logic in Bevy uses the Entity Component System paradigm, which is often shortened to ECS. ECS is a software pattern that involves breaking your program up into Entities, Components, and Systems. Entities are unique \"things\" that are assigned groups of Components, which are then processed using Systems.\n\nFor example, one entity might have a `Position` and `Velocity` component, whereas another entity might have a `Position` and `UI` component. You might have a movement system that runs on all entities with a Position and Velocity component.\n\nThe ECS pattern encourages clean, decoupled designs by forcing you to break up your app data and logic into its core components. It also helps make your code faster by optimizing memory access patterns and making parallelism easier.\n\n## Concepts\n\nBevy ECS is Bevy\'s implementation of the ECS pattern. Unlike other Rust ECS implementations, which often require complex lifetimes, traits, builder patterns, or macros, Bevy ECS uses normal Rust data types for all of these concepts:\n\n### Components\n\nComponents are normal Rust structs. They are data stored in a `World` and specific instances of Components correlate to Entities.\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Component)]\nstruct Position { x: f32, y: f32 }\n```\n\n### Worlds\n\nEntities, Components, and Resources are stored in a `World`. Worlds, much like `std::collections`\'s `HashSet` and `Vec`, expose operations to insert, read, write, and remove the data they store.\n\n```rust\nuse bevy_ecs::world::World;\n\nlet world = World::default();\n```\n\n### Entities\n\nEntities are unique identifiers that correlate to zero or more Components.\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Component)]\nstruct Position { x: f32, y: f32 }\n#[derive(Component)]\nstruct Velocity { x: f32, y: f32 }\n\nlet mut world = World::new();\n\nlet entity = world\n .spawn((Position { x: 0.0, y: 0.0 }, Velocity { x: 1.0, y: 0.0 }))\n .id();\n\nlet entity_ref = world.entity(entity);\nlet position = entity_ref.get::<Position>().unwrap();\nlet velocity = entity_ref.get::<Velocity>().unwrap();\n```\n\n### Systems\n\nSystems are normal Rust functions. Thanks to the Rust type system, Bevy ECS can use function parameter types to determine what data needs to be sent to the system. It also uses this \"data access\" information to determine what Systems can run in parallel with each other.\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Component)]\nstruct Position { x: f32, y: f32 }\n\nfn print_position(query: Query<(Entity, &Position)>) {\n for (entity, position) in &query {\n println!(\"Entity {} is at position: x {}, y {}\", entity, position.x, position.y);\n }\n}\n```\n\n### Resources\n\nApps often require unique resources, such as asset collections, renderers, audio servers, time, etc. Bevy ECS makes this pattern a first class citizen. `Resource` is a special kind of component that does not belong to any entity. Instead, it is identified uniquely by its type:\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Resource, Default)]\nstruct Time {\n seconds: f32,\n}\n\nlet mut world = World::new();\n\nworld.insert_resource(Time::default());\n\nlet time = world.get_resource::<Time>().unwrap();\n\n// You can also access resources from Systems\nfn print_time(time: Res<Time>) {\n println!(\"{}\", time.seconds);\n}\n```\n\n### Schedules\n\nSchedules run a set of Systems according to some execution strategy.\nSystems can be added to any number of System Sets, which are used to control their scheduling metadata.\n\nThe built in \"parallel executor\" considers dependencies between systems and (by default) run as many of them in parallel as possible. This maximizes performance, while keeping the system execution safe. To control the system ordering, define explicit dependencies between systems and their sets.\n\n## Using Bevy ECS\n\nBevy ECS should feel very natural for those familiar with Rust syntax:\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Component)]\nstruct Position { x: f32, y: f32 }\n#[derive(Component)]\nstruct Velocity { x: f32, y: f32 }\n\n// This system moves each entity with a Position and Velocity component\nfn movement(mut query: Query<(&mut Position, &Velocity)>) {\n for (mut position, velocity) in &mut query {\n position.x += velocity.x;\n position.y += velocity.y;\n }\n}\n\nfn main() {\n // Create a new empty World to hold our Entities and Components\n let mut world = World::new();\n\n // Spawn an entity with Position and Velocity components\n world.spawn((\n Position { x: 0.0, y: 0.0 },\n Velocity { x: 1.0, y: 0.0 },\n ));\n\n // Create a new Schedule, which defines an execution strategy for Systems\n let mut schedule = Schedule::default();\n\n // Add our system to the schedule\n schedule.add_systems(movement);\n\n // Run the schedule once. If your app has a \"loop\", you would run this once per loop\n schedule.run(&mut world);\n}\n```\n\n## Features\n\n### Query Filters\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Component)]\nstruct Position { x: f32, y: f32 }\n#[derive(Component)]\nstruct Player;\n#[derive(Component)]\nstruct Alive;\n\n// Gets the Position component of all Entities with Player component and without the Alive\n// component.\nfn system(query: Query<&Position, (With<Player>, Without<Alive>)>) {\n for position in &query {\n }\n}\n```\n\n### Change Detection\n\nBevy ECS tracks _all_ changes to Components and Resources.\n\nQueries can filter for changed Components:\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Component)]\nstruct Position { x: f32, y: f32 }\n#[derive(Component)]\nstruct Velocity { x: f32, y: f32 }\n\n// Gets the Position component of all Entities whose Velocity has changed since the last run of the System\nfn system_changed(query: Query<&Position, Changed<Velocity>>) {\n for position in &query {\n }\n}\n\n// Gets the Position component of all Entities that had a Velocity component added since the last run of the System\nfn system_added(query: Query<&Position, Added<Velocity>>) {\n for position in &query {\n }\n}\n```\n\nResources also expose change state:\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Resource)]\nstruct Time(f32);\n\n// Prints \"time changed!\" if the Time resource has changed since the last run of the System\nfn system(time: Res<Time>) {\n if time.is_changed() {\n println!(\"time changed!\");\n }\n}\n```\n\n### Component Storage\n\nBevy ECS supports multiple component storage types.\n\nComponents can be stored in:\n\n* **Tables**: Fast and cache friendly iteration, but slower adding and removing of components. This is the default storage type.\n* **Sparse Sets**: Fast adding and removing of components, but slower iteration.\n\nComponent storage types are configurable, and they default to table storage if the storage is not manually defined.\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Component)]\nstruct TableStoredComponent;\n\n#[derive(Component)]\n#[component(storage = \"SparseSet\")]\nstruct SparseStoredComponent;\n```\n\n### Component Bundles\n\nDefine sets of Components that should be added together.\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Default, Component)]\nstruct Player;\n#[derive(Default, Component)]\nstruct Position { x: f32, y: f32 }\n#[derive(Default, Component)]\nstruct Velocity { x: f32, y: f32 }\n\n#[derive(Bundle, Default)]\nstruct PlayerBundle {\n player: Player,\n position: Position,\n velocity: Velocity,\n}\n\nlet mut world = World::new();\n\n// Spawn a new entity and insert the default PlayerBundle\nworld.spawn(PlayerBundle::default());\n\n// Bundles play well with Rust\'s struct update syntax\nworld.spawn(PlayerBundle {\n position: Position { x: 1.0, y: 1.0 },\n ..Default::default()\n});\n```\n\n### Messages\n\nMessages offer a communication channel between one or more systems.\nThey can be sent using the `MessageWriter` system parameter and received with `MessageReader`.\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Message)]\nstruct Message(String);\n\nfn writer(mut writer: MessageWriter<Message>) {\n writer.write(Message(\"Hello!\".to_string()));\n}\n\nfn reader(mut reader: MessageReader<Message>) {\n for Message(message) in reader.read() {\n println!(\"{}\", message);\n }\n}\n```\n\n### Observers\n\nObservers are systems that watch for a \"trigger\" of a specific `Event`:\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(Event)]\nstruct Speak {\n message: String\n}\n\nlet mut world = World::new();\n\nworld.add_observer(|event: On<Speak>| {\n println!(\"{}\", event.message);\n});\n\nworld.trigger(Speak {\n message: \"Hello!\".to_string(),\n});\n```\n\nThese differ from `MessageReader` and `MessageWriter` in that they are \"reactive\".\nRather than happening at a specific point in a schedule, they happen _immediately_ whenever a trigger happens.\nTriggers can trigger other triggers, and they all will be evaluated at the same time!\n\nIf the event is an `EntityEvent`, it can also be triggered to target specific entities:\n\n```rust\nuse bevy_ecs::prelude::*;\n\n#[derive(EntityEvent)]\nstruct Explode {\n entity: Entity,\n}\n\nlet mut world = World::new();\nlet entity = world.spawn_empty().id();\n\nworld.add_observer(|explode: On<Explode>, mut commands: Commands| {\n println!(\"Entity {} goes BOOM!\", explode.entity);\n commands.entity(explode.entity).despawn();\n});\n\nworld.trigger(Explode { entity });\n```\n\n[bevy]: https://bevy.org/\n"include_str!("../README.md")]
2#![cfg_attr(
3 any(docsrs, docsrs_dep),
4 expect(
5 internal_features,
6 reason = "rustdoc_internals is needed for fake_variadic"
7 )
8)]
9#![cfg_attr(any(docsrs, docsrs_dep), feature(rustdoc_internals))]
10#![cfg_attr(docsrs, feature(doc_cfg))]
11#![expect(unsafe_code, reason = "Unsafe code is used to improve performance.")]
12#![doc(
13 html_logo_url = "https://bevy.org/assets/icon.png",
14 html_favicon_url = "https://bevy.org/assets/icon.png"
15)]
16#![no_std]
17
18#[cfg(feature = "std")]
19extern crate std;
20
21#[cfg(target_pointer_width = "16")]
22compile_error!("bevy_ecs cannot safely compile for a 16-bit platform.");
23
24extern crate alloc;
25
26extern crate self as bevy_ecs;
28
29pub mod archetype;
30pub mod batching;
31pub mod bundle;
32pub mod change_detection;
33pub mod component;
34pub mod entity;
35pub mod entity_disabling;
36pub mod error;
37pub mod event;
38pub mod hierarchy;
39pub mod intern;
40pub mod label;
41pub mod lifecycle;
42pub mod message;
43pub mod name;
44pub mod never;
45pub mod observer;
46pub mod query;
47#[cfg(feature = "bevy_reflect")]
48pub mod reflect;
49pub mod relationship;
50pub mod resource;
51pub mod schedule;
52pub mod spawn;
53pub mod storage;
54pub mod system;
55pub mod template;
56pub mod traversal;
57pub mod world;
58
59pub use bevy_ptr as ptr;
60
61#[cfg(feature = "hotpatching")]
62use message::Message;
63
64pub mod prelude {
68 #[doc(hidden)]
69 pub use crate::{
70 bundle::Bundle,
71 change_detection::{
72 ContiguousMut, ContiguousRef, DetectChanges, DetectChangesMut, Mut, Ref,
73 },
74 children,
75 component::Component,
76 entity::{ContainsEntity, Entity, EntityMapper},
77 error::{BevyError, Result, ResultSeverityExt, Severity},
78 event::{EntityEvent, Event},
79 hierarchy::{ChildOf, ChildSpawner, ChildSpawnerCommands, Children},
80 lifecycle::{Add, Despawn, Discard, Insert, Remove, RemovedComponents},
81 message::{
82 Message, MessageMutator, MessageReader, MessageWriter, Messages, PopulatedMessageReader,
83 },
84 name::{Name, NameOrEntity},
85 observer::{Observer, ObserverSystemExt, On},
86 query::{Added, Allow, AnyOf, Changed, Has, Or, QueryBuilder, QueryState, With, Without},
87 related,
88 relationship::RelationshipTarget,
89 resource::Resource,
90 schedule::{
91 common_conditions::*, ApplyDeferred, IntoScheduleConfigs, IntoSystemSet, Schedule,
92 Schedules, SystemCondition, SystemSet,
93 },
94 spawn::{Spawn, SpawnIter, SpawnRelated, SpawnWith, WithOneRelated, WithRelated},
95 system::{
96 Command, Commands, Deferred, EntityCommand, EntityCommands, If, In, InMut, InRef,
97 IntoSystem, Local, NonSend, NonSendMut, ParamSet, Populated, Query, ReadOnlySystem,
98 Res, ResMut, Single, System, SystemIn, SystemInput, SystemParamBuilder,
99 SystemParamFunction,
100 },
101 template::{template, FromTemplate, Template},
102 world::{
103 EntityMut, EntityRef, EntityWorldMut, FilteredResources, FilteredResourcesMut,
104 FromWorld, World,
105 },
106 };
107
108 #[doc(hidden)]
109 #[cfg(feature = "std")]
110 pub use crate::system::ParallelCommands;
111
112 #[doc(hidden)]
113 #[cfg(feature = "bevy_reflect")]
114 pub use crate::reflect::{
115 AppTypeRegistry, ReflectComponent, ReflectEvent, ReflectFromWorld, ReflectMessage,
116 ReflectResource,
117 };
118
119 #[doc(hidden)]
120 #[cfg(feature = "reflect_functions")]
121 pub use crate::reflect::AppFunctionRegistry;
122}
123
124pub use bevy_ecs_macros::VariantDefaults;
125
126#[doc(hidden)]
130pub mod __macro_exports {
131 pub use crate::query::DebugCheckedUnwrap;
135 pub use alloc::vec::Vec;
136}
137
138#[cfg(feature = "hotpatching")]
142#[derive(impl bevy_ecs::message::Message for HotPatched where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Message, #[automatically_derived]
impl ::core::default::Default for HotPatched {
#[inline]
fn default() -> HotPatched { HotPatched {} }
}Default)]
143pub struct HotPatched;
144
145#[cfg(feature = "hotpatching")]
154#[derive(impl bevy_ecs::resource::Resource for HotPatchChanges where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {}resource::Resource, #[automatically_derived]
impl ::core::default::Default for HotPatchChanges {
#[inline]
fn default() -> HotPatchChanges { HotPatchChanges {} }
}Default)]
155pub struct HotPatchChanges;
156
157#[cfg(test)]
158mod tests {
159 use crate::{
160 bundle::Bundle,
161 change_detection::Ref,
162 component::Component,
163 entity::{Entity, EntityHashSet, EntityMapper, EntityNotSpawnedError},
164 entity_disabling::DefaultQueryFilters,
165 prelude::Or,
166 query::{Added, Changed, FilteredAccess, QueryFilter, With, Without},
167 resource::Resource,
168 world::{error::EntityDespawnError, EntityMut, EntityRef, Mut, World},
169 };
170 use alloc::{string::String, sync::Arc, vec, vec::Vec};
171 use bevy_platform::collections::HashSet;
172 use bevy_tasks::{ComputeTaskPool, TaskPool};
173 use core::{
174 any::TypeId,
175 marker::PhantomData,
176 sync::atomic::{AtomicUsize, Ordering},
177 };
178 use std::sync::Mutex;
179
180 #[derive(Component, Debug, PartialEq, Eq, Hash, Clone, Copy)]
181 struct A(usize);
182 #[derive(Resource, Debug, PartialEq, Eq)]
183 struct ResA(usize);
184 #[derive(Component, Debug, PartialEq, Eq, Hash, Clone, Copy)]
185 struct B(usize);
186 #[derive(Component, Debug, PartialEq, Eq, Clone, Copy)]
187 struct C;
188
189 #[derive(Default)]
190 struct NonSendA(PhantomData<*mut ()>);
191
192 #[derive(Component, Clone, Debug)]
193 struct DropCk(Arc<AtomicUsize>);
194 impl DropCk {
195 fn new_pair() -> (Self, Arc<AtomicUsize>) {
196 let atomic = Arc::new(AtomicUsize::new(0));
197 (DropCk(atomic.clone()), atomic)
198 }
199 }
200
201 impl Drop for DropCk {
202 fn drop(&mut self) {
203 self.0.as_ref().fetch_add(1, Ordering::Relaxed);
204 }
205 }
206
207 #[expect(
208 dead_code,
209 reason = "This struct is used to test how `Drop` behavior works in regards to SparseSet storage, and as such is solely a wrapper around `DropCk` to make it use the SparseSet storage. Because of this, the inner field is intentionally never read."
210 )]
211 #[derive(Component, Clone, Debug)]
212 #[component(storage = "SparseSet")]
213 struct DropCkSparse(DropCk);
214
215 #[derive(Component, Copy, Clone, PartialEq, Eq, Debug)]
216 #[component(storage = "Table")]
217 struct TableStored(&'static str);
218 #[derive(Component, Copy, Clone, PartialEq, Eq, Hash, Debug)]
219 #[component(storage = "SparseSet")]
220 struct SparseStored(u32);
221
222 #[test]
223 fn random_access() {
224 let mut world = World::new();
225
226 let e = world.spawn((TableStored("abc"), SparseStored(123))).id();
227 let f = world
228 .spawn((TableStored("def"), SparseStored(456), A(1)))
229 .id();
230 assert_eq!(world.get::<TableStored>(e).unwrap().0, "abc");
231 assert_eq!(world.get::<SparseStored>(e).unwrap().0, 123);
232 assert_eq!(world.get::<TableStored>(f).unwrap().0, "def");
233 assert_eq!(world.get::<SparseStored>(f).unwrap().0, 456);
234
235 world.get_mut::<TableStored>(e).unwrap().0 = "xyz";
237 assert_eq!(world.get::<TableStored>(e).unwrap().0, "xyz");
238
239 world.get_mut::<SparseStored>(f).unwrap().0 = 42;
241 assert_eq!(world.get::<SparseStored>(f).unwrap().0, 42);
242 }
243
244 #[test]
245 fn bundle_derive() {
246 let mut world = World::new();
247
248 #[derive(Bundle, PartialEq, Debug)]
249 struct FooBundle {
250 x: TableStored,
251 y: SparseStored,
252 }
253 let ids: Vec<_> =
254 <FooBundle as Bundle>::component_ids(&mut world.components_registrator()).collect();
255
256 assert_eq!(
257 ids,
258 &[
259 world.register_component::<TableStored>(),
260 world.register_component::<SparseStored>(),
261 ]
262 );
263
264 let e1 = world
265 .spawn(FooBundle {
266 x: TableStored("abc"),
267 y: SparseStored(123),
268 })
269 .id();
270 let e2 = world
271 .spawn((TableStored("def"), SparseStored(456), A(1)))
272 .id();
273 assert_eq!(world.get::<TableStored>(e1).unwrap().0, "abc");
274 assert_eq!(world.get::<SparseStored>(e1).unwrap().0, 123);
275 assert_eq!(world.get::<TableStored>(e2).unwrap().0, "def");
276 assert_eq!(world.get::<SparseStored>(e2).unwrap().0, 456);
277
278 world.get_mut::<TableStored>(e1).unwrap().0 = "xyz";
280 assert_eq!(world.get::<TableStored>(e1).unwrap().0, "xyz");
281
282 world.get_mut::<SparseStored>(e2).unwrap().0 = 42;
284 assert_eq!(world.get::<SparseStored>(e2).unwrap().0, 42);
285
286 assert_eq!(
287 world.entity_mut(e1).take::<FooBundle>().unwrap(),
288 FooBundle {
289 x: TableStored("xyz"),
290 y: SparseStored(123),
291 }
292 );
293
294 #[derive(Bundle, PartialEq, Debug)]
295 struct NestedBundle {
296 a: A,
297 foo: FooBundle,
298 b: B,
299 }
300
301 let ids: Vec<_> =
302 <NestedBundle as Bundle>::component_ids(&mut world.components_registrator()).collect();
303
304 assert_eq!(
305 ids,
306 &[
307 world.register_component::<A>(),
308 world.register_component::<TableStored>(),
309 world.register_component::<SparseStored>(),
310 world.register_component::<B>(),
311 ]
312 );
313
314 let e3 = world
315 .spawn(NestedBundle {
316 a: A(1),
317 foo: FooBundle {
318 x: TableStored("ghi"),
319 y: SparseStored(789),
320 },
321 b: B(2),
322 })
323 .id();
324
325 assert_eq!(world.get::<TableStored>(e3).unwrap().0, "ghi");
326 assert_eq!(world.get::<SparseStored>(e3).unwrap().0, 789);
327 assert_eq!(world.get::<A>(e3).unwrap().0, 1);
328 assert_eq!(world.get::<B>(e3).unwrap().0, 2);
329 assert_eq!(
330 world.entity_mut(e3).take::<NestedBundle>().unwrap(),
331 NestedBundle {
332 a: A(1),
333 foo: FooBundle {
334 x: TableStored("ghi"),
335 y: SparseStored(789),
336 },
337 b: B(2),
338 }
339 );
340
341 #[derive(Default, Component, PartialEq, Debug)]
342 struct Ignored;
343
344 #[derive(Bundle, PartialEq, Debug)]
345 struct BundleWithIgnored {
346 c: C,
347 #[bundle(ignore)]
348 ignored: Ignored,
349 }
350
351 let ids: Vec<_> =
352 <BundleWithIgnored as Bundle>::component_ids(&mut world.components_registrator())
353 .collect();
354
355 assert_eq!(ids, &[world.register_component::<C>(),]);
356
357 let e4 = world
358 .spawn(BundleWithIgnored {
359 c: C,
360 ignored: Ignored,
361 })
362 .id();
363
364 assert_eq!(world.get::<C>(e4).unwrap(), &C);
365 assert_eq!(world.get::<Ignored>(e4), None);
366
367 assert_eq!(
368 world.entity_mut(e4).take::<BundleWithIgnored>().unwrap(),
369 BundleWithIgnored {
370 c: C,
371 ignored: Ignored,
372 }
373 );
374 }
375
376 #[test]
377 fn spawning_with_manual_entity_allocation() {
378 let mut world = World::new();
379 let start = world.entities().count_spawned();
380 let e1 = world.entity_allocator_mut().alloc();
381 world.spawn_at(e1, (TableStored("abc"), A(123))).unwrap();
382
383 let e2 = world.entity_allocator_mut().alloc();
384 assert!(matches!(
385 world.try_despawn_no_free(e2),
386 Err(EntityDespawnError(
387 EntityNotSpawnedError::ValidButNotSpawned(_)
388 ))
389 ));
390 assert!(!world.despawn(e2));
391 world.entity_allocator_mut().free(e2);
392
393 let e3 = world.entity_allocator_mut().alloc();
394 let e3 = world
395 .spawn_at(e3, (TableStored("junk"), A(0)))
396 .unwrap()
397 .despawn_no_free();
398 world.spawn_at(e3, (TableStored("def"), A(456))).unwrap();
399
400 assert_eq!(world.entities.count_spawned(), start + 2);
401 assert!(world.despawn(e1));
402 assert_eq!(world.entities.count_spawned(), start + 1);
403 assert!(world.get::<TableStored>(e1).is_none());
404 assert!(world.get::<A>(e1).is_none());
405 assert_eq!(world.get::<TableStored>(e3).unwrap().0, "def");
406 assert_eq!(world.get::<A>(e3).unwrap().0, 456);
407 }
408
409 #[test]
410 fn despawn_table_storage() {
411 let mut world = World::new();
412 let e = world.spawn((TableStored("abc"), A(123))).id();
413 let f = world.spawn((TableStored("def"), A(456))).id();
414 assert_eq!(world.query::<&TableStored>().query(&world).count(), 2);
415 assert!(world.despawn(e));
416 assert_eq!(world.query::<&TableStored>().query(&world).count(), 1);
417 assert!(world.get::<TableStored>(e).is_none());
418 assert!(world.get::<A>(e).is_none());
419 assert_eq!(world.get::<TableStored>(f).unwrap().0, "def");
420 assert_eq!(world.get::<A>(f).unwrap().0, 456);
421 }
422
423 #[test]
424 fn despawn_mixed_storage() {
425 let mut world = World::new();
426
427 let e = world.spawn((TableStored("abc"), SparseStored(123))).id();
428 let f = world.spawn((TableStored("def"), SparseStored(456))).id();
429 assert_eq!(world.query::<&TableStored>().query(&world).count(), 2);
430 assert!(world.despawn(e));
431 assert_eq!(world.query::<&TableStored>().query(&world).count(), 1);
432 assert!(world.get::<TableStored>(e).is_none());
433 assert!(world.get::<SparseStored>(e).is_none());
434 assert_eq!(world.get::<TableStored>(f).unwrap().0, "def");
435 assert_eq!(world.get::<SparseStored>(f).unwrap().0, 456);
436 }
437
438 #[test]
439 fn query_all() {
440 let mut world = World::new();
441 let e = world.spawn((TableStored("abc"), A(123))).id();
442 let f = world.spawn((TableStored("def"), A(456))).id();
443
444 let ents = world
445 .query::<(Entity, &A, &TableStored)>()
446 .iter(&world)
447 .map(|(e, &i, &s)| (e, i, s))
448 .collect::<Vec<_>>();
449 assert_eq!(
450 ents,
451 &[
452 (e, A(123), TableStored("abc")),
453 (f, A(456), TableStored("def"))
454 ]
455 );
456 }
457
458 #[test]
459 fn query_all_for_each() {
460 let mut world = World::new();
461 let e = world.spawn((TableStored("abc"), A(123))).id();
462 let f = world.spawn((TableStored("def"), A(456))).id();
463
464 let mut results = Vec::new();
465 world
466 .query::<(Entity, &A, &TableStored)>()
467 .iter(&world)
468 .for_each(|(e, &i, &s)| results.push((e, i, s)));
469 assert_eq!(
470 results,
471 &[
472 (e, A(123), TableStored("abc")),
473 (f, A(456), TableStored("def"))
474 ]
475 );
476 }
477
478 #[test]
479 fn query_single_component() {
480 let mut world = World::new();
481 let e = world.spawn((TableStored("abc"), A(123))).id();
482 let f = world.spawn((TableStored("def"), A(456), B(1))).id();
483 let ents = world
484 .query::<(Entity, &A)>()
485 .iter(&world)
486 .map(|(e, &i)| (e, i))
487 .collect::<HashSet<_>>();
488 assert!(ents.contains(&(e, A(123))));
489 assert!(ents.contains(&(f, A(456))));
490 }
491
492 #[test]
493 fn stateful_query_handles_new_archetype() {
494 let mut world = World::new();
495 let e = world.spawn((TableStored("abc"), A(123))).id();
496 let mut query = world.query::<(Entity, &A)>();
497
498 let ents = query.iter(&world).map(|(e, &i)| (e, i)).collect::<Vec<_>>();
499 assert_eq!(ents, &[(e, A(123))]);
500
501 let f = world.spawn((TableStored("def"), A(456), B(1))).id();
502 let ents = query.iter(&world).map(|(e, &i)| (e, i)).collect::<Vec<_>>();
503 assert_eq!(ents, &[(e, A(123)), (f, A(456))]);
504 }
505
506 #[test]
507 fn query_single_component_for_each() {
508 let mut world = World::new();
509 let e = world.spawn((TableStored("abc"), A(123))).id();
510 let f = world.spawn((TableStored("def"), A(456), B(1))).id();
511 let mut results = <HashSet<_>>::default();
512 world
513 .query::<(Entity, &A)>()
514 .iter(&world)
515 .for_each(|(e, &i)| {
516 results.insert((e, i));
517 });
518 assert!(results.contains(&(e, A(123))));
519 assert!(results.contains(&(f, A(456))));
520 }
521
522 #[test]
523 fn par_for_each_dense() {
524 ComputeTaskPool::get_or_init(TaskPool::default);
525 let mut world = World::new();
526 let e1 = world.spawn(A(1)).id();
527 let e2 = world.spawn(A(2)).id();
528 let e3 = world.spawn(A(3)).id();
529 let e4 = world.spawn((A(4), B(1))).id();
530 let e5 = world.spawn((A(5), B(1))).id();
531 let results = Arc::new(Mutex::new(Vec::new()));
532 world
533 .query::<(Entity, &A)>()
534 .par_iter(&world)
535 .for_each(|(e, &A(i))| {
536 results.lock().unwrap().push((e, i));
537 });
538 results.lock().unwrap().sort();
539 let mut expected = [(e1, 1), (e2, 2), (e3, 3), (e4, 4), (e5, 5)];
540 expected.sort();
541 assert_eq!(&*results.lock().unwrap(), &expected);
542 }
543
544 #[test]
545 fn par_for_each_sparse() {
546 ComputeTaskPool::get_or_init(TaskPool::default);
547 let mut world = World::new();
548 let e1 = world.spawn(SparseStored(1)).id();
549 let e2 = world.spawn(SparseStored(2)).id();
550 let e3 = world.spawn(SparseStored(3)).id();
551 let e4 = world.spawn((SparseStored(4), A(1))).id();
552 let e5 = world.spawn((SparseStored(5), A(1))).id();
553 let results = Arc::new(Mutex::new(Vec::new()));
554 world
555 .query::<(Entity, &SparseStored)>()
556 .par_iter(&world)
557 .for_each(|(e, &SparseStored(i))| results.lock().unwrap().push((e, i)));
558 results.lock().unwrap().sort();
559 let mut expected = [(e1, 1), (e2, 2), (e3, 3), (e4, 4), (e5, 5)];
560 expected.sort();
561 assert_eq!(&*results.lock().unwrap(), &expected);
562 }
563
564 #[test]
565 fn query_missing_component() {
566 let mut world = World::new();
567 world.spawn((TableStored("abc"), A(123)));
568 world.spawn((TableStored("def"), A(456)));
569 assert!(world.query::<(&B, &A)>().iter(&world).next().is_none());
570 }
571
572 #[test]
573 fn query_sparse_component() {
574 let mut world = World::new();
575 world.spawn((TableStored("abc"), A(123)));
576 let f = world.spawn((TableStored("def"), A(456), B(1))).id();
577 let ents = world
578 .query::<(Entity, &B)>()
579 .iter(&world)
580 .map(|(e, &b)| (e, b))
581 .collect::<Vec<_>>();
582 assert_eq!(ents, &[(f, B(1))]);
583 }
584
585 #[test]
586 fn query_filter_with() {
587 let mut world = World::new();
588 world.spawn((A(123), B(1)));
589 world.spawn(A(456));
590 let result = world
591 .query_filtered::<&A, With<B>>()
592 .iter(&world)
593 .cloned()
594 .collect::<Vec<_>>();
595 assert_eq!(result, vec![A(123)]);
596 }
597
598 #[test]
599 fn query_filter_with_for_each() {
600 let mut world = World::new();
601 world.spawn((A(123), B(1)));
602 world.spawn(A(456));
603
604 let mut results = Vec::new();
605 world
606 .query_filtered::<&A, With<B>>()
607 .iter(&world)
608 .for_each(|i| results.push(*i));
609 assert_eq!(results, vec![A(123)]);
610 }
611
612 #[test]
613 fn query_filter_with_sparse() {
614 let mut world = World::new();
615
616 world.spawn((A(123), SparseStored(321)));
617 world.spawn(A(456));
618 let result = world
619 .query_filtered::<&A, With<SparseStored>>()
620 .iter(&world)
621 .cloned()
622 .collect::<Vec<_>>();
623 assert_eq!(result, vec![A(123)]);
624 }
625
626 #[test]
627 fn query_filter_with_sparse_for_each() {
628 let mut world = World::new();
629
630 world.spawn((A(123), SparseStored(321)));
631 world.spawn(A(456));
632 let mut results = Vec::new();
633 world
634 .query_filtered::<&A, With<SparseStored>>()
635 .iter(&world)
636 .for_each(|i| results.push(*i));
637 assert_eq!(results, vec![A(123)]);
638 }
639
640 #[test]
641 fn query_filter_without() {
642 let mut world = World::new();
643 world.spawn((A(123), B(321)));
644 world.spawn(A(456));
645 let result = world
646 .query_filtered::<&A, Without<B>>()
647 .iter(&world)
648 .cloned()
649 .collect::<Vec<_>>();
650 assert_eq!(result, vec![A(456)]);
651 }
652
653 #[test]
654 fn query_optional_component_table() {
655 let mut world = World::new();
656 let e = world.spawn((TableStored("abc"), A(123))).id();
657 let f = world.spawn((TableStored("def"), A(456), B(1))).id();
658 world.spawn(TableStored("abc"));
660 let ents = world
661 .query::<(Entity, Option<&B>, &A)>()
662 .iter(&world)
663 .map(|(e, b, &i)| (e, b.copied(), i))
664 .collect::<HashSet<_>>();
665 assert!(ents.contains(&(e, None, A(123))));
666 assert!(ents.contains(&(f, Some(B(1)), A(456))));
667 }
668
669 #[test]
670 fn query_optional_component_sparse() {
671 let mut world = World::new();
672
673 let e = world.spawn((TableStored("abc"), A(123))).id();
674 let f = world
675 .spawn((TableStored("def"), A(456), SparseStored(1)))
676 .id();
677 let ents = world
680 .query::<(Entity, Option<&SparseStored>, &A)>()
681 .iter(&world)
682 .map(|(e, b, &i)| (e, b.copied(), i))
683 .collect::<HashSet<_>>();
684 assert_eq!(
685 ents,
686 [(e, None, A(123)), (f, Some(SparseStored(1)), A(456))]
687 .into_iter()
688 .collect::<HashSet<_>>()
689 );
690 }
691
692 #[test]
693 fn query_optional_component_sparse_no_match() {
694 let mut world = World::new();
695
696 let e = world.spawn((TableStored("abc"), A(123))).id();
697 let f = world.spawn((TableStored("def"), A(456))).id();
698 world.spawn(TableStored("abc"));
700 let ents = world
701 .query::<(Entity, Option<&SparseStored>, &A)>()
702 .iter(&world)
703 .map(|(e, b, &i)| (e, b.copied(), i))
704 .collect::<Vec<_>>();
705 assert_eq!(ents, &[(e, None, A(123)), (f, None, A(456))]);
706 }
707
708 #[test]
709 fn add_remove_components() {
710 let mut world = World::new();
711 let e1 = world.spawn((A(1), B(3), TableStored("abc"))).id();
712 let e2 = world.spawn((A(2), B(4), TableStored("xyz"))).id();
713
714 assert_eq!(
715 world
716 .query::<(Entity, &A, &B)>()
717 .iter(&world)
718 .map(|(e, &i, &b)| (e, i, b))
719 .collect::<HashSet<_>>(),
720 [(e1, A(1), B(3)), (e2, A(2), B(4))]
721 .into_iter()
722 .collect::<HashSet<_>>()
723 );
724 assert_eq!(world.entity_mut(e1).take::<A>(), Some(A(1)));
725 assert_eq!(
726 world
727 .query::<(Entity, &A, &B)>()
728 .iter(&world)
729 .map(|(e, &i, &b)| (e, i, b))
730 .collect::<Vec<_>>(),
731 &[(e2, A(2), B(4))]
732 );
733 assert_eq!(
734 world
735 .query::<(Entity, &B, &TableStored)>()
736 .iter(&world)
737 .map(|(e, &B(b), &TableStored(s))| (e, b, s))
738 .collect::<HashSet<_>>(),
739 [(e2, 4, "xyz"), (e1, 3, "abc")]
740 .into_iter()
741 .collect::<HashSet<_>>()
742 );
743 world.entity_mut(e1).insert(A(43));
744 assert_eq!(
745 world
746 .query::<(Entity, &A, &B)>()
747 .iter(&world)
748 .map(|(e, &i, &b)| (e, i, b))
749 .collect::<HashSet<_>>(),
750 [(e2, A(2), B(4)), (e1, A(43), B(3))]
751 .into_iter()
752 .collect::<HashSet<_>>()
753 );
754 world.entity_mut(e1).insert(C);
755 assert_eq!(
756 world
757 .query::<(Entity, &C)>()
758 .iter(&world)
759 .map(|(e, &f)| (e, f))
760 .collect::<Vec<_>>(),
761 &[(e1, C)]
762 );
763 }
764
765 #[test]
766 fn table_add_remove_many() {
767 let mut world = World::default();
768 #[cfg(miri)]
769 let (mut entities, to) = {
770 let to = 10;
771 (Vec::with_capacity(to), to)
772 };
773 #[cfg(not(miri))]
774 let (mut entities, to) = {
775 let to = 10_000;
776 (Vec::with_capacity(to), to)
777 };
778
779 for _ in 0..to {
780 entities.push(world.spawn(B(0)).id());
781 }
782
783 for (i, entity) in entities.iter().cloned().enumerate() {
784 world.entity_mut(entity).insert(A(i));
785 }
786
787 for (i, entity) in entities.iter().cloned().enumerate() {
788 assert_eq!(world.entity_mut(entity).take::<A>(), Some(A(i)));
789 }
790 }
791
792 #[test]
793 fn sparse_set_add_remove_many() {
794 let mut world = World::default();
795
796 let mut entities = Vec::with_capacity(1000);
797 for _ in 0..4 {
798 entities.push(world.spawn(A(2)).id());
799 }
800
801 for (i, entity) in entities.iter().cloned().enumerate() {
802 world.entity_mut(entity).insert(SparseStored(i as u32));
803 }
804
805 for (i, entity) in entities.iter().cloned().enumerate() {
806 assert_eq!(
807 world.entity_mut(entity).take::<SparseStored>(),
808 Some(SparseStored(i as u32))
809 );
810 }
811 }
812
813 #[test]
814 fn remove_missing() {
815 let mut world = World::new();
816 let e = world.spawn((TableStored("abc"), A(123))).id();
817 assert!(world.entity_mut(e).take::<B>().is_none());
818 }
819
820 #[test]
821 fn spawn_batch() {
822 let mut world = World::new();
823 world.spawn_batch((0..100).map(|x| (A(x), TableStored("abc"))));
824 let values = world
825 .query::<&A>()
826 .iter(&world)
827 .map(|v| v.0)
828 .collect::<Vec<_>>();
829 let expected = (0..100).collect::<Vec<_>>();
830 assert_eq!(values, expected);
831 }
832
833 #[test]
834 fn query_get() {
835 let mut world = World::new();
836 let a = world.spawn((TableStored("abc"), A(123))).id();
837 let b = world.spawn((TableStored("def"), A(456))).id();
838 let c = world.spawn((TableStored("ghi"), A(789), B(1))).id();
839
840 let mut i32_query = world.query::<&A>();
841 assert_eq!(i32_query.get(&world, a).unwrap().0, 123);
842 assert_eq!(i32_query.get(&world, b).unwrap().0, 456);
843
844 let mut i32_bool_query = world.query::<(&A, &B)>();
845 assert!(i32_bool_query.get(&world, a).is_err());
846 assert_eq!(i32_bool_query.get(&world, c).unwrap(), (&A(789), &B(1)));
847 assert!(world.despawn(a));
848 assert!(i32_query.get(&world, a).is_err());
849 }
850
851 #[test]
852 fn query_get_works_across_sparse_removal() {
853 let mut world = World::new();
855 let a = world.spawn((TableStored("abc"), SparseStored(123))).id();
856 let b = world.spawn((TableStored("def"), SparseStored(456))).id();
857 let c = world
858 .spawn((TableStored("ghi"), SparseStored(789), B(1)))
859 .id();
860
861 let mut query = world.query::<&TableStored>();
862 assert_eq!(query.get(&world, a).unwrap(), &TableStored("abc"));
863 assert_eq!(query.get(&world, b).unwrap(), &TableStored("def"));
864 assert_eq!(query.get(&world, c).unwrap(), &TableStored("ghi"));
865
866 world.entity_mut(b).remove::<SparseStored>();
867 world.entity_mut(c).remove::<SparseStored>();
868
869 assert_eq!(query.get(&world, a).unwrap(), &TableStored("abc"));
870 assert_eq!(query.get(&world, b).unwrap(), &TableStored("def"));
871 assert_eq!(query.get(&world, c).unwrap(), &TableStored("ghi"));
872 }
873
874 #[test]
875 fn remove_tracking() {
876 let mut world = World::new();
877
878 let a = world.spawn((SparseStored(0), A(123))).id();
879 let b = world.spawn((SparseStored(1), A(123))).id();
880
881 world.entity_mut(a).despawn();
882 assert_eq!(
883 world.removed::<A>().collect::<Vec<_>>(),
884 &[a],
885 "despawning results in 'removed component' state for table components"
886 );
887 assert_eq!(
888 world.removed::<SparseStored>().collect::<Vec<_>>(),
889 &[a],
890 "despawning results in 'removed component' state for sparse set components"
891 );
892
893 world.entity_mut(b).insert(B(1));
894 assert_eq!(
895 world.removed::<A>().collect::<Vec<_>>(),
896 &[a],
897 "archetype moves does not result in 'removed component' state"
898 );
899
900 world.entity_mut(b).remove::<A>();
901 assert_eq!(
902 world.removed::<A>().collect::<Vec<_>>(),
903 &[a, b],
904 "removing a component results in a 'removed component' state"
905 );
906
907 world.clear_trackers();
908 assert_eq!(
909 world.removed::<A>().collect::<Vec<_>>(),
910 &[],
911 "clearing trackers clears removals"
912 );
913 assert_eq!(
914 world.removed::<SparseStored>().collect::<Vec<_>>(),
915 &[],
916 "clearing trackers clears removals"
917 );
918 assert_eq!(
919 world.removed::<B>().collect::<Vec<_>>(),
920 &[],
921 "clearing trackers clears removals"
922 );
923
924 }
944
945 #[test]
946 fn added_tracking() {
947 let mut world = World::new();
948 let a = world.spawn(A(123)).id();
949
950 assert_eq!(world.query::<&A>().iter(&world).count(), 1);
951 assert_eq!(
952 world.query_filtered::<(), Added<A>>().iter(&world).count(),
953 1
954 );
955 assert_eq!(world.query::<&A>().iter(&world).count(), 1);
956 assert_eq!(
957 world.query_filtered::<(), Added<A>>().iter(&world).count(),
958 1
959 );
960 assert!(world.query::<&A>().get(&world, a).is_ok());
961 assert!(world
962 .query_filtered::<(), Added<A>>()
963 .get(&world, a)
964 .is_ok());
965 assert!(world.query::<&A>().get(&world, a).is_ok());
966 assert!(world
967 .query_filtered::<(), Added<A>>()
968 .get(&world, a)
969 .is_ok());
970
971 world.clear_trackers();
972
973 assert_eq!(world.query::<&A>().iter(&world).count(), 1);
974 assert_eq!(
975 world.query_filtered::<(), Added<A>>().iter(&world).count(),
976 0
977 );
978 assert_eq!(world.query::<&A>().iter(&world).count(), 1);
979 assert_eq!(
980 world.query_filtered::<(), Added<A>>().iter(&world).count(),
981 0
982 );
983 assert!(world.query::<&A>().get(&world, a).is_ok());
984 assert!(world
985 .query_filtered::<(), Added<A>>()
986 .get(&world, a)
987 .is_err());
988 assert!(world.query::<&A>().get(&world, a).is_ok());
989 assert!(world
990 .query_filtered::<(), Added<A>>()
991 .get(&world, a)
992 .is_err());
993 }
994
995 #[test]
996 fn added_queries() {
997 let mut world = World::default();
998 let e1 = world.spawn(A(0)).id();
999
1000 fn get_added<Com: Component>(world: &mut World) -> Vec<Entity> {
1001 world
1002 .query_filtered::<Entity, Added<Com>>()
1003 .iter(world)
1004 .collect::<Vec<Entity>>()
1005 }
1006
1007 assert_eq!(get_added::<A>(&mut world), vec![e1]);
1008 world.entity_mut(e1).insert(B(0));
1009 assert_eq!(get_added::<A>(&mut world), vec![e1]);
1010 assert_eq!(get_added::<B>(&mut world), vec![e1]);
1011
1012 world.clear_trackers();
1013 assert!(get_added::<A>(&mut world).is_empty());
1014 let e2 = world.spawn((A(1), B(1))).id();
1015 assert_eq!(get_added::<A>(&mut world), vec![e2]);
1016 assert_eq!(get_added::<B>(&mut world), vec![e2]);
1017
1018 let added = world
1019 .query_filtered::<Entity, (Added<A>, Added<B>)>()
1020 .iter(&world)
1021 .collect::<Vec<Entity>>();
1022 assert_eq!(added, vec![e2]);
1023 }
1024
1025 #[test]
1026 fn changed_trackers() {
1027 let mut world = World::default();
1028 let e1 = world.spawn((A(0), B(0))).id();
1029 let e2 = world.spawn((A(0), B(0))).id();
1030 let e3 = world.spawn((A(0), B(0))).id();
1031 world.spawn((A(0), B(0)));
1032
1033 world.clear_trackers();
1034
1035 for (i, mut a) in world.query::<&mut A>().iter_mut(&mut world).enumerate() {
1036 if i % 2 == 0 {
1037 a.0 += 1;
1038 }
1039 }
1040
1041 fn get_filtered<F: QueryFilter>(world: &mut World) -> EntityHashSet {
1042 world
1043 .query_filtered::<Entity, F>()
1044 .iter(world)
1045 .collect::<EntityHashSet>()
1046 }
1047
1048 assert_eq!(
1049 get_filtered::<Changed<A>>(&mut world),
1050 [e1, e3].into_iter().collect::<EntityHashSet>()
1051 );
1052
1053 world.entity_mut(e1).insert(C);
1055
1056 assert_eq!(
1057 get_filtered::<Changed<A>>(&mut world),
1058 [e3, e1].into_iter().collect::<EntityHashSet>(),
1059 "changed entities list should not change"
1060 );
1061
1062 world.entity_mut(e1).insert((A(0), B(0)));
1064
1065 assert_eq!(
1066 get_filtered::<Changed<A>>(&mut world),
1067 [e3, e1].into_iter().collect::<EntityHashSet>(),
1068 "changed entities list should not change"
1069 );
1070
1071 assert!(world.despawn(e2));
1073 assert_eq!(
1074 get_filtered::<Changed<A>>(&mut world),
1075 [e3, e1].into_iter().collect::<EntityHashSet>(),
1076 "changed entities list should not change"
1077 );
1078
1079 assert!(world.despawn(e1));
1081 assert_eq!(
1082 get_filtered::<Changed<A>>(&mut world),
1083 [e3].into_iter().collect::<EntityHashSet>(),
1084 "e1 should no longer be returned"
1085 );
1086
1087 world.clear_trackers();
1088
1089 assert!(get_filtered::<Changed<A>>(&mut world).is_empty());
1090
1091 let e4 = world.spawn_empty().id();
1092
1093 world.entity_mut(e4).insert(A(0));
1094 assert_eq!(
1095 get_filtered::<Changed<A>>(&mut world),
1096 [e4].into_iter().collect::<EntityHashSet>()
1097 );
1098 assert_eq!(
1099 get_filtered::<Added<A>>(&mut world),
1100 [e4].into_iter().collect::<EntityHashSet>()
1101 );
1102
1103 world.entity_mut(e4).insert(A(1));
1104 assert_eq!(
1105 get_filtered::<Changed<A>>(&mut world),
1106 [e4].into_iter().collect::<EntityHashSet>()
1107 );
1108
1109 world.clear_trackers();
1110
1111 world.entity_mut(e4).insert((A(0), B(0)));
1114
1115 assert!(get_filtered::<Added<A>>(&mut world).is_empty());
1116 assert_eq!(
1117 get_filtered::<Changed<A>>(&mut world),
1118 [e4].into_iter().collect::<EntityHashSet>()
1119 );
1120 assert_eq!(
1121 get_filtered::<Added<B>>(&mut world),
1122 [e4].into_iter().collect::<EntityHashSet>()
1123 );
1124 assert_eq!(
1125 get_filtered::<Changed<B>>(&mut world),
1126 [e4].into_iter().collect::<EntityHashSet>()
1127 );
1128 }
1129
1130 #[test]
1131 fn changed_trackers_sparse() {
1132 let mut world = World::default();
1133 let e1 = world.spawn(SparseStored(0)).id();
1134 let e2 = world.spawn(SparseStored(0)).id();
1135 let e3 = world.spawn(SparseStored(0)).id();
1136 world.spawn(SparseStored(0));
1137
1138 world.clear_trackers();
1139
1140 for (i, mut a) in world
1141 .query::<&mut SparseStored>()
1142 .iter_mut(&mut world)
1143 .enumerate()
1144 {
1145 if i % 2 == 0 {
1146 a.0 += 1;
1147 }
1148 }
1149
1150 fn get_filtered<F: QueryFilter>(world: &mut World) -> EntityHashSet {
1151 world
1152 .query_filtered::<Entity, F>()
1153 .iter(world)
1154 .collect::<EntityHashSet>()
1155 }
1156
1157 assert_eq!(
1158 get_filtered::<Changed<SparseStored>>(&mut world),
1159 [e1, e3].into_iter().collect::<EntityHashSet>()
1160 );
1161
1162 world.entity_mut(e1).insert(C);
1164
1165 assert_eq!(get_filtered::<Changed<SparseStored>>(&mut world), [e3, e1].into_iter().collect::<EntityHashSet>(), "changed entities list should not change (although the order will due to archetype moves)");
1166
1167 world.entity_mut(e1).insert(SparseStored(0));
1169 assert_eq!(
1170 get_filtered::<Changed<SparseStored>>(&mut world),
1171 [e3, e1].into_iter().collect::<EntityHashSet>(),
1172 "changed entities list should not change"
1173 );
1174
1175 assert!(world.despawn(e2));
1177 assert_eq!(
1178 get_filtered::<Changed<SparseStored>>(&mut world),
1179 [e3, e1].into_iter().collect::<EntityHashSet>(),
1180 "changed entities list should not change"
1181 );
1182
1183 assert!(world.despawn(e1));
1185 assert_eq!(
1186 get_filtered::<Changed<SparseStored>>(&mut world),
1187 [e3].into_iter().collect::<EntityHashSet>(),
1188 "e1 should no longer be returned"
1189 );
1190
1191 world.clear_trackers();
1192
1193 assert!(get_filtered::<Changed<SparseStored>>(&mut world).is_empty());
1194
1195 let e4 = world.spawn_empty().id();
1196
1197 world.entity_mut(e4).insert(SparseStored(0));
1198 assert_eq!(
1199 get_filtered::<Changed<SparseStored>>(&mut world),
1200 [e4].into_iter().collect::<EntityHashSet>()
1201 );
1202 assert_eq!(
1203 get_filtered::<Added<SparseStored>>(&mut world),
1204 [e4].into_iter().collect::<EntityHashSet>()
1205 );
1206
1207 world.entity_mut(e4).insert(A(1));
1208 assert_eq!(
1209 get_filtered::<Changed<SparseStored>>(&mut world),
1210 [e4].into_iter().collect::<EntityHashSet>()
1211 );
1212
1213 world.clear_trackers();
1214
1215 world.entity_mut(e4).insert(SparseStored(0));
1218
1219 assert!(get_filtered::<Added<SparseStored>>(&mut world).is_empty());
1220 assert_eq!(
1221 get_filtered::<Changed<SparseStored>>(&mut world),
1222 [e4].into_iter().collect::<EntityHashSet>()
1223 );
1224 }
1225
1226 #[test]
1227 fn empty_spawn() {
1228 let mut world = World::default();
1229 let e = world.spawn_empty().id();
1230 let mut e_mut = world.entity_mut(e);
1231 e_mut.insert(A(0));
1232 assert_eq!(e_mut.get::<A>().unwrap(), &A(0));
1233 }
1234
1235 #[test]
1236 fn changed_query() {
1237 let mut world = World::default();
1238 let e1 = world.spawn((A(0), B(0))).id();
1239
1240 fn get_changed(world: &mut World) -> Vec<Entity> {
1241 world
1242 .query_filtered::<Entity, Changed<A>>()
1243 .iter(world)
1244 .collect::<Vec<Entity>>()
1245 }
1246 assert_eq!(get_changed(&mut world), vec![e1]);
1247 world.clear_trackers();
1248 assert_eq!(get_changed(&mut world), vec![]);
1249 *world.get_mut(e1).unwrap() = A(1);
1250 assert_eq!(get_changed(&mut world), vec![e1]);
1251 }
1252
1253 #[test]
1254 fn resource() {
1255 use crate::resource::Resource;
1256
1257 #[derive(Resource, PartialEq, Debug)]
1258 struct Num(i32);
1259
1260 #[derive(Resource, PartialEq, Debug)]
1261 struct BigNum(u64);
1262 let mut world = World::default();
1263 assert!(world.get_resource::<Num>().is_none());
1264 assert!(!world.contains_resource::<Num>());
1265 assert!(!world.is_resource_added::<Num>());
1266 assert!(!world.is_resource_changed::<Num>());
1267
1268 world.insert_resource(Num(123));
1269 let resource_id = world.components().get_id(TypeId::of::<Num>()).unwrap();
1270
1271 assert_eq!(world.resource::<Num>().0, 123);
1272 assert!(world.contains_resource::<Num>());
1273 assert!(world.is_resource_added::<Num>());
1274 assert!(world.is_resource_changed::<Num>());
1275
1276 world.insert_resource(BigNum(456));
1277 assert_eq!(world.resource::<BigNum>().0, 456u64);
1278
1279 world.insert_resource(BigNum(789));
1280 assert_eq!(world.resource::<BigNum>().0, 789);
1281
1282 {
1283 let mut value = world.resource_mut::<BigNum>();
1284 assert_eq!(value.0, 789);
1285 value.0 = 10;
1286 }
1287
1288 assert_eq!(
1289 world.resource::<BigNum>().0,
1290 10,
1291 "resource changes are preserved"
1292 );
1293
1294 assert_eq!(
1295 world.remove_resource::<BigNum>(),
1296 Some(BigNum(10)),
1297 "removed resource has the correct value"
1298 );
1299 assert_eq!(
1300 world.get_resource::<BigNum>(),
1301 None,
1302 "removed resource no longer exists"
1303 );
1304 assert_eq!(
1305 world.remove_resource::<BigNum>(),
1306 None,
1307 "double remove returns nothing"
1308 );
1309
1310 world.insert_resource(BigNum(1));
1311 assert_eq!(
1312 world.get_resource::<BigNum>(),
1313 Some(&BigNum(1)),
1314 "re-inserting resources works"
1315 );
1316
1317 assert_eq!(
1318 world.get_resource::<Num>(),
1319 Some(&Num(123)),
1320 "other resources are unaffected"
1321 );
1322
1323 let current_resource_id = world.components().get_id(TypeId::of::<Num>()).unwrap();
1324 assert_eq!(
1325 resource_id, current_resource_id,
1326 "resource id does not change after removing / re-adding"
1327 );
1328 }
1329
1330 #[test]
1331 fn remove() {
1332 let mut world = World::default();
1333 let e1 = world.spawn((A(1), B(1), TableStored("a"))).id();
1334
1335 let mut e = world.entity_mut(e1);
1336 assert_eq!(e.get::<TableStored>(), Some(&TableStored("a")));
1337 assert_eq!(e.get::<A>(), Some(&A(1)));
1338 assert_eq!(e.get::<B>(), Some(&B(1)));
1339 assert_eq!(
1340 e.get::<C>(),
1341 None,
1342 "C is not in the entity, so it should not exist"
1343 );
1344
1345 e.remove::<(A, B, C)>();
1346 assert_eq!(
1347 e.get::<TableStored>(),
1348 Some(&TableStored("a")),
1349 "TableStored is not in the removed bundle, so it should exist"
1350 );
1351 assert_eq!(
1352 e.get::<A>(),
1353 None,
1354 "Num is in the removed bundle, so it should not exist"
1355 );
1356 assert_eq!(
1357 e.get::<B>(),
1358 None,
1359 "f64 is in the removed bundle, so it should not exist"
1360 );
1361 assert_eq!(
1362 e.get::<C>(),
1363 None,
1364 "usize is in the removed bundle, so it should not exist"
1365 );
1366 }
1367
1368 #[test]
1369 fn take() {
1370 let mut world = World::default();
1371 world.spawn((A(1), B(1), TableStored("1")));
1372 let e2 = world.spawn((A(2), B(2), TableStored("2"))).id();
1373 world.spawn((A(3), B(3), TableStored("3")));
1374
1375 let mut query = world.query::<(&B, &TableStored)>();
1376 let results = query
1377 .iter(&world)
1378 .map(|(a, b)| (a.0, b.0))
1379 .collect::<HashSet<_>>();
1380 assert_eq!(
1381 results,
1382 [(1, "1"), (2, "2"), (3, "3"),]
1383 .into_iter()
1384 .collect::<HashSet<_>>()
1385 );
1386
1387 let removed_bundle = world.entity_mut(e2).take::<(B, TableStored)>().unwrap();
1388 assert_eq!(removed_bundle, (B(2), TableStored("2")));
1389
1390 let results = query
1391 .iter(&world)
1392 .map(|(a, b)| (a.0, b.0))
1393 .collect::<HashSet<_>>();
1394 assert_eq!(
1395 results,
1396 [(1, "1"), (3, "3"),].into_iter().collect::<HashSet<_>>()
1397 );
1398
1399 let mut a_query = world.query::<&A>();
1400 let results = a_query.iter(&world).map(|a| a.0).collect::<HashSet<_>>();
1401 assert_eq!(results, [1, 3, 2].into_iter().collect::<HashSet<_>>());
1402
1403 let entity_ref = world.entity(e2);
1404 assert_eq!(
1405 entity_ref.get::<A>(),
1406 Some(&A(2)),
1407 "A is not in the removed bundle, so it should exist"
1408 );
1409 assert_eq!(
1410 entity_ref.get::<B>(),
1411 None,
1412 "B is in the removed bundle, so it should not exist"
1413 );
1414 assert_eq!(
1415 entity_ref.get::<TableStored>(),
1416 None,
1417 "TableStored is in the removed bundle, so it should not exist"
1418 );
1419 }
1420
1421 #[test]
1422 fn non_send() {
1423 let mut world = World::default();
1424 world.insert_non_send(123i32);
1425 world.insert_non_send(456i64);
1426 assert_eq!(*world.non_send::<i32>(), 123);
1427 assert_eq!(*world.non_send_mut::<i64>(), 456);
1428 }
1429
1430 #[test]
1431 fn non_send_points_to_distinct_data() {
1432 let mut world = World::default();
1433 world.insert_resource(ResA(123));
1434 world.insert_non_send(ResA(456));
1435 assert_eq!(*world.resource::<ResA>(), ResA(123));
1436 assert_eq!(*world.non_send::<ResA>(), ResA(456));
1437 }
1438
1439 #[test]
1440 #[should_panic]
1441 fn non_send_panic() {
1442 let mut world = World::default();
1443 world.insert_non_send(0i32);
1444 std::thread::spawn(move || {
1445 let _ = world.non_send_mut::<i32>();
1446 })
1447 .join()
1448 .unwrap();
1449 }
1450
1451 #[test]
1452 fn exact_size_query() {
1453 let mut world = World::default();
1454 world.spawn((A(0), B(0)));
1455 world.spawn((A(0), B(0)));
1456 world.spawn((A(0), B(0), C));
1457 world.spawn(C);
1458
1459 let mut query = world.query::<(&A, &B)>();
1460 assert_eq!(query.iter(&world).len(), 3);
1461 }
1462
1463 #[test]
1464 #[should_panic]
1465 fn duplicate_components_panic() {
1466 let mut world = World::new();
1467 world.spawn((A(1), A(2)));
1468 }
1469
1470 #[test]
1471 #[should_panic]
1472 fn ref_and_mut_query_panic() {
1473 let mut world = World::new();
1474 world.query::<(&A, &mut A)>();
1475 }
1476
1477 #[test]
1478 #[should_panic]
1479 fn entity_ref_and_mut_query_panic() {
1480 let mut world = World::new();
1481 world.query::<(EntityRef, &mut A)>();
1482 }
1483
1484 #[test]
1485 #[should_panic]
1486 fn mut_and_ref_query_panic() {
1487 let mut world = World::new();
1488 world.query::<(&mut A, &A)>();
1489 }
1490
1491 #[test]
1492 #[should_panic]
1493 fn mut_and_entity_ref_query_panic() {
1494 let mut world = World::new();
1495 world.query::<(&mut A, EntityRef)>();
1496 }
1497
1498 #[test]
1499 #[should_panic]
1500 fn entity_ref_and_entity_mut_query_panic() {
1501 let mut world = World::new();
1502 world.query::<(EntityRef, EntityMut)>();
1503 }
1504
1505 #[test]
1506 #[should_panic]
1507 fn entity_mut_and_entity_mut_query_panic() {
1508 let mut world = World::new();
1509 world.query::<(EntityMut, EntityMut)>();
1510 }
1511
1512 #[test]
1513 fn entity_ref_and_entity_ref_query_no_panic() {
1514 let mut world = World::new();
1515 world.query::<(EntityRef, EntityRef)>();
1516 }
1517
1518 #[test]
1519 #[should_panic]
1520 fn mut_and_mut_query_panic() {
1521 let mut world = World::new();
1522 world.query::<(&mut A, &mut A)>();
1523 }
1524
1525 #[test]
1526 #[should_panic]
1527 fn multiple_worlds_same_query_iter() {
1528 let mut world_a = World::new();
1529 let world_b = World::new();
1530 let mut query = world_a.query::<&A>();
1531 query.iter(&world_a);
1532 query.iter(&world_b);
1533 }
1534
1535 #[test]
1536 fn query_filters_dont_collide_with_fetches() {
1537 let mut world = World::new();
1538 world.query_filtered::<&mut A, Changed<A>>();
1539 }
1540
1541 #[test]
1542 fn filtered_query_access() {
1543 let mut world = World::new();
1544 world.remove_resource::<DefaultQueryFilters>();
1546 let query = world.query_filtered::<&mut A, Changed<B>>();
1547
1548 let mut expected = FilteredAccess::default();
1549 let a_id = world.components.get_id(TypeId::of::<A>()).unwrap();
1550 let b_id = world.components.get_id(TypeId::of::<B>()).unwrap();
1551 expected.add_write(a_id);
1552 expected.add_read(b_id);
1553 assert!(
1554 query.component_access.eq(&expected),
1555 "ComponentId access from query fetch and query filter should be combined"
1556 );
1557 }
1558
1559 #[test]
1560 #[should_panic]
1561 fn multiple_worlds_same_query_get() {
1562 let mut world_a = World::new();
1563 let world_b = World::new();
1564 let mut query = world_a.query::<&A>();
1565 let _ = query.get(&world_a, Entity::from_raw_u32(10_000).unwrap());
1566 let _ = query.get(&world_b, Entity::from_raw_u32(10_000).unwrap());
1567 }
1568
1569 #[test]
1570 #[should_panic]
1571 fn multiple_worlds_same_query_for_each() {
1572 let mut world_a = World::new();
1573 let world_b = World::new();
1574 let mut query = world_a.query::<&A>();
1575 query.iter(&world_a).for_each(|_| {});
1576 query.iter(&world_b).for_each(|_| {});
1577 }
1578
1579 #[test]
1580 fn resource_scope() {
1581 let mut world = World::default();
1582 assert!(world.try_resource_scope::<ResA, _>(|_, _| {}).is_none());
1583 world.insert_resource(ResA(0));
1584 world.resource_scope(|world: &mut World, mut value: Mut<ResA>| {
1585 value.0 += 1;
1586 assert!(!world.contains_resource::<ResA>());
1587 });
1588 assert_eq!(world.resource::<ResA>().0, 1);
1589 }
1590
1591 #[cfg(feature = "std")]
1592 #[test]
1593 fn resource_scope_unwind() {
1594 #[derive(Debug, PartialEq)]
1595 struct Panic;
1596
1597 let mut world = World::default();
1598 assert!(world.try_resource_scope::<ResA, _>(|_, _| {}).is_none());
1599 world.insert_resource(ResA(0));
1600 let panic = std::panic::catch_unwind(core::panic::AssertUnwindSafe(|| {
1601 world.resource_scope(|world: &mut World, _value: Mut<ResA>| {
1602 assert!(!world.contains_resource::<ResA>());
1603 std::panic::panic_any(Panic);
1604 });
1605 unreachable!();
1606 }));
1607 assert_eq!(panic.unwrap_err().downcast_ref::<Panic>(), Some(&Panic));
1608 assert!(world.contains_resource::<ResA>());
1609 }
1610
1611 #[test]
1612 fn resource_scope_resources_cleared() {
1613 let mut world = World::default();
1614 assert!(world.try_resource_scope::<ResA, _>(|_, _| {}).is_none());
1615 world.insert_resource(ResA(0));
1616 let r = world.try_resource_scope(|world: &mut World, _value: Mut<ResA>| {
1617 assert!(!world.contains_resource::<ResA>());
1618 world.clear_resources();
1619 });
1620 assert_eq!(r, Some(()));
1621 assert!(world.contains_resource::<ResA>());
1622 }
1623
1624 #[test]
1625 #[should_panic]
1626 fn non_send_drop_from_different_thread() {
1627 let mut world = World::default();
1628 world.insert_non_send(NonSendA::default());
1629
1630 let thread = std::thread::spawn(move || {
1631 drop(world);
1634 });
1635
1636 if let Err(err) = thread.join() {
1637 std::panic::resume_unwind(err);
1638 }
1639 }
1640
1641 #[test]
1642 fn non_send_drop_from_same_thread() {
1643 let mut world = World::default();
1644 world.insert_non_send(NonSendA::default());
1645 drop(world);
1646 }
1647
1648 #[test]
1649 fn insert_overwrite_drop() {
1650 let (dropck1, dropped1) = DropCk::new_pair();
1651 let (dropck2, dropped2) = DropCk::new_pair();
1652 let mut world = World::default();
1653 world.spawn(dropck1).insert(dropck2);
1654 assert_eq!(dropped1.load(Ordering::Relaxed), 1);
1655 assert_eq!(dropped2.load(Ordering::Relaxed), 0);
1656 drop(world);
1657 assert_eq!(dropped1.load(Ordering::Relaxed), 1);
1658 assert_eq!(dropped2.load(Ordering::Relaxed), 1);
1659 }
1660
1661 #[test]
1662 fn insert_overwrite_drop_sparse() {
1663 let (dropck1, dropped1) = DropCk::new_pair();
1664 let (dropck2, dropped2) = DropCk::new_pair();
1665 let mut world = World::default();
1666
1667 world
1668 .spawn(DropCkSparse(dropck1))
1669 .insert(DropCkSparse(dropck2));
1670 assert_eq!(dropped1.load(Ordering::Relaxed), 1);
1671 assert_eq!(dropped2.load(Ordering::Relaxed), 0);
1672 drop(world);
1673 assert_eq!(dropped1.load(Ordering::Relaxed), 1);
1674 assert_eq!(dropped2.load(Ordering::Relaxed), 1);
1675 }
1676
1677 #[test]
1678 fn clear_entities() {
1679 let mut world = World::default();
1680
1681 world.insert_resource(ResA(0));
1682 world.spawn(A(1));
1683 world.spawn(SparseStored(1));
1684
1685 let mut q1 = world.query::<&A>();
1686 let mut q2 = world.query::<&SparseStored>();
1687
1688 assert_eq!(q1.query(&world).count(), 1);
1689 assert_eq!(q2.query(&world).count(), 1);
1690
1691 world.clear_entities();
1692
1693 assert_eq!(
1694 q1.query(&world).count(),
1695 0,
1696 "world should not contain table components"
1697 );
1698 assert_eq!(
1699 q2.query(&world).count(),
1700 0,
1701 "world should not contain sparse set components"
1702 );
1703 assert_eq!(
1704 world.get_resource::<ResA>(),
1705 None,
1706 "world should not contain resources"
1707 );
1708 }
1709
1710 #[test]
1711 fn test_is_archetypal_size_hints() {
1712 let mut world = World::default();
1713 macro_rules! query_min_size {
1714 ($query:ty, $filter:ty) => {
1715 world
1716 .query_filtered::<$query, $filter>()
1717 .iter(&world)
1718 .size_hint()
1719 .0
1720 };
1721 }
1722
1723 world.spawn((A(1), B(1), C));
1724 world.spawn((A(1), C));
1725 world.spawn((A(1), B(1)));
1726 world.spawn((B(1), C));
1727 world.spawn(A(1));
1728 world.spawn(C);
1729 assert_eq!(2, query_min_size![(), (With<A>, Without<B>)]);
1730 assert_eq!(3, query_min_size![&B, Or<(With<A>, With<C>)>]);
1731 assert_eq!(1, query_min_size![&B, (With<A>, With<C>)]);
1732 assert_eq!(1, query_min_size![(&A, &B), With<C>]);
1733 assert_eq!(4, query_min_size![&A, ()], "Simple Archetypal");
1734 assert_eq!(4, query_min_size![Ref<A>, ()]);
1735 assert_eq!(0, query_min_size![(), Added<A>], "Simple Added");
1738 assert_eq!(0, query_min_size![(), Changed<A>], "Simple Changed");
1739 assert_eq!(0, query_min_size![(&A, &B), Changed<A>]);
1740 assert_eq!(0, query_min_size![&A, (Changed<A>, With<B>)]);
1741 assert_eq!(0, query_min_size![(&A, &B), Or<(Changed<A>, Changed<B>)>]);
1742 }
1743
1744 #[test]
1745 fn insert_batch() {
1746 let mut world = World::default();
1747 let e0 = world.spawn(A(0)).id();
1748 let e1 = world.spawn(B(0)).id();
1749
1750 let values = vec![(e0, (A(1), B(0))), (e1, (A(0), B(1)))];
1751
1752 world.insert_batch(values);
1753
1754 assert_eq!(
1755 world.get::<A>(e0),
1756 Some(&A(1)),
1757 "first entity's A component should have been replaced"
1758 );
1759 assert_eq!(
1760 world.get::<B>(e0),
1761 Some(&B(0)),
1762 "first entity should have received B component"
1763 );
1764 assert_eq!(
1765 world.get::<A>(e1),
1766 Some(&A(0)),
1767 "second entity should have received A component"
1768 );
1769 assert_eq!(
1770 world.get::<B>(e1),
1771 Some(&B(1)),
1772 "second entity's B component should have been replaced"
1773 );
1774 }
1775
1776 #[test]
1777 fn insert_batch_same_archetype() {
1778 let mut world = World::default();
1779 let e0 = world.spawn((A(0), B(0))).id();
1780 let e1 = world.spawn((A(0), B(0))).id();
1781 let e2 = world.spawn(B(0)).id();
1782
1783 let values = vec![(e0, (B(1), C)), (e1, (B(2), C)), (e2, (B(3), C))];
1784
1785 world.insert_batch(values);
1786 let mut query = world.query::<(Option<&A>, &B, &C)>();
1787 let component_values = query.get_many(&world, [e0, e1, e2]).unwrap();
1788
1789 assert_eq!(
1790 component_values,
1791 [(Some(&A(0)), &B(1), &C), (Some(&A(0)), &B(2), &C), (None, &B(3), &C)],
1792 "all entities should have had their B component replaced, received C component, and had their A component (or lack thereof) unchanged"
1793 );
1794 }
1795
1796 #[test]
1797 fn insert_batch_if_new() {
1798 let mut world = World::default();
1799 let e0 = world.spawn(A(0)).id();
1800 let e1 = world.spawn(B(0)).id();
1801
1802 let values = vec![(e0, (A(1), B(0))), (e1, (A(0), B(1)))];
1803
1804 world.insert_batch_if_new(values);
1805
1806 assert_eq!(
1807 world.get::<A>(e0),
1808 Some(&A(0)),
1809 "first entity's A component should not have been replaced"
1810 );
1811 assert_eq!(
1812 world.get::<B>(e0),
1813 Some(&B(0)),
1814 "first entity should have received B component"
1815 );
1816 assert_eq!(
1817 world.get::<A>(e1),
1818 Some(&A(0)),
1819 "second entity should have received A component"
1820 );
1821 assert_eq!(
1822 world.get::<B>(e1),
1823 Some(&B(0)),
1824 "second entity's B component should not have been replaced"
1825 );
1826 }
1827
1828 #[test]
1829 fn try_insert_batch() {
1830 let mut world = World::default();
1831 let e0 = world.spawn(A(0)).id();
1832 let e1 = Entity::from_raw_u32(10_000).unwrap();
1833
1834 let values = vec![(e0, (A(1), B(0))), (e1, (A(0), B(1)))];
1835
1836 let error = world.try_insert_batch(values).unwrap_err();
1837
1838 assert_eq!(e1, error.entities[0]);
1839
1840 assert_eq!(
1841 world.get::<A>(e0),
1842 Some(&A(1)),
1843 "first entity's A component should have been replaced"
1844 );
1845 assert_eq!(
1846 world.get::<B>(e0),
1847 Some(&B(0)),
1848 "first entity should have received B component"
1849 );
1850 }
1851
1852 #[test]
1853 fn try_insert_batch_if_new() {
1854 let mut world = World::default();
1855 let e0 = world.spawn(A(0)).id();
1856 let e1 = Entity::from_raw_u32(10_000).unwrap();
1857
1858 let values = vec![(e0, (A(1), B(0))), (e1, (A(0), B(1)))];
1859
1860 let error = world.try_insert_batch_if_new(values).unwrap_err();
1861
1862 assert_eq!(e1, error.entities[0]);
1863
1864 assert_eq!(
1865 world.get::<A>(e0),
1866 Some(&A(0)),
1867 "first entity's A component should not have been replaced"
1868 );
1869 assert_eq!(
1870 world.get::<B>(e0),
1871 Some(&B(0)),
1872 "first entity should have received B component"
1873 );
1874 }
1875
1876 #[derive(Default)]
1877 struct CaptureMapper(Vec<Entity>);
1878 impl EntityMapper for CaptureMapper {
1879 fn get_mapped(&mut self, source: Entity) -> Entity {
1880 self.0.push(source);
1881 source
1882 }
1883
1884 fn set_mapped(&mut self, _source: Entity, _target: Entity) {}
1885 }
1886
1887 #[test]
1888 fn map_struct_entities() {
1889 #[derive(Component)]
1890 #[expect(
1891 unused,
1892 reason = "extra fields are used to ensure the derive works properly"
1893 )]
1894 struct Foo(usize, #[entities] Entity);
1895
1896 #[derive(Component)]
1897 #[expect(
1898 unused,
1899 reason = "extra fields are used to ensure the derive works properly"
1900 )]
1901 struct Bar {
1902 #[entities]
1903 a: Entity,
1904 b: usize,
1905 #[entities]
1906 c: Vec<Entity>,
1907 }
1908
1909 let mut world = World::new();
1910 let e1 = world.spawn_empty().id();
1911 let e2 = world.spawn_empty().id();
1912 let e3 = world.spawn_empty().id();
1913
1914 let mut foo = Foo(1, e1);
1915 let mut mapper = CaptureMapper::default();
1916 Component::map_entities(&mut foo, &mut mapper);
1917 assert_eq!(&mapper.0, &[e1]);
1918
1919 let mut bar = Bar {
1920 a: e1,
1921 b: 1,
1922 c: vec![e2, e3],
1923 };
1924 let mut mapper = CaptureMapper::default();
1925 Component::map_entities(&mut bar, &mut mapper);
1926 assert_eq!(&mapper.0, &[e1, e2, e3]);
1927 }
1928
1929 #[test]
1930 fn map_enum_entities() {
1931 #[derive(Component)]
1932 #[expect(
1933 unused,
1934 reason = "extra fields are used to ensure the derive works properly"
1935 )]
1936 enum Foo {
1937 Bar(usize, #[entities] Entity),
1938 Baz {
1939 #[entities]
1940 a: Entity,
1941 b: usize,
1942 #[entities]
1943 c: Vec<Entity>,
1944 },
1945 }
1946
1947 let mut world = World::new();
1948 let e1 = world.spawn_empty().id();
1949 let e2 = world.spawn_empty().id();
1950 let e3 = world.spawn_empty().id();
1951
1952 let mut foo = Foo::Bar(1, e1);
1953 let mut mapper = CaptureMapper::default();
1954 Component::map_entities(&mut foo, &mut mapper);
1955 assert_eq!(&mapper.0, &[e1]);
1956
1957 let mut foo = Foo::Baz {
1958 a: e1,
1959 b: 1,
1960 c: vec![e2, e3],
1961 };
1962 let mut mapper = CaptureMapper::default();
1963 Component::map_entities(&mut foo, &mut mapper);
1964 assert_eq!(&mapper.0, &[e1, e2, e3]);
1965 }
1966
1967 #[expect(
1968 dead_code,
1969 reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."
1970 )]
1971 #[derive(Component)]
1972 struct ComponentA(u32);
1973
1974 #[expect(
1975 dead_code,
1976 reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."
1977 )]
1978 #[derive(Component)]
1979 struct ComponentB(u32);
1980
1981 #[derive(Bundle)]
1982 struct Simple(ComponentA);
1983
1984 #[expect(
1985 dead_code,
1986 reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."
1987 )]
1988 #[derive(Bundle)]
1989 struct Tuple(Simple, ComponentB);
1990
1991 #[expect(
1992 dead_code,
1993 reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."
1994 )]
1995 #[derive(Bundle)]
1996 struct Record {
1997 field0: Simple,
1998 field1: ComponentB,
1999 }
2000
2001 #[expect(
2002 dead_code,
2003 reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."
2004 )]
2005 #[derive(Component)]
2006 struct MyEntities {
2007 #[entities]
2008 entities: Vec<Entity>,
2009 #[entities]
2010 another_one: Entity,
2011 #[entities]
2012 maybe_entity: Option<Entity>,
2013 something_else: String,
2014 }
2015
2016 #[expect(
2017 dead_code,
2018 reason = "This struct is used as a compilation test to test the derive macros, and as such is intentionally never constructed."
2019 )]
2020 #[derive(Component)]
2021 struct MyEntitiesTuple(#[entities] Vec<Entity>, #[entities] Entity, usize);
2022
2023 #[test]
2024 fn clone_entities() {
2025 use crate::entity::{ComponentCloneCtx, SourceComponent};
2026
2027 #[expect(
2028 dead_code,
2029 reason = "This struct is used as a compilation test to test the derive macros, and as such this field is intentionally never used."
2030 )]
2031 #[derive(Component)]
2032 #[component(clone_behavior = Ignore)]
2033 struct IgnoreClone;
2034
2035 #[expect(
2036 dead_code,
2037 reason = "This struct is used as a compilation test to test the derive macros, and as such this field is intentionally never used."
2038 )]
2039 #[derive(Component)]
2040 #[component(clone_behavior = Default)]
2041 struct DefaultClone;
2042
2043 #[expect(
2044 dead_code,
2045 reason = "This struct is used as a compilation test to test the derive macros, and as such this field is intentionally never used."
2046 )]
2047 #[derive(Component)]
2048 #[component(clone_behavior = Custom(custom_clone))]
2049 struct CustomClone;
2050
2051 #[expect(
2052 dead_code,
2053 reason = "This struct is used as a compilation test to test the derive macros, and as such this field is intentionally never used."
2054 )]
2055 #[derive(Component, Clone)]
2056 #[component(clone_behavior = clone::<Self>())]
2057 struct CloneFunction;
2058
2059 #[expect(
2060 dead_code,
2061 reason = "This struct is used as a compilation test to test the derive macros, and as such this field is intentionally never used."
2062 )]
2063 fn custom_clone(_source: &SourceComponent, _ctx: &mut ComponentCloneCtx) {}
2064 }
2065
2066 #[test]
2067 #[cfg_attr(miri, ignore = "This test takes ~460s on CI")]
2068 fn queue_register_component_toctou() {
2069 for _ in 0..1000 {
2070 let w = World::new();
2071
2072 std::thread::scope(|s| {
2073 let c1 = s.spawn(|| w.components_queue().queue_register_component::<A>());
2074 let c2 = s.spawn(|| w.components_queue().queue_register_component::<A>());
2075 assert_eq!(c1.join().unwrap(), c2.join().unwrap());
2076 });
2077 }
2078 }
2079}