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