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!(
574                result,
575                Err(ScheduleBuildError::DependencySort(
576                    DiGraphToposortError::Loop(_)
577                ))
578            ));
579        }
580
581        #[test]
582        fn dependency_loop_from_chain() {
583            let mut schedule = Schedule::default();
584            schedule.configure_sets((TestSystems::X, TestSystems::X).chain());
585            let mut world = World::new();
586            let result = schedule.initialize(&mut world);
587            assert!(matches!(
588                result,
589                Err(ScheduleBuildError::DependencySort(
590                    DiGraphToposortError::Loop(_)
591                ))
592            ));
593        }
594
595        #[test]
596        fn dependency_cycle() {
597            let mut world = World::new();
598            let mut schedule = Schedule::default();
599
600            schedule.configure_sets(TestSystems::A.after(TestSystems::B));
601            schedule.configure_sets(TestSystems::B.after(TestSystems::A));
602
603            let result = schedule.initialize(&mut world);
604            assert!(matches!(
605                result,
606                Err(ScheduleBuildError::DependencySort(
607                    DiGraphToposortError::Cycle(_)
608                ))
609            ));
610
611            fn foo() {}
612            fn bar() {}
613
614            let mut world = World::new();
615            let mut schedule = Schedule::default();
616
617            schedule.add_systems((foo.after(bar), bar.after(foo)));
618            let result = schedule.initialize(&mut world);
619            assert!(matches!(
620                result,
621                Err(ScheduleBuildError::FlatDependencySort(
622                    DiGraphToposortError::Cycle(_)
623                ))
624            ));
625        }
626
627        #[test]
628        fn hierarchy_loop() {
629            let mut schedule = Schedule::default();
630            schedule.configure_sets(TestSystems::X.in_set(TestSystems::X));
631            let mut world = World::new();
632            let result = schedule.initialize(&mut world);
633            assert!(matches!(
634                result,
635                Err(ScheduleBuildError::HierarchySort(
636                    DiGraphToposortError::Loop(_)
637                ))
638            ));
639        }
640
641        #[test]
642        fn hierarchy_cycle() {
643            let mut world = World::new();
644            let mut schedule = Schedule::default();
645
646            schedule.configure_sets(TestSystems::A.in_set(TestSystems::B));
647            schedule.configure_sets(TestSystems::B.in_set(TestSystems::A));
648
649            let result = schedule.initialize(&mut world);
650            assert!(matches!(
651                result,
652                Err(ScheduleBuildError::HierarchySort(
653                    DiGraphToposortError::Cycle(_)
654                ))
655            ));
656        }
657
658        #[test]
659        fn system_type_set_ambiguity() {
660            // Define some systems.
661            fn foo() {}
662            fn bar() {}
663
664            let mut world = World::new();
665            let mut schedule = Schedule::default();
666
667            // Schedule `bar` to run after `foo`.
668            schedule.add_systems((foo, bar.after(foo)));
669
670            // There's only one `foo`, so it's fine.
671            let result = schedule.initialize(&mut world);
672            assert!(result.is_ok());
673
674            // Schedule another `foo`.
675            schedule.add_systems(foo);
676
677            // When there are multiple instances of `foo`, dependencies on
678            // `foo` are no longer allowed. Too much ambiguity.
679            let result = schedule.initialize(&mut world);
680            assert!(matches!(
681                result,
682                Err(ScheduleBuildError::SystemTypeSetAmbiguity(_))
683            ));
684
685            // same goes for `ambiguous_with`
686            let mut schedule = Schedule::default();
687            schedule.add_systems(foo);
688            schedule.add_systems(bar.ambiguous_with(foo));
689            let result = schedule.initialize(&mut world);
690            assert!(result.is_ok());
691            schedule.add_systems(foo);
692            let result = schedule.initialize(&mut world);
693            assert!(matches!(
694                result,
695                Err(ScheduleBuildError::SystemTypeSetAmbiguity(_))
696            ));
697        }
698
699        #[test]
700        #[should_panic]
701        fn configure_system_type_set() {
702            fn foo() {}
703            let mut schedule = Schedule::default();
704            schedule.configure_sets(foo.into_system_set());
705        }
706
707        #[test]
708        fn hierarchy_redundancy() {
709            let mut world = World::new();
710            let mut schedule = Schedule::default();
711
712            schedule.set_build_settings(ScheduleBuildSettings {
713                hierarchy_detection: LogLevel::Error,
714                ..Default::default()
715            });
716
717            // Add `A`.
718            schedule.configure_sets(TestSystems::A);
719
720            // Add `B` as child of `A`.
721            schedule.configure_sets(TestSystems::B.in_set(TestSystems::A));
722
723            // Add `X` as child of both `A` and `B`.
724            schedule.configure_sets(TestSystems::X.in_set(TestSystems::A).in_set(TestSystems::B));
725
726            // `X` cannot be the `A`'s child and grandchild at the same time.
727            let result = schedule.initialize(&mut world);
728            assert!(matches!(
729                result,
730                Err(ScheduleBuildError::Elevated(
731                    ScheduleBuildWarning::HierarchyRedundancy(_)
732                ))
733            ));
734        }
735
736        #[test]
737        fn cross_dependency() {
738            let mut world = World::new();
739            let mut schedule = Schedule::default();
740
741            // Add `B` and give it both kinds of relationships with `A`.
742            schedule.configure_sets(TestSystems::B.in_set(TestSystems::A));
743            schedule.configure_sets(TestSystems::B.after(TestSystems::A));
744            let result = schedule.initialize(&mut world);
745            assert!(matches!(
746                result,
747                Err(ScheduleBuildError::CrossDependency(_))
748            ));
749        }
750
751        #[test]
752        fn sets_have_order_but_intersect() {
753            let mut world = World::new();
754            let mut schedule = Schedule::default();
755
756            fn foo() {}
757
758            // Add `foo` to both `A` and `C`.
759            schedule.add_systems(foo.in_set(TestSystems::A).in_set(TestSystems::C));
760
761            // Order `A -> B -> C`.
762            schedule.configure_sets((
763                TestSystems::A,
764                TestSystems::B.after(TestSystems::A),
765                TestSystems::C.after(TestSystems::B),
766            ));
767
768            let result = schedule.initialize(&mut world);
769            // `foo` can't be in both `A` and `C` because they can't run at the same time.
770            assert!(matches!(
771                result,
772                Err(ScheduleBuildError::SetsHaveOrderButIntersect(_))
773            ));
774        }
775
776        #[test]
777        fn ambiguity() {
778            #[derive(Resource)]
779            struct X;
780
781            fn res_ref(_x: Res<X>) {}
782            fn res_mut(_x: ResMut<X>) {}
783
784            let mut world = World::new();
785            let mut schedule = Schedule::default();
786
787            schedule.set_build_settings(ScheduleBuildSettings {
788                ambiguity_detection: LogLevel::Error,
789                ..Default::default()
790            });
791
792            schedule.add_systems((res_ref, res_mut));
793            let result = schedule.initialize(&mut world);
794            assert!(matches!(
795                result,
796                Err(ScheduleBuildError::Elevated(
797                    ScheduleBuildWarning::Ambiguity(_)
798                ))
799            ));
800        }
801    }
802
803    mod system_ambiguity {
804        #[cfg(feature = "trace")]
805        use alloc::collections::BTreeSet;
806
807        use super::*;
808        use crate::prelude::*;
809
810        #[derive(Resource)]
811        struct R;
812
813        #[derive(Component)]
814        struct A;
815
816        #[derive(Component)]
817        struct B;
818
819        #[derive(Message)]
820        struct E;
821
822        fn empty_system() {}
823        fn res_system(_res: Res<R>) {}
824        fn resmut_system(_res: ResMut<R>) {}
825        fn nonsend_system(_ns: NonSend<R>) {}
826        fn nonsendmut_system(_ns: NonSendMut<R>) {}
827        fn read_component_system(_query: Query<&A>) {}
828        fn write_component_system(_query: Query<&mut A>) {}
829        fn with_filtered_component_system(_query: Query<&mut A, With<B>>) {}
830        fn without_filtered_component_system(_query: Query<&mut A, Without<B>>) {}
831        fn entity_ref_system(_query: Query<EntityRef>) {}
832        fn entity_mut_system(_query: Query<EntityMut>) {}
833        fn message_reader_system(_reader: MessageReader<E>) {}
834        fn message_writer_system(_writer: MessageWriter<E>) {}
835        fn message_resource_system(_events: ResMut<Messages<E>>) {}
836        fn read_world_system(_world: &World) {}
837        fn write_world_system(_world: &mut World) {}
838
839        // Tests for conflict detection
840
841        #[test]
842        fn one_of_everything() {
843            let mut world = World::new();
844            world.insert_resource(R);
845            world.spawn(A);
846            world.init_resource::<Messages<E>>();
847
848            let mut schedule = Schedule::default();
849            schedule
850                // nonsendmut system deliberately conflicts with resmut system
851                .add_systems((resmut_system, write_component_system, message_writer_system));
852
853            let _ = schedule.initialize(&mut world);
854
855            assert_eq!(schedule.graph().conflicting_systems().len(), 0);
856        }
857
858        #[test]
859        fn read_only() {
860            let mut world = World::new();
861            world.insert_resource(R);
862            world.spawn(A);
863            world.init_resource::<Messages<E>>();
864
865            let mut schedule = Schedule::default();
866            schedule.add_systems((
867                empty_system,
868                empty_system,
869                res_system,
870                res_system,
871                nonsend_system,
872                nonsend_system,
873                read_component_system,
874                read_component_system,
875                entity_ref_system,
876                entity_ref_system,
877                message_reader_system,
878                message_reader_system,
879                read_world_system,
880                read_world_system,
881            ));
882
883            let _ = schedule.initialize(&mut world);
884
885            assert_eq!(schedule.graph().conflicting_systems().len(), 0);
886        }
887
888        #[test]
889        fn read_world() {
890            let mut world = World::new();
891            world.insert_resource(R);
892            world.spawn(A);
893            world.init_resource::<Messages<E>>();
894
895            let mut schedule = Schedule::default();
896            schedule.add_systems((
897                resmut_system,
898                write_component_system,
899                message_writer_system,
900                read_world_system,
901            ));
902
903            let _ = schedule.initialize(&mut world);
904
905            assert_eq!(schedule.graph().conflicting_systems().len(), 3);
906        }
907
908        #[test]
909        fn resources() {
910            let mut world = World::new();
911            world.insert_resource(R);
912
913            let mut schedule = Schedule::default();
914            schedule.add_systems((resmut_system, res_system));
915
916            let _ = schedule.initialize(&mut world);
917
918            assert_eq!(schedule.graph().conflicting_systems().len(), 1);
919        }
920
921        #[test]
922        fn nonsend() {
923            let mut world = World::new();
924            world.insert_resource(R);
925
926            let mut schedule = Schedule::default();
927            schedule.add_systems((nonsendmut_system, nonsend_system));
928
929            let _ = schedule.initialize(&mut world);
930
931            assert_eq!(schedule.graph().conflicting_systems().len(), 1);
932        }
933
934        #[test]
935        fn components() {
936            let mut world = World::new();
937            world.spawn(A);
938
939            let mut schedule = Schedule::default();
940            schedule.add_systems((read_component_system, write_component_system));
941
942            let _ = schedule.initialize(&mut world);
943
944            assert_eq!(schedule.graph().conflicting_systems().len(), 1);
945        }
946
947        #[test]
948        fn filtered_components() {
949            let mut world = World::new();
950            world.spawn(A);
951
952            let mut schedule = Schedule::default();
953            schedule.add_systems((
954                with_filtered_component_system,
955                without_filtered_component_system,
956            ));
957
958            let _ = schedule.initialize(&mut world);
959
960            assert_eq!(schedule.graph().conflicting_systems().len(), 0);
961        }
962
963        #[test]
964        fn events() {
965            let mut world = World::new();
966            world.init_resource::<Messages<E>>();
967
968            let mut schedule = Schedule::default();
969            schedule.add_systems((
970                // All of these systems clash
971                message_reader_system,
972                message_writer_system,
973                message_resource_system,
974            ));
975
976            let _ = schedule.initialize(&mut world);
977
978            assert_eq!(schedule.graph().conflicting_systems().len(), 3);
979        }
980
981        #[test]
982        fn resource_mut_and_entity_ref() {
983            let mut world = World::new();
984            world.insert_resource(R);
985
986            let mut schedule = Schedule::default();
987            schedule.add_systems((resmut_system, entity_ref_system));
988
989            let _ = schedule.initialize(&mut world);
990
991            assert_eq!(schedule.graph().conflicting_systems().len(), 0);
992        }
993
994        #[test]
995        fn resource_and_entity_mut() {
996            let mut world = World::new();
997            world.insert_resource(R);
998
999            let mut schedule = Schedule::default();
1000            schedule.add_systems((res_system, nonsend_system, entity_mut_system));
1001
1002            let _ = schedule.initialize(&mut world);
1003
1004            assert_eq!(schedule.graph().conflicting_systems().len(), 0);
1005        }
1006
1007        #[test]
1008        fn write_component_and_entity_ref() {
1009            let mut world = World::new();
1010            world.insert_resource(R);
1011
1012            let mut schedule = Schedule::default();
1013            schedule.add_systems((write_component_system, entity_ref_system));
1014
1015            let _ = schedule.initialize(&mut world);
1016
1017            assert_eq!(schedule.graph().conflicting_systems().len(), 1);
1018        }
1019
1020        #[test]
1021        fn read_component_and_entity_mut() {
1022            let mut world = World::new();
1023            world.insert_resource(R);
1024
1025            let mut schedule = Schedule::default();
1026            schedule.add_systems((read_component_system, entity_mut_system));
1027
1028            let _ = schedule.initialize(&mut world);
1029
1030            assert_eq!(schedule.graph().conflicting_systems().len(), 1);
1031        }
1032
1033        #[test]
1034        fn exclusive() {
1035            let mut world = World::new();
1036            world.insert_resource(R);
1037            world.spawn(A);
1038            world.init_resource::<Messages<E>>();
1039
1040            let mut schedule = Schedule::default();
1041            schedule.add_systems((
1042                // All 3 of these conflict with each other
1043                write_world_system,
1044                write_world_system,
1045                res_system,
1046            ));
1047
1048            let _ = schedule.initialize(&mut world);
1049
1050            assert_eq!(schedule.graph().conflicting_systems().len(), 3);
1051        }
1052
1053        // Tests for silencing and resolving ambiguities
1054        #[test]
1055        fn before_and_after() {
1056            let mut world = World::new();
1057            world.init_resource::<Messages<E>>();
1058
1059            let mut schedule = Schedule::default();
1060            schedule.add_systems((
1061                message_reader_system.before(message_writer_system),
1062                message_writer_system,
1063                message_resource_system.after(message_writer_system),
1064            ));
1065
1066            let _ = schedule.initialize(&mut world);
1067
1068            assert_eq!(schedule.graph().conflicting_systems().len(), 0);
1069        }
1070
1071        #[test]
1072        fn ignore_all_ambiguities() {
1073            let mut world = World::new();
1074            world.insert_resource(R);
1075
1076            let mut schedule = Schedule::default();
1077            schedule.add_systems((
1078                resmut_system.ambiguous_with_all(),
1079                res_system,
1080                nonsend_system,
1081            ));
1082
1083            let _ = schedule.initialize(&mut world);
1084
1085            assert_eq!(schedule.graph().conflicting_systems().len(), 0);
1086        }
1087
1088        #[test]
1089        fn ambiguous_with_label() {
1090            let mut world = World::new();
1091            world.insert_resource(R);
1092
1093            #[derive(SystemSet, Hash, PartialEq, Eq, Debug, Clone)]
1094            struct IgnoreMe;
1095
1096            let mut schedule = Schedule::default();
1097            schedule.add_systems((
1098                resmut_system.ambiguous_with(IgnoreMe),
1099                res_system.in_set(IgnoreMe),
1100                nonsend_system.in_set(IgnoreMe),
1101            ));
1102
1103            let _ = schedule.initialize(&mut world);
1104
1105            assert_eq!(schedule.graph().conflicting_systems().len(), 0);
1106        }
1107
1108        #[test]
1109        fn ambiguous_with_system() {
1110            let mut world = World::new();
1111
1112            let mut schedule = Schedule::default();
1113            schedule.add_systems((
1114                write_component_system.ambiguous_with(read_component_system),
1115                read_component_system,
1116            ));
1117            let _ = schedule.initialize(&mut world);
1118
1119            assert_eq!(schedule.graph().conflicting_systems().len(), 0);
1120        }
1121
1122        #[derive(ScheduleLabel, Hash, PartialEq, Eq, Debug, Clone)]
1123        struct TestSchedule;
1124
1125        // Tests that the correct ambiguities were reported in the correct order.
1126        #[test]
1127        #[cfg(feature = "trace")]
1128        fn correct_ambiguities() {
1129            fn system_a(_res: ResMut<R>) {}
1130            fn system_b(_res: ResMut<R>) {}
1131            fn system_c(_res: ResMut<R>) {}
1132            fn system_d(_res: ResMut<R>) {}
1133            fn system_e(_res: ResMut<R>) {}
1134
1135            let mut world = World::new();
1136            world.insert_resource(R);
1137
1138            let mut schedule = Schedule::new(TestSchedule);
1139            schedule.add_systems((
1140                system_a,
1141                system_b,
1142                system_c.ambiguous_with_all(),
1143                system_d.ambiguous_with(system_b),
1144                system_e.after(system_a),
1145            ));
1146
1147            schedule.graph_mut().initialize(&mut world);
1148            let _ = schedule
1149                .graph_mut()
1150                .build_schedule(&mut world, &BTreeSet::new());
1151
1152            let ambiguities: Vec<_> = schedule
1153                .graph()
1154                .conflicting_systems()
1155                .to_string(schedule.graph(), world.components())
1156                .map(|item| {
1157                    (
1158                        item.0,
1159                        item.1,
1160                        item.2
1161                            .into_iter()
1162                            .map(|name| name.to_string())
1163                            .collect::<Vec<_>>(),
1164                    )
1165                })
1166                .collect();
1167
1168            let expected = &[
1169                (
1170                    "system_d".to_string(),
1171                    "system_a".to_string(),
1172                    vec!["bevy_ecs::schedule::tests::system_ambiguity::R".into()],
1173                ),
1174                (
1175                    "system_d".to_string(),
1176                    "system_e".to_string(),
1177                    vec!["bevy_ecs::schedule::tests::system_ambiguity::R".into()],
1178                ),
1179                (
1180                    "system_b".to_string(),
1181                    "system_a".to_string(),
1182                    vec!["bevy_ecs::schedule::tests::system_ambiguity::R".into()],
1183                ),
1184                (
1185                    "system_b".to_string(),
1186                    "system_e".to_string(),
1187                    vec!["bevy_ecs::schedule::tests::system_ambiguity::R".into()],
1188                ),
1189            ];
1190
1191            // ordering isn't stable so do this
1192            for entry in expected {
1193                assert!(ambiguities.contains(entry));
1194            }
1195        }
1196
1197        // Test that anonymous set names work properly
1198        // Related issue https://github.com/bevyengine/bevy/issues/9641
1199        #[test]
1200        #[cfg(feature = "trace")]
1201        fn anonymous_set_name() {
1202            let mut schedule = Schedule::new(TestSchedule);
1203            schedule.add_systems((resmut_system, resmut_system).run_if(|| true));
1204
1205            let mut world = World::new();
1206            schedule.graph_mut().initialize(&mut world);
1207            let _ = schedule
1208                .graph_mut()
1209                .build_schedule(&mut world, &BTreeSet::new());
1210
1211            let ambiguities: Vec<_> = schedule
1212                .graph()
1213                .conflicting_systems()
1214                .to_string(schedule.graph(), world.components())
1215                .map(|item| {
1216                    (
1217                        item.0,
1218                        item.1,
1219                        item.2
1220                            .into_iter()
1221                            .map(|name| name.to_string())
1222                            .collect::<Vec<_>>(),
1223                    )
1224                })
1225                .collect();
1226
1227            assert_eq!(
1228                ambiguities[0],
1229                (
1230                    "resmut_system (in set (resmut_system, resmut_system))".to_string(),
1231                    "resmut_system (in set (resmut_system, resmut_system))".to_string(),
1232                    vec!["bevy_ecs::schedule::tests::system_ambiguity::R".into()],
1233                )
1234            );
1235        }
1236
1237        #[test]
1238        fn ignore_component_resource_ambiguities() {
1239            let mut world = World::new();
1240            world.insert_resource(R);
1241            world.allow_ambiguous_resource::<R>();
1242            let mut schedule = Schedule::new(TestSchedule);
1243
1244            // check resource
1245            schedule.add_systems((resmut_system, res_system));
1246            schedule.initialize(&mut world).unwrap();
1247            assert!(schedule.graph().conflicting_systems().is_empty());
1248
1249            // check components
1250            world.allow_ambiguous_component::<A>();
1251            schedule.add_systems((write_component_system, read_component_system));
1252            schedule.initialize(&mut world).unwrap();
1253            assert!(schedule.graph().conflicting_systems().is_empty());
1254        }
1255    }
1256
1257    #[cfg(feature = "bevy_debug_stepping")]
1258    mod stepping {
1259        use super::*;
1260        use bevy_ecs::system::SystemState;
1261
1262        #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
1263        pub struct TestSchedule;
1264
1265        macro_rules! assert_executor_supports_stepping {
1266            ($executor:expr) => {
1267                // create a test schedule
1268                let mut schedule = Schedule::new(TestSchedule);
1269                schedule
1270                    .set_executor_kind($executor)
1271                    .add_systems(|| -> () { panic!("Executor ignored Stepping") });
1272
1273                // Add our schedule to stepping & and enable stepping; this should
1274                // prevent any systems in the schedule from running
1275                let mut stepping = Stepping::default();
1276                stepping.add_schedule(TestSchedule).enable();
1277
1278                // create a world, and add the stepping resource
1279                let mut world = World::default();
1280                world.insert_resource(stepping);
1281
1282                // start a new frame by running ihe begin_frame() system
1283                let mut system_state: SystemState<Option<ResMut<Stepping>>> =
1284                    SystemState::new(&mut world);
1285                let res = system_state.get_mut(&mut world);
1286                Stepping::begin_frame(res);
1287
1288                // now run the schedule; this will panic if the executor doesn't
1289                // handle stepping
1290                schedule.run(&mut world);
1291            };
1292        }
1293
1294        /// verify the [`SingleThreadedExecutor`] supports stepping
1295        #[test]
1296        fn single_threaded_executor() {
1297            assert_executor_supports_stepping!(ExecutorKind::SingleThreaded);
1298        }
1299
1300        /// verify the [`MultiThreadedExecutor`] supports stepping
1301        #[test]
1302        fn multi_threaded_executor() {
1303            assert_executor_supports_stepping!(ExecutorKind::MultiThreaded);
1304        }
1305    }
1306}