Skip to content

Commit b9102b8

Browse files
committed
Introduce tests for derive(WorldQuery) (#4625)
The only tests we had for `derive(WorldQuery)` checked that the derive doesnt panic/emit a `compiler_error!`. This PR adds tests that actually assert the returned values of a query using the derived `WorldQuery` impl. Also adds a compile fail test to check that we correctly error on read only world queries containing mutable world queries.
1 parent 990d5c0 commit b9102b8

File tree

3 files changed

+232
-5
lines changed

3 files changed

+232
-5
lines changed

crates/bevy_ecs/src/query/mod.rs

Lines changed: 184 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,19 @@ unsafe fn debug_checked_unreachable() -> ! {
1919

2020
#[cfg(test)]
2121
mod tests {
22-
use super::AnyOf;
22+
use super::WorldQuery;
23+
use crate::prelude::{AnyOf, Entity, Or, With, Without};
2324
use crate::{self as bevy_ecs, component::Component, world::World};
2425
use std::collections::HashSet;
2526

26-
#[derive(Component, Debug, Hash, Eq, PartialEq)]
27+
#[derive(Component, Debug, Hash, Eq, PartialEq, Clone, Copy)]
2728
struct A(usize);
28-
#[derive(Component, Debug, Eq, PartialEq)]
29+
#[derive(Component, Debug, Eq, PartialEq, Clone, Copy)]
2930
struct B(usize);
30-
#[derive(Component, Debug, Eq, PartialEq)]
31+
#[derive(Component, Debug, Eq, PartialEq, Clone, Copy)]
3132
struct C(usize);
3233

33-
#[derive(Component, Debug, Eq, PartialEq)]
34+
#[derive(Component, Debug, Eq, PartialEq, Clone, Copy)]
3435
#[component(storage = "SparseSet")]
3536
struct Sparse(usize);
3637

@@ -337,4 +338,182 @@ mod tests {
337338
vec![(Some(&A(1)), Some(&B(2))), (Some(&A(2)), None),]
338339
);
339340
}
341+
342+
#[test]
343+
#[should_panic = "&mut bevy_ecs::query::tests::A conflicts with a previous access in this query."]
344+
fn self_conflicting_worldquery() {
345+
#[derive(WorldQuery)]
346+
#[world_query(mutable)]
347+
struct SelfConflicting {
348+
a: &'static mut A,
349+
b: &'static mut A,
350+
}
351+
352+
let mut world = World::new();
353+
world.query::<SelfConflicting>();
354+
}
355+
356+
#[test]
357+
fn derived_worldqueries() {
358+
let mut world = World::new();
359+
360+
world.spawn().insert_bundle((A(10), B(18), C(3), Sparse(4)));
361+
362+
world.spawn().insert_bundle((A(101), B(148), C(13)));
363+
world.spawn().insert_bundle((A(51), B(46), Sparse(72)));
364+
world.spawn().insert_bundle((A(398), C(6), Sparse(9)));
365+
world.spawn().insert_bundle((B(11), C(28), Sparse(92)));
366+
367+
world.spawn().insert_bundle((C(18348), Sparse(101)));
368+
world.spawn().insert_bundle((B(839), Sparse(5)));
369+
world.spawn().insert_bundle((B(6721), C(122)));
370+
world.spawn().insert_bundle((A(220), Sparse(63)));
371+
world.spawn().insert_bundle((A(1092), C(382)));
372+
world.spawn().insert_bundle((A(2058), B(3019)));
373+
374+
world.spawn().insert_bundle((B(38), C(8), Sparse(100)));
375+
world.spawn().insert_bundle((A(111), C(52), Sparse(1)));
376+
world.spawn().insert_bundle((A(599), B(39), Sparse(13)));
377+
world.spawn().insert_bundle((A(55), B(66), C(77)));
378+
379+
world.spawn();
380+
381+
{
382+
#[derive(WorldQuery)]
383+
struct CustomAB {
384+
a: &'static A,
385+
b: &'static B,
386+
}
387+
388+
let custom_param_data = world
389+
.query::<CustomAB>()
390+
.iter(&world)
391+
.map(|item| (*item.a, *item.b))
392+
.collect::<Vec<_>>();
393+
let normal_data = world
394+
.query::<(&A, &B)>()
395+
.iter(&world)
396+
.map(|(a, b)| (*a, *b))
397+
.collect::<Vec<_>>();
398+
assert_eq!(custom_param_data, normal_data);
399+
}
400+
401+
{
402+
#[derive(WorldQuery)]
403+
struct FancyParam {
404+
e: Entity,
405+
b: &'static B,
406+
opt: Option<&'static Sparse>,
407+
}
408+
409+
let custom_param_data = world
410+
.query::<FancyParam>()
411+
.iter(&world)
412+
.map(|fancy| (fancy.e, *fancy.b, fancy.opt.copied()))
413+
.collect::<Vec<_>>();
414+
let normal_data = world
415+
.query::<(Entity, &B, Option<&Sparse>)>()
416+
.iter(&world)
417+
.map(|(e, b, opt)| (e, *b, opt.copied()))
418+
.collect::<Vec<_>>();
419+
assert_eq!(custom_param_data, normal_data);
420+
}
421+
422+
{
423+
#[derive(WorldQuery)]
424+
struct MaybeBSparse {
425+
blah: Option<(&'static B, &'static Sparse)>,
426+
}
427+
#[derive(WorldQuery)]
428+
struct MatchEverything {
429+
abcs: AnyOf<(&'static A, &'static B, &'static C)>,
430+
opt_bsparse: MaybeBSparse,
431+
}
432+
433+
let custom_param_data = world
434+
.query::<MatchEverything>()
435+
.iter(&world)
436+
.map(
437+
|MatchEverythingItem {
438+
abcs: (a, b, c),
439+
opt_bsparse: MaybeBSparseItem { blah: bsparse },
440+
}| {
441+
(
442+
(a.copied(), b.copied(), c.copied()),
443+
bsparse.map(|(b, sparse)| (*b, *sparse)),
444+
)
445+
},
446+
)
447+
.collect::<Vec<_>>();
448+
let normal_data = world
449+
.query::<(AnyOf<(&A, &B, &C)>, Option<(&B, &Sparse)>)>()
450+
.iter(&world)
451+
.map(|((a, b, c), bsparse)| {
452+
(
453+
(a.copied(), b.copied(), c.copied()),
454+
bsparse.map(|(b, sparse)| (*b, *sparse)),
455+
)
456+
})
457+
.collect::<Vec<_>>();
458+
assert_eq!(custom_param_data, normal_data)
459+
}
460+
461+
{
462+
#[derive(WorldQuery)]
463+
struct AOrBFilter {
464+
a: Or<(With<A>, With<B>)>,
465+
}
466+
#[derive(WorldQuery)]
467+
struct NoSparseThatsSlow {
468+
no: Without<Sparse>,
469+
}
470+
471+
let custom_param_entities = world
472+
.query_filtered::<Entity, (AOrBFilter, NoSparseThatsSlow)>()
473+
.iter(&world)
474+
.collect::<Vec<_>>();
475+
let normal_entities = world
476+
.query_filtered::<Entity, (Or<(With<A>, With<B>)>, Without<Sparse>)>()
477+
.iter(&world)
478+
.collect::<Vec<_>>();
479+
assert_eq!(custom_param_entities, normal_entities);
480+
}
481+
482+
{
483+
#[derive(WorldQuery)]
484+
struct CSparseFilter {
485+
tuple_structs_pls: With<C>,
486+
ugh: With<Sparse>,
487+
}
488+
489+
let custom_param_entities = world
490+
.query_filtered::<Entity, CSparseFilter>()
491+
.iter(&world)
492+
.collect::<Vec<_>>();
493+
let normal_entities = world
494+
.query_filtered::<Entity, (With<C>, With<Sparse>)>()
495+
.iter(&world)
496+
.collect::<Vec<_>>();
497+
assert_eq!(custom_param_entities, normal_entities);
498+
}
499+
500+
{
501+
#[derive(WorldQuery)]
502+
struct WithoutComps {
503+
_1: Without<A>,
504+
_2: Without<B>,
505+
_3: Without<C>,
506+
}
507+
508+
let custom_param_entities = world
509+
.query_filtered::<Entity, WithoutComps>()
510+
.iter(&world)
511+
.collect::<Vec<_>>();
512+
let normal_entities = world
513+
.query_filtered::<Entity, (Without<A>, Without<B>, Without<C>)>()
514+
.iter(&world)
515+
.collect::<Vec<_>>();
516+
assert_eq!(custom_param_entities, normal_entities);
517+
}
518+
}
340519
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use bevy_ecs::prelude::*;
2+
use bevy_ecs::query::WorldQuery;
3+
4+
#[derive(Component)]
5+
struct Foo;
6+
7+
#[derive(WorldQuery)]
8+
struct MutableUnmarked {
9+
a: &'static mut Foo,
10+
}
11+
12+
#[derive(WorldQuery)]
13+
#[world_query(mutable)]
14+
struct MutableMarked {
15+
a: &'static mut Foo,
16+
}
17+
18+
#[derive(WorldQuery)]
19+
struct NestedMutableUnmarked {
20+
a: MutableMarked,
21+
}
22+
23+
fn main() {}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error[E0277]: the trait bound `WriteFetch<'_, Foo>: ReadOnlyFetch` is not satisfied
2+
--> tests/ui/world_query_derive.rs:7:10
3+
|
4+
7 | #[derive(WorldQuery)]
5+
| ^^^^^^^^^^ the trait `ReadOnlyFetch` is not implemented for `WriteFetch<'_, Foo>`
6+
|
7+
note: required by a bound in `_::assert_readonly`
8+
--> tests/ui/world_query_derive.rs:7:10
9+
|
10+
7 | #[derive(WorldQuery)]
11+
| ^^^^^^^^^^ required by this bound in `_::assert_readonly`
12+
= note: this error originates in the derive macro `WorldQuery` (in Nightly builds, run with -Z macro-backtrace for more info)
13+
14+
error[E0277]: the trait bound `MutableMarkedFetch<'_>: ReadOnlyFetch` is not satisfied
15+
--> tests/ui/world_query_derive.rs:18:10
16+
|
17+
18 | #[derive(WorldQuery)]
18+
| ^^^^^^^^^^ the trait `ReadOnlyFetch` is not implemented for `MutableMarkedFetch<'_>`
19+
|
20+
note: required by a bound in `_::assert_readonly`
21+
--> tests/ui/world_query_derive.rs:18:10
22+
|
23+
18 | #[derive(WorldQuery)]
24+
| ^^^^^^^^^^ required by this bound in `_::assert_readonly`
25+
= note: this error originates in the derive macro `WorldQuery` (in Nightly builds, run with -Z macro-backtrace for more info)

0 commit comments

Comments
 (0)