From 99079117804da6311dee44e1773412fa0138602f Mon Sep 17 00:00:00 2001 From: harudagondi Date: Tue, 28 Jun 2022 16:37:02 +0800 Subject: [PATCH 1/6] Update impl to support archetypal filters --- crates/bevy_ecs/src/query/filter.rs | 15 +++++++++++++++ crates/bevy_ecs/src/query/iter.rs | 11 +++-------- crates/bevy_ecs/src/query/mod.rs | 14 ++++++++++++++ 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 1626dd43fc013..8b125964f0962 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -739,3 +739,18 @@ impl_tick_filter!( ChangedFetch, ComponentTicks::is_changed ); + +pub trait ArchetypeFilter {} + +impl ArchetypeFilter for With {} +impl ArchetypeFilter for Without {} + +macro_rules! impl_archetype_filter_tuple { + ($($filter: ident),*) => { + impl<$($filter: ArchetypeFilter),*> ArchetypeFilter for ($($filter,)*) {} + + impl<$($filter: ArchetypeFilter),*> ArchetypeFilter for Or<($($filter,)*)> {} + }; +} + +all_tuples!(impl_archetype_filter_tuple, 0, 15, F); diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index b3c304f231aa6..58c6d40b2a708 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -2,7 +2,7 @@ use crate::{ archetype::{ArchetypeId, Archetypes}, entity::{Entities, Entity}, prelude::World, - query::{Fetch, QueryState, WorldQuery}, + query::{ArchetypeFilter, Fetch, QueryState, WorldQuery}, storage::{TableId, Tables}, }; use std::{borrow::Borrow, iter::FusedIterator, marker::PhantomData, mem::MaybeUninit}; @@ -346,15 +346,10 @@ where } } -// NOTE: We can cheaply implement this for unfiltered Queries because we have: -// (1) pre-computed archetype matches -// (2) each archetype pre-computes length -// (3) there are no per-entity filters -// TODO: add an ArchetypeOnlyFilter that enables us to implement this for filters like With. -// This would need to be added to all types that implement Filter with Filter::IS_ARCHETYPAL = true -impl<'w, 's, Q: WorldQuery, QF> ExactSizeIterator for QueryIter<'w, 's, Q, QF, ()> +impl<'w, 's, Q: WorldQuery, QF, F> ExactSizeIterator for QueryIter<'w, 's, Q, QF, F> where QF: Fetch<'w, State = Q::State>, + F: WorldQuery + ArchetypeFilter, { fn len(&self) -> usize { self.query_state diff --git a/crates/bevy_ecs/src/query/mod.rs b/crates/bevy_ecs/src/query/mod.rs index d037c4a541974..4547ce188a777 100644 --- a/crates/bevy_ecs/src/query/mod.rs +++ b/crates/bevy_ecs/src/query/mod.rs @@ -51,6 +51,20 @@ mod tests { assert_eq!(values, vec![&B(3)]); } + #[test] + fn query_filtered_len() { + let mut world = World::new(); + world.spawn().insert_bundle((A(1), B(1))); + world.spawn().insert_bundle((A(2),)); + world.spawn().insert_bundle((A(3),)); + + let mut values = world.query_filtered::<&A, With>(); + assert_eq!(values.iter(&world).len(), 1); + + let mut values = world.query_filtered::<&A, Without>(); + assert_eq!(values.iter(&world).len(), 2); + } + #[test] fn query_iter_combinations() { let mut world = World::new(); From 0d4632afcc922f66919b8a8afac253c00f8053d5 Mon Sep 17 00:00:00 2001 From: harudagondi Date: Tue, 28 Jun 2022 20:19:12 +0800 Subject: [PATCH 2/6] Added more tests --- crates/bevy_ecs/src/query/mod.rs | 56 ++++++++++++++++++- .../ui/query_exact_sized_iterator_safety.rs | 16 ++++++ .../query_exact_sized_iterator_safety.stderr | 51 +++++++++++++++++ 3 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.rs create mode 100644 crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.stderr diff --git a/crates/bevy_ecs/src/query/mod.rs b/crates/bevy_ecs/src/query/mod.rs index 4547ce188a777..6fc625509745f 100644 --- a/crates/bevy_ecs/src/query/mod.rs +++ b/crates/bevy_ecs/src/query/mod.rs @@ -58,11 +58,63 @@ mod tests { world.spawn().insert_bundle((A(2),)); world.spawn().insert_bundle((A(3),)); + fn are_sizes_equal_to(iter: impl ExactSizeIterator, n: usize) -> bool { + [ + iter.size_hint().0, + iter.size_hint().1.unwrap(), + iter.len(), + iter.count(), + ] + .into_iter() + .all(|x| x == n) + } + let mut values = world.query_filtered::<&A, With>(); - assert_eq!(values.iter(&world).len(), 1); + assert!(are_sizes_equal_to(values.iter(&world), 1)); + let mut values = world.query_filtered::<&A, Without>(); + assert!(are_sizes_equal_to(values.iter(&world), 2)); + let mut world = World::new(); + world.spawn().insert_bundle((A(1), B(1), C(1))); + world.spawn().insert_bundle((A(2), B(2))); + world.spawn().insert_bundle((A(3), B(3))); + world.spawn().insert_bundle((A(4), C(4))); + world.spawn().insert_bundle((A(5), C(5))); + world.spawn().insert_bundle((A(6), C(6))); + world.spawn().insert_bundle((A(7),)); + world.spawn().insert_bundle((A(8),)); + world.spawn().insert_bundle((A(9),)); + world.spawn().insert_bundle((A(10),)); + + // With/Without for B and C + let mut values = world.query_filtered::<&A, With>(); + assert!(are_sizes_equal_to(values.iter(&world), 3)); + let mut values = world.query_filtered::<&A, With>(); + assert!(are_sizes_equal_to(values.iter(&world), 4)); let mut values = world.query_filtered::<&A, Without>(); - assert_eq!(values.iter(&world).len(), 2); + assert!(are_sizes_equal_to(values.iter(&world), 7)); + let mut values = world.query_filtered::<&A, Without>(); + assert!(are_sizes_equal_to(values.iter(&world), 6)); + + // With/Without (And) combinations + let mut values = world.query_filtered::<&A, (With, With)>(); + assert!(are_sizes_equal_to(values.iter(&world), 1)); + let mut values = world.query_filtered::<&A, (With, Without)>(); + assert!(are_sizes_equal_to(values.iter(&world), 2)); + let mut values = world.query_filtered::<&A, (Without, With)>(); + assert!(are_sizes_equal_to(values.iter(&world), 3)); + let mut values = world.query_filtered::<&A, (Without, Without)>(); + assert!(are_sizes_equal_to(values.iter(&world), 4)); + + // With/Without Or<()> combinations + let mut values = world.query_filtered::<&A, Or<(With, With)>>(); + assert!(are_sizes_equal_to(values.iter(&world), 6)); + let mut values = world.query_filtered::<&A, Or<(With, Without)>>(); + assert!(are_sizes_equal_to(values.iter(&world), 7)); + let mut values = world.query_filtered::<&A, Or<(Without, With)>>(); + assert!(are_sizes_equal_to(values.iter(&world), 8)); + let mut values = world.query_filtered::<&A, Or<(Without, Without)>>(); + assert!(are_sizes_equal_to(values.iter(&world), 9)); } #[test] diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.rs b/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.rs new file mode 100644 index 0000000000000..181b8f65b6518 --- /dev/null +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.rs @@ -0,0 +1,16 @@ +use bevy_ecs::prelude::*; + +#[derive(Component)] +struct Foo; + +fn on_changed(query: Query<&Foo, Changed>) { + // this should fail to compile + println!("{}", query.iter().len()) +} + +fn on_added(query: Query<&Foo, Added>) { + // this should fail to compile + println!("{}", query.iter().len()) +} + +fn main() {} \ No newline at end of file diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.stderr b/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.stderr new file mode 100644 index 0000000000000..0bc5aef5d94f2 --- /dev/null +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.stderr @@ -0,0 +1,51 @@ +error[E0599]: the method `len` exists for struct `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Changed>`, but its trait bounds were not satisfied + --> tests/ui/query_exact_sized_iterator_safety.rs:8:33 + | +8 | println!("{}", query.iter().len()) + | ^^^ method cannot be called on `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Changed>` due to unsatisfied trait bounds + | + ::: C:/Programming/rust/bevy/crates/bevy_ecs/src/query/iter.rs:16:1 + | +16 | pub struct QueryIter<'w, 's, Q: WorldQuery, QF: Fetch<'w, State = Q::State>, F: WorldQuery> { + | ------------------------------------------------------------------------------------------- doesn't satisfy `_: ExactSizeIterator` + | + ::: C:/Programming/rust/bevy/crates/bevy_ecs/src/query/filter.rs:703:1 + | +703 | / impl_tick_filter!( +704 | | /// A filter on a component that only retains results added or mutably dereferenced after the system last ran. +705 | | /// +706 | | /// A common use for this filter is avoiding redundant work when values have not changed. +... | +740 | | ComponentTicks::is_changed +741 | | ); + | |_- doesn't satisfy `bevy_ecs::query::Changed: ArchetypeFilter` + | + = note: the following trait bounds were not satisfied: + `bevy_ecs::query::Changed: ArchetypeFilter` + which is required by `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Changed>: ExactSizeIterator` + +error[E0599]: the method `len` exists for struct `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Added>`, but its trait bounds were not satisfied + --> tests/ui/query_exact_sized_iterator_safety.rs:13:33 + | +13 | println!("{}", query.iter().len()) + | ^^^ method cannot be called on `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Added>` due to unsatisfied trait bounds + | + ::: C:/Programming/rust/bevy/crates/bevy_ecs/src/query/iter.rs:16:1 + | +16 | pub struct QueryIter<'w, 's, Q: WorldQuery, QF: Fetch<'w, State = Q::State>, F: WorldQuery> { + | ------------------------------------------------------------------------------------------- doesn't satisfy `_: ExactSizeIterator` + | + ::: C:/Programming/rust/bevy/crates/bevy_ecs/src/query/filter.rs:668:1 + | +668 | / impl_tick_filter!( +669 | | /// A filter on a component that only retains results added after the system last ran. +670 | | /// +671 | | /// A common use for this filter is one-time initialization. +... | +700 | | ComponentTicks::is_added +701 | | ); + | |_- doesn't satisfy `bevy_ecs::query::Added: ArchetypeFilter` + | + = note: the following trait bounds were not satisfied: + `bevy_ecs::query::Added: ArchetypeFilter` + which is required by `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Added>: ExactSizeIterator` From d12f6bfbba785fa1acad35d94456199a126c023d Mon Sep 17 00:00:00 2001 From: harudagondi Date: Tue, 28 Jun 2022 21:09:42 +0800 Subject: [PATCH 3/6] Added docs for `ArchetypeFilter` --- crates/bevy_ecs/src/query/filter.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 8b125964f0962..ad79241b10980 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -740,6 +740,18 @@ impl_tick_filter!( ComponentTicks::is_changed ); +/// A marker trait to indicate that the filter works at an archetype level. +/// +/// This is needed to implement [`ExactSizeIterator`](std::iter::ExactSizeIterator) for +/// [`QueryIter`](crate::query::QueryIter) that contains archetype-level filters. +/// +/// The trait must only be implement for filters where its corresponding [`Fetch::IS_ARCHETYPAL`](crate::query::Fetch::IS_ARCHETYPAL) +/// is [`prim@true`]. As such, only the [`With`] and [`Without`] filters can implement the trait. +/// [Tuples](prim@tuple) and [`Or`] filters are automatically implemented with the trait only if its containing types +/// also implement the same trait. +/// +/// [`Added`] and [`Changed`] works with entities, and therefore are not archetypal. As such +/// they do not implement [`ArchetypeFilter`]. pub trait ArchetypeFilter {} impl ArchetypeFilter for With {} From 6a5a7e962f7cfc2a21b781ee9acb27cb009e3d2c Mon Sep 17 00:00:00 2001 From: harudagondi Date: Tue, 28 Jun 2022 21:34:50 +0800 Subject: [PATCH 4/6] Fix CI error of trybuild --- .../tests/ui/query_exact_sized_iterator_safety.stderr | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.stderr b/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.stderr index 0bc5aef5d94f2..38f275b05e136 100644 --- a/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.stderr +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.stderr @@ -4,12 +4,12 @@ error[E0599]: the method `len` exists for struct `QueryIter<'_, '_, &Foo, ReadFe 8 | println!("{}", query.iter().len()) | ^^^ method cannot be called on `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Changed>` due to unsatisfied trait bounds | - ::: C:/Programming/rust/bevy/crates/bevy_ecs/src/query/iter.rs:16:1 + ::: /home/runner/work/bevy/bevy/crates/bevy_ecs/src/query/iter.rs:16:1 | 16 | pub struct QueryIter<'w, 's, Q: WorldQuery, QF: Fetch<'w, State = Q::State>, F: WorldQuery> { | ------------------------------------------------------------------------------------------- doesn't satisfy `_: ExactSizeIterator` | - ::: C:/Programming/rust/bevy/crates/bevy_ecs/src/query/filter.rs:703:1 + ::: /home/runner/work/bevy/bevy/crates/bevy_ecs/src/query/filter.rs:703:1 | 703 | / impl_tick_filter!( 704 | | /// A filter on a component that only retains results added or mutably dereferenced after the system last ran. @@ -23,19 +23,18 @@ error[E0599]: the method `len` exists for struct `QueryIter<'_, '_, &Foo, ReadFe = note: the following trait bounds were not satisfied: `bevy_ecs::query::Changed: ArchetypeFilter` which is required by `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Changed>: ExactSizeIterator` - error[E0599]: the method `len` exists for struct `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Added>`, but its trait bounds were not satisfied --> tests/ui/query_exact_sized_iterator_safety.rs:13:33 | 13 | println!("{}", query.iter().len()) | ^^^ method cannot be called on `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Added>` due to unsatisfied trait bounds | - ::: C:/Programming/rust/bevy/crates/bevy_ecs/src/query/iter.rs:16:1 + ::: /home/runner/work/bevy/bevy/crates/bevy_ecs/src/query/iter.rs:16:1 | 16 | pub struct QueryIter<'w, 's, Q: WorldQuery, QF: Fetch<'w, State = Q::State>, F: WorldQuery> { | ------------------------------------------------------------------------------------------- doesn't satisfy `_: ExactSizeIterator` | - ::: C:/Programming/rust/bevy/crates/bevy_ecs/src/query/filter.rs:668:1 + ::: /home/runner/work/bevy/bevy/crates/bevy_ecs/src/query/filter.rs:668:1 | 668 | / impl_tick_filter!( 669 | | /// A filter on a component that only retains results added after the system last ran. @@ -48,4 +47,4 @@ error[E0599]: the method `len` exists for struct `QueryIter<'_, '_, &Foo, ReadFe | = note: the following trait bounds were not satisfied: `bevy_ecs::query::Added: ArchetypeFilter` - which is required by `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Added>: ExactSizeIterator` + which is required by `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Added>: ExactSizeIterator` \ No newline at end of file From 2f28544d7abbc83d39397211be00a0dbde8be14f Mon Sep 17 00:00:00 2001 From: harudagondi Date: Tue, 28 Jun 2022 22:07:08 +0800 Subject: [PATCH 5/6] Fix trybuild and prevent leaking of filepaths --- .../ui/query_exact_sized_iterator_safety.rs | 6 +- .../query_exact_sized_iterator_safety.stderr | 79 +++++++------------ 2 files changed, 33 insertions(+), 52 deletions(-) diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.rs b/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.rs index 181b8f65b6518..e634bd86d217c 100644 --- a/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.rs +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.rs @@ -5,12 +5,14 @@ struct Foo; fn on_changed(query: Query<&Foo, Changed>) { // this should fail to compile - println!("{}", query.iter().len()) + is_exact_size_iterator(query.iter()); } fn on_added(query: Query<&Foo, Added>) { // this should fail to compile - println!("{}", query.iter().len()) + is_exact_size_iterator(query.iter()); } +fn is_exact_size_iterator(_iter: T) {} + fn main() {} \ No newline at end of file diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.stderr b/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.stderr index 38f275b05e136..22ea39040d131 100644 --- a/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.stderr +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.stderr @@ -1,50 +1,29 @@ -error[E0599]: the method `len` exists for struct `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Changed>`, but its trait bounds were not satisfied - --> tests/ui/query_exact_sized_iterator_safety.rs:8:33 - | -8 | println!("{}", query.iter().len()) - | ^^^ method cannot be called on `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Changed>` due to unsatisfied trait bounds - | - ::: /home/runner/work/bevy/bevy/crates/bevy_ecs/src/query/iter.rs:16:1 - | -16 | pub struct QueryIter<'w, 's, Q: WorldQuery, QF: Fetch<'w, State = Q::State>, F: WorldQuery> { - | ------------------------------------------------------------------------------------------- doesn't satisfy `_: ExactSizeIterator` - | - ::: /home/runner/work/bevy/bevy/crates/bevy_ecs/src/query/filter.rs:703:1 - | -703 | / impl_tick_filter!( -704 | | /// A filter on a component that only retains results added or mutably dereferenced after the system last ran. -705 | | /// -706 | | /// A common use for this filter is avoiding redundant work when values have not changed. -... | -740 | | ComponentTicks::is_changed -741 | | ); - | |_- doesn't satisfy `bevy_ecs::query::Changed: ArchetypeFilter` - | - = note: the following trait bounds were not satisfied: - `bevy_ecs::query::Changed: ArchetypeFilter` - which is required by `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Changed>: ExactSizeIterator` -error[E0599]: the method `len` exists for struct `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Added>`, but its trait bounds were not satisfied - --> tests/ui/query_exact_sized_iterator_safety.rs:13:33 - | -13 | println!("{}", query.iter().len()) - | ^^^ method cannot be called on `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Added>` due to unsatisfied trait bounds - | - ::: /home/runner/work/bevy/bevy/crates/bevy_ecs/src/query/iter.rs:16:1 - | -16 | pub struct QueryIter<'w, 's, Q: WorldQuery, QF: Fetch<'w, State = Q::State>, F: WorldQuery> { - | ------------------------------------------------------------------------------------------- doesn't satisfy `_: ExactSizeIterator` - | - ::: /home/runner/work/bevy/bevy/crates/bevy_ecs/src/query/filter.rs:668:1 - | -668 | / impl_tick_filter!( -669 | | /// A filter on a component that only retains results added after the system last ran. -670 | | /// -671 | | /// A common use for this filter is one-time initialization. -... | -700 | | ComponentTicks::is_added -701 | | ); - | |_- doesn't satisfy `bevy_ecs::query::Added: ArchetypeFilter` - | - = note: the following trait bounds were not satisfied: - `bevy_ecs::query::Added: ArchetypeFilter` - which is required by `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Added>: ExactSizeIterator` \ No newline at end of file +error[E0277]: the trait bound `bevy_ecs::query::Changed: ArchetypeFilter` is not satisfied + --> tests/ui/query_exact_sized_iterator_safety.rs:8:28 + | +8 | is_exact_size_iterator(query.iter()); + | ---------------------- ^^^^^^^^^^^^ the trait `ArchetypeFilter` is not implemented for `bevy_ecs::query::Changed` + | | + | required by a bound introduced by this call + | + = note: required because of the requirements on the impl of `ExactSizeIterator` for `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Changed>` +note: required by a bound in `is_exact_size_iterator` + --> tests/ui/query_exact_sized_iterator_safety.rs:16:30 + | +16 | fn is_exact_size_iterator(_iter: T) {} + | ^^^^^^^^^^^^^^^^^ required by this bound in `is_exact_size_iterator` + +error[E0277]: the trait bound `bevy_ecs::query::Added: ArchetypeFilter` is not satisfied + --> tests/ui/query_exact_sized_iterator_safety.rs:13:28 + | +13 | is_exact_size_iterator(query.iter()); + | ---------------------- ^^^^^^^^^^^^ the trait `ArchetypeFilter` is not implemented for `bevy_ecs::query::Added` + | | + | required by a bound introduced by this call + | + = note: required because of the requirements on the impl of `ExactSizeIterator` for `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Added>` +note: required by a bound in `is_exact_size_iterator` + --> tests/ui/query_exact_sized_iterator_safety.rs:16:30 + | +16 | fn is_exact_size_iterator(_iter: T) {} + | ^^^^^^^^^^^^^^^^^ required by this bound in `is_exact_size_iterator` From 7eeb72caca8b8ec012d2fc547a279cc5535b399f Mon Sep 17 00:00:00 2001 From: harudagondi Date: Tue, 28 Jun 2022 23:59:31 +0800 Subject: [PATCH 6/6] Implemented some more tests --- crates/bevy_ecs/src/query/mod.rs | 125 ++++++++++++++++++++++++------- 1 file changed, 100 insertions(+), 25 deletions(-) diff --git a/crates/bevy_ecs/src/query/mod.rs b/crates/bevy_ecs/src/query/mod.rs index 6fc625509745f..74de0fd802bee 100644 --- a/crates/bevy_ecs/src/query/mod.rs +++ b/crates/bevy_ecs/src/query/mod.rs @@ -31,6 +31,8 @@ mod tests { struct B(usize); #[derive(Component, Debug, Eq, PartialEq, Clone, Copy)] struct C(usize); + #[derive(Component, Debug, Eq, PartialEq, Clone, Copy)] + struct D(usize); #[derive(Component, Debug, Eq, PartialEq, Clone, Copy)] #[component(storage = "SparseSet")] @@ -58,21 +60,18 @@ mod tests { world.spawn().insert_bundle((A(2),)); world.spawn().insert_bundle((A(3),)); - fn are_sizes_equal_to(iter: impl ExactSizeIterator, n: usize) -> bool { - [ - iter.size_hint().0, - iter.size_hint().1.unwrap(), - iter.len(), - iter.count(), - ] - .into_iter() - .all(|x| x == n) - } - let mut values = world.query_filtered::<&A, With>(); - assert!(are_sizes_equal_to(values.iter(&world), 1)); + let n = 1; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); let mut values = world.query_filtered::<&A, Without>(); - assert!(are_sizes_equal_to(values.iter(&world), 2)); + let n = 2; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); let mut world = World::new(); world.spawn().insert_bundle((A(1), B(1), C(1))); @@ -88,33 +87,109 @@ mod tests { // With/Without for B and C let mut values = world.query_filtered::<&A, With>(); - assert!(are_sizes_equal_to(values.iter(&world), 3)); + let n = 3; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); let mut values = world.query_filtered::<&A, With>(); - assert!(are_sizes_equal_to(values.iter(&world), 4)); + let n = 4; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); let mut values = world.query_filtered::<&A, Without>(); - assert!(are_sizes_equal_to(values.iter(&world), 7)); + let n = 7; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); let mut values = world.query_filtered::<&A, Without>(); - assert!(are_sizes_equal_to(values.iter(&world), 6)); + let n = 6; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); // With/Without (And) combinations let mut values = world.query_filtered::<&A, (With, With)>(); - assert!(are_sizes_equal_to(values.iter(&world), 1)); + let n = 1; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); let mut values = world.query_filtered::<&A, (With, Without)>(); - assert!(are_sizes_equal_to(values.iter(&world), 2)); + let n = 2; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); let mut values = world.query_filtered::<&A, (Without, With)>(); - assert!(are_sizes_equal_to(values.iter(&world), 3)); + let n = 3; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); let mut values = world.query_filtered::<&A, (Without, Without)>(); - assert!(are_sizes_equal_to(values.iter(&world), 4)); + let n = 4; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); // With/Without Or<()> combinations let mut values = world.query_filtered::<&A, Or<(With, With)>>(); - assert!(are_sizes_equal_to(values.iter(&world), 6)); + let n = 6; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); let mut values = world.query_filtered::<&A, Or<(With, Without)>>(); - assert!(are_sizes_equal_to(values.iter(&world), 7)); + let n = 7; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); let mut values = world.query_filtered::<&A, Or<(Without, With)>>(); - assert!(are_sizes_equal_to(values.iter(&world), 8)); + let n = 8; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); let mut values = world.query_filtered::<&A, Or<(Without, Without)>>(); - assert!(are_sizes_equal_to(values.iter(&world), 9)); + let n = 9; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); + + let mut values = world.query_filtered::<&A, (Or<(With,)>, Or<(With,)>)>(); + let n = 1; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); + let mut values = world.query_filtered::<&A, Or<(Or<(With, With)>, With)>>(); + let n = 6; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); + + world.spawn().insert_bundle((A(11), D(11))); + + let mut values = world.query_filtered::<&A, Or<(Or<(With, With)>, With)>>(); + let n = 7; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); + let mut values = world.query_filtered::<&A, Or<(Or<(With, With)>, Without)>>(); + let n = 10; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); } #[test]