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