Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/system/mod.rs
9383 views
1
//! Tools for controlling behavior in an ECS application.
2
//!
3
//! Systems define how an ECS based application behaves.
4
//! Systems are added to a [`Schedule`](crate::schedule::Schedule), which is then run.
5
//! A system is usually written as a normal function, which is automatically converted into a system.
6
//!
7
//! System functions can have parameters, through which one can query and mutate Bevy ECS state.
8
//! Only types that implement [`SystemParam`] can be used, automatically fetching data from
9
//! the [`World`].
10
//!
11
//! System functions often look like this:
12
//!
13
//! ```
14
//! # use bevy_ecs::prelude::*;
15
//! #
16
//! # #[derive(Component)]
17
//! # struct Player { alive: bool }
18
//! # #[derive(Component)]
19
//! # struct Score(u32);
20
//! # #[derive(Resource)]
21
//! # struct Round(u32);
22
//! #
23
//! fn update_score_system(
24
//! mut query: Query<(&Player, &mut Score)>,
25
//! mut round: ResMut<Round>,
26
//! ) {
27
//! for (player, mut score) in &mut query {
28
//! if player.alive {
29
//! score.0 += round.0;
30
//! }
31
//! }
32
//! round.0 += 1;
33
//! }
34
//! # bevy_ecs::system::assert_is_system(update_score_system);
35
//! ```
36
//!
37
//! # System ordering
38
//!
39
//! By default, the execution of systems is parallel and not deterministic.
40
//! Not all systems can run together: if a system mutably accesses data,
41
//! no other system that reads or writes that data can be run at the same time.
42
//! These systems are said to be **incompatible**.
43
//!
44
//! The relative order in which incompatible systems are run matters.
45
//! When this is not specified, a **system order ambiguity** exists in your schedule.
46
//! You can **explicitly order** systems:
47
//!
48
//! - by calling the `.before(this_system)` or `.after(that_system)` methods when adding them to your schedule
49
//! - by adding them to a [`SystemSet`], and then using `.configure_sets(ThisSet.before(ThatSet))` syntax to configure many systems at once
50
//! - through the use of `.add_systems((system_a, system_b, system_c).chain())`
51
//!
52
//! [`SystemSet`]: crate::schedule::SystemSet
53
//!
54
//! ## Example
55
//!
56
//! ```
57
//! # use bevy_ecs::prelude::*;
58
//! # let mut schedule = Schedule::default();
59
//! # let mut world = World::new();
60
//! // Configure these systems to run in order using `chain()`.
61
//! schedule.add_systems((print_first, print_last).chain());
62
//! // Prints "HelloWorld!"
63
//! schedule.run(&mut world);
64
//!
65
//! // Configure this system to run in between the other two systems
66
//! // using explicit dependencies.
67
//! schedule.add_systems(print_mid.after(print_first).before(print_last));
68
//! // Prints "Hello, World!"
69
//! schedule.run(&mut world);
70
//!
71
//! fn print_first() {
72
//! print!("Hello");
73
//! }
74
//! fn print_mid() {
75
//! print!(", ");
76
//! }
77
//! fn print_last() {
78
//! println!("World!");
79
//! }
80
//! ```
81
//!
82
//! # System return type
83
//!
84
//! Systems added to a schedule through [`add_systems`](crate::schedule::Schedule) may either return
85
//! empty `()` or a [`Result`](crate::error::Result). Other contexts (like one shot systems) allow
86
//! systems to return arbitrary values.
87
//!
88
//! # System parameter list
89
//! Following is the complete list of accepted types as system parameters:
90
//!
91
//! - [`Query`]
92
//! - [`Res`] and `Option<Res>`
93
//! - [`ResMut`] and `Option<ResMut>`
94
//! - [`Commands`]
95
//! - [`Local`]
96
//! - [`MessageReader`](crate::message::MessageReader)
97
//! - [`MessageWriter`](crate::message::MessageWriter)
98
//! - [`NonSend`] and `Option<NonSend>`
99
//! - [`NonSendMut`] and `Option<NonSendMut>`
100
//! - [`RemovedComponents`](crate::lifecycle::RemovedComponents)
101
//! - [`SystemName`]
102
//! - [`SystemChangeTick`]
103
//! - [`Archetypes`](crate::archetype::Archetypes) (Provides Archetype metadata)
104
//! - [`Bundles`](crate::bundle::Bundles) (Provides Bundles metadata)
105
//! - [`Components`](crate::component::Components) (Provides Components metadata)
106
//! - [`Entities`](crate::entity::Entities) (Provides Entities metadata)
107
//! - All tuples between 1 to 16 elements where each element implements [`SystemParam`]
108
//! - [`ParamSet`]
109
//! - [`()` (unit primitive type)](https://doc.rust-lang.org/stable/std/primitive.unit.html)
110
//!
111
//! In addition, the following parameters can be used when constructing a dynamic system with [`SystemParamBuilder`],
112
//! but will only provide an empty value when used with an ordinary system:
113
//!
114
//! - [`FilteredResources`](crate::world::FilteredResources)
115
//! - [`FilteredResourcesMut`](crate::world::FilteredResourcesMut)
116
//! - [`DynSystemParam`]
117
//! - [`Vec<P>`] where `P: SystemParam`
118
//! - [`ParamSet<Vec<P>>`] where `P: SystemParam`
119
//!
120
//! [`Vec<P>`]: alloc::vec::Vec
121
122
mod adapter_system;
123
mod builder;
124
mod combinator;
125
mod commands;
126
mod exclusive_function_system;
127
mod exclusive_system_param;
128
mod function_system;
129
mod input;
130
mod observer_system;
131
mod query;
132
mod schedule_system;
133
mod system;
134
mod system_name;
135
mod system_param;
136
mod system_registry;
137
138
use core::any::TypeId;
139
140
pub use adapter_system::*;
141
pub use builder::*;
142
pub use combinator::*;
143
pub use commands::*;
144
pub use exclusive_function_system::*;
145
pub use exclusive_system_param::*;
146
pub use function_system::*;
147
pub use input::*;
148
pub use observer_system::*;
149
pub use query::*;
150
pub use schedule_system::*;
151
pub use system::*;
152
pub use system_name::*;
153
pub use system_param::*;
154
pub use system_registry::*;
155
156
use crate::world::{FromWorld, World};
157
158
/// Conversion trait to turn something into a [`System`].
159
///
160
/// Use this to get a system from a function. Also note that every system implements this trait as
161
/// well.
162
///
163
/// # Usage notes
164
///
165
/// This trait should only be used as a bound for trait implementations or as an
166
/// argument to a function. If a system needs to be returned from a function or
167
/// stored somewhere, use [`System`] instead of this trait.
168
///
169
/// # Examples
170
///
171
/// ```
172
/// use bevy_ecs::prelude::*;
173
///
174
/// fn my_system_function(a_usize_local: Local<usize>) {}
175
///
176
/// let system = IntoSystem::into_system(my_system_function);
177
/// ```
178
// This trait has to be generic because we have potentially overlapping impls, in particular
179
// because Rust thinks a type could impl multiple different `FnMut` combinations
180
// even though none can currently
181
#[diagnostic::on_unimplemented(
182
message = "`{Self}` is not a valid system with input `{In}` and output `{Out}`",
183
label = "invalid system"
184
)]
185
pub trait IntoSystem<In: SystemInput, Out, Marker>: Sized {
186
/// The type of [`System`] that this instance converts into.
187
type System: System<In = In, Out = Out>;
188
189
/// Turns this value into its corresponding [`System`].
190
fn into_system(this: Self) -> Self::System;
191
192
/// Pass the output of this system `A` into a second system `B`, creating a new compound system.
193
///
194
/// The second system must have [`In<T>`](crate::system::In) as its first parameter,
195
/// where `T` is the return type of the first system.
196
fn pipe<B, BIn, BOut, MarkerB>(self, system: B) -> IntoPipeSystem<Self, B>
197
where
198
Out: 'static,
199
B: IntoSystem<BIn, BOut, MarkerB>,
200
for<'a> BIn: SystemInput<Inner<'a> = Out>,
201
{
202
IntoPipeSystem::new(self, system)
203
}
204
205
/// Pass the output of this system into the passed function `f`, creating a new system that
206
/// outputs the value returned from the function.
207
///
208
/// ```
209
/// # use bevy_ecs::prelude::*;
210
/// # let mut schedule = Schedule::default();
211
/// // Ignores the output of a system that may fail.
212
/// schedule.add_systems(my_system.map(drop));
213
/// # let mut world = World::new();
214
/// # world.insert_resource(T);
215
/// # schedule.run(&mut world);
216
///
217
/// # #[derive(Resource)] struct T;
218
/// # type Err = ();
219
/// fn my_system(res: Res<T>) -> Result<(), Err> {
220
/// // ...
221
/// # Err(())
222
/// }
223
/// ```
224
fn map<T, F>(self, f: F) -> IntoAdapterSystem<F, Self>
225
where
226
F: Send + Sync + 'static + FnMut(Out) -> T,
227
{
228
IntoAdapterSystem::new(f, self)
229
}
230
231
/// Passes a mutable reference to `value` as input to the system each run,
232
/// turning it into a system that takes no input.
233
///
234
/// `Self` can have any [`SystemInput`] type that takes a mutable reference
235
/// to `T`, such as [`InMut`].
236
///
237
/// # Example
238
///
239
/// ```
240
/// # use bevy_ecs::prelude::*;
241
/// #
242
/// fn my_system(InMut(value): InMut<usize>) {
243
/// *value += 1;
244
/// if *value > 10 {
245
/// println!("Value is greater than 10!");
246
/// }
247
/// }
248
///
249
/// # let mut schedule = Schedule::default();
250
/// schedule.add_systems(my_system.with_input(0));
251
/// # bevy_ecs::system::assert_is_system(my_system.with_input(0));
252
/// ```
253
fn with_input<T>(self, value: T) -> WithInputWrapper<Self::System, T>
254
where
255
for<'i> In: SystemInput<Inner<'i> = &'i mut T>,
256
T: Send + Sync + 'static,
257
{
258
WithInputWrapper::new(self, value)
259
}
260
261
/// Passes a mutable reference to a value of type `T` created via
262
/// [`FromWorld`] as input to the system each run, turning it into a system
263
/// that takes no input.
264
///
265
/// `Self` can have any [`SystemInput`] type that takes a mutable reference
266
/// to `T`, such as [`InMut`].
267
///
268
/// # Example
269
///
270
/// ```
271
/// # use bevy_ecs::prelude::*;
272
/// #
273
/// struct MyData {
274
/// value: usize,
275
/// }
276
///
277
/// impl FromWorld for MyData {
278
/// fn from_world(world: &mut World) -> Self {
279
/// // Fetch from the world the data needed to create `MyData`
280
/// # MyData { value: 0 }
281
/// }
282
/// }
283
///
284
/// fn my_system(InMut(data): InMut<MyData>) {
285
/// data.value += 1;
286
/// if data.value > 10 {
287
/// println!("Value is greater than 10!");
288
/// }
289
/// }
290
/// # let mut schedule = Schedule::default();
291
/// schedule.add_systems(my_system.with_input_from::<MyData>());
292
/// # bevy_ecs::system::assert_is_system(my_system.with_input_from::<MyData>());
293
/// ```
294
fn with_input_from<T>(self) -> WithInputFromWrapper<Self::System, T>
295
where
296
for<'i> In: SystemInput<Inner<'i> = &'i mut T>,
297
T: FromWorld + Send + Sync + 'static,
298
{
299
WithInputFromWrapper::new(self)
300
}
301
302
/// Get the [`TypeId`] of the [`System`] produced after calling [`into_system`](`IntoSystem::into_system`).
303
#[inline]
304
fn system_type_id(&self) -> TypeId {
305
TypeId::of::<Self::System>()
306
}
307
}
308
309
// All systems implicitly implement IntoSystem.
310
impl<T: System> IntoSystem<T::In, T::Out, ()> for T {
311
type System = T;
312
fn into_system(this: Self) -> Self {
313
this
314
}
315
}
316
317
/// Ensure that a given function is a [system](System).
318
///
319
/// This should be used when writing doc examples,
320
/// to confirm that systems used in an example are
321
/// valid systems.
322
///
323
/// # Examples
324
///
325
/// The following example will panic when run since the
326
/// system's parameters mutably access the same component
327
/// multiple times.
328
///
329
/// ```should_panic
330
/// # use bevy_ecs::{prelude::*, system::assert_is_system};
331
/// #
332
/// # #[derive(Component)]
333
/// # struct Transform;
334
/// #
335
/// fn my_system(query1: Query<&mut Transform>, query2: Query<&mut Transform>) {
336
/// // ...
337
/// }
338
///
339
/// assert_is_system(my_system);
340
/// ```
341
pub fn assert_is_system<In: SystemInput, Out: 'static, Marker>(
342
system: impl IntoSystem<In, Out, Marker>,
343
) {
344
let mut system = IntoSystem::into_system(system);
345
346
// Initialize the system, which will panic if the system has access conflicts.
347
let mut world = World::new();
348
system.initialize(&mut world);
349
}
350
351
/// Ensure that a given function is a [read-only system](ReadOnlySystem).
352
///
353
/// This should be used when writing doc examples,
354
/// to confirm that systems used in an example are
355
/// valid systems.
356
///
357
/// # Examples
358
///
359
/// The following example will fail to compile
360
/// since the system accesses a component mutably.
361
///
362
/// ```compile_fail
363
/// # use bevy_ecs::{prelude::*, system::assert_is_read_only_system};
364
/// #
365
/// # #[derive(Component)]
366
/// # struct Transform;
367
/// #
368
/// fn my_system(query: Query<&mut Transform>) {
369
/// // ...
370
/// }
371
///
372
/// assert_is_read_only_system(my_system);
373
/// ```
374
pub fn assert_is_read_only_system<In, Out, Marker, S>(system: S)
375
where
376
In: SystemInput,
377
Out: 'static,
378
S: IntoSystem<In, Out, Marker>,
379
S::System: ReadOnlySystem,
380
{
381
assert_is_system(system);
382
}
383
384
/// Ensures that the provided system doesn't conflict with itself.
385
///
386
/// This function will panic if the provided system conflict with itself.
387
///
388
/// Note: this will run the system on an empty world.
389
pub fn assert_system_does_not_conflict<Out, Params, S: IntoSystem<(), Out, Params>>(sys: S) {
390
let mut world = World::new();
391
let mut system = IntoSystem::into_system(sys);
392
system.initialize(&mut world);
393
system.run((), &mut world).unwrap();
394
}
395
396
#[cfg(test)]
397
#[expect(clippy::print_stdout, reason = "Allowed in tests.")]
398
mod tests {
399
use alloc::{vec, vec::Vec};
400
use bevy_utils::default;
401
use core::any::TypeId;
402
use std::println;
403
404
use crate::{
405
archetype::Archetypes,
406
bundle::Bundles,
407
change_detection::DetectChanges,
408
component::{Component, Components},
409
entity::{Entities, Entity},
410
error::Result,
411
lifecycle::RemovedComponents,
412
name::Name,
413
prelude::{Add, AnyOf, EntityRef, On},
414
query::{Added, Changed, Or, SpawnDetails, Spawned, With, Without},
415
resource::Resource,
416
schedule::{
417
common_conditions::resource_exists, ApplyDeferred, IntoScheduleConfigs, Schedule,
418
SystemCondition,
419
},
420
system::{
421
Commands, ExclusiveMarker, In, InMut, IntoSystem, Local, NonSend, NonSendMut, ParamSet,
422
Query, Res, ResMut, Single, StaticSystemParam, System, SystemState,
423
},
424
world::{DeferredWorld, EntityMut, FromWorld, World},
425
};
426
427
use super::ScheduleSystem;
428
429
#[derive(Resource, PartialEq, Debug)]
430
enum SystemRan {
431
Yes,
432
No,
433
}
434
435
#[derive(Component, Debug, Eq, PartialEq, Default)]
436
struct A;
437
#[derive(Component)]
438
struct B;
439
#[derive(Component)]
440
struct C;
441
#[derive(Component)]
442
struct D;
443
#[derive(Component)]
444
struct E;
445
#[derive(Component)]
446
struct F;
447
448
#[derive(Resource)]
449
struct ResA;
450
#[derive(Resource)]
451
struct ResB;
452
#[derive(Resource)]
453
struct ResC;
454
#[derive(Resource)]
455
struct ResD;
456
#[derive(Resource)]
457
struct ResE;
458
#[derive(Resource)]
459
struct ResF;
460
461
#[derive(Component, Debug)]
462
struct W<T>(T);
463
464
#[test]
465
fn simple_system() {
466
fn sys(query: Query<&A>) {
467
for a in &query {
468
println!("{a:?}");
469
}
470
}
471
472
let mut system = IntoSystem::into_system(sys);
473
let mut world = World::new();
474
world.spawn(A);
475
476
system.initialize(&mut world);
477
system.run((), &mut world).unwrap();
478
}
479
480
fn run_system<Marker, S: IntoScheduleConfigs<ScheduleSystem, Marker>>(
481
world: &mut World,
482
system: S,
483
) {
484
let mut schedule = Schedule::default();
485
schedule.add_systems(system);
486
schedule.run(world);
487
}
488
489
#[test]
490
fn get_many_is_ordered() {
491
use crate::resource::Resource;
492
const ENTITIES_COUNT: usize = 1000;
493
494
#[derive(Resource)]
495
struct EntitiesArray(Vec<Entity>);
496
497
fn query_system(
498
mut ran: ResMut<SystemRan>,
499
entities_array: Res<EntitiesArray>,
500
q: Query<&W<usize>>,
501
) {
502
let entities_array: [Entity; ENTITIES_COUNT] =
503
entities_array.0.clone().try_into().unwrap();
504
505
for (i, w) in (0..ENTITIES_COUNT).zip(q.get_many(entities_array).unwrap()) {
506
assert_eq!(i, w.0);
507
}
508
509
*ran = SystemRan::Yes;
510
}
511
512
fn query_system_mut(
513
mut ran: ResMut<SystemRan>,
514
entities_array: Res<EntitiesArray>,
515
mut q: Query<&mut W<usize>>,
516
) {
517
let entities_array: [Entity; ENTITIES_COUNT] =
518
entities_array.0.clone().try_into().unwrap();
519
520
for (i, w) in (0..ENTITIES_COUNT).zip(q.get_many_mut(entities_array).unwrap()) {
521
assert_eq!(i, w.0);
522
}
523
524
*ran = SystemRan::Yes;
525
}
526
527
let mut world = World::default();
528
world.insert_resource(SystemRan::No);
529
let entity_ids = (0..ENTITIES_COUNT)
530
.map(|i| world.spawn(W(i)).id())
531
.collect();
532
world.insert_resource(EntitiesArray(entity_ids));
533
534
run_system(&mut world, query_system);
535
assert_eq!(*world.resource::<SystemRan>(), SystemRan::Yes);
536
537
world.insert_resource(SystemRan::No);
538
run_system(&mut world, query_system_mut);
539
assert_eq!(*world.resource::<SystemRan>(), SystemRan::Yes);
540
}
541
542
#[test]
543
fn or_param_set_system() {
544
// Regression test for issue #762
545
fn query_system(
546
mut ran: ResMut<SystemRan>,
547
mut set: ParamSet<(
548
Query<(), Or<(Changed<A>, Changed<B>)>>,
549
Query<(), Or<(Added<A>, Added<B>)>>,
550
)>,
551
) {
552
let changed = set.p0().iter().count();
553
let added = set.p1().iter().count();
554
555
assert_eq!(changed, 1);
556
assert_eq!(added, 1);
557
558
*ran = SystemRan::Yes;
559
}
560
561
let mut world = World::default();
562
world.insert_resource(SystemRan::No);
563
world.spawn((A, B));
564
565
run_system(&mut world, query_system);
566
567
assert_eq!(*world.resource::<SystemRan>(), SystemRan::Yes);
568
}
569
570
#[test]
571
fn changed_resource_system() {
572
use crate::resource::Resource;
573
574
#[derive(Resource)]
575
struct Flipper(bool);
576
577
#[derive(Resource)]
578
struct Added(usize);
579
580
#[derive(Resource)]
581
struct Changed(usize);
582
583
fn incr_e_on_flip(
584
value: Res<Flipper>,
585
mut changed: ResMut<Changed>,
586
mut added: ResMut<Added>,
587
) {
588
if value.is_added() {
589
added.0 += 1;
590
}
591
592
if value.is_changed() {
593
changed.0 += 1;
594
}
595
}
596
597
let mut world = World::default();
598
world.insert_resource(Flipper(false));
599
world.insert_resource(Added(0));
600
world.insert_resource(Changed(0));
601
602
let mut schedule = Schedule::default();
603
604
schedule.add_systems((incr_e_on_flip, ApplyDeferred, World::clear_trackers).chain());
605
606
schedule.run(&mut world);
607
assert_eq!(world.resource::<Added>().0, 1);
608
assert_eq!(world.resource::<Changed>().0, 1);
609
610
schedule.run(&mut world);
611
assert_eq!(world.resource::<Added>().0, 1);
612
assert_eq!(world.resource::<Changed>().0, 1);
613
614
world.resource_mut::<Flipper>().0 = true;
615
schedule.run(&mut world);
616
assert_eq!(world.resource::<Added>().0, 1);
617
assert_eq!(world.resource::<Changed>().0, 2);
618
}
619
620
#[test]
621
#[should_panic = "error[B0001]"]
622
fn option_has_no_filter_with() {
623
fn sys(_: Query<(Option<&A>, &mut B)>, _: Query<&mut B, Without<A>>) {}
624
let mut world = World::default();
625
run_system(&mut world, sys);
626
}
627
628
#[test]
629
fn option_doesnt_remove_unrelated_filter_with() {
630
fn sys(_: Query<(Option<&A>, &mut B, &A)>, _: Query<&mut B, Without<A>>) {}
631
let mut world = World::default();
632
run_system(&mut world, sys);
633
}
634
635
#[test]
636
fn any_of_working() {
637
fn sys(_: Query<AnyOf<(&mut A, &B)>>) {}
638
let mut world = World::default();
639
run_system(&mut world, sys);
640
}
641
642
#[test]
643
fn any_of_with_and_without_common() {
644
fn sys(_: Query<(&mut D, &C, AnyOf<(&A, &B)>)>, _: Query<&mut D, Without<C>>) {}
645
let mut world = World::default();
646
run_system(&mut world, sys);
647
}
648
649
#[test]
650
#[should_panic]
651
fn any_of_with_mut_and_ref() {
652
fn sys(_: Query<AnyOf<(&mut A, &A)>>) {}
653
let mut world = World::default();
654
run_system(&mut world, sys);
655
}
656
657
#[test]
658
#[should_panic]
659
fn any_of_with_ref_and_mut() {
660
fn sys(_: Query<AnyOf<(&A, &mut A)>>) {}
661
let mut world = World::default();
662
run_system(&mut world, sys);
663
}
664
665
#[test]
666
#[should_panic]
667
fn any_of_with_mut_and_option() {
668
fn sys(_: Query<AnyOf<(&mut A, Option<&A>)>>) {}
669
let mut world = World::default();
670
run_system(&mut world, sys);
671
}
672
673
#[test]
674
fn any_of_with_entity_and_mut() {
675
fn sys(_: Query<AnyOf<(Entity, &mut A)>>) {}
676
let mut world = World::default();
677
run_system(&mut world, sys);
678
}
679
680
#[test]
681
fn any_of_with_empty_and_mut() {
682
fn sys(_: Query<AnyOf<((), &mut A)>>) {}
683
let mut world = World::default();
684
run_system(&mut world, sys);
685
}
686
687
#[test]
688
#[should_panic = "error[B0001]"]
689
fn any_of_has_no_filter_with() {
690
fn sys(_: Query<(AnyOf<(&A, ())>, &mut B)>, _: Query<&mut B, Without<A>>) {}
691
let mut world = World::default();
692
run_system(&mut world, sys);
693
}
694
695
#[test]
696
#[should_panic]
697
fn any_of_with_conflicting() {
698
fn sys(_: Query<AnyOf<(&mut A, &mut A)>>) {}
699
let mut world = World::default();
700
run_system(&mut world, sys);
701
}
702
703
#[test]
704
fn any_of_has_filter_with_when_both_have_it() {
705
fn sys(_: Query<(AnyOf<(&A, &A)>, &mut B)>, _: Query<&mut B, Without<A>>) {}
706
let mut world = World::default();
707
run_system(&mut world, sys);
708
}
709
710
#[test]
711
fn any_of_doesnt_remove_unrelated_filter_with() {
712
fn sys(_: Query<(AnyOf<(&A, ())>, &mut B, &A)>, _: Query<&mut B, Without<A>>) {}
713
let mut world = World::default();
714
run_system(&mut world, sys);
715
}
716
717
#[test]
718
fn any_of_and_without() {
719
fn sys(_: Query<(AnyOf<(&A, &B)>, &mut C)>, _: Query<&mut C, (Without<A>, Without<B>)>) {}
720
let mut world = World::default();
721
run_system(&mut world, sys);
722
}
723
724
#[test]
725
#[should_panic = "error[B0001]"]
726
fn or_has_no_filter_with() {
727
fn sys(_: Query<&mut B, Or<(With<A>, With<B>)>>, _: Query<&mut B, Without<A>>) {}
728
let mut world = World::default();
729
run_system(&mut world, sys);
730
}
731
732
#[test]
733
fn or_has_filter_with_when_both_have_it() {
734
fn sys(_: Query<&mut B, Or<(With<A>, With<A>)>>, _: Query<&mut B, Without<A>>) {}
735
let mut world = World::default();
736
run_system(&mut world, sys);
737
}
738
739
#[test]
740
fn or_has_filter_with() {
741
fn sys(
742
_: Query<&mut C, Or<(With<A>, With<B>)>>,
743
_: Query<&mut C, (Without<A>, Without<B>)>,
744
) {
745
}
746
let mut world = World::default();
747
run_system(&mut world, sys);
748
}
749
750
#[test]
751
fn or_expanded_with_and_without_common() {
752
fn sys(_: Query<&mut D, (With<A>, Or<(With<B>, With<C>)>)>, _: Query<&mut D, Without<A>>) {}
753
let mut world = World::default();
754
run_system(&mut world, sys);
755
}
756
757
#[test]
758
fn or_expanded_nested_with_and_without_common() {
759
fn sys(
760
_: Query<&mut E, (Or<((With<B>, With<C>), (With<C>, With<D>))>, With<A>)>,
761
_: Query<&mut E, (Without<B>, Without<D>)>,
762
) {
763
}
764
let mut world = World::default();
765
run_system(&mut world, sys);
766
}
767
768
#[test]
769
#[should_panic = "error[B0001]"]
770
fn or_expanded_nested_with_and_disjoint_without() {
771
fn sys(
772
_: Query<&mut E, (Or<((With<B>, With<C>), (With<C>, With<D>))>, With<A>)>,
773
_: Query<&mut E, Without<D>>,
774
) {
775
}
776
let mut world = World::default();
777
run_system(&mut world, sys);
778
}
779
780
#[test]
781
#[should_panic = "error[B0001]"]
782
fn or_expanded_nested_or_with_and_disjoint_without() {
783
fn sys(
784
_: Query<&mut D, Or<(Or<(With<A>, With<B>)>, Or<(With<A>, With<C>)>)>>,
785
_: Query<&mut D, Without<A>>,
786
) {
787
}
788
let mut world = World::default();
789
run_system(&mut world, sys);
790
}
791
792
#[test]
793
fn or_expanded_nested_with_and_common_nested_without() {
794
fn sys(
795
_: Query<&mut D, Or<((With<A>, With<B>), (With<B>, With<C>))>>,
796
_: Query<&mut D, Or<(Without<D>, Without<B>)>>,
797
) {
798
}
799
let mut world = World::default();
800
run_system(&mut world, sys);
801
}
802
803
#[test]
804
fn or_with_without_and_compatible_with_without() {
805
fn sys(
806
_: Query<&mut C, Or<(With<A>, Without<B>)>>,
807
_: Query<&mut C, (With<B>, Without<A>)>,
808
) {
809
}
810
let mut world = World::default();
811
run_system(&mut world, sys);
812
}
813
814
#[test]
815
#[should_panic = "error[B0001]"]
816
fn with_and_disjoint_or_empty_without() {
817
fn sys(_: Query<&mut B, With<A>>, _: Query<&mut B, Or<((), Without<A>)>>) {}
818
let mut world = World::default();
819
run_system(&mut world, sys);
820
}
821
822
#[test]
823
#[should_panic = "error[B0001]"]
824
fn or_expanded_with_and_disjoint_nested_without() {
825
fn sys(
826
_: Query<&mut D, Or<(With<A>, With<B>)>>,
827
_: Query<&mut D, Or<(Without<A>, Without<B>)>>,
828
) {
829
}
830
let mut world = World::default();
831
run_system(&mut world, sys);
832
}
833
834
#[test]
835
#[should_panic = "error[B0001]"]
836
fn or_expanded_nested_with_and_disjoint_nested_without() {
837
fn sys(
838
_: Query<&mut D, Or<((With<A>, With<B>), (With<B>, With<C>))>>,
839
_: Query<&mut D, Or<(Without<A>, Without<B>)>>,
840
) {
841
}
842
let mut world = World::default();
843
run_system(&mut world, sys);
844
}
845
846
#[test]
847
fn or_doesnt_remove_unrelated_filter_with() {
848
fn sys(_: Query<&mut B, (Or<(With<A>, With<B>)>, With<A>)>, _: Query<&mut B, Without<A>>) {}
849
let mut world = World::default();
850
run_system(&mut world, sys);
851
}
852
853
#[test]
854
#[should_panic]
855
fn conflicting_query_mut_system() {
856
fn sys(_q1: Query<&mut A>, _q2: Query<&mut A>) {}
857
858
let mut world = World::default();
859
run_system(&mut world, sys);
860
}
861
862
#[test]
863
fn disjoint_query_mut_system() {
864
fn sys(_q1: Query<&mut A, With<B>>, _q2: Query<&mut A, Without<B>>) {}
865
866
let mut world = World::default();
867
run_system(&mut world, sys);
868
}
869
870
#[test]
871
fn disjoint_query_mut_read_component_system() {
872
fn sys(_q1: Query<(&mut A, &B)>, _q2: Query<&mut A, Without<B>>) {}
873
874
let mut world = World::default();
875
run_system(&mut world, sys);
876
}
877
878
#[test]
879
#[should_panic]
880
fn conflicting_query_immut_system() {
881
fn sys(_q1: Query<&A>, _q2: Query<&mut A>) {}
882
883
let mut world = World::default();
884
run_system(&mut world, sys);
885
}
886
887
#[test]
888
#[should_panic]
889
fn changed_trackers_or_conflict() {
890
fn sys(_: Query<&mut A>, _: Query<(), Or<(Changed<A>,)>>) {}
891
892
let mut world = World::default();
893
run_system(&mut world, sys);
894
}
895
896
#[test]
897
fn query_set_system() {
898
fn sys(mut _set: ParamSet<(Query<&mut A>, Query<&A>)>) {}
899
let mut world = World::default();
900
run_system(&mut world, sys);
901
}
902
903
#[test]
904
#[should_panic]
905
fn conflicting_query_with_query_set_system() {
906
fn sys(_query: Query<&mut A>, _set: ParamSet<(Query<&mut A>, Query<&B>)>) {}
907
908
let mut world = World::default();
909
run_system(&mut world, sys);
910
}
911
912
#[test]
913
#[should_panic]
914
fn conflicting_query_sets_system() {
915
fn sys(_set_1: ParamSet<(Query<&mut A>,)>, _set_2: ParamSet<(Query<&mut A>, Query<&B>)>) {}
916
917
let mut world = World::default();
918
run_system(&mut world, sys);
919
}
920
921
#[derive(Default, Resource)]
922
struct BufferRes {
923
_buffer: Vec<u8>,
924
}
925
926
fn test_for_conflicting_resources<Marker, S: IntoSystem<(), (), Marker>>(sys: S) {
927
let mut world = World::default();
928
world.insert_resource(BufferRes::default());
929
world.insert_resource(ResA);
930
world.insert_resource(ResB);
931
run_system(&mut world, sys);
932
}
933
934
#[test]
935
#[should_panic]
936
fn conflicting_system_resources() {
937
fn sys(_: ResMut<BufferRes>, _: Res<BufferRes>) {}
938
test_for_conflicting_resources(sys);
939
}
940
941
#[test]
942
#[should_panic]
943
fn conflicting_system_resources_reverse_order() {
944
fn sys(_: Res<BufferRes>, _: ResMut<BufferRes>) {}
945
test_for_conflicting_resources(sys);
946
}
947
948
#[test]
949
#[should_panic]
950
fn conflicting_system_resources_multiple_mutable() {
951
fn sys(_: ResMut<BufferRes>, _: ResMut<BufferRes>) {}
952
test_for_conflicting_resources(sys);
953
}
954
955
#[test]
956
fn nonconflicting_system_resources() {
957
fn sys(_: Local<BufferRes>, _: ResMut<BufferRes>, _: Local<A>, _: ResMut<ResA>) {}
958
test_for_conflicting_resources(sys);
959
}
960
961
#[test]
962
fn local_system() {
963
let mut world = World::default();
964
world.insert_resource(ProtoFoo { value: 1 });
965
world.insert_resource(SystemRan::No);
966
967
struct Foo {
968
value: u32,
969
}
970
971
#[derive(Resource)]
972
struct ProtoFoo {
973
value: u32,
974
}
975
976
impl FromWorld for Foo {
977
fn from_world(world: &mut World) -> Self {
978
Foo {
979
value: world.resource::<ProtoFoo>().value + 1,
980
}
981
}
982
}
983
984
fn sys(local: Local<Foo>, mut system_ran: ResMut<SystemRan>) {
985
assert_eq!(local.value, 2);
986
*system_ran = SystemRan::Yes;
987
}
988
989
run_system(&mut world, sys);
990
991
// ensure the system actually ran
992
assert_eq!(*world.resource::<SystemRan>(), SystemRan::Yes);
993
}
994
995
#[test]
996
#[expect(
997
dead_code,
998
reason = "The `NotSend1` and `NotSend2` structs is used to verify that a system will run, even if the system params include a non-Send resource. As such, the inner value doesn't matter."
999
)]
1000
fn non_send_option_system() {
1001
let mut world = World::default();
1002
1003
world.insert_resource(SystemRan::No);
1004
// Two structs are used, one which is inserted and one which is not, to verify that wrapping
1005
// non-Send resources in an `Option` will allow the system to run regardless of their
1006
// existence.
1007
struct NotSend1(alloc::rc::Rc<i32>);
1008
struct NotSend2(alloc::rc::Rc<i32>);
1009
world.insert_non_send(NotSend1(alloc::rc::Rc::new(0)));
1010
1011
fn sys(
1012
op: Option<NonSend<NotSend1>>,
1013
mut _op2: Option<NonSendMut<NotSend2>>,
1014
mut system_ran: ResMut<SystemRan>,
1015
) {
1016
op.expect("NonSend should exist");
1017
*system_ran = SystemRan::Yes;
1018
}
1019
1020
run_system(&mut world, sys);
1021
// ensure the system actually ran
1022
assert_eq!(*world.resource::<SystemRan>(), SystemRan::Yes);
1023
}
1024
1025
#[test]
1026
#[expect(
1027
dead_code,
1028
reason = "The `NotSend1` and `NotSend2` structs are used to verify that a system will run, even if the system params include a non-Send resource. As such, the inner value doesn't matter."
1029
)]
1030
fn non_send_system() {
1031
let mut world = World::default();
1032
1033
world.insert_resource(SystemRan::No);
1034
struct NotSend1(alloc::rc::Rc<i32>);
1035
struct NotSend2(alloc::rc::Rc<i32>);
1036
1037
world.insert_non_send(NotSend1(alloc::rc::Rc::new(1)));
1038
world.insert_non_send(NotSend2(alloc::rc::Rc::new(2)));
1039
1040
fn sys(
1041
_op: NonSend<NotSend1>,
1042
mut _op2: NonSendMut<NotSend2>,
1043
mut system_ran: ResMut<SystemRan>,
1044
) {
1045
*system_ran = SystemRan::Yes;
1046
}
1047
1048
run_system(&mut world, sys);
1049
assert_eq!(*world.resource::<SystemRan>(), SystemRan::Yes);
1050
}
1051
1052
#[test]
1053
fn function_system_as_exclusive() {
1054
let mut world = World::default();
1055
1056
world.insert_resource(SystemRan::No);
1057
1058
fn sys(_marker: ExclusiveMarker, mut system_ran: ResMut<SystemRan>) {
1059
*system_ran = SystemRan::Yes;
1060
}
1061
1062
let mut sys = IntoSystem::into_system(sys);
1063
sys.initialize(&mut world);
1064
assert!(sys.is_exclusive());
1065
1066
run_system(&mut world, sys);
1067
assert_eq!(*world.resource::<SystemRan>(), SystemRan::Yes);
1068
}
1069
1070
#[test]
1071
fn removal_tracking() {
1072
let mut world = World::new();
1073
1074
let entity_to_despawn = world.spawn(W(1)).id();
1075
let entity_to_remove_w_from = world.spawn(W(2)).id();
1076
let spurious_entity = world.spawn_empty().id();
1077
1078
// Track which entities we want to operate on
1079
#[derive(Resource)]
1080
struct Despawned(Entity);
1081
world.insert_resource(Despawned(entity_to_despawn));
1082
1083
#[derive(Resource)]
1084
struct Removed(Entity);
1085
world.insert_resource(Removed(entity_to_remove_w_from));
1086
1087
// Verify that all the systems actually ran
1088
#[derive(Default, Resource)]
1089
struct NSystems(usize);
1090
world.insert_resource(NSystems::default());
1091
1092
// First, check that removal detection is triggered if and only if we despawn an entity with the correct component
1093
world.entity_mut(entity_to_despawn).despawn();
1094
world.entity_mut(spurious_entity).despawn();
1095
1096
fn validate_despawn(
1097
mut removed_i32: RemovedComponents<W<i32>>,
1098
despawned: Res<Despawned>,
1099
mut n_systems: ResMut<NSystems>,
1100
) {
1101
assert_eq!(
1102
removed_i32.read().collect::<Vec<_>>(),
1103
&[despawned.0],
1104
"despawning causes the correct entity to show up in the 'RemovedComponent' system parameter."
1105
);
1106
1107
n_systems.0 += 1;
1108
}
1109
1110
run_system(&mut world, validate_despawn);
1111
1112
// Reset the trackers to clear the buffer of removed components
1113
// Ordinarily, this is done in a system added by MinimalPlugins
1114
world.clear_trackers();
1115
1116
// Then, try removing a component
1117
world.spawn(W(3));
1118
world.spawn(W(4));
1119
world.entity_mut(entity_to_remove_w_from).remove::<W<i32>>();
1120
1121
fn validate_remove(
1122
mut removed_i32: RemovedComponents<W<i32>>,
1123
despawned: Res<Despawned>,
1124
removed: Res<Removed>,
1125
mut n_systems: ResMut<NSystems>,
1126
) {
1127
// The despawned entity from the previous frame was
1128
// double buffered so we now have it in this system as well.
1129
assert_eq!(
1130
removed_i32.read().collect::<Vec<_>>(),
1131
&[despawned.0, removed.0],
1132
"removing a component causes the correct entity to show up in the 'RemovedComponent' system parameter."
1133
);
1134
1135
n_systems.0 += 1;
1136
}
1137
1138
run_system(&mut world, validate_remove);
1139
1140
// Verify that both systems actually ran
1141
assert_eq!(world.resource::<NSystems>().0, 2);
1142
}
1143
1144
#[test]
1145
fn world_collections_system() {
1146
let mut world = World::default();
1147
world.insert_resource(SystemRan::No);
1148
world.spawn((W(42), W(true)));
1149
fn sys(
1150
archetypes: &Archetypes,
1151
components: &Components,
1152
entities: &Entities,
1153
bundles: &Bundles,
1154
query: Query<Entity, With<W<i32>>>,
1155
mut system_ran: ResMut<SystemRan>,
1156
) {
1157
assert_eq!(query.iter().count(), 1, "entity exists");
1158
for entity in &query {
1159
let location = entities.get_spawned(entity).unwrap();
1160
let archetype = archetypes.get(location.archetype_id).unwrap();
1161
let archetype_components = archetype.components();
1162
let bundle_id = bundles
1163
.get_id(TypeId::of::<(W<i32>, W<bool>)>())
1164
.expect("Bundle used to spawn entity should exist");
1165
let bundle_info = bundles.get(bundle_id).unwrap();
1166
let mut bundle_components = bundle_info.contributed_components().to_vec();
1167
bundle_components.sort();
1168
for component_id in &bundle_components {
1169
assert!(
1170
components.get_info(*component_id).is_some(),
1171
"every bundle component exists in Components"
1172
);
1173
}
1174
assert_eq!(
1175
bundle_components, archetype_components,
1176
"entity's bundle components exactly match entity's archetype components"
1177
);
1178
}
1179
*system_ran = SystemRan::Yes;
1180
}
1181
1182
run_system(&mut world, sys);
1183
1184
// ensure the system actually ran
1185
assert_eq!(*world.resource::<SystemRan>(), SystemRan::Yes);
1186
}
1187
1188
#[test]
1189
fn get_system_conflicts() {
1190
fn sys_x(_: Res<ResA>, _: Res<ResB>, _: Query<(&C, &D)>) {}
1191
1192
fn sys_y(_: Res<ResA>, _: ResMut<ResB>, _: Query<(&C, &mut D)>) {}
1193
1194
let mut world = World::default();
1195
let mut x = IntoSystem::into_system(sys_x);
1196
let mut y = IntoSystem::into_system(sys_y);
1197
let x_access = x.initialize(&mut world);
1198
let y_access = y.initialize(&mut world);
1199
1200
let conflicts = x_access.get_conflicts(&y_access);
1201
let b_id = world
1202
.components()
1203
.get_resource_id(TypeId::of::<ResB>())
1204
.unwrap();
1205
let d_id = world.components().get_id(TypeId::of::<D>()).unwrap();
1206
assert_eq!(conflicts, vec![b_id, d_id].into());
1207
}
1208
1209
#[test]
1210
fn query_is_empty() {
1211
fn without_filter(not_empty: Query<&A>, empty: Query<&B>) {
1212
assert!(!not_empty.is_empty());
1213
assert!(empty.is_empty());
1214
}
1215
1216
fn with_filter(not_empty: Query<&A, With<C>>, empty: Query<&A, With<D>>) {
1217
assert!(!not_empty.is_empty());
1218
assert!(empty.is_empty());
1219
}
1220
1221
let mut world = World::default();
1222
world.spawn(A).insert(C);
1223
1224
let mut without_filter = IntoSystem::into_system(without_filter);
1225
without_filter.initialize(&mut world);
1226
without_filter.run((), &mut world).unwrap();
1227
1228
let mut with_filter = IntoSystem::into_system(with_filter);
1229
with_filter.initialize(&mut world);
1230
with_filter.run((), &mut world).unwrap();
1231
}
1232
1233
#[test]
1234
fn can_have_16_parameters() {
1235
fn sys_x(
1236
_: Res<ResA>,
1237
_: Res<ResB>,
1238
_: Res<ResC>,
1239
_: Res<ResD>,
1240
_: Res<ResE>,
1241
_: Res<ResF>,
1242
_: Query<&A>,
1243
_: Query<&B>,
1244
_: Query<&C>,
1245
_: Query<&D>,
1246
_: Query<&E>,
1247
_: Query<&F>,
1248
_: Query<(&A, &B)>,
1249
_: Query<(&C, &D)>,
1250
_: Query<(&E, &F)>,
1251
) {
1252
}
1253
fn sys_y(
1254
_: (
1255
Res<ResA>,
1256
Res<ResB>,
1257
Res<ResC>,
1258
Res<ResD>,
1259
Res<ResE>,
1260
Res<ResF>,
1261
Query<&A>,
1262
Query<&B>,
1263
Query<&C>,
1264
Query<&D>,
1265
Query<&E>,
1266
Query<&F>,
1267
Query<(&A, &B)>,
1268
Query<(&C, &D)>,
1269
Query<(&E, &F)>,
1270
),
1271
) {
1272
}
1273
let mut world = World::default();
1274
let mut x = IntoSystem::into_system(sys_x);
1275
let mut y = IntoSystem::into_system(sys_y);
1276
x.initialize(&mut world);
1277
y.initialize(&mut world);
1278
}
1279
1280
#[test]
1281
fn read_system_state() {
1282
#[derive(Eq, PartialEq, Debug, Resource)]
1283
struct A(usize);
1284
1285
#[derive(Component, Eq, PartialEq, Debug)]
1286
struct B(usize);
1287
1288
let mut world = World::default();
1289
world.insert_resource(A(42));
1290
world.spawn(B(7));
1291
1292
let mut system_state: SystemState<(
1293
Res<A>,
1294
Option<Single<&B>>,
1295
ParamSet<(Query<&C>, Query<&D>)>,
1296
)> = SystemState::new(&mut world);
1297
let (a, query, _) = system_state.get(&world);
1298
assert_eq!(*a, A(42), "returned resource matches initial value");
1299
assert_eq!(
1300
**query.unwrap(),
1301
B(7),
1302
"returned component matches initial value"
1303
);
1304
}
1305
1306
#[test]
1307
fn write_system_state() {
1308
#[derive(Resource, Eq, PartialEq, Debug)]
1309
struct A(usize);
1310
1311
#[derive(Component, Eq, PartialEq, Debug)]
1312
struct B(usize);
1313
1314
let mut world = World::default();
1315
world.insert_resource(A(42));
1316
world.spawn(B(7));
1317
1318
let mut system_state: SystemState<(ResMut<A>, Option<Single<&mut B>>)> =
1319
SystemState::new(&mut world);
1320
1321
// The following line shouldn't compile because the parameters used are not ReadOnlySystemParam
1322
// let (a, query) = system_state.get(&world);
1323
1324
let (a, query) = system_state.get_mut(&mut world);
1325
assert_eq!(*a, A(42), "returned resource matches initial value");
1326
assert_eq!(
1327
**query.unwrap(),
1328
B(7),
1329
"returned component matches initial value"
1330
);
1331
}
1332
1333
#[test]
1334
fn system_state_change_detection() {
1335
#[derive(Component, Eq, PartialEq, Debug)]
1336
struct A(usize);
1337
1338
let mut world = World::default();
1339
let entity = world.spawn(A(1)).id();
1340
1341
let mut system_state: SystemState<Option<Single<&A, Changed<A>>>> =
1342
SystemState::new(&mut world);
1343
{
1344
let query = system_state.get(&world);
1345
assert_eq!(**query.unwrap(), A(1));
1346
}
1347
1348
{
1349
let query = system_state.get(&world);
1350
assert!(query.is_none());
1351
}
1352
1353
world.entity_mut(entity).get_mut::<A>().unwrap().0 = 2;
1354
{
1355
let query = system_state.get(&world);
1356
assert_eq!(**query.unwrap(), A(2));
1357
}
1358
}
1359
1360
#[test]
1361
fn system_state_spawned() {
1362
let mut world = World::default();
1363
world.spawn(A);
1364
let spawn_tick = world.change_tick();
1365
1366
let mut system_state: SystemState<Option<Single<(&A, SpawnDetails), Spawned>>> =
1367
SystemState::new(&mut world);
1368
{
1369
let query = system_state.get(&world);
1370
assert_eq!(query.unwrap().1.spawn_tick(), spawn_tick);
1371
}
1372
1373
{
1374
let query = system_state.get(&world);
1375
assert!(query.is_none());
1376
}
1377
}
1378
1379
#[test]
1380
#[should_panic]
1381
fn system_state_invalid_world() {
1382
let mut world = World::default();
1383
let mut system_state = SystemState::<Query<&A>>::new(&mut world);
1384
let mismatched_world = World::default();
1385
system_state.get(&mismatched_world);
1386
}
1387
1388
#[test]
1389
fn system_state_archetype_update() {
1390
#[derive(Component, Eq, PartialEq, Debug)]
1391
struct A(usize);
1392
1393
#[derive(Component, Eq, PartialEq, Debug)]
1394
struct B(usize);
1395
1396
let mut world = World::default();
1397
world.spawn(A(1));
1398
1399
let mut system_state = SystemState::<Query<&A>>::new(&mut world);
1400
{
1401
let query = system_state.get(&world);
1402
assert_eq!(
1403
query.iter().collect::<Vec<_>>(),
1404
vec![&A(1)],
1405
"exactly one component returned"
1406
);
1407
}
1408
1409
world.spawn((A(2), B(2)));
1410
{
1411
let query = system_state.get(&world);
1412
assert_eq!(
1413
query.iter().collect::<Vec<_>>(),
1414
vec![&A(1), &A(2)],
1415
"components from both archetypes returned"
1416
);
1417
}
1418
}
1419
1420
#[test]
1421
#[expect(
1422
dead_code,
1423
reason = "This test exists to show that read-only world-only queries can return data that lives as long as `'world`."
1424
)]
1425
fn long_life_test() {
1426
struct ResourceHolder<'w> {
1427
value: &'w ResA,
1428
}
1429
1430
struct Holder<'w> {
1431
value: &'w A,
1432
}
1433
1434
struct State {
1435
state: SystemState<Res<'static, ResA>>,
1436
state_q: SystemState<Query<'static, 'static, &'static A>>,
1437
}
1438
1439
impl State {
1440
fn hold_res<'w>(&mut self, world: &'w World) -> ResourceHolder<'w> {
1441
let a = self.state.get(world);
1442
ResourceHolder {
1443
value: a.into_inner(),
1444
}
1445
}
1446
fn hold_component<'w>(&mut self, world: &'w World, entity: Entity) -> Holder<'w> {
1447
let q = self.state_q.get(world);
1448
let a = q.get_inner(entity).unwrap();
1449
Holder { value: a }
1450
}
1451
fn hold_components<'w>(&mut self, world: &'w World) -> Vec<Holder<'w>> {
1452
let mut components = Vec::new();
1453
let q = self.state_q.get(world);
1454
for a in q.iter_inner() {
1455
components.push(Holder { value: a });
1456
}
1457
components
1458
}
1459
}
1460
}
1461
1462
#[test]
1463
fn immutable_mut_test() {
1464
#[derive(Component, Eq, PartialEq, Debug, Clone, Copy)]
1465
struct A(usize);
1466
1467
let mut world = World::default();
1468
world.spawn(A(1));
1469
world.spawn(A(2));
1470
1471
let mut system_state = SystemState::<Query<&mut A>>::new(&mut world);
1472
{
1473
let mut query = system_state.get_mut(&mut world);
1474
assert_eq!(
1475
query.iter_mut().map(|m| *m).collect::<Vec<A>>(),
1476
vec![A(1), A(2)],
1477
"both components returned by iter_mut of &mut"
1478
);
1479
assert_eq!(
1480
query.iter().collect::<Vec<&A>>(),
1481
vec![&A(1), &A(2)],
1482
"both components returned by iter of &mut"
1483
);
1484
}
1485
}
1486
1487
#[test]
1488
fn convert_mut_to_immut() {
1489
{
1490
let mut world = World::new();
1491
1492
fn mutable_query(mut query: Query<&mut A>) {
1493
for _ in &mut query {}
1494
1495
immutable_query(query.as_readonly());
1496
}
1497
1498
fn immutable_query(_: Query<&A>) {}
1499
1500
let mut sys = IntoSystem::into_system(mutable_query);
1501
sys.initialize(&mut world);
1502
}
1503
1504
{
1505
let mut world = World::new();
1506
1507
fn mutable_query(mut query: Query<Option<&mut A>>) {
1508
for _ in &mut query {}
1509
1510
immutable_query(query.as_readonly());
1511
}
1512
1513
fn immutable_query(_: Query<Option<&A>>) {}
1514
1515
let mut sys = IntoSystem::into_system(mutable_query);
1516
sys.initialize(&mut world);
1517
}
1518
1519
{
1520
let mut world = World::new();
1521
1522
fn mutable_query(mut query: Query<(&mut A, &B)>) {
1523
for _ in &mut query {}
1524
1525
immutable_query(query.as_readonly());
1526
}
1527
1528
fn immutable_query(_: Query<(&A, &B)>) {}
1529
1530
let mut sys = IntoSystem::into_system(mutable_query);
1531
sys.initialize(&mut world);
1532
}
1533
1534
{
1535
let mut world = World::new();
1536
1537
fn mutable_query(mut query: Query<(&mut A, &mut B)>) {
1538
for _ in &mut query {}
1539
1540
immutable_query(query.as_readonly());
1541
}
1542
1543
fn immutable_query(_: Query<(&A, &B)>) {}
1544
1545
let mut sys = IntoSystem::into_system(mutable_query);
1546
sys.initialize(&mut world);
1547
}
1548
1549
{
1550
let mut world = World::new();
1551
1552
fn mutable_query(mut query: Query<(&mut A, &mut B), With<C>>) {
1553
for _ in &mut query {}
1554
1555
immutable_query(query.as_readonly());
1556
}
1557
1558
fn immutable_query(_: Query<(&A, &B), With<C>>) {}
1559
1560
let mut sys = IntoSystem::into_system(mutable_query);
1561
sys.initialize(&mut world);
1562
}
1563
1564
{
1565
let mut world = World::new();
1566
1567
fn mutable_query(mut query: Query<(&mut A, &mut B), Without<C>>) {
1568
for _ in &mut query {}
1569
1570
immutable_query(query.as_readonly());
1571
}
1572
1573
fn immutable_query(_: Query<(&A, &B), Without<C>>) {}
1574
1575
let mut sys = IntoSystem::into_system(mutable_query);
1576
sys.initialize(&mut world);
1577
}
1578
1579
{
1580
let mut world = World::new();
1581
1582
fn mutable_query(mut query: Query<(&mut A, &mut B), Added<C>>) {
1583
for _ in &mut query {}
1584
1585
immutable_query(query.as_readonly());
1586
}
1587
1588
fn immutable_query(_: Query<(&A, &B), Added<C>>) {}
1589
1590
let mut sys = IntoSystem::into_system(mutable_query);
1591
sys.initialize(&mut world);
1592
}
1593
1594
{
1595
let mut world = World::new();
1596
1597
fn mutable_query(mut query: Query<(&mut A, &mut B), Changed<C>>) {
1598
for _ in &mut query {}
1599
1600
immutable_query(query.as_readonly());
1601
}
1602
1603
fn immutable_query(_: Query<(&A, &B), Changed<C>>) {}
1604
1605
let mut sys = IntoSystem::into_system(mutable_query);
1606
sys.initialize(&mut world);
1607
}
1608
1609
{
1610
let mut world = World::new();
1611
1612
fn mutable_query(mut query: Query<(&mut A, &mut B, SpawnDetails), Spawned>) {
1613
for _ in &mut query {}
1614
1615
immutable_query(query.as_readonly());
1616
}
1617
1618
fn immutable_query(_: Query<(&A, &B, SpawnDetails), Spawned>) {}
1619
1620
let mut sys = IntoSystem::into_system(mutable_query);
1621
sys.initialize(&mut world);
1622
}
1623
}
1624
1625
#[test]
1626
fn commands_param_set() {
1627
// Regression test for #4676
1628
let mut world = World::new();
1629
let entity = world.spawn_empty().id();
1630
1631
run_system(
1632
&mut world,
1633
move |mut commands_set: ParamSet<(Commands, Commands)>| {
1634
commands_set.p0().entity(entity).insert(A);
1635
commands_set.p1().entity(entity).insert(B);
1636
},
1637
);
1638
1639
let entity = world.entity(entity);
1640
assert!(entity.contains::<A>());
1641
assert!(entity.contains::<B>());
1642
}
1643
1644
#[test]
1645
fn into_iter_impl() {
1646
let mut world = World::new();
1647
world.spawn(W(42u32));
1648
run_system(&mut world, |mut q: Query<&mut W<u32>>| {
1649
for mut a in &mut q {
1650
assert_eq!(a.0, 42);
1651
a.0 = 0;
1652
}
1653
for a in &q {
1654
assert_eq!(a.0, 0);
1655
}
1656
});
1657
}
1658
1659
#[test]
1660
#[should_panic]
1661
fn assert_system_does_not_conflict() {
1662
fn system(_query: Query<(&mut W<u32>, &mut W<u32>)>) {}
1663
super::assert_system_does_not_conflict(system);
1664
}
1665
1666
#[test]
1667
#[should_panic]
1668
fn assert_world_and_entity_mut_system_does_conflict_first() {
1669
fn system(_query: &World, _q2: Query<EntityMut>) {}
1670
super::assert_system_does_not_conflict(system);
1671
}
1672
1673
#[test]
1674
#[should_panic]
1675
fn assert_world_and_entity_mut_system_does_conflict_second() {
1676
fn system(_: Query<EntityMut>, _: &World) {}
1677
super::assert_system_does_not_conflict(system);
1678
}
1679
1680
#[test]
1681
#[should_panic]
1682
fn assert_entity_ref_and_entity_mut_system_does_conflict() {
1683
fn system(_query: Query<EntityRef>, _q2: Query<EntityMut>) {}
1684
super::assert_system_does_not_conflict(system);
1685
}
1686
1687
#[test]
1688
#[should_panic]
1689
fn assert_entity_mut_system_does_conflict() {
1690
fn system(_query: Query<EntityMut>, _q2: Query<EntityMut>) {}
1691
super::assert_system_does_not_conflict(system);
1692
}
1693
1694
#[test]
1695
#[should_panic]
1696
fn assert_deferred_world_and_entity_ref_system_does_conflict_first() {
1697
fn system(_world: DeferredWorld, _query: Query<EntityRef>) {}
1698
super::assert_system_does_not_conflict(system);
1699
}
1700
1701
#[test]
1702
#[should_panic]
1703
fn assert_deferred_world_and_entity_ref_system_does_conflict_second() {
1704
fn system(_query: Query<EntityRef>, _world: DeferredWorld) {}
1705
super::assert_system_does_not_conflict(system);
1706
}
1707
1708
#[test]
1709
fn assert_deferred_world_and_empty_query_does_not_conflict_first() {
1710
fn system(_world: DeferredWorld, _query: Query<Entity>) {}
1711
super::assert_system_does_not_conflict(system);
1712
}
1713
1714
#[test]
1715
fn assert_deferred_world_and_empty_query_does_not_conflict_second() {
1716
fn system(_query: Query<Entity>, _world: DeferredWorld) {}
1717
super::assert_system_does_not_conflict(system);
1718
}
1719
1720
#[test]
1721
#[should_panic]
1722
fn panic_inside_system() {
1723
let mut world = World::new();
1724
let system: fn() = || {
1725
panic!("this system panics");
1726
};
1727
run_system(&mut world, system);
1728
}
1729
1730
#[test]
1731
fn assert_systems() {
1732
use core::str::FromStr;
1733
1734
use crate::{prelude::*, system::assert_is_system};
1735
1736
/// Mocks a system that returns a value of type `T`.
1737
fn returning<T>() -> T {
1738
unimplemented!()
1739
}
1740
1741
/// Mocks an exclusive system that takes an input and returns an output.
1742
fn exclusive_in_out<A, B>(_: In<A>, _: &mut World) -> B {
1743
unimplemented!()
1744
}
1745
1746
fn static_system_param(_: StaticSystemParam<Query<'static, 'static, &W<u32>>>) {
1747
unimplemented!()
1748
}
1749
1750
fn exclusive_with_state(
1751
_: &mut World,
1752
_: Local<bool>,
1753
_: (&mut QueryState<&W<i32>>, &mut SystemState<Query<&W<u32>>>),
1754
_: (),
1755
) {
1756
unimplemented!()
1757
}
1758
1759
fn not(In(val): In<bool>) -> bool {
1760
!val
1761
}
1762
1763
assert_is_system(returning::<Result<u32, std::io::Error>>.map(Result::unwrap));
1764
assert_is_system(returning::<Option<()>>.map(drop));
1765
assert_is_system(returning::<&str>.map(u64::from_str).map(Result::unwrap));
1766
assert_is_system(static_system_param);
1767
assert_is_system(
1768
exclusive_in_out::<(), Result<(), std::io::Error>>.map(|_out| {
1769
#[cfg(feature = "trace")]
1770
if let Err(error) = _out {
1771
tracing::error!("{}", error);
1772
}
1773
}),
1774
);
1775
assert_is_system(exclusive_with_state);
1776
assert_is_system(returning::<bool>.pipe(exclusive_in_out::<bool, ()>));
1777
1778
returning::<()>.run_if(returning::<bool>.pipe(not));
1779
}
1780
1781
#[test]
1782
fn pipe_change_detection() {
1783
#[derive(Resource, Default)]
1784
struct Flag;
1785
1786
#[derive(Default)]
1787
struct Info {
1788
// If true, the respective system will mutate `Flag`.
1789
do_first: bool,
1790
do_second: bool,
1791
1792
// Will be set to true if the respective system saw that `Flag` changed.
1793
first_flag: bool,
1794
second_flag: bool,
1795
}
1796
1797
fn first(In(mut info): In<Info>, mut flag: ResMut<Flag>) -> Info {
1798
if flag.is_changed() {
1799
info.first_flag = true;
1800
}
1801
if info.do_first {
1802
*flag = Flag;
1803
}
1804
1805
info
1806
}
1807
1808
fn second(In(mut info): In<Info>, mut flag: ResMut<Flag>) -> Info {
1809
if flag.is_changed() {
1810
info.second_flag = true;
1811
}
1812
if info.do_second {
1813
*flag = Flag;
1814
}
1815
1816
info
1817
}
1818
1819
let mut world = World::new();
1820
world.init_resource::<Flag>();
1821
let mut sys = IntoSystem::into_system(first.pipe(second));
1822
sys.initialize(&mut world);
1823
1824
sys.run(default(), &mut world).unwrap();
1825
1826
// The second system should observe a change made in the first system.
1827
let info = sys
1828
.run(
1829
Info {
1830
do_first: true,
1831
..default()
1832
},
1833
&mut world,
1834
)
1835
.unwrap();
1836
assert!(!info.first_flag);
1837
assert!(info.second_flag);
1838
1839
// When a change is made in the second system, the first system
1840
// should observe it the next time they are run.
1841
let info1 = sys
1842
.run(
1843
Info {
1844
do_second: true,
1845
..default()
1846
},
1847
&mut world,
1848
)
1849
.unwrap();
1850
let info2 = sys.run(default(), &mut world).unwrap();
1851
assert!(!info1.first_flag);
1852
assert!(!info1.second_flag);
1853
assert!(info2.first_flag);
1854
assert!(!info2.second_flag);
1855
}
1856
1857
#[test]
1858
fn test_combinator_clone() {
1859
let mut world = World::new();
1860
#[derive(Resource)]
1861
struct A;
1862
#[derive(Resource)]
1863
struct B;
1864
#[derive(Resource, PartialEq, Eq, Debug)]
1865
struct C(i32);
1866
1867
world.insert_resource(A);
1868
world.insert_resource(C(0));
1869
let mut sched = Schedule::default();
1870
sched.add_systems(
1871
(
1872
|mut res: ResMut<C>| {
1873
res.0 += 1;
1874
},
1875
|mut res: ResMut<C>| {
1876
res.0 += 2;
1877
},
1878
)
1879
.distributive_run_if(resource_exists::<A>.or(resource_exists::<B>)),
1880
);
1881
sched.initialize(&mut world).unwrap();
1882
sched.run(&mut world);
1883
assert_eq!(world.get_resource(), Some(&C(3)));
1884
}
1885
1886
#[test]
1887
#[cfg_attr(not(feature = "debug"), ignore)]
1888
#[should_panic(
1889
expected = "Encountered an error in system `bevy_ecs::system::tests::simple_fallible_system::sys`: error"
1890
)]
1891
fn simple_fallible_system() {
1892
fn sys() -> Result {
1893
Err("error")?;
1894
Ok(())
1895
}
1896
1897
let mut world = World::new();
1898
run_system(&mut world, sys);
1899
}
1900
1901
#[test]
1902
#[cfg_attr(not(feature = "debug"), ignore)]
1903
#[should_panic(
1904
expected = "Encountered an error in system `bevy_ecs::system::tests::simple_fallible_exclusive_system::sys`: error"
1905
)]
1906
fn simple_fallible_exclusive_system() {
1907
fn sys(_world: &mut World) -> Result {
1908
Err("error")?;
1909
Ok(())
1910
}
1911
1912
let mut world = World::new();
1913
run_system(&mut world, sys);
1914
}
1915
1916
// Regression test for
1917
// https://github.com/bevyengine/bevy/issues/18778
1918
//
1919
// Dear rustc team, please reach out if you encounter this
1920
// in a crater run and we can work something out!
1921
//
1922
// These todo! macro calls should never be removed;
1923
// they're intended to demonstrate real-world usage
1924
// in a way that's clearer than simply calling `panic!`
1925
//
1926
// Because type inference behaves differently for functions and closures,
1927
// we need to test both, in addition to explicitly annotating the return type
1928
// to ensure that there are no upstream regressions there.
1929
#[test]
1930
fn nondiverging_never_trait_impls() {
1931
// This test is a compilation test:
1932
// no meaningful logic is ever actually evaluated.
1933
// It is simply intended to check that the correct traits are implemented
1934
// when todo! or similar nondiverging panics are used.
1935
let mut world = World::new();
1936
let mut schedule = Schedule::default();
1937
1938
fn sys(_query: Query<&Name>) {
1939
todo!()
1940
}
1941
1942
schedule.add_systems(sys);
1943
schedule.add_systems(|_query: Query<&Name>| {});
1944
schedule.add_systems(|_query: Query<&Name>| todo!());
1945
schedule.add_systems(|_query: Query<&Name>| -> () { todo!() });
1946
1947
fn obs(_event: On<Add, Name>) {
1948
todo!()
1949
}
1950
1951
world.add_observer(obs);
1952
world.add_observer(|_event: On<Add, Name>| {});
1953
world.add_observer(|_event: On<Add, Name>| todo!());
1954
world.add_observer(|_event: On<Add, Name>| -> () { todo!() });
1955
1956
fn my_command(_world: &mut World) {
1957
todo!()
1958
}
1959
1960
world.commands().queue(my_command);
1961
world.commands().queue(|_world: &mut World| {});
1962
world.commands().queue(|_world: &mut World| todo!());
1963
world
1964
.commands()
1965
.queue(|_world: &mut World| -> () { todo!() });
1966
}
1967
1968
#[test]
1969
fn with_input() {
1970
fn sys(InMut(v): InMut<usize>) {
1971
*v += 1;
1972
}
1973
1974
let mut world = World::new();
1975
let mut system = IntoSystem::into_system(sys.with_input(42));
1976
system.initialize(&mut world);
1977
system.run((), &mut world).unwrap();
1978
assert_eq!(*system.value(), 43);
1979
}
1980
1981
#[test]
1982
fn with_input_from() {
1983
struct TestData(usize);
1984
1985
impl FromWorld for TestData {
1986
fn from_world(_world: &mut World) -> Self {
1987
Self(5)
1988
}
1989
}
1990
1991
fn sys(InMut(v): InMut<TestData>) {
1992
v.0 += 1;
1993
}
1994
1995
let mut world = World::new();
1996
let mut system = IntoSystem::into_system(sys.with_input_from::<TestData>());
1997
assert!(system.value().is_none());
1998
system.initialize(&mut world);
1999
assert!(system.value().is_some());
2000
system.run((), &mut world).unwrap();
2001
assert_eq!(system.value().unwrap().0, 6);
2002
}
2003
}
2004
2005