bevy_ecs/schedule/
mod.rs

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