fixed tests

This commit is contained in:
Elliott Pierce 2025-05-31 11:40:23 -04:00
parent c100f9e6ed
commit ea9a3bee7f
12 changed files with 54 additions and 27 deletions

View File

@ -1579,7 +1579,7 @@ mod tests {
app.add_systems(EnterMainMenu, (foo, bar)); app.add_systems(EnterMainMenu, (foo, bar));
app.world_mut().run_schedule(EnterMainMenu); app.world_mut().run_schedule(EnterMainMenu);
assert_eq!(app.world().entities().len(), 2); assert_eq!(app.world().entities().count_active(), 2);
} }
#[test] #[test]

View File

@ -22,6 +22,6 @@ impl EntityCountDiagnosticsPlugin {
pub const ENTITY_COUNT: DiagnosticPath = DiagnosticPath::const_new("entity_count"); pub const ENTITY_COUNT: DiagnosticPath = DiagnosticPath::const_new("entity_count");
pub fn diagnostic_system(mut diagnostics: Diagnostics, entities: &Entities) { pub fn diagnostic_system(mut diagnostics: Diagnostics, entities: &Entities) {
diagnostics.add_measurement(&Self::ENTITY_COUNT, || entities.len() as f64); diagnostics.add_measurement(&Self::ENTITY_COUNT, || entities.count_active() as f64);
} }
} }

View File

@ -1,9 +1,3 @@
use alloc::{borrow::ToOwned, boxed::Box, collections::VecDeque, vec::Vec};
use bevy_platform::collections::{HashMap, HashSet};
use bevy_ptr::{Ptr, PtrMut};
use bumpalo::Bump;
use core::any::TypeId;
use crate::{ use crate::{
archetype::Archetype, archetype::Archetype,
bundle::Bundle, bundle::Bundle,
@ -13,6 +7,11 @@ use crate::{
relationship::RelationshipHookMode, relationship::RelationshipHookMode,
world::World, world::World,
}; };
use alloc::{borrow::ToOwned, boxed::Box, collections::VecDeque, vec::Vec};
use bevy_platform::collections::{HashMap, HashSet};
use bevy_ptr::{Ptr, PtrMut};
use bumpalo::Bump;
use core::any::TypeId;
use super::EntitiesAllocator; use super::EntitiesAllocator;
@ -348,6 +347,8 @@ pub struct EntityCloner {
move_components: bool, move_components: bool,
linked_cloning: bool, linked_cloning: bool,
default_clone_fn: ComponentCloneFn, default_clone_fn: ComponentCloneFn,
/// Represents a queue of entities to clone.
/// These will have targets in the entity map, which will need to be constructed.
clone_queue: VecDeque<Entity>, clone_queue: VecDeque<Entity>,
deferred_commands: VecDeque<Box<dyn FnOnce(&mut World, &mut dyn EntityMapper)>>, deferred_commands: VecDeque<Box<dyn FnOnce(&mut World, &mut dyn EntityMapper)>>,
} }
@ -458,6 +459,10 @@ impl EntityCloner {
relationship_hook_insert_mode: RelationshipHookMode, relationship_hook_insert_mode: RelationshipHookMode,
) -> Entity { ) -> Entity {
let target = mapper.get_mapped(source); let target = mapper.get_mapped(source);
// The target may need to be constructed if it hasn't been already.
// If this fails, it either didn't need to be constructed (ok) or doesn't exist (caught better later).
let _ = world.construct_empty(target);
// PERF: reusing allocated space across clones would be more efficient. Consider an allocation model similar to `Commands`. // PERF: reusing allocated space across clones would be more efficient. Consider an allocation model similar to `Commands`.
let bundle_scratch_allocator = Bump::new(); let bundle_scratch_allocator = Bump::new();
let mut bundle_scratch: BundleScratch; let mut bundle_scratch: BundleScratch;
@ -536,6 +541,7 @@ impl EntityCloner {
} }
world.flush(); world.flush();
world.entity_mut(target);
for deferred in self.deferred_commands.drain(..) { for deferred in self.deferred_commands.drain(..) {
(deferred)(world, mapper); (deferred)(world, mapper);

View File

@ -662,6 +662,9 @@ impl SparseSetIndex for Entity {
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct EntitiesAllocator { pub struct EntitiesAllocator {
free: Vec<Entity>, free: Vec<Entity>,
/// This is continually subtracted from.
/// If it wraps to a very large number, it will be outside the bounds of `free`,
/// and a new row will be needed.
free_len: AtomicU32, free_len: AtomicU32,
next_row: AtomicU32, next_row: AtomicU32,
} }
@ -675,7 +678,12 @@ impl EntitiesAllocator {
} }
pub(crate) fn free(&mut self, freed: Entity) { pub(crate) fn free(&mut self, freed: Entity) {
self.free.truncate(*self.free_len.get_mut() as usize); let expected_len = *self.free_len.get_mut() as usize;
if expected_len > self.free.len() {
self.free.clear();
} else {
self.free.truncate(expected_len);
}
self.free.push(freed); self.free.push(freed);
*self.free_len.get_mut() = self.free.len() as u32; *self.free_len.get_mut() = self.free.len() as u32;
} }
@ -846,7 +854,7 @@ impl Entities {
} }
let index = row.index() as usize; let index = row.index() as usize;
if self.meta.len() >= index { if self.meta.len() <= index {
// TODO: hint unlikely once stable. // TODO: hint unlikely once stable.
expand(&mut self.meta, index + 1); expand(&mut self.meta, index + 1);
} }
@ -985,6 +993,19 @@ impl Entities {
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.len() == 0 self.len() == 0
} }
/// Counts the number of entities currently participating in the world, those that have locations.
pub fn count_active(&self) -> u32 {
self.meta
.iter()
.filter(|meta| meta.location.is_some())
.count() as u32
}
/// Returns true if there are any entities active in the world, entities that have locations.
pub fn any_active(&self) -> bool {
self.meta.iter().any(|meta| meta.location.is_some())
}
} }
/// An error that occurs when a specified [`Entity`] can not be constructed. /// An error that occurs when a specified [`Entity`] can not be constructed.

View File

@ -358,9 +358,9 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
let e = world.spawn((TableStored("abc"), A(123))).id(); let e = world.spawn((TableStored("abc"), A(123))).id();
let f = world.spawn((TableStored("def"), A(456))).id(); let f = world.spawn((TableStored("def"), A(456))).id();
assert_eq!(world.entities.len(), 2); assert_eq!(world.entities.count_active(), 2);
assert!(world.despawn(e)); assert!(world.despawn(e));
assert_eq!(world.entities.len(), 1); assert_eq!(world.entities.count_active(), 1);
assert!(world.get::<TableStored>(e).is_none()); assert!(world.get::<TableStored>(e).is_none());
assert!(world.get::<A>(e).is_none()); assert!(world.get::<A>(e).is_none());
assert_eq!(world.get::<TableStored>(f).unwrap().0, "def"); assert_eq!(world.get::<TableStored>(f).unwrap().0, "def");
@ -373,9 +373,9 @@ mod tests {
let e = world.spawn((TableStored("abc"), SparseStored(123))).id(); let e = world.spawn((TableStored("abc"), SparseStored(123))).id();
let f = world.spawn((TableStored("def"), SparseStored(456))).id(); let f = world.spawn((TableStored("def"), SparseStored(456))).id();
assert_eq!(world.entities.len(), 2); assert_eq!(world.entities.count_active(), 2);
assert!(world.despawn(e)); assert!(world.despawn(e));
assert_eq!(world.entities.len(), 1); assert_eq!(world.entities.count_active(), 1);
assert!(world.get::<TableStored>(e).is_none()); assert!(world.get::<TableStored>(e).is_none());
assert!(world.get::<SparseStored>(e).is_none()); assert!(world.get::<SparseStored>(e).is_none());
assert_eq!(world.get::<TableStored>(f).unwrap().0, "def"); assert_eq!(world.get::<TableStored>(f).unwrap().0, "def");
@ -1610,7 +1610,7 @@ mod tests {
assert_eq!(q1.iter(&world).len(), 1); assert_eq!(q1.iter(&world).len(), 1);
assert_eq!(q2.iter(&world).len(), 1); assert_eq!(q2.iter(&world).len(), 1);
assert_eq!(world.entities().len(), 2); assert_eq!(world.entities().count_active(), 2);
world.clear_entities(); world.clear_entities();
@ -1625,7 +1625,7 @@ mod tests {
"world should not contain sparse set components" "world should not contain sparse set components"
); );
assert_eq!( assert_eq!(
world.entities().len(), world.entities().count_active(),
0, 0,
"world should not have any entities" "world should not have any entities"
); );

View File

@ -1079,7 +1079,7 @@ mod tests {
world.spawn(A).flush(); world.spawn(A).flush();
assert_eq!(vec!["add_2", "add_1"], world.resource::<Order>().0); assert_eq!(vec!["add_2", "add_1"], world.resource::<Order>().0);
// Our A entity plus our two observers // Our A entity plus our two observers
assert_eq!(world.entities().len(), 3); assert_eq!(world.entities().count_active(), 3);
} }
#[test] #[test]

View File

@ -2321,7 +2321,7 @@ mod tests {
.spawn((W(1u32), W(2u64))) .spawn((W(1u32), W(2u64)))
.id(); .id();
command_queue.apply(&mut world); command_queue.apply(&mut world);
assert_eq!(world.entities().len(), 1); assert_eq!(world.entities().count_active(), 1);
let results = world let results = world
.query::<(&W<u32>, &W<u64>)>() .query::<(&W<u32>, &W<u64>)>()
.iter(&world) .iter(&world)

View File

@ -423,9 +423,9 @@ mod tests {
#[test] #[test]
fn command_processing() { fn command_processing() {
let mut world = World::new(); let mut world = World::new();
assert_eq!(world.entities.len(), 0); assert_eq!(world.entities.count_active(), 0);
world.run_system_once(spawn_entity).unwrap(); world.run_system_once(spawn_entity).unwrap();
assert_eq!(world.entities.len(), 1); assert_eq!(world.entities.count_active(), 1);
} }
#[test] #[test]

View File

@ -657,9 +657,9 @@ mod tests {
let exclusive_system_id = world.register_system(|world: &mut World| { let exclusive_system_id = world.register_system(|world: &mut World| {
world.spawn_empty(); world.spawn_empty();
}); });
let entity_count = world.entities.len(); let entity_count = world.entities.count_active();
let _ = world.run_system(exclusive_system_id); let _ = world.run_system(exclusive_system_id);
assert_eq!(world.entities.len(), entity_count + 1); assert_eq!(world.entities.count_active(), entity_count + 1);
} }
#[test] #[test]

View File

@ -420,12 +420,12 @@ mod test {
let mut world = World::new(); let mut world = World::new();
queue.apply(&mut world); queue.apply(&mut world);
assert_eq!(world.entities().len(), 2); assert_eq!(world.entities().count_active(), 2);
// The previous call to `apply` cleared the queue. // The previous call to `apply` cleared the queue.
// This call should do nothing. // This call should do nothing.
queue.apply(&mut world); queue.apply(&mut world);
assert_eq!(world.entities().len(), 2); assert_eq!(world.entities().count_active(), 2);
} }
#[expect( #[expect(
@ -459,7 +459,7 @@ mod test {
queue.push(SpawnCommand); queue.push(SpawnCommand);
queue.push(SpawnCommand); queue.push(SpawnCommand);
queue.apply(&mut world); queue.apply(&mut world);
assert_eq!(world.entities().len(), 3); assert_eq!(world.entities().count_active(), 3);
} }
#[test] #[test]

View File

@ -5818,7 +5818,6 @@ mod tests {
); );
world.commands().queue(count_flush); world.commands().queue(count_flush);
let entity = world.spawn_empty().id(); let entity = world.spawn_empty().id();
assert_eq!(world.resource::<TestFlush>().0, 1);
world.commands().queue(count_flush); world.commands().queue(count_flush);
let mut a = world.entity_mut(entity); let mut a = world.entity_mut(entity);
a.trigger(TestEvent); a.trigger(TestEvent);

View File

@ -3645,7 +3645,7 @@ impl fmt::Debug for World {
// Accessing any data stored in the world would be unsound. // Accessing any data stored in the world would be unsound.
f.debug_struct("World") f.debug_struct("World")
.field("id", &self.id) .field("id", &self.id)
.field("entity_count", &self.entities.len()) .field("entity_count", &self.entities.count_active())
.field("archetype_count", &self.archetypes.len()) .field("archetype_count", &self.archetypes.len())
.field("component_count", &self.components.len()) .field("component_count", &self.components.len())
.field("resource_count", &self.storages.resources.len()) .field("resource_count", &self.storages.resources.len())
@ -4349,6 +4349,7 @@ mod tests {
.is_ok()); .is_ok());
world.entity_mut(e1).despawn(); world.entity_mut(e1).despawn();
assert!(world.get_entity_mut(e2).is_ok());
assert!(matches!( assert!(matches!(
world.get_entity_mut(e1).map(|_| {}), world.get_entity_mut(e1).map(|_| {}),