Skip to main content

bevy_ecs/schedule/
mod.rs

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