bevy_ecs/schedule/
mod.rs

1//! Contains APIs for ordering systems and executing them on a [`World`](crate::world::World)
2
3mod auto_insert_apply_deferred;
4mod condition;
5mod config;
6mod error;
7mod executor;
8mod node;
9mod pass;
10mod schedule;
11mod set;
12mod stepping;
13
14pub use self::graph::GraphInfo;
15pub use self::{condition::*, config::*, error::*, executor::*, node::*, schedule::*, set::*};
16pub use pass::ScheduleBuildPass;
17
18/// An implementation of a graph data structure.
19pub mod graph;
20
21/// Included optional schedule build passes.
22pub mod passes {
23    pub use crate::schedule::auto_insert_apply_deferred::*;
24}
25
26use self::graph::*;
27
28#[cfg(test)]
29mod tests {
30    use super::*;
31    #[cfg(feature = "trace")]
32    use alloc::string::ToString;
33    use alloc::{vec, vec::Vec};
34    use core::sync::atomic::{AtomicU32, Ordering};
35
36    pub use crate::{
37        prelude::World,
38        resource::Resource,
39        schedule::{Schedule, SystemSet},
40        system::{Res, ResMut},
41    };
42
43    #[derive(SystemSet, Clone, Debug, PartialEq, Eq, Hash)]
44    enum TestSystems {
45        A,
46        B,
47        C,
48        D,
49        X,
50    }
51
52    #[derive(Resource, Default)]
53    struct SystemOrder(Vec<u32>);
54
55    #[derive(Resource, Default)]
56    struct RunConditionBool(bool);
57
58    #[derive(Resource, Default)]
59    struct Counter(AtomicU32);
60
61    fn make_exclusive_system(tag: u32) -> impl FnMut(&mut World) {
62        move |world| world.resource_mut::<SystemOrder>().0.push(tag)
63    }
64
65    fn make_function_system(tag: u32) -> impl FnMut(ResMut<SystemOrder>) {
66        move |mut resource: ResMut<SystemOrder>| resource.0.push(tag)
67    }
68
69    fn named_system(mut resource: ResMut<SystemOrder>) {
70        resource.0.push(u32::MAX);
71    }
72
73    fn named_exclusive_system(world: &mut World) {
74        world.resource_mut::<SystemOrder>().0.push(u32::MAX);
75    }
76
77    fn counting_system(counter: Res<Counter>) {
78        counter.0.fetch_add(1, Ordering::Relaxed);
79    }
80
81    mod system_execution {
82        use super::*;
83
84        #[test]
85        fn run_system() {
86            let mut world = World::default();
87            let mut schedule = Schedule::default();
88
89            world.init_resource::<SystemOrder>();
90
91            schedule.add_systems(make_function_system(0));
92            schedule.run(&mut world);
93
94            assert_eq!(world.resource::<SystemOrder>().0, vec![0]);
95        }
96
97        #[test]
98        fn run_exclusive_system() {
99            let mut world = World::default();
100            let mut schedule = Schedule::default();
101
102            world.init_resource::<SystemOrder>();
103
104            schedule.add_systems(make_exclusive_system(0));
105            schedule.run(&mut world);
106
107            assert_eq!(world.resource::<SystemOrder>().0, vec![0]);
108        }
109
110        #[test]
111        #[cfg(not(miri))]
112        fn parallel_execution() {
113            use alloc::sync::Arc;
114            use bevy_tasks::{ComputeTaskPool, TaskPool};
115            use std::sync::Barrier;
116
117            let mut world = World::default();
118            let mut schedule = Schedule::default();
119            let thread_count = ComputeTaskPool::get_or_init(TaskPool::default).thread_num();
120
121            let barrier = Arc::new(Barrier::new(thread_count));
122
123            for _ in 0..thread_count {
124                let inner = barrier.clone();
125                schedule.add_systems(move || {
126                    inner.wait();
127                });
128            }
129
130            schedule.run(&mut world);
131        }
132    }
133
134    mod system_ordering {
135        use super::*;
136
137        #[test]
138        fn order_systems() {
139            let mut world = World::default();
140            let mut schedule = Schedule::default();
141
142            world.init_resource::<SystemOrder>();
143
144            schedule.add_systems((
145                named_system,
146                make_function_system(1).before(named_system),
147                make_function_system(0)
148                    .after(named_system)
149                    .in_set(TestSystems::A),
150            ));
151            schedule.run(&mut world);
152
153            assert_eq!(world.resource::<SystemOrder>().0, vec![1, u32::MAX, 0]);
154
155            world.insert_resource(SystemOrder::default());
156
157            assert_eq!(world.resource::<SystemOrder>().0, vec![]);
158
159            // modify the schedule after it's been initialized and test ordering with sets
160            schedule.configure_sets(TestSystems::A.after(named_system));
161            schedule.add_systems((
162                make_function_system(3)
163                    .before(TestSystems::A)
164                    .after(named_system),
165                make_function_system(4).after(TestSystems::A),
166            ));
167            schedule.run(&mut world);
168
169            assert_eq!(
170                world.resource::<SystemOrder>().0,
171                vec![1, u32::MAX, 3, 0, 4]
172            );
173        }
174
175        #[test]
176        fn order_exclusive_systems() {
177            let mut world = World::default();
178            let mut schedule = Schedule::default();
179
180            world.init_resource::<SystemOrder>();
181
182            schedule.add_systems((
183                named_exclusive_system,
184                make_exclusive_system(1).before(named_exclusive_system),
185                make_exclusive_system(0).after(named_exclusive_system),
186            ));
187            schedule.run(&mut world);
188
189            assert_eq!(world.resource::<SystemOrder>().0, vec![1, u32::MAX, 0]);
190        }
191
192        #[test]
193        fn add_systems_correct_order() {
194            let mut world = World::new();
195            let mut schedule = Schedule::default();
196
197            world.init_resource::<SystemOrder>();
198
199            schedule.add_systems(
200                (
201                    make_function_system(0),
202                    make_function_system(1),
203                    make_exclusive_system(2),
204                    make_function_system(3),
205                )
206                    .chain(),
207            );
208
209            schedule.run(&mut world);
210            assert_eq!(world.resource::<SystemOrder>().0, vec![0, 1, 2, 3]);
211        }
212
213        #[test]
214        fn add_systems_correct_order_nested() {
215            let mut world = World::new();
216            let mut schedule = Schedule::default();
217
218            world.init_resource::<SystemOrder>();
219
220            schedule.add_systems(
221                (
222                    (make_function_system(0), make_function_system(1)).chain(),
223                    make_function_system(2),
224                    (make_function_system(3), make_function_system(4)).chain(),
225                    (
226                        make_function_system(5),
227                        (make_function_system(6), make_function_system(7)),
228                    ),
229                    (
230                        (make_function_system(8), make_function_system(9)).chain(),
231                        make_function_system(10),
232                    ),
233                )
234                    .chain(),
235            );
236
237            schedule.run(&mut world);
238            let order = &world.resource::<SystemOrder>().0;
239            assert_eq!(
240                &order[0..5],
241                &[0, 1, 2, 3, 4],
242                "first five items should be exactly ordered"
243            );
244            let unordered = &order[5..8];
245            assert!(
246                unordered.contains(&5) && unordered.contains(&6) && unordered.contains(&7),
247                "unordered must be 5, 6, and 7 in any order"
248            );
249            let partially_ordered = &order[8..11];
250            assert!(
251                partially_ordered == [8, 9, 10] || partially_ordered == [10, 8, 9],
252                "partially_ordered must be [8, 9, 10] or [10, 8, 9]"
253            );
254            assert_eq!(order.len(), 11, "must have exactly 11 order entries");
255        }
256    }
257
258    mod conditions {
259
260        use crate::{
261            change_detection::DetectChanges,
262            error::{ignore, DefaultErrorHandler, Result},
263        };
264
265        use super::*;
266
267        #[test]
268        fn system_with_condition_bool() {
269            let mut world = World::default();
270            let mut schedule = Schedule::default();
271
272            world.init_resource::<RunConditionBool>();
273            world.init_resource::<SystemOrder>();
274
275            schedule.add_systems(
276                make_function_system(0).run_if(|condition: Res<RunConditionBool>| condition.0),
277            );
278
279            schedule.run(&mut world);
280            assert_eq!(world.resource::<SystemOrder>().0, vec![]);
281
282            world.resource_mut::<RunConditionBool>().0 = true;
283            schedule.run(&mut world);
284            assert_eq!(world.resource::<SystemOrder>().0, vec![0]);
285        }
286
287        #[test]
288        fn system_with_condition_result_bool() {
289            let mut world = World::default();
290            world.insert_resource(DefaultErrorHandler(ignore));
291            let mut schedule = Schedule::default();
292
293            world.init_resource::<SystemOrder>();
294
295            schedule.add_systems((
296                make_function_system(0).run_if(|| -> Result<bool> { Err(core::fmt::Error.into()) }),
297                make_function_system(1).run_if(|| -> Result<bool> { Ok(false) }),
298            ));
299
300            schedule.run(&mut world);
301            assert_eq!(world.resource::<SystemOrder>().0, vec![]);
302
303            schedule.add_systems(make_function_system(2).run_if(|| -> Result<bool> { Ok(true) }));
304
305            schedule.run(&mut world);
306            assert_eq!(world.resource::<SystemOrder>().0, vec![2]);
307        }
308
309        #[test]
310        fn systems_with_distributive_condition() {
311            let mut world = World::default();
312            let mut schedule = Schedule::default();
313
314            world.insert_resource(RunConditionBool(true));
315            world.init_resource::<SystemOrder>();
316
317            fn change_condition(mut condition: ResMut<RunConditionBool>) {
318                condition.0 = false;
319            }
320
321            schedule.add_systems(
322                (
323                    make_function_system(0),
324                    change_condition,
325                    make_function_system(1),
326                )
327                    .chain()
328                    .distributive_run_if(|condition: Res<RunConditionBool>| condition.0),
329            );
330
331            schedule.run(&mut world);
332            assert_eq!(world.resource::<SystemOrder>().0, vec![0]);
333        }
334
335        #[test]
336        fn run_exclusive_system_with_condition() {
337            let mut world = World::default();
338            let mut schedule = Schedule::default();
339
340            world.init_resource::<RunConditionBool>();
341            world.init_resource::<SystemOrder>();
342
343            schedule.add_systems(
344                make_exclusive_system(0).run_if(|condition: Res<RunConditionBool>| condition.0),
345            );
346
347            schedule.run(&mut world);
348            assert_eq!(world.resource::<SystemOrder>().0, vec![]);
349
350            world.resource_mut::<RunConditionBool>().0 = true;
351            schedule.run(&mut world);
352            assert_eq!(world.resource::<SystemOrder>().0, vec![0]);
353        }
354
355        #[test]
356        fn multiple_conditions_on_system() {
357            let mut world = World::default();
358            let mut schedule = Schedule::default();
359
360            world.init_resource::<Counter>();
361
362            schedule.add_systems((
363                counting_system.run_if(|| false).run_if(|| false),
364                counting_system.run_if(|| true).run_if(|| false),
365                counting_system.run_if(|| false).run_if(|| true),
366                counting_system.run_if(|| true).run_if(|| true),
367            ));
368
369            schedule.run(&mut world);
370            assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
371        }
372
373        #[test]
374        fn multiple_conditions_on_system_sets() {
375            let mut world = World::default();
376            let mut schedule = Schedule::default();
377
378            world.init_resource::<Counter>();
379
380            schedule.configure_sets(TestSystems::A.run_if(|| false).run_if(|| false));
381            schedule.add_systems(counting_system.in_set(TestSystems::A));
382            schedule.configure_sets(TestSystems::B.run_if(|| true).run_if(|| false));
383            schedule.add_systems(counting_system.in_set(TestSystems::B));
384            schedule.configure_sets(TestSystems::C.run_if(|| false).run_if(|| true));
385            schedule.add_systems(counting_system.in_set(TestSystems::C));
386            schedule.configure_sets(TestSystems::D.run_if(|| true).run_if(|| true));
387            schedule.add_systems(counting_system.in_set(TestSystems::D));
388
389            schedule.run(&mut world);
390            assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
391        }
392
393        #[test]
394        fn systems_nested_in_system_sets() {
395            let mut world = World::default();
396            let mut schedule = Schedule::default();
397
398            world.init_resource::<Counter>();
399
400            schedule.configure_sets(TestSystems::A.run_if(|| false));
401            schedule.add_systems(counting_system.in_set(TestSystems::A).run_if(|| false));
402            schedule.configure_sets(TestSystems::B.run_if(|| true));
403            schedule.add_systems(counting_system.in_set(TestSystems::B).run_if(|| false));
404            schedule.configure_sets(TestSystems::C.run_if(|| false));
405            schedule.add_systems(counting_system.in_set(TestSystems::C).run_if(|| true));
406            schedule.configure_sets(TestSystems::D.run_if(|| true));
407            schedule.add_systems(counting_system.in_set(TestSystems::D).run_if(|| true));
408
409            schedule.run(&mut world);
410            assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
411        }
412
413        #[test]
414        fn system_conditions_and_change_detection() {
415            #[derive(Resource, Default)]
416            struct Bool2(pub bool);
417
418            let mut world = World::default();
419            world.init_resource::<Counter>();
420            world.init_resource::<RunConditionBool>();
421            world.init_resource::<Bool2>();
422            let mut schedule = Schedule::default();
423
424            schedule.add_systems(
425                counting_system
426                    .run_if(|res1: Res<RunConditionBool>| res1.is_changed())
427                    .run_if(|res2: Res<Bool2>| res2.is_changed()),
428            );
429
430            // both resource were just added.
431            schedule.run(&mut world);
432            assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
433
434            // nothing has changed
435            schedule.run(&mut world);
436            assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
437
438            // RunConditionBool has changed, but counting_system did not run
439            world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
440            schedule.run(&mut world);
441            assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
442
443            // internal state for the bool2 run criteria was updated in the
444            // previous run, so system still does not run
445            world.get_resource_mut::<Bool2>().unwrap().0 = false;
446            schedule.run(&mut world);
447            assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
448
449            // internal state for bool2 was updated, so system still does not run
450            world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
451            schedule.run(&mut world);
452            assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
453
454            // now check that it works correctly changing Bool2 first and then RunConditionBool
455            world.get_resource_mut::<Bool2>().unwrap().0 = false;
456            world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
457            schedule.run(&mut world);
458            assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 2);
459        }
460
461        #[test]
462        fn system_set_conditions_and_change_detection() {
463            #[derive(Resource, Default)]
464            struct Bool2(pub bool);
465
466            let mut world = World::default();
467            world.init_resource::<Counter>();
468            world.init_resource::<RunConditionBool>();
469            world.init_resource::<Bool2>();
470            let mut schedule = Schedule::default();
471
472            schedule.configure_sets(
473                TestSystems::A
474                    .run_if(|res1: Res<RunConditionBool>| res1.is_changed())
475                    .run_if(|res2: Res<Bool2>| res2.is_changed()),
476            );
477
478            schedule.add_systems(counting_system.in_set(TestSystems::A));
479
480            // both resource were just added.
481            schedule.run(&mut world);
482            assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
483
484            // nothing has changed
485            schedule.run(&mut world);
486            assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
487
488            // RunConditionBool has changed, but counting_system did not run
489            world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
490            schedule.run(&mut world);
491            assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
492
493            // internal state for the bool2 run criteria was updated in the
494            // previous run, so system still does not run
495            world.get_resource_mut::<Bool2>().unwrap().0 = false;
496            schedule.run(&mut world);
497            assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
498
499            // internal state for bool2 was updated, so system still does not run
500            world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
501            schedule.run(&mut world);
502            assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
503
504            // the system only runs when both are changed on the same run
505            world.get_resource_mut::<Bool2>().unwrap().0 = false;
506            world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
507            schedule.run(&mut world);
508            assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 2);
509        }
510
511        #[test]
512        fn mixed_conditions_and_change_detection() {
513            #[derive(Resource, Default)]
514            struct Bool2(pub bool);
515
516            let mut world = World::default();
517            world.init_resource::<Counter>();
518            world.init_resource::<RunConditionBool>();
519            world.init_resource::<Bool2>();
520            let mut schedule = Schedule::default();
521
522            schedule.configure_sets(
523                TestSystems::A.run_if(|res1: Res<RunConditionBool>| res1.is_changed()),
524            );
525
526            schedule.add_systems(
527                counting_system
528                    .run_if(|res2: Res<Bool2>| res2.is_changed())
529                    .in_set(TestSystems::A),
530            );
531
532            // both resource were just added.
533            schedule.run(&mut world);
534            assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
535
536            // nothing has changed
537            schedule.run(&mut world);
538            assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
539
540            // RunConditionBool has changed, but counting_system did not run
541            world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
542            schedule.run(&mut world);
543            assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
544
545            // we now only change bool2 and the system also should not run
546            world.get_resource_mut::<Bool2>().unwrap().0 = false;
547            schedule.run(&mut world);
548            assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
549
550            // internal state for the bool2 run criteria was updated in the
551            // previous run, so system still does not run
552            world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
553            schedule.run(&mut world);
554            assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 1);
555
556            // the system only runs when both are changed on the same run
557            world.get_resource_mut::<Bool2>().unwrap().0 = false;
558            world.get_resource_mut::<RunConditionBool>().unwrap().0 = false;
559            schedule.run(&mut world);
560            assert_eq!(world.resource::<Counter>().0.load(Ordering::Relaxed), 2);
561        }
562    }
563
564    mod schedule_build_errors {
565        use super::*;
566
567        #[test]
568        fn dependency_loop() {
569            let mut schedule = Schedule::default();
570            schedule.configure_sets(TestSystems::X.after(TestSystems::X));
571            let mut world = World::new();
572            let result = schedule.initialize(&mut world);
573            assert!(matches!(result, Err(ScheduleBuildError::DependencyLoop(_))));
574        }
575
576        #[test]
577        fn dependency_loop_from_chain() {
578            let mut schedule = Schedule::default();
579            schedule.configure_sets((TestSystems::X, TestSystems::X).chain());
580            let mut world = World::new();
581            let result = schedule.initialize(&mut world);
582            assert!(matches!(result, Err(ScheduleBuildError::DependencyLoop(_))));
583        }
584
585        #[test]
586        fn dependency_cycle() {
587            let mut world = World::new();
588            let mut schedule = Schedule::default();
589
590            schedule.configure_sets(TestSystems::A.after(TestSystems::B));
591            schedule.configure_sets(TestSystems::B.after(TestSystems::A));
592
593            let result = schedule.initialize(&mut world);
594            assert!(matches!(
595                result,
596                Err(ScheduleBuildError::DependencyCycle(_))
597            ));
598
599            fn foo() {}
600            fn bar() {}
601
602            let mut world = World::new();
603            let mut schedule = Schedule::default();
604
605            schedule.add_systems((foo.after(bar), bar.after(foo)));
606            let result = schedule.initialize(&mut world);
607            assert!(matches!(
608                result,
609                Err(ScheduleBuildError::DependencyCycle(_))
610            ));
611        }
612
613        #[test]
614        fn hierarchy_loop() {
615            let mut schedule = Schedule::default();
616            schedule.configure_sets(TestSystems::X.in_set(TestSystems::X));
617            let mut world = World::new();
618            let result = schedule.initialize(&mut world);
619            assert!(matches!(result, Err(ScheduleBuildError::HierarchyLoop(_))));
620        }
621
622        #[test]
623        fn hierarchy_cycle() {
624            let mut world = World::new();
625            let mut schedule = Schedule::default();
626
627            schedule.configure_sets(TestSystems::A.in_set(TestSystems::B));
628            schedule.configure_sets(TestSystems::B.in_set(TestSystems::A));
629
630            let result = schedule.initialize(&mut world);
631            assert!(matches!(result, Err(ScheduleBuildError::HierarchyCycle(_))));
632        }
633
634        #[test]
635        fn system_type_set_ambiguity() {
636            // Define some systems.
637            fn foo() {}
638            fn bar() {}
639
640            let mut world = World::new();
641            let mut schedule = Schedule::default();
642
643            // Schedule `bar` to run after `foo`.
644            schedule.add_systems((foo, bar.after(foo)));
645
646            // There's only one `foo`, so it's fine.
647            let result = schedule.initialize(&mut world);
648            assert!(result.is_ok());
649
650            // Schedule another `foo`.
651            schedule.add_systems(foo);
652
653            // When there are multiple instances of `foo`, dependencies on
654            // `foo` are no longer allowed. Too much ambiguity.
655            let result = schedule.initialize(&mut world);
656            assert!(matches!(
657                result,
658                Err(ScheduleBuildError::SystemTypeSetAmbiguity(_))
659            ));
660
661            // same goes for `ambiguous_with`
662            let mut schedule = Schedule::default();
663            schedule.add_systems(foo);
664            schedule.add_systems(bar.ambiguous_with(foo));
665            let result = schedule.initialize(&mut world);
666            assert!(result.is_ok());
667            schedule.add_systems(foo);
668            let result = schedule.initialize(&mut world);
669            assert!(matches!(
670                result,
671                Err(ScheduleBuildError::SystemTypeSetAmbiguity(_))
672            ));
673        }
674
675        #[test]
676        #[should_panic]
677        fn configure_system_type_set() {
678            fn foo() {}
679            let mut schedule = Schedule::default();
680            schedule.configure_sets(foo.into_system_set());
681        }
682
683        #[test]
684        fn hierarchy_redundancy() {
685            let mut world = World::new();
686            let mut schedule = Schedule::default();
687
688            schedule.set_build_settings(ScheduleBuildSettings {
689                hierarchy_detection: LogLevel::Error,
690                ..Default::default()
691            });
692
693            // Add `A`.
694            schedule.configure_sets(TestSystems::A);
695
696            // Add `B` as child of `A`.
697            schedule.configure_sets(TestSystems::B.in_set(TestSystems::A));
698
699            // Add `X` as child of both `A` and `B`.
700            schedule.configure_sets(TestSystems::X.in_set(TestSystems::A).in_set(TestSystems::B));
701
702            // `X` cannot be the `A`'s child and grandchild at the same time.
703            let result = schedule.initialize(&mut world);
704            assert!(matches!(
705                result,
706                Err(ScheduleBuildError::Elevated(
707                    ScheduleBuildWarning::HierarchyRedundancy(_)
708                ))
709            ));
710        }
711
712        #[test]
713        fn cross_dependency() {
714            let mut world = World::new();
715            let mut schedule = Schedule::default();
716
717            // Add `B` and give it both kinds of relationships with `A`.
718            schedule.configure_sets(TestSystems::B.in_set(TestSystems::A));
719            schedule.configure_sets(TestSystems::B.after(TestSystems::A));
720            let result = schedule.initialize(&mut world);
721            assert!(matches!(
722                result,
723                Err(ScheduleBuildError::CrossDependency(_, _))
724            ));
725        }
726
727        #[test]
728        fn sets_have_order_but_intersect() {
729            let mut world = World::new();
730            let mut schedule = Schedule::default();
731
732            fn foo() {}
733
734            // Add `foo` to both `A` and `C`.
735            schedule.add_systems(foo.in_set(TestSystems::A).in_set(TestSystems::C));
736
737            // Order `A -> B -> C`.
738            schedule.configure_sets((
739                TestSystems::A,
740                TestSystems::B.after(TestSystems::A),
741                TestSystems::C.after(TestSystems::B),
742            ));
743
744            let result = schedule.initialize(&mut world);
745            // `foo` can't be in both `A` and `C` because they can't run at the same time.
746            assert!(matches!(
747                result,
748                Err(ScheduleBuildError::SetsHaveOrderButIntersect(_, _))
749            ));
750        }
751
752        #[test]
753        fn ambiguity() {
754            #[derive(Resource)]
755            struct X;
756
757            fn res_ref(_x: Res<X>) {}
758            fn res_mut(_x: ResMut<X>) {}
759
760            let mut world = World::new();
761            let mut schedule = Schedule::default();
762
763            schedule.set_build_settings(ScheduleBuildSettings {
764                ambiguity_detection: LogLevel::Error,
765                ..Default::default()
766            });
767
768            schedule.add_systems((res_ref, res_mut));
769            let result = schedule.initialize(&mut world);
770            assert!(matches!(
771                result,
772                Err(ScheduleBuildError::Elevated(
773                    ScheduleBuildWarning::Ambiguity(_)
774                ))
775            ));
776        }
777    }
778
779    mod system_ambiguity {
780        #[cfg(feature = "trace")]
781        use alloc::collections::BTreeSet;
782
783        use super::*;
784        use crate::prelude::*;
785
786        #[derive(Resource)]
787        struct R;
788
789        #[derive(Component)]
790        struct A;
791
792        #[derive(Component)]
793        struct B;
794
795        #[derive(Message)]
796        struct E;
797
798        #[derive(Resource, Component)]
799        struct RC;
800
801        fn empty_system() {}
802        fn res_system(_res: Res<R>) {}
803        fn resmut_system(_res: ResMut<R>) {}
804        fn nonsend_system(_ns: NonSend<R>) {}
805        fn nonsendmut_system(_ns: NonSendMut<R>) {}
806        fn read_component_system(_query: Query<&A>) {}
807        fn write_component_system(_query: Query<&mut A>) {}
808        fn with_filtered_component_system(_query: Query<&mut A, With<B>>) {}
809        fn without_filtered_component_system(_query: Query<&mut A, Without<B>>) {}
810        fn entity_ref_system(_query: Query<EntityRef>) {}
811        fn entity_mut_system(_query: Query<EntityMut>) {}
812        fn message_reader_system(_reader: MessageReader<E>) {}
813        fn message_writer_system(_writer: MessageWriter<E>) {}
814        fn message_resource_system(_events: ResMut<Messages<E>>) {}
815        fn read_world_system(_world: &World) {}
816        fn write_world_system(_world: &mut World) {}
817
818        // Tests for conflict detection
819
820        #[test]
821        fn one_of_everything() {
822            let mut world = World::new();
823            world.insert_resource(R);
824            world.spawn(A);
825            world.init_resource::<Messages<E>>();
826
827            let mut schedule = Schedule::default();
828            schedule
829                // nonsendmut system deliberately conflicts with resmut system
830                .add_systems((resmut_system, write_component_system, message_writer_system));
831
832            let _ = schedule.initialize(&mut world);
833
834            assert_eq!(schedule.graph().conflicting_systems().len(), 0);
835        }
836
837        #[test]
838        fn read_only() {
839            let mut world = World::new();
840            world.insert_resource(R);
841            world.spawn(A);
842            world.init_resource::<Messages<E>>();
843
844            let mut schedule = Schedule::default();
845            schedule.add_systems((
846                empty_system,
847                empty_system,
848                res_system,
849                res_system,
850                nonsend_system,
851                nonsend_system,
852                read_component_system,
853                read_component_system,
854                entity_ref_system,
855                entity_ref_system,
856                message_reader_system,
857                message_reader_system,
858                read_world_system,
859                read_world_system,
860            ));
861
862            let _ = schedule.initialize(&mut world);
863
864            assert_eq!(schedule.graph().conflicting_systems().len(), 0);
865        }
866
867        #[test]
868        fn read_world() {
869            let mut world = World::new();
870            world.insert_resource(R);
871            world.spawn(A);
872            world.init_resource::<Messages<E>>();
873
874            let mut schedule = Schedule::default();
875            schedule.add_systems((
876                resmut_system,
877                write_component_system,
878                message_writer_system,
879                read_world_system,
880            ));
881
882            let _ = schedule.initialize(&mut world);
883
884            assert_eq!(schedule.graph().conflicting_systems().len(), 3);
885        }
886
887        #[test]
888        fn resources() {
889            let mut world = World::new();
890            world.insert_resource(R);
891
892            let mut schedule = Schedule::default();
893            schedule.add_systems((resmut_system, res_system));
894
895            let _ = schedule.initialize(&mut world);
896
897            assert_eq!(schedule.graph().conflicting_systems().len(), 1);
898        }
899
900        #[test]
901        fn nonsend() {
902            let mut world = World::new();
903            world.insert_resource(R);
904
905            let mut schedule = Schedule::default();
906            schedule.add_systems((nonsendmut_system, nonsend_system));
907
908            let _ = schedule.initialize(&mut world);
909
910            assert_eq!(schedule.graph().conflicting_systems().len(), 1);
911        }
912
913        #[test]
914        fn components() {
915            let mut world = World::new();
916            world.spawn(A);
917
918            let mut schedule = Schedule::default();
919            schedule.add_systems((read_component_system, write_component_system));
920
921            let _ = schedule.initialize(&mut world);
922
923            assert_eq!(schedule.graph().conflicting_systems().len(), 1);
924        }
925
926        #[test]
927        fn filtered_components() {
928            let mut world = World::new();
929            world.spawn(A);
930
931            let mut schedule = Schedule::default();
932            schedule.add_systems((
933                with_filtered_component_system,
934                without_filtered_component_system,
935            ));
936
937            let _ = schedule.initialize(&mut world);
938
939            assert_eq!(schedule.graph().conflicting_systems().len(), 0);
940        }
941
942        #[test]
943        fn events() {
944            let mut world = World::new();
945            world.init_resource::<Messages<E>>();
946
947            let mut schedule = Schedule::default();
948            schedule.add_systems((
949                // All of these systems clash
950                message_reader_system,
951                message_writer_system,
952                message_resource_system,
953            ));
954
955            let _ = schedule.initialize(&mut world);
956
957            assert_eq!(schedule.graph().conflicting_systems().len(), 3);
958        }
959
960        /// Test that when a struct is both a Resource and a Component, they do not
961        /// conflict with each other.
962        #[test]
963        fn shared_resource_mut_component() {
964            let mut world = World::new();
965            world.insert_resource(RC);
966
967            let mut schedule = Schedule::default();
968            schedule.add_systems((|_: ResMut<RC>| {}, |_: Query<&mut RC>| {}));
969
970            let _ = schedule.initialize(&mut world);
971
972            assert_eq!(schedule.graph().conflicting_systems().len(), 0);
973        }
974
975        #[test]
976        fn resource_mut_and_entity_ref() {
977            let mut world = World::new();
978            world.insert_resource(R);
979
980            let mut schedule = Schedule::default();
981            schedule.add_systems((resmut_system, entity_ref_system));
982
983            let _ = schedule.initialize(&mut world);
984
985            assert_eq!(schedule.graph().conflicting_systems().len(), 0);
986        }
987
988        #[test]
989        fn resource_and_entity_mut() {
990            let mut world = World::new();
991            world.insert_resource(R);
992
993            let mut schedule = Schedule::default();
994            schedule.add_systems((res_system, nonsend_system, entity_mut_system));
995
996            let _ = schedule.initialize(&mut world);
997
998            assert_eq!(schedule.graph().conflicting_systems().len(), 0);
999        }
1000
1001        #[test]
1002        fn write_component_and_entity_ref() {
1003            let mut world = World::new();
1004            world.insert_resource(R);
1005
1006            let mut schedule = Schedule::default();
1007            schedule.add_systems((write_component_system, entity_ref_system));
1008
1009            let _ = schedule.initialize(&mut world);
1010
1011            assert_eq!(schedule.graph().conflicting_systems().len(), 1);
1012        }
1013
1014        #[test]
1015        fn read_component_and_entity_mut() {
1016            let mut world = World::new();
1017            world.insert_resource(R);
1018
1019            let mut schedule = Schedule::default();
1020            schedule.add_systems((read_component_system, entity_mut_system));
1021
1022            let _ = schedule.initialize(&mut world);
1023
1024            assert_eq!(schedule.graph().conflicting_systems().len(), 1);
1025        }
1026
1027        #[test]
1028        fn exclusive() {
1029            let mut world = World::new();
1030            world.insert_resource(R);
1031            world.spawn(A);
1032            world.init_resource::<Messages<E>>();
1033
1034            let mut schedule = Schedule::default();
1035            schedule.add_systems((
1036                // All 3 of these conflict with each other
1037                write_world_system,
1038                write_world_system,
1039                res_system,
1040            ));
1041
1042            let _ = schedule.initialize(&mut world);
1043
1044            assert_eq!(schedule.graph().conflicting_systems().len(), 3);
1045        }
1046
1047        // Tests for silencing and resolving ambiguities
1048        #[test]
1049        fn before_and_after() {
1050            let mut world = World::new();
1051            world.init_resource::<Messages<E>>();
1052
1053            let mut schedule = Schedule::default();
1054            schedule.add_systems((
1055                message_reader_system.before(message_writer_system),
1056                message_writer_system,
1057                message_resource_system.after(message_writer_system),
1058            ));
1059
1060            let _ = schedule.initialize(&mut world);
1061
1062            assert_eq!(schedule.graph().conflicting_systems().len(), 0);
1063        }
1064
1065        #[test]
1066        fn ignore_all_ambiguities() {
1067            let mut world = World::new();
1068            world.insert_resource(R);
1069
1070            let mut schedule = Schedule::default();
1071            schedule.add_systems((
1072                resmut_system.ambiguous_with_all(),
1073                res_system,
1074                nonsend_system,
1075            ));
1076
1077            let _ = schedule.initialize(&mut world);
1078
1079            assert_eq!(schedule.graph().conflicting_systems().len(), 0);
1080        }
1081
1082        #[test]
1083        fn ambiguous_with_label() {
1084            let mut world = World::new();
1085            world.insert_resource(R);
1086
1087            #[derive(SystemSet, Hash, PartialEq, Eq, Debug, Clone)]
1088            struct IgnoreMe;
1089
1090            let mut schedule = Schedule::default();
1091            schedule.add_systems((
1092                resmut_system.ambiguous_with(IgnoreMe),
1093                res_system.in_set(IgnoreMe),
1094                nonsend_system.in_set(IgnoreMe),
1095            ));
1096
1097            let _ = schedule.initialize(&mut world);
1098
1099            assert_eq!(schedule.graph().conflicting_systems().len(), 0);
1100        }
1101
1102        #[test]
1103        fn ambiguous_with_system() {
1104            let mut world = World::new();
1105
1106            let mut schedule = Schedule::default();
1107            schedule.add_systems((
1108                write_component_system.ambiguous_with(read_component_system),
1109                read_component_system,
1110            ));
1111            let _ = schedule.initialize(&mut world);
1112
1113            assert_eq!(schedule.graph().conflicting_systems().len(), 0);
1114        }
1115
1116        #[derive(ScheduleLabel, Hash, PartialEq, Eq, Debug, Clone)]
1117        struct TestSchedule;
1118
1119        // Tests that the correct ambiguities were reported in the correct order.
1120        #[test]
1121        #[cfg(feature = "trace")]
1122        fn correct_ambiguities() {
1123            fn system_a(_res: ResMut<R>) {}
1124            fn system_b(_res: ResMut<R>) {}
1125            fn system_c(_res: ResMut<R>) {}
1126            fn system_d(_res: ResMut<R>) {}
1127            fn system_e(_res: ResMut<R>) {}
1128
1129            let mut world = World::new();
1130            world.insert_resource(R);
1131
1132            let mut schedule = Schedule::new(TestSchedule);
1133            schedule.add_systems((
1134                system_a,
1135                system_b,
1136                system_c.ambiguous_with_all(),
1137                system_d.ambiguous_with(system_b),
1138                system_e.after(system_a),
1139            ));
1140
1141            schedule.graph_mut().initialize(&mut world);
1142            let _ = schedule
1143                .graph_mut()
1144                .build_schedule(&mut world, &BTreeSet::new());
1145
1146            let ambiguities: Vec<_> = schedule
1147                .graph()
1148                .conflicts_to_string(schedule.graph().conflicting_systems(), world.components())
1149                .map(|item| {
1150                    (
1151                        item.0,
1152                        item.1,
1153                        item.2
1154                            .into_iter()
1155                            .map(|name| name.to_string())
1156                            .collect::<Vec<_>>(),
1157                    )
1158                })
1159                .collect();
1160
1161            let expected = &[
1162                (
1163                    "system_d".to_string(),
1164                    "system_a".to_string(),
1165                    vec!["bevy_ecs::schedule::tests::system_ambiguity::R".into()],
1166                ),
1167                (
1168                    "system_d".to_string(),
1169                    "system_e".to_string(),
1170                    vec!["bevy_ecs::schedule::tests::system_ambiguity::R".into()],
1171                ),
1172                (
1173                    "system_b".to_string(),
1174                    "system_a".to_string(),
1175                    vec!["bevy_ecs::schedule::tests::system_ambiguity::R".into()],
1176                ),
1177                (
1178                    "system_b".to_string(),
1179                    "system_e".to_string(),
1180                    vec!["bevy_ecs::schedule::tests::system_ambiguity::R".into()],
1181                ),
1182            ];
1183
1184            // ordering isn't stable so do this
1185            for entry in expected {
1186                assert!(ambiguities.contains(entry));
1187            }
1188        }
1189
1190        // Test that anonymous set names work properly
1191        // Related issue https://github.com/bevyengine/bevy/issues/9641
1192        #[test]
1193        #[cfg(feature = "trace")]
1194        fn anonymous_set_name() {
1195            let mut schedule = Schedule::new(TestSchedule);
1196            schedule.add_systems((resmut_system, resmut_system).run_if(|| true));
1197
1198            let mut world = World::new();
1199            schedule.graph_mut().initialize(&mut world);
1200            let _ = schedule
1201                .graph_mut()
1202                .build_schedule(&mut world, &BTreeSet::new());
1203
1204            let ambiguities: Vec<_> = schedule
1205                .graph()
1206                .conflicts_to_string(schedule.graph().conflicting_systems(), world.components())
1207                .map(|item| {
1208                    (
1209                        item.0,
1210                        item.1,
1211                        item.2
1212                            .into_iter()
1213                            .map(|name| name.to_string())
1214                            .collect::<Vec<_>>(),
1215                    )
1216                })
1217                .collect();
1218
1219            assert_eq!(
1220                ambiguities[0],
1221                (
1222                    "resmut_system (in set (resmut_system, resmut_system))".to_string(),
1223                    "resmut_system (in set (resmut_system, resmut_system))".to_string(),
1224                    vec!["bevy_ecs::schedule::tests::system_ambiguity::R".into()],
1225                )
1226            );
1227        }
1228
1229        #[test]
1230        fn ignore_component_resource_ambiguities() {
1231            let mut world = World::new();
1232            world.insert_resource(R);
1233            world.allow_ambiguous_resource::<R>();
1234            let mut schedule = Schedule::new(TestSchedule);
1235
1236            // check resource
1237            schedule.add_systems((resmut_system, res_system));
1238            schedule.initialize(&mut world).unwrap();
1239            assert!(schedule.graph().conflicting_systems().is_empty());
1240
1241            // check components
1242            world.allow_ambiguous_component::<A>();
1243            schedule.add_systems((write_component_system, read_component_system));
1244            schedule.initialize(&mut world).unwrap();
1245            assert!(schedule.graph().conflicting_systems().is_empty());
1246        }
1247    }
1248
1249    #[cfg(feature = "bevy_debug_stepping")]
1250    mod stepping {
1251        use super::*;
1252        use bevy_ecs::system::SystemState;
1253
1254        #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
1255        pub struct TestSchedule;
1256
1257        macro_rules! assert_executor_supports_stepping {
1258            ($executor:expr) => {
1259                // create a test schedule
1260                let mut schedule = Schedule::new(TestSchedule);
1261                schedule
1262                    .set_executor_kind($executor)
1263                    .add_systems(|| -> () { panic!("Executor ignored Stepping") });
1264
1265                // Add our schedule to stepping & and enable stepping; this should
1266                // prevent any systems in the schedule from running
1267                let mut stepping = Stepping::default();
1268                stepping.add_schedule(TestSchedule).enable();
1269
1270                // create a world, and add the stepping resource
1271                let mut world = World::default();
1272                world.insert_resource(stepping);
1273
1274                // start a new frame by running ihe begin_frame() system
1275                let mut system_state: SystemState<Option<ResMut<Stepping>>> =
1276                    SystemState::new(&mut world);
1277                let res = system_state.get_mut(&mut world);
1278                Stepping::begin_frame(res);
1279
1280                // now run the schedule; this will panic if the executor doesn't
1281                // handle stepping
1282                schedule.run(&mut world);
1283            };
1284        }
1285
1286        /// verify the [`SimpleExecutor`] supports stepping
1287        #[test]
1288        #[expect(deprecated, reason = "We still need to test this.")]
1289        fn simple_executor() {
1290            assert_executor_supports_stepping!(ExecutorKind::Simple);
1291        }
1292
1293        /// verify the [`SingleThreadedExecutor`] supports stepping
1294        #[test]
1295        fn single_threaded_executor() {
1296            assert_executor_supports_stepping!(ExecutorKind::SingleThreaded);
1297        }
1298
1299        /// verify the [`MultiThreadedExecutor`] supports stepping
1300        #[test]
1301        fn multi_threaded_executor() {
1302            assert_executor_supports_stepping!(ExecutorKind::MultiThreaded);
1303        }
1304    }
1305}