1use crate::{
2 resource::Resource,
3 schedule::{InternedScheduleLabel, NodeId, Schedule, ScheduleLabel, SystemKey},
4 system::{IntoSystem, ResMut},
5};
6use alloc::vec::Vec;
7use bevy_platform::collections::HashMap;
8use bevy_utils::TypeIdMap;
9use core::any::TypeId;
10use fixedbitset::FixedBitSet;
11use log::{info, warn};
12use thiserror::Error;
13
14#[cfg(not(feature = "bevy_debug_stepping"))]
15use log::error;
16
17#[cfg(test)]
18use log::debug;
19
20#[derive(Debug, Default, PartialEq, Eq, Copy, Clone)]
21enum Action {
22 #[default]
24 RunAll,
25
26 Waiting,
28
29 Continue,
33
34 Step,
36}
37
38#[derive(Debug, Copy, Clone)]
39enum SystemBehavior {
40 AlwaysRun,
42
43 NeverRun,
45
46 Break,
51
52 Continue,
56}
57
58#[derive(Debug, Default, Clone, Copy)]
60struct Cursor {
61 pub schedule: usize,
63 pub system: usize,
65}
66
67enum SystemIdentifier {
69 Type(TypeId),
70 Node(NodeId),
71}
72
73enum Update {
76 SetAction(Action),
78 AddSchedule(InternedScheduleLabel),
80 RemoveSchedule(InternedScheduleLabel),
82 ClearSchedule(InternedScheduleLabel),
84 SetBehavior(InternedScheduleLabel, SystemIdentifier, SystemBehavior),
86 ClearBehavior(InternedScheduleLabel, SystemIdentifier),
88}
89
90#[derive(Error, Debug)]
91#[error("not available until all configured schedules have been run; try again next frame")]
92pub struct NotReady;
93
94#[derive(Resource, Default)]
95pub struct Stepping {
97 schedule_states: HashMap<InternedScheduleLabel, ScheduleState>,
99
100 schedule_order: Vec<InternedScheduleLabel>,
102
103 cursor: Cursor,
105
106 previous_schedule: Option<usize>,
108
109 action: Action,
111
112 updates: Vec<Update>,
114}
115
116impl core::fmt::Debug for Stepping {
117 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
118 write!(
119 f,
120 "Stepping {{ action: {:?}, schedules: {:?}, order: {:?}",
121 self.action,
122 self.schedule_states.keys(),
123 self.schedule_order
124 )?;
125 if self.action != Action::RunAll {
126 let Cursor { schedule, system } = self.cursor;
127 match self.schedule_order.get(schedule) {
128 Some(label) => write!(f, "cursor: {label:?}[{system}], ")?,
129 None => write!(f, "cursor: None, ")?,
130 };
131 }
132 write!(f, "}}")
133 }
134}
135
136impl Stepping {
137 pub fn new() -> Self {
139 Stepping::default()
140 }
141
142 pub fn begin_frame(stepping: Option<ResMut<Self>>) {
146 if let Some(mut stepping) = stepping {
147 stepping.next_frame();
148 }
149 }
150
151 pub fn schedules(&self) -> Result<&Vec<InternedScheduleLabel>, NotReady> {
154 if self.schedule_order.len() == self.schedule_states.len() {
155 Ok(&self.schedule_order)
156 } else {
157 Err(NotReady)
158 }
159 }
160
161 pub fn cursor(&self) -> Option<(InternedScheduleLabel, NodeId)> {
168 if self.action == Action::RunAll {
169 return None;
170 }
171 let label = self.schedule_order.get(self.cursor.schedule)?;
172 let state = self.schedule_states.get(label)?;
173 state
174 .node_ids
175 .get(self.cursor.system)
176 .map(|node_id| (*label, NodeId::System(*node_id)))
177 }
178
179 pub fn add_schedule(&mut self, schedule: impl ScheduleLabel) -> &mut Self {
181 self.updates.push(Update::AddSchedule(schedule.intern()));
182 self
183 }
184
185 pub fn remove_schedule(&mut self, schedule: impl ScheduleLabel) -> &mut Self {
190 self.updates.push(Update::RemoveSchedule(schedule.intern()));
191 self
192 }
193
194 pub fn clear_schedule(&mut self, schedule: impl ScheduleLabel) -> &mut Self {
196 self.updates.push(Update::ClearSchedule(schedule.intern()));
197 self
198 }
199
200 pub fn enable(&mut self) -> &mut Self {
202 #[cfg(feature = "bevy_debug_stepping")]
203 self.updates.push(Update::SetAction(Action::Waiting));
204 #[cfg(not(feature = "bevy_debug_stepping"))]
205 error!(
206 "Stepping cannot be enabled; \
207 bevy was compiled without the bevy_debug_stepping feature"
208 );
209 self
210 }
211
212 pub fn disable(&mut self) -> &mut Self {
214 self.updates.push(Update::SetAction(Action::RunAll));
215 self
216 }
217
218 pub fn is_enabled(&self) -> bool {
220 self.action != Action::RunAll
221 }
222
223 pub fn step_frame(&mut self) -> &mut Self {
227 self.updates.push(Update::SetAction(Action::Step));
228 self
229 }
230
231 pub fn continue_frame(&mut self) -> &mut Self {
236 self.updates.push(Update::SetAction(Action::Continue));
237 self
238 }
239
240 pub fn always_run<Marker>(
245 &mut self,
246 schedule: impl ScheduleLabel,
247 system: impl IntoSystem<(), (), Marker>,
248 ) -> &mut Self {
249 let type_id = system.system_type_id();
250 self.updates.push(Update::SetBehavior(
251 schedule.intern(),
252 SystemIdentifier::Type(type_id),
253 SystemBehavior::AlwaysRun,
254 ));
255
256 self
257 }
258
259 pub fn always_run_node(&mut self, schedule: impl ScheduleLabel, node: NodeId) -> &mut Self {
261 self.updates.push(Update::SetBehavior(
262 schedule.intern(),
263 SystemIdentifier::Node(node),
264 SystemBehavior::AlwaysRun,
265 ));
266 self
267 }
268
269 pub fn never_run<Marker>(
271 &mut self,
272 schedule: impl ScheduleLabel,
273 system: impl IntoSystem<(), (), Marker>,
274 ) -> &mut Self {
275 let type_id = system.system_type_id();
276 self.updates.push(Update::SetBehavior(
277 schedule.intern(),
278 SystemIdentifier::Type(type_id),
279 SystemBehavior::NeverRun,
280 ));
281
282 self
283 }
284
285 pub fn never_run_node(&mut self, schedule: impl ScheduleLabel, node: NodeId) -> &mut Self {
287 self.updates.push(Update::SetBehavior(
288 schedule.intern(),
289 SystemIdentifier::Node(node),
290 SystemBehavior::NeverRun,
291 ));
292 self
293 }
294
295 pub fn set_breakpoint<Marker>(
297 &mut self,
298 schedule: impl ScheduleLabel,
299 system: impl IntoSystem<(), (), Marker>,
300 ) -> &mut Self {
301 let type_id = system.system_type_id();
302 self.updates.push(Update::SetBehavior(
303 schedule.intern(),
304 SystemIdentifier::Type(type_id),
305 SystemBehavior::Break,
306 ));
307
308 self
309 }
310
311 pub fn set_breakpoint_node(&mut self, schedule: impl ScheduleLabel, node: NodeId) -> &mut Self {
313 self.updates.push(Update::SetBehavior(
314 schedule.intern(),
315 SystemIdentifier::Node(node),
316 SystemBehavior::Break,
317 ));
318 self
319 }
320
321 pub fn clear_breakpoint<Marker>(
323 &mut self,
324 schedule: impl ScheduleLabel,
325 system: impl IntoSystem<(), (), Marker>,
326 ) -> &mut Self {
327 self.clear_system(schedule, system);
328
329 self
330 }
331
332 pub fn clear_breakpoint_node(
334 &mut self,
335 schedule: impl ScheduleLabel,
336 node: NodeId,
337 ) -> &mut Self {
338 self.clear_node(schedule, node);
339 self
340 }
341
342 pub fn clear_system<Marker>(
344 &mut self,
345 schedule: impl ScheduleLabel,
346 system: impl IntoSystem<(), (), Marker>,
347 ) -> &mut Self {
348 let type_id = system.system_type_id();
349 self.updates.push(Update::ClearBehavior(
350 schedule.intern(),
351 SystemIdentifier::Type(type_id),
352 ));
353
354 self
355 }
356
357 pub fn clear_node(&mut self, schedule: impl ScheduleLabel, node: NodeId) -> &mut Self {
359 self.updates.push(Update::ClearBehavior(
360 schedule.intern(),
361 SystemIdentifier::Node(node),
362 ));
363 self
364 }
365
366 fn first_system_index_for_schedule(&self, index: usize) -> usize {
368 let Some(label) = self.schedule_order.get(index) else {
369 return 0;
370 };
371 let Some(state) = self.schedule_states.get(label) else {
372 return 0;
373 };
374 state.first.unwrap_or(0)
375 }
376
377 fn reset_cursor(&mut self) {
379 self.cursor = Cursor {
380 schedule: 0,
381 system: self.first_system_index_for_schedule(0),
382 };
383 }
384
385 fn next_frame(&mut self) {
387 if self.action != Action::RunAll {
390 self.action = Action::Waiting;
391 self.previous_schedule = None;
392
393 if self.cursor.schedule >= self.schedule_order.len() {
395 self.reset_cursor();
396 }
397 }
398
399 if self.updates.is_empty() {
400 return;
401 }
402
403 let mut reset_cursor = false;
404 for update in self.updates.drain(..) {
405 match update {
406 Update::SetAction(Action::RunAll) => {
407 self.action = Action::RunAll;
408 reset_cursor = true;
409 }
410 Update::SetAction(action) => {
411 #[expect(
416 clippy::match_same_arms,
417 reason = "Readability would be negatively impacted by combining the `(Waiting, RunAll)` and `(Continue, RunAll)` match arms."
418 )]
419 match (self.action, action) {
420 (Action::RunAll, Action::RunAll)
423 | (Action::Waiting, Action::Waiting)
424 | (Action::Continue, Action::Continue)
425 | (Action::Step, Action::Step)
426 | (Action::Continue, Action::Waiting)
427 | (Action::Step, Action::Waiting) => continue,
428
429 (Action::RunAll, Action::Waiting) => info!("enabled stepping"),
431 (Action::RunAll, _) => {
432 warn!(
433 "stepping not enabled; call Stepping::enable() \
434 before step_frame() or continue_frame()"
435 );
436 continue;
437 }
438
439 (Action::Waiting, Action::RunAll) => info!("disabled stepping"),
441 (Action::Waiting, Action::Continue) => info!("continue frame"),
442 (Action::Waiting, Action::Step) => info!("step frame"),
443
444 (Action::Continue, Action::RunAll) => info!("disabled stepping"),
446 (Action::Continue, Action::Step) => {
447 warn!("ignoring step_frame(); already continuing next frame");
448 continue;
449 }
450
451 (Action::Step, Action::RunAll) => info!("disabled stepping"),
453 (Action::Step, Action::Continue) => {
454 warn!("ignoring continue_frame(); already stepping next frame");
455 continue;
456 }
457 }
458
459 self.action = action;
461 }
462 Update::AddSchedule(l) => {
463 self.schedule_states.insert(l, ScheduleState::default());
464 }
465 Update::RemoveSchedule(label) => {
466 self.schedule_states.remove(&label);
467 if let Some(index) = self.schedule_order.iter().position(|l| l == &label) {
468 self.schedule_order.remove(index);
469 }
470 reset_cursor = true;
471 }
472 Update::ClearSchedule(label) => match self.schedule_states.get_mut(&label) {
473 Some(state) => state.clear_behaviors(),
474 None => {
475 warn!(
476 "stepping is not enabled for schedule {label:?}; \
477 use `.add_stepping({label:?})` to enable stepping"
478 );
479 }
480 },
481 Update::SetBehavior(label, system, behavior) => {
482 match self.schedule_states.get_mut(&label) {
483 Some(state) => state.set_behavior(system, behavior),
484 None => {
485 warn!(
486 "stepping is not enabled for schedule {label:?}; \
487 use `.add_stepping({label:?})` to enable stepping"
488 );
489 }
490 }
491 }
492 Update::ClearBehavior(label, system) => {
493 match self.schedule_states.get_mut(&label) {
494 Some(state) => state.clear_behavior(system),
495 None => {
496 warn!(
497 "stepping is not enabled for schedule {label:?}; \
498 use `.add_stepping({label:?})` to enable stepping"
499 );
500 }
501 }
502 }
503 }
504 }
505
506 if reset_cursor {
507 self.reset_cursor();
508 }
509 }
510
511 pub fn skipped_systems(&mut self, schedule: &Schedule) -> Option<FixedBitSet> {
514 if self.action == Action::RunAll {
515 return None;
516 }
517
518 let label = schedule.label();
520 let state = self.schedule_states.get_mut(&label)?;
521
522 let index = self.schedule_order.iter().position(|l| *l == label);
531 let index = match (index, self.previous_schedule) {
532 (Some(index), _) => index,
533 (None, None) => {
534 self.schedule_order.insert(0, label);
535 0
536 }
537 (None, Some(last)) => {
538 self.schedule_order.insert(last + 1, label);
539 last + 1
540 }
541 };
542 self.previous_schedule = Some(index);
545
546 #[cfg(test)]
547 debug!(
548 "cursor {:?}, index {}, label {:?}",
549 self.cursor, index, label
550 );
551
552 let cursor = self.cursor;
556 let (skip_list, next_system) = if index == cursor.schedule {
557 let (skip_list, next_system) =
558 state.skipped_systems(schedule, cursor.system, self.action);
559
560 if self.action == Action::Step {
563 self.action = Action::Waiting;
564 }
565 (skip_list, next_system)
566 } else {
567 let (skip_list, _) = state.skipped_systems(schedule, 0, Action::Waiting);
570 (skip_list, Some(cursor.system))
571 };
572
573 match next_system {
580 Some(i) => self.cursor.system = i,
581 None => {
582 let index = cursor.schedule + 1;
583 self.cursor = Cursor {
584 schedule: index,
585 system: self.first_system_index_for_schedule(index),
586 };
587
588 #[cfg(test)]
589 debug!("advanced schedule index: {} -> {}", cursor.schedule, index);
590 }
591 }
592
593 Some(skip_list)
594 }
595}
596
597#[derive(Default)]
598struct ScheduleState {
599 behaviors: HashMap<NodeId, SystemBehavior>,
601
602 node_ids: Vec<SystemKey>,
608
609 behavior_updates: TypeIdMap<Option<SystemBehavior>>,
612
613 first: Option<usize>,
615}
616
617impl ScheduleState {
618 fn set_behavior(&mut self, system: SystemIdentifier, behavior: SystemBehavior) {
620 self.first = None;
621 match system {
622 SystemIdentifier::Node(node_id) => {
623 self.behaviors.insert(node_id, behavior);
624 }
625 SystemIdentifier::Type(type_id) => {
629 self.behavior_updates.insert(type_id, Some(behavior));
630 }
631 }
632 }
633
634 fn clear_behavior(&mut self, system: SystemIdentifier) {
636 self.first = None;
637 match system {
638 SystemIdentifier::Node(node_id) => {
639 self.behaviors.remove(&node_id);
640 }
641 SystemIdentifier::Type(type_id) => {
643 self.behavior_updates.insert(type_id, None);
644 }
645 }
646 }
647
648 fn clear_behaviors(&mut self) {
650 self.behaviors.clear();
651 self.behavior_updates.clear();
652 self.first = None;
653 }
654
655 fn apply_behavior_updates(&mut self, schedule: &Schedule) {
658 for (key, system) in schedule.systems().unwrap() {
664 let behavior = self.behavior_updates.get(&system.type_id());
665 match behavior {
666 None => continue,
667 Some(None) => {
668 self.behaviors.remove(&NodeId::System(key));
669 }
670 Some(Some(behavior)) => {
671 self.behaviors.insert(NodeId::System(key), *behavior);
672 }
673 }
674 }
675 self.behavior_updates.clear();
676
677 #[cfg(test)]
678 debug!("apply_updates(): {:?}", self.behaviors);
679 }
680
681 fn skipped_systems(
682 &mut self,
683 schedule: &Schedule,
684 start: usize,
685 mut action: Action,
686 ) -> (FixedBitSet, Option<usize>) {
687 use core::cmp::Ordering;
688
689 if self.node_ids.len() != schedule.systems_len() {
692 self.node_ids.clone_from(&schedule.executable().system_ids);
693 }
694
695 if !self.behavior_updates.is_empty() {
699 self.apply_behavior_updates(schedule);
700 }
701
702 if self.first.is_none() {
704 for (i, (key, _)) in schedule.systems().unwrap().enumerate() {
705 match self.behaviors.get(&NodeId::System(key)) {
706 Some(SystemBehavior::AlwaysRun | SystemBehavior::NeverRun) => continue,
707 Some(_) | None => {
708 self.first = Some(i);
709 break;
710 }
711 }
712 }
713 }
714
715 let mut skip = FixedBitSet::with_capacity(schedule.systems_len());
716 let mut pos = start;
717
718 for (i, (key, _system)) in schedule.systems().unwrap().enumerate() {
719 let behavior = self
720 .behaviors
721 .get(&NodeId::System(key))
722 .unwrap_or(&SystemBehavior::Continue);
723
724 #[cfg(test)]
725 debug!(
726 "skipped_systems(): systems[{}], pos {}, Action::{:?}, Behavior::{:?}, {}",
727 i,
728 pos,
729 action,
730 behavior,
731 _system.name()
732 );
733
734 match (action, behavior) {
735 (_, SystemBehavior::NeverRun) => {
740 skip.insert(i);
741 if i == pos {
742 pos += 1;
743 }
744 }
745 (_, SystemBehavior::AlwaysRun) => {
750 if i == pos {
751 pos += 1;
752 }
753 }
754 (Action::Waiting, _) => skip.insert(i),
757
758 (Action::Step, _) => match i.cmp(&pos) {
764 Ordering::Less => skip.insert(i),
765 Ordering::Equal => {
766 pos += 1;
767 action = Action::Waiting;
768 }
769 Ordering::Greater => unreachable!(),
770 },
771 (Action::Continue, SystemBehavior::Continue) => {
775 if i < start {
776 skip.insert(i);
777 }
778 }
779 (Action::Continue, SystemBehavior::Break) => {
788 if i != start {
789 skip.insert(i);
790
791 if i > start {
794 action = Action::Waiting;
795 }
796 }
797 }
798 (Action::RunAll, _) => unreachable!(),
801 }
802
803 if i == pos && action != Action::Waiting {
806 pos += 1;
807 }
808 }
809
810 if pos >= schedule.systems_len() {
813 (skip, None)
814 } else {
815 (skip, Some(pos))
816 }
817 }
818}
819
820#[cfg(all(test, feature = "bevy_debug_stepping"))]
821#[expect(clippy::print_stdout, reason = "Allowed in tests.")]
822mod tests {
823 use super::*;
824 use crate::{prelude::*, schedule::ScheduleLabel};
825 use alloc::{format, vec};
826 use slotmap::SlotMap;
827 use std::println;
828
829 #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
830 struct TestSchedule;
831
832 #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
833 struct TestScheduleA;
834
835 #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
836 struct TestScheduleB;
837
838 #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
839 struct TestScheduleC;
840
841 #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
842 struct TestScheduleD;
843
844 fn first_system() {}
845 fn second_system() {}
846 fn third_system() {}
847
848 fn setup() -> (Schedule, World) {
849 let mut world = World::new();
850 let mut schedule = Schedule::new(TestSchedule);
851 schedule.add_systems((first_system, second_system).chain());
852 schedule.initialize(&mut world).unwrap();
853 (schedule, world)
854 }
855
856 macro_rules! assert_skip_list_eq {
859 ($actual:expr, $expected:expr, $system_names:expr) => {
860 let actual = $actual;
861 let expected = $expected;
862 let systems: &Vec<&str> = $system_names;
863
864 if (actual != expected) {
865 use core::fmt::Write as _;
866
867 let mut msg = format!(
870 "Schedule:\n {:9} {:16}{:6} {:6} {:6}\n",
871 "index", "name", "expect", "actual", "result"
872 );
873 for (i, name) in systems.iter().enumerate() {
874 let _ = write!(msg, " system[{:1}] {:16}", i, name);
875 match (expected.contains(i), actual.contains(i)) {
876 (true, true) => msg.push_str("skip skip pass\n"),
877 (true, false) => {
878 msg.push_str("skip run FAILED; system should not have run\n")
879 }
880 (false, true) => {
881 msg.push_str("run skip FAILED; system should have run\n")
882 }
883 (false, false) => msg.push_str("run run pass\n"),
884 }
885 }
886 assert_eq!(actual, expected, "{}", msg);
887 }
888 };
889 }
890
891 macro_rules! assert_systems_run {
894 ($schedule:expr, $skipped_systems:expr, $($system:expr),*) => {
895 let systems: Vec<(TypeId, alloc::string::String)> = $schedule.systems().unwrap()
898 .map(|(_, system)| {
899 (system.type_id(), system.name().as_string())
900 })
901 .collect();
902
903 let mut expected = FixedBitSet::with_capacity(systems.len());
905 $(
906 let sys = IntoSystem::into_system($system);
907 for (i, (type_id, _)) in systems.iter().enumerate() {
908 if sys.type_id() == *type_id {
909 expected.insert(i);
910 }
911 }
912 )*
913
914 expected.toggle_range(..);
916
917 let actual = match $skipped_systems {
919 None => FixedBitSet::with_capacity(systems.len()),
920 Some(b) => b,
921 };
922 let system_names: Vec<&str> = systems
923 .iter()
924 .map(|(_,n)| n.rsplit_once("::").unwrap().1)
925 .collect();
926
927 assert_skip_list_eq!(actual, expected, &system_names);
928 };
929 }
930
931 macro_rules! assert_schedule_runs {
938 ($schedule:expr, $stepping:expr, $($system:expr),*) => {
939 $stepping.next_frame();
942 assert_systems_run!($schedule, $stepping.skipped_systems($schedule), $($system),*);
943 };
944 }
945
946 #[test]
947 fn stepping_disabled() {
948 let (schedule, _world) = setup();
949
950 let mut stepping = Stepping::new();
951 stepping.add_schedule(TestSchedule).disable().next_frame();
952
953 assert!(stepping.skipped_systems(&schedule).is_none());
954 assert!(stepping.cursor().is_none());
955 }
956
957 #[test]
958 fn unknown_schedule() {
959 let (schedule, _world) = setup();
960
961 let mut stepping = Stepping::new();
962 stepping.enable().next_frame();
963
964 assert!(stepping.skipped_systems(&schedule).is_none());
965 }
966
967 #[test]
968 fn disabled_always_run() {
969 let (schedule, _world) = setup();
970
971 let mut stepping = Stepping::new();
972 stepping
973 .add_schedule(TestSchedule)
974 .disable()
975 .always_run(TestSchedule, first_system);
976
977 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
978 }
979
980 #[test]
981 fn waiting_always_run() {
982 let (schedule, _world) = setup();
983
984 let mut stepping = Stepping::new();
985 stepping
986 .add_schedule(TestSchedule)
987 .enable()
988 .always_run(TestSchedule, first_system);
989
990 assert_schedule_runs!(&schedule, &mut stepping, first_system);
991 }
992
993 #[test]
994 fn step_always_run() {
995 let (schedule, _world) = setup();
996
997 let mut stepping = Stepping::new();
998 stepping
999 .add_schedule(TestSchedule)
1000 .enable()
1001 .always_run(TestSchedule, first_system)
1002 .step_frame();
1003
1004 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1005 }
1006
1007 #[test]
1008 fn continue_always_run() {
1009 let (schedule, _world) = setup();
1010
1011 let mut stepping = Stepping::new();
1012 stepping
1013 .add_schedule(TestSchedule)
1014 .enable()
1015 .always_run(TestSchedule, first_system)
1016 .continue_frame();
1017
1018 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1019 }
1020
1021 #[test]
1022 fn disabled_never_run() {
1023 let (schedule, _world) = setup();
1024
1025 let mut stepping = Stepping::new();
1026 stepping
1027 .add_schedule(TestSchedule)
1028 .never_run(TestSchedule, first_system)
1029 .disable();
1030 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1031 }
1032
1033 #[test]
1034 fn waiting_never_run() {
1035 let (schedule, _world) = setup();
1036
1037 let mut stepping = Stepping::new();
1038 stepping
1039 .add_schedule(TestSchedule)
1040 .enable()
1041 .never_run(TestSchedule, first_system);
1042
1043 assert_schedule_runs!(&schedule, &mut stepping,);
1044 }
1045
1046 #[test]
1047 fn step_never_run() {
1048 let (schedule, _world) = setup();
1049
1050 let mut stepping = Stepping::new();
1051 stepping
1052 .add_schedule(TestSchedule)
1053 .enable()
1054 .never_run(TestSchedule, first_system)
1055 .step_frame();
1056
1057 assert_schedule_runs!(&schedule, &mut stepping, second_system);
1058 }
1059
1060 #[test]
1061 fn continue_never_run() {
1062 let (schedule, _world) = setup();
1063
1064 let mut stepping = Stepping::new();
1065 stepping
1066 .add_schedule(TestSchedule)
1067 .enable()
1068 .never_run(TestSchedule, first_system)
1069 .continue_frame();
1070
1071 assert_schedule_runs!(&schedule, &mut stepping, second_system);
1072 }
1073
1074 #[test]
1075 fn disabled_breakpoint() {
1076 let (schedule, _world) = setup();
1077
1078 let mut stepping = Stepping::new();
1079 stepping
1080 .add_schedule(TestSchedule)
1081 .disable()
1082 .set_breakpoint(TestSchedule, second_system);
1083
1084 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1085 }
1086
1087 #[test]
1088 fn waiting_breakpoint() {
1089 let (schedule, _world) = setup();
1090
1091 let mut stepping = Stepping::new();
1092 stepping
1093 .add_schedule(TestSchedule)
1094 .enable()
1095 .set_breakpoint(TestSchedule, second_system);
1096
1097 assert_schedule_runs!(&schedule, &mut stepping,);
1098 }
1099
1100 #[test]
1101 fn step_breakpoint() {
1102 let (schedule, _world) = setup();
1103
1104 let mut stepping = Stepping::new();
1105 stepping
1106 .add_schedule(TestSchedule)
1107 .enable()
1108 .set_breakpoint(TestSchedule, second_system)
1109 .step_frame();
1110
1111 assert_schedule_runs!(&schedule, &mut stepping, first_system);
1114 stepping.step_frame();
1115 assert_schedule_runs!(&schedule, &mut stepping, second_system);
1116
1117 stepping.step_frame();
1120 assert_schedule_runs!(&schedule, &mut stepping, first_system);
1121
1122 assert_schedule_runs!(&schedule, &mut stepping,);
1124 }
1125
1126 #[test]
1127 fn continue_breakpoint() {
1128 let (schedule, _world) = setup();
1129
1130 let mut stepping = Stepping::new();
1131 stepping
1132 .add_schedule(TestSchedule)
1133 .enable()
1134 .set_breakpoint(TestSchedule, second_system)
1135 .continue_frame();
1136
1137 assert_schedule_runs!(&schedule, &mut stepping, first_system);
1138 stepping.continue_frame();
1139 assert_schedule_runs!(&schedule, &mut stepping, second_system);
1140 stepping.continue_frame();
1141 assert_schedule_runs!(&schedule, &mut stepping, first_system);
1142 }
1143
1144 #[test]
1147 fn continue_step_continue_with_breakpoint() {
1148 let mut world = World::new();
1149 let mut schedule = Schedule::new(TestSchedule);
1150 schedule.add_systems((first_system, second_system, third_system).chain());
1151 schedule.initialize(&mut world).unwrap();
1152
1153 let mut stepping = Stepping::new();
1154 stepping
1155 .add_schedule(TestSchedule)
1156 .enable()
1157 .set_breakpoint(TestSchedule, second_system);
1158
1159 stepping.continue_frame();
1160 assert_schedule_runs!(&schedule, &mut stepping, first_system);
1161
1162 stepping.step_frame();
1163 assert_schedule_runs!(&schedule, &mut stepping, second_system);
1164
1165 stepping.continue_frame();
1166 assert_schedule_runs!(&schedule, &mut stepping, third_system);
1167 }
1168
1169 #[test]
1170 fn clear_breakpoint() {
1171 let (schedule, _world) = setup();
1172
1173 let mut stepping = Stepping::new();
1174 stepping
1175 .add_schedule(TestSchedule)
1176 .enable()
1177 .set_breakpoint(TestSchedule, second_system)
1178 .continue_frame();
1179
1180 assert_schedule_runs!(&schedule, &mut stepping, first_system);
1181 stepping.continue_frame();
1182 assert_schedule_runs!(&schedule, &mut stepping, second_system);
1183
1184 stepping.clear_breakpoint(TestSchedule, second_system);
1185 stepping.continue_frame();
1186 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1187 }
1188
1189 #[test]
1190 fn clear_system() {
1191 let (schedule, _world) = setup();
1192
1193 let mut stepping = Stepping::new();
1194 stepping
1195 .add_schedule(TestSchedule)
1196 .enable()
1197 .never_run(TestSchedule, second_system)
1198 .continue_frame();
1199 assert_schedule_runs!(&schedule, &mut stepping, first_system);
1200
1201 stepping.clear_system(TestSchedule, second_system);
1202 stepping.continue_frame();
1203 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1204 }
1205
1206 #[test]
1207 fn clear_schedule() {
1208 let (schedule, _world) = setup();
1209
1210 let mut stepping = Stepping::new();
1211 stepping
1212 .add_schedule(TestSchedule)
1213 .enable()
1214 .never_run(TestSchedule, first_system)
1215 .never_run(TestSchedule, second_system)
1216 .continue_frame();
1217 assert_schedule_runs!(&schedule, &mut stepping,);
1218
1219 stepping.clear_schedule(TestSchedule);
1220 stepping.continue_frame();
1221 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1222 }
1223
1224 #[test]
1227 fn set_behavior_then_clear_schedule() {
1228 let (schedule, _world) = setup();
1229
1230 let mut stepping = Stepping::new();
1231 stepping
1232 .add_schedule(TestSchedule)
1233 .enable()
1234 .continue_frame();
1235 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1236
1237 stepping.never_run(TestSchedule, first_system);
1238 stepping.clear_schedule(TestSchedule);
1239 stepping.continue_frame();
1240 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1241 }
1242
1243 #[test]
1246 fn clear_schedule_then_set_behavior() {
1247 let (schedule, _world) = setup();
1248
1249 let mut stepping = Stepping::new();
1250 stepping
1251 .add_schedule(TestSchedule)
1252 .enable()
1253 .continue_frame();
1254 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1255
1256 stepping.clear_schedule(TestSchedule);
1257 stepping.never_run(TestSchedule, first_system);
1258 stepping.continue_frame();
1259 assert_schedule_runs!(&schedule, &mut stepping, second_system);
1260 }
1261
1262 #[test]
1266 fn multiple_calls_per_frame_continue() {
1267 let (schedule, _world) = setup();
1268
1269 let mut stepping = Stepping::new();
1270 stepping
1271 .add_schedule(TestSchedule)
1272 .enable()
1273 .always_run(TestSchedule, second_system)
1274 .continue_frame();
1275
1276 stepping.next_frame();
1279 assert_systems_run!(
1280 &schedule,
1281 stepping.skipped_systems(&schedule),
1282 first_system,
1283 second_system
1284 );
1285 assert_systems_run!(
1286 &schedule,
1287 stepping.skipped_systems(&schedule),
1288 second_system
1289 );
1290 }
1291 #[test]
1292 fn multiple_calls_per_frame_step() {
1293 let (schedule, _world) = setup();
1294
1295 let mut stepping = Stepping::new();
1296 stepping.add_schedule(TestSchedule).enable().step_frame();
1297
1298 stepping.next_frame();
1301 assert_systems_run!(&schedule, stepping.skipped_systems(&schedule), first_system);
1302 assert_systems_run!(&schedule, stepping.skipped_systems(&schedule),);
1303 }
1304
1305 #[test]
1306 fn step_duplicate_systems() {
1307 let mut world = World::new();
1308 let mut schedule = Schedule::new(TestSchedule);
1309 schedule.add_systems((first_system, first_system, second_system).chain());
1310 schedule.initialize(&mut world).unwrap();
1311
1312 let mut stepping = Stepping::new();
1313 stepping.add_schedule(TestSchedule).enable();
1314
1315 let system_names = vec!["first_system", "first_system", "second_system"];
1317 for system_index in 0..3 {
1320 let mut expected = FixedBitSet::with_capacity(3);
1323 expected.set_range(.., true);
1324 expected.set(system_index, false);
1325
1326 stepping.step_frame();
1328 stepping.next_frame();
1329 let skip_list = stepping
1330 .skipped_systems(&schedule)
1331 .expect("TestSchedule has been added to Stepping");
1332
1333 assert_skip_list_eq!(skip_list, expected, &system_names);
1334 }
1335 }
1336
1337 #[test]
1338 fn step_run_if_false() {
1339 let mut world = World::new();
1340 let mut schedule = Schedule::new(TestSchedule);
1341
1342 let first_system: fn() = move || {
1349 panic!("first_system should not be run");
1350 };
1351
1352 #[derive(Resource)]
1356 struct RunCount(usize);
1357 world.insert_resource(RunCount(0));
1358 let second_system = |mut run_count: ResMut<RunCount>| {
1359 println!("I have run!");
1360 run_count.0 += 1;
1361 };
1362
1363 schedule.add_systems((first_system.run_if(|| false), second_system).chain());
1366 schedule.initialize(&mut world).unwrap();
1367
1368 let mut stepping = Stepping::new();
1370 stepping.add_schedule(TestSchedule).enable();
1371 world.insert_resource(stepping);
1372
1373 let mut stepping = world.resource_mut::<Stepping>();
1377 stepping.step_frame();
1378 stepping.next_frame();
1379 schedule.run(&mut world);
1380 assert_eq!(
1381 world.resource::<RunCount>().0,
1382 0,
1383 "second_system should not have run"
1384 );
1385
1386 let mut stepping = world.resource_mut::<Stepping>();
1388 stepping.step_frame();
1389 stepping.next_frame();
1390 schedule.run(&mut world);
1391 assert_eq!(
1392 world.resource::<RunCount>().0,
1393 1,
1394 "second_system should have run"
1395 );
1396 }
1397
1398 #[test]
1399 fn remove_schedule() {
1400 let (schedule, _world) = setup();
1401 let mut stepping = Stepping::new();
1402 stepping.add_schedule(TestSchedule).enable();
1403
1404 assert_schedule_runs!(&schedule, &mut stepping,);
1406 assert!(!stepping.schedules().unwrap().is_empty());
1407
1408 stepping.remove_schedule(TestSchedule);
1410 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1411 assert!(stepping.schedules().unwrap().is_empty());
1412 }
1413
1414 #[test]
1416 fn schedules() {
1417 let mut world = World::new();
1418
1419 let mut schedule_a = Schedule::new(TestScheduleA);
1421 schedule_a.initialize(&mut world).unwrap();
1422 let mut schedule_b = Schedule::new(TestScheduleB);
1423 schedule_b.initialize(&mut world).unwrap();
1424 let mut schedule_c = Schedule::new(TestScheduleC);
1425 schedule_c.initialize(&mut world).unwrap();
1426 let mut schedule_d = Schedule::new(TestScheduleD);
1427 schedule_d.initialize(&mut world).unwrap();
1428
1429 let mut stepping = Stepping::new();
1431 stepping
1432 .add_schedule(TestScheduleA)
1433 .add_schedule(TestScheduleB)
1434 .add_schedule(TestScheduleC)
1435 .add_schedule(TestScheduleD)
1436 .enable()
1437 .next_frame();
1438
1439 assert!(stepping.schedules().is_err());
1440
1441 stepping.skipped_systems(&schedule_b);
1442 assert!(stepping.schedules().is_err());
1443 stepping.skipped_systems(&schedule_a);
1444 assert!(stepping.schedules().is_err());
1445 stepping.skipped_systems(&schedule_c);
1446 assert!(stepping.schedules().is_err());
1447
1448 stepping.skipped_systems(&schedule_d);
1451 assert!(stepping.schedules().is_ok());
1452
1453 assert_eq!(
1454 *stepping.schedules().unwrap(),
1455 vec![
1456 TestScheduleB.intern(),
1457 TestScheduleA.intern(),
1458 TestScheduleC.intern(),
1459 TestScheduleD.intern(),
1460 ]
1461 );
1462 }
1463
1464 #[test]
1465 fn verify_cursor() {
1466 fn cursor(schedule: &Schedule, index: usize) -> (InternedScheduleLabel, NodeId) {
1468 let node_id = schedule.executable().system_ids[index];
1469 (schedule.label(), NodeId::System(node_id))
1470 }
1471
1472 let mut world = World::new();
1473 let mut slotmap = SlotMap::<SystemKey, ()>::with_key();
1474
1475 let mut schedule_a = Schedule::new(TestScheduleA);
1477 schedule_a.add_systems((|| {}, || {}, || {}, || {}).chain());
1478 schedule_a.initialize(&mut world).unwrap();
1479 let mut schedule_b = Schedule::new(TestScheduleB);
1480 schedule_b.add_systems((|| {}, || {}, || {}, || {}).chain());
1481 schedule_b.initialize(&mut world).unwrap();
1482
1483 let mut stepping = Stepping::new();
1485 stepping
1486 .add_schedule(TestScheduleA)
1487 .add_schedule(TestScheduleB)
1488 .enable();
1489
1490 assert!(stepping.cursor().is_none());
1491
1492 let mut cursors = Vec::new();
1495 for _ in 0..9 {
1496 stepping.step_frame().next_frame();
1497 cursors.push(stepping.cursor());
1498 stepping.skipped_systems(&schedule_a);
1499 stepping.skipped_systems(&schedule_b);
1500 cursors.push(stepping.cursor());
1501 }
1502
1503 #[rustfmt::skip]
1504 assert_eq!(
1505 cursors,
1506 vec![
1507 None, Some(cursor(&schedule_a, 1)),
1509 Some(cursor(&schedule_a, 1)), Some(cursor(&schedule_a, 2)),
1510 Some(cursor(&schedule_a, 2)), Some(cursor(&schedule_a, 3)),
1511 Some(cursor(&schedule_a, 3)), Some(cursor(&schedule_b, 0)),
1512 Some(cursor(&schedule_b, 0)), Some(cursor(&schedule_b, 1)),
1513 Some(cursor(&schedule_b, 1)), Some(cursor(&schedule_b, 2)),
1514 Some(cursor(&schedule_b, 2)), Some(cursor(&schedule_b, 3)),
1515 Some(cursor(&schedule_b, 3)), None,
1516 Some(cursor(&schedule_a, 0)), Some(cursor(&schedule_a, 1)),
1517 ]
1518 );
1519
1520 let sys0 = slotmap.insert(());
1521 let sys1 = slotmap.insert(());
1522 let _sys2 = slotmap.insert(());
1523 let sys3 = slotmap.insert(());
1524
1525 stepping
1529 .disable()
1531 .enable()
1532 .set_breakpoint_node(TestScheduleA, NodeId::System(sys1))
1533 .always_run_node(TestScheduleA, NodeId::System(sys3))
1534 .never_run_node(TestScheduleB, NodeId::System(sys0));
1535
1536 let mut cursors = Vec::new();
1537 for _ in 0..9 {
1538 stepping.step_frame().next_frame();
1539 cursors.push(stepping.cursor());
1540 stepping.skipped_systems(&schedule_a);
1541 stepping.skipped_systems(&schedule_b);
1542 cursors.push(stepping.cursor());
1543 }
1544
1545 #[rustfmt::skip]
1546 assert_eq!(
1547 cursors,
1548 vec![
1549 Some(cursor(&schedule_a, 0)), Some(cursor(&schedule_a, 1)),
1551 Some(cursor(&schedule_a, 1)), Some(cursor(&schedule_a, 2)),
1552 Some(cursor(&schedule_a, 2)), Some(cursor(&schedule_b, 1)),
1553 Some(cursor(&schedule_b, 1)), Some(cursor(&schedule_b, 2)),
1554 Some(cursor(&schedule_b, 2)), Some(cursor(&schedule_b, 3)),
1555 Some(cursor(&schedule_b, 3)), None,
1556 Some(cursor(&schedule_a, 0)), Some(cursor(&schedule_a, 1)),
1557 Some(cursor(&schedule_a, 1)), Some(cursor(&schedule_a, 2)),
1558 Some(cursor(&schedule_a, 2)), Some(cursor(&schedule_b, 1)),
1559 ]
1560 );
1561 }
1562}