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