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