1use crate::{
2 resource::Resource,
3 schedule::{InternedScheduleLabel, NodeId, Schedule, ScheduleLabel},
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, *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 label = match self.schedule_order.get(index) {
369 None => return 0,
370 Some(label) => label,
371 };
372 let state = match self.schedule_states.get(label) {
373 None => return 0,
374 Some(state) => state,
375 };
376 state.first.unwrap_or(0)
377 }
378
379 fn reset_cursor(&mut self) {
381 self.cursor = Cursor {
382 schedule: 0,
383 system: self.first_system_index_for_schedule(0),
384 };
385 }
386
387 fn next_frame(&mut self) {
389 if self.action != Action::RunAll {
392 self.action = Action::Waiting;
393 self.previous_schedule = None;
394
395 if self.cursor.schedule >= self.schedule_order.len() {
397 self.reset_cursor();
398 }
399 }
400
401 if self.updates.is_empty() {
402 return;
403 }
404
405 let mut reset_cursor = false;
406 for update in self.updates.drain(..) {
407 match update {
408 Update::SetAction(Action::RunAll) => {
409 self.action = Action::RunAll;
410 reset_cursor = true;
411 }
412 Update::SetAction(action) => {
413 #[expect(
418 clippy::match_same_arms,
419 reason = "Readability would be negatively impacted by combining the `(Waiting, RunAll)` and `(Continue, RunAll)` match arms."
420 )]
421 match (self.action, action) {
422 (Action::RunAll, Action::RunAll)
425 | (Action::Waiting, Action::Waiting)
426 | (Action::Continue, Action::Continue)
427 | (Action::Step, Action::Step)
428 | (Action::Continue, Action::Waiting)
429 | (Action::Step, Action::Waiting) => continue,
430
431 (Action::RunAll, Action::Waiting) => info!("enabled stepping"),
433 (Action::RunAll, _) => {
434 warn!(
435 "stepping not enabled; call Stepping::enable() \
436 before step_frame() or continue_frame()"
437 );
438 continue;
439 }
440
441 (Action::Waiting, Action::RunAll) => info!("disabled stepping"),
443 (Action::Waiting, Action::Continue) => info!("continue frame"),
444 (Action::Waiting, Action::Step) => info!("step frame"),
445
446 (Action::Continue, Action::RunAll) => info!("disabled stepping"),
448 (Action::Continue, Action::Step) => {
449 warn!("ignoring step_frame(); already continuing next frame");
450 continue;
451 }
452
453 (Action::Step, Action::RunAll) => info!("disabled stepping"),
455 (Action::Step, Action::Continue) => {
456 warn!("ignoring continue_frame(); already stepping next frame");
457 continue;
458 }
459 }
460
461 self.action = action;
463 }
464 Update::AddSchedule(l) => {
465 self.schedule_states.insert(l, ScheduleState::default());
466 }
467 Update::RemoveSchedule(label) => {
468 self.schedule_states.remove(&label);
469 if let Some(index) = self.schedule_order.iter().position(|l| l == &label) {
470 self.schedule_order.remove(index);
471 }
472 reset_cursor = true;
473 }
474 Update::ClearSchedule(label) => match self.schedule_states.get_mut(&label) {
475 Some(state) => state.clear_behaviors(),
476 None => {
477 warn!(
478 "stepping is not enabled for schedule {:?}; \
479 use `.add_stepping({:?})` to enable stepping",
480 label, label
481 );
482 }
483 },
484 Update::SetBehavior(label, system, behavior) => {
485 match self.schedule_states.get_mut(&label) {
486 Some(state) => state.set_behavior(system, behavior),
487 None => {
488 warn!(
489 "stepping is not enabled for schedule {:?}; \
490 use `.add_stepping({:?})` to enable stepping",
491 label, label
492 );
493 }
494 }
495 }
496 Update::ClearBehavior(label, system) => {
497 match self.schedule_states.get_mut(&label) {
498 Some(state) => state.clear_behavior(system),
499 None => {
500 warn!(
501 "stepping is not enabled for schedule {:?}; \
502 use `.add_stepping({:?})` to enable stepping",
503 label, label
504 );
505 }
506 }
507 }
508 }
509 }
510
511 if reset_cursor {
512 self.reset_cursor();
513 }
514 }
515
516 pub fn skipped_systems(&mut self, schedule: &Schedule) -> Option<FixedBitSet> {
519 if self.action == Action::RunAll {
520 return None;
521 }
522
523 let label = schedule.label();
525 let state = self.schedule_states.get_mut(&label)?;
526
527 let index = self.schedule_order.iter().position(|l| *l == label);
536 let index = match (index, self.previous_schedule) {
537 (Some(index), _) => index,
538 (None, None) => {
539 self.schedule_order.insert(0, label);
540 0
541 }
542 (None, Some(last)) => {
543 self.schedule_order.insert(last + 1, label);
544 last + 1
545 }
546 };
547 self.previous_schedule = Some(index);
550
551 #[cfg(test)]
552 debug!(
553 "cursor {:?}, index {}, label {:?}",
554 self.cursor, index, label
555 );
556
557 let cursor = self.cursor;
561 let (skip_list, next_system) = if index == cursor.schedule {
562 let (skip_list, next_system) =
563 state.skipped_systems(schedule, cursor.system, self.action);
564
565 if self.action == Action::Step {
568 self.action = Action::Waiting;
569 }
570 (skip_list, next_system)
571 } else {
572 let (skip_list, _) = state.skipped_systems(schedule, 0, Action::Waiting);
575 (skip_list, Some(cursor.system))
576 };
577
578 match next_system {
585 Some(i) => self.cursor.system = i,
586 None => {
587 let index = cursor.schedule + 1;
588 self.cursor = Cursor {
589 schedule: index,
590 system: self.first_system_index_for_schedule(index),
591 };
592
593 #[cfg(test)]
594 debug!("advanced schedule index: {} -> {}", cursor.schedule, index);
595 }
596 }
597
598 Some(skip_list)
599 }
600}
601
602#[derive(Default)]
603struct ScheduleState {
604 behaviors: HashMap<NodeId, SystemBehavior>,
606
607 node_ids: Vec<NodeId>,
613
614 behavior_updates: TypeIdMap<Option<SystemBehavior>>,
617
618 first: Option<usize>,
620}
621
622impl ScheduleState {
623 fn set_behavior(&mut self, system: SystemIdentifier, behavior: SystemBehavior) {
625 self.first = None;
626 match system {
627 SystemIdentifier::Node(node_id) => {
628 self.behaviors.insert(node_id, behavior);
629 }
630 SystemIdentifier::Type(type_id) => {
634 self.behavior_updates.insert(type_id, Some(behavior));
635 }
636 }
637 }
638
639 fn clear_behavior(&mut self, system: SystemIdentifier) {
641 self.first = None;
642 match system {
643 SystemIdentifier::Node(node_id) => {
644 self.behaviors.remove(&node_id);
645 }
646 SystemIdentifier::Type(type_id) => {
648 self.behavior_updates.insert(type_id, None);
649 }
650 }
651 }
652
653 fn clear_behaviors(&mut self) {
655 self.behaviors.clear();
656 self.behavior_updates.clear();
657 self.first = None;
658 }
659
660 fn apply_behavior_updates(&mut self, schedule: &Schedule) {
663 for (node_id, system) in schedule.systems().unwrap() {
669 let behavior = self.behavior_updates.get(&system.type_id());
670 match behavior {
671 None => continue,
672 Some(None) => {
673 self.behaviors.remove(&node_id);
674 }
675 Some(Some(behavior)) => {
676 self.behaviors.insert(node_id, *behavior);
677 }
678 }
679 }
680 self.behavior_updates.clear();
681
682 #[cfg(test)]
683 debug!("apply_updates(): {:?}", self.behaviors);
684 }
685
686 fn skipped_systems(
687 &mut self,
688 schedule: &Schedule,
689 start: usize,
690 mut action: Action,
691 ) -> (FixedBitSet, Option<usize>) {
692 use core::cmp::Ordering;
693
694 if self.node_ids.len() != schedule.systems_len() {
697 self.node_ids.clone_from(&schedule.executable().system_ids);
698 }
699
700 if !self.behavior_updates.is_empty() {
704 self.apply_behavior_updates(schedule);
705 }
706
707 if self.first.is_none() {
709 for (i, (node_id, _)) in schedule.systems().unwrap().enumerate() {
710 match self.behaviors.get(&node_id) {
711 Some(SystemBehavior::AlwaysRun | SystemBehavior::NeverRun) => continue,
712 Some(_) | None => {
713 self.first = Some(i);
714 break;
715 }
716 }
717 }
718 }
719
720 let mut skip = FixedBitSet::with_capacity(schedule.systems_len());
721 let mut pos = start;
722
723 for (i, (node_id, _system)) in schedule.systems().unwrap().enumerate() {
724 let behavior = self
725 .behaviors
726 .get(&node_id)
727 .unwrap_or(&SystemBehavior::Continue);
728
729 #[cfg(test)]
730 debug!(
731 "skipped_systems(): systems[{}], pos {}, Action::{:?}, Behavior::{:?}, {}",
732 i,
733 pos,
734 action,
735 behavior,
736 _system.name()
737 );
738
739 match (action, behavior) {
740 (_, SystemBehavior::NeverRun) => {
745 skip.insert(i);
746 if i == pos {
747 pos += 1;
748 }
749 }
750 (_, SystemBehavior::AlwaysRun) => {
755 if i == pos {
756 pos += 1;
757 }
758 }
759 (Action::Waiting, _) => skip.insert(i),
762
763 (Action::Step, _) => match i.cmp(&pos) {
769 Ordering::Less => skip.insert(i),
770 Ordering::Equal => {
771 pos += 1;
772 action = Action::Waiting;
773 }
774 Ordering::Greater => unreachable!(),
775 },
776 (Action::Continue, SystemBehavior::Continue) => {
780 if i < start {
781 skip.insert(i);
782 }
783 }
784 (Action::Continue, SystemBehavior::Break) => {
793 if i != start {
794 skip.insert(i);
795
796 if i > start {
799 action = Action::Waiting;
800 }
801 }
802 }
803 (Action::RunAll, _) => unreachable!(),
806 }
807
808 if i == pos && action != Action::Waiting {
811 pos += 1;
812 }
813 }
814
815 if pos >= schedule.systems_len() {
818 (skip, None)
819 } else {
820 (skip, Some(pos))
821 }
822 }
823}
824
825#[cfg(all(test, feature = "bevy_debug_stepping"))]
826#[expect(clippy::print_stdout, reason = "Allowed in tests.")]
827mod tests {
828 use super::*;
829 use crate::{prelude::*, schedule::ScheduleLabel};
830 use alloc::{format, vec};
831 use std::println;
832
833 #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
834 struct TestSchedule;
835
836 #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
837 struct TestScheduleA;
838
839 #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
840 struct TestScheduleB;
841
842 #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
843 struct TestScheduleC;
844
845 #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
846 struct TestScheduleD;
847
848 fn first_system() {}
849 fn second_system() {}
850 fn third_system() {}
851
852 fn setup() -> (Schedule, World) {
853 let mut world = World::new();
854 let mut schedule = Schedule::new(TestSchedule);
855 schedule.add_systems((first_system, second_system).chain());
856 schedule.initialize(&mut world).unwrap();
857 (schedule, world)
858 }
859
860 macro_rules! assert_skip_list_eq {
863 ($actual:expr, $expected:expr, $system_names:expr) => {
864 let actual = $actual;
865 let expected = $expected;
866 let systems: &Vec<&str> = $system_names;
867
868 if (actual != expected) {
869 use core::fmt::Write as _;
870
871 let mut msg = format!(
874 "Schedule:\n {:9} {:16}{:6} {:6} {:6}\n",
875 "index", "name", "expect", "actual", "result"
876 );
877 for (i, name) in systems.iter().enumerate() {
878 let _ = write!(msg, " system[{:1}] {:16}", i, name);
879 match (expected.contains(i), actual.contains(i)) {
880 (true, true) => msg.push_str("skip skip pass\n"),
881 (true, false) => {
882 msg.push_str("skip run FAILED; system should not have run\n")
883 }
884 (false, true) => {
885 msg.push_str("run skip FAILED; system should have run\n")
886 }
887 (false, false) => msg.push_str("run run pass\n"),
888 }
889 }
890 assert_eq!(actual, expected, "{}", msg);
891 }
892 };
893 }
894
895 macro_rules! assert_systems_run {
898 ($schedule:expr, $skipped_systems:expr, $($system:expr),*) => {
899 let systems: Vec<(TypeId, alloc::borrow::Cow<'static, str>)> = $schedule.systems().unwrap()
902 .map(|(_, system)| {
903 (system.type_id(), system.name())
904 })
905 .collect();
906
907 let mut expected = FixedBitSet::with_capacity(systems.len());
909 $(
910 let sys = IntoSystem::into_system($system);
911 for (i, (type_id, _)) in systems.iter().enumerate() {
912 if sys.type_id() == *type_id {
913 expected.insert(i);
914 }
915 }
916 )*
917
918 expected.toggle_range(..);
920
921 let actual = match $skipped_systems {
923 None => FixedBitSet::with_capacity(systems.len()),
924 Some(b) => b,
925 };
926 let system_names: Vec<&str> = systems
927 .iter()
928 .map(|(_,n)| n.rsplit_once("::").unwrap().1)
929 .collect();
930
931 assert_skip_list_eq!(actual, expected, &system_names);
932 };
933 }
934
935 macro_rules! assert_schedule_runs {
942 ($schedule:expr, $stepping:expr, $($system:expr),*) => {
943 $stepping.next_frame();
946 assert_systems_run!($schedule, $stepping.skipped_systems($schedule), $($system),*);
947 };
948 }
949
950 #[test]
951 fn stepping_disabled() {
952 let (schedule, _world) = setup();
953
954 let mut stepping = Stepping::new();
955 stepping.add_schedule(TestSchedule).disable().next_frame();
956
957 assert!(stepping.skipped_systems(&schedule).is_none());
958 assert!(stepping.cursor().is_none());
959 }
960
961 #[test]
962 fn unknown_schedule() {
963 let (schedule, _world) = setup();
964
965 let mut stepping = Stepping::new();
966 stepping.enable().next_frame();
967
968 assert!(stepping.skipped_systems(&schedule).is_none());
969 }
970
971 #[test]
972 fn disabled_always_run() {
973 let (schedule, _world) = setup();
974
975 let mut stepping = Stepping::new();
976 stepping
977 .add_schedule(TestSchedule)
978 .disable()
979 .always_run(TestSchedule, first_system);
980
981 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
982 }
983
984 #[test]
985 fn waiting_always_run() {
986 let (schedule, _world) = setup();
987
988 let mut stepping = Stepping::new();
989 stepping
990 .add_schedule(TestSchedule)
991 .enable()
992 .always_run(TestSchedule, first_system);
993
994 assert_schedule_runs!(&schedule, &mut stepping, first_system);
995 }
996
997 #[test]
998 fn step_always_run() {
999 let (schedule, _world) = setup();
1000
1001 let mut stepping = Stepping::new();
1002 stepping
1003 .add_schedule(TestSchedule)
1004 .enable()
1005 .always_run(TestSchedule, first_system)
1006 .step_frame();
1007
1008 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1009 }
1010
1011 #[test]
1012 fn continue_always_run() {
1013 let (schedule, _world) = setup();
1014
1015 let mut stepping = Stepping::new();
1016 stepping
1017 .add_schedule(TestSchedule)
1018 .enable()
1019 .always_run(TestSchedule, first_system)
1020 .continue_frame();
1021
1022 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1023 }
1024
1025 #[test]
1026 fn disabled_never_run() {
1027 let (schedule, _world) = setup();
1028
1029 let mut stepping = Stepping::new();
1030 stepping
1031 .add_schedule(TestSchedule)
1032 .never_run(TestSchedule, first_system)
1033 .disable();
1034 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1035 }
1036
1037 #[test]
1038 fn waiting_never_run() {
1039 let (schedule, _world) = setup();
1040
1041 let mut stepping = Stepping::new();
1042 stepping
1043 .add_schedule(TestSchedule)
1044 .enable()
1045 .never_run(TestSchedule, first_system);
1046
1047 assert_schedule_runs!(&schedule, &mut stepping,);
1048 }
1049
1050 #[test]
1051 fn step_never_run() {
1052 let (schedule, _world) = setup();
1053
1054 let mut stepping = Stepping::new();
1055 stepping
1056 .add_schedule(TestSchedule)
1057 .enable()
1058 .never_run(TestSchedule, first_system)
1059 .step_frame();
1060
1061 assert_schedule_runs!(&schedule, &mut stepping, second_system);
1062 }
1063
1064 #[test]
1065 fn continue_never_run() {
1066 let (schedule, _world) = setup();
1067
1068 let mut stepping = Stepping::new();
1069 stepping
1070 .add_schedule(TestSchedule)
1071 .enable()
1072 .never_run(TestSchedule, first_system)
1073 .continue_frame();
1074
1075 assert_schedule_runs!(&schedule, &mut stepping, second_system);
1076 }
1077
1078 #[test]
1079 fn disabled_breakpoint() {
1080 let (schedule, _world) = setup();
1081
1082 let mut stepping = Stepping::new();
1083 stepping
1084 .add_schedule(TestSchedule)
1085 .disable()
1086 .set_breakpoint(TestSchedule, second_system);
1087
1088 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1089 }
1090
1091 #[test]
1092 fn waiting_breakpoint() {
1093 let (schedule, _world) = setup();
1094
1095 let mut stepping = Stepping::new();
1096 stepping
1097 .add_schedule(TestSchedule)
1098 .enable()
1099 .set_breakpoint(TestSchedule, second_system);
1100
1101 assert_schedule_runs!(&schedule, &mut stepping,);
1102 }
1103
1104 #[test]
1105 fn step_breakpoint() {
1106 let (schedule, _world) = setup();
1107
1108 let mut stepping = Stepping::new();
1109 stepping
1110 .add_schedule(TestSchedule)
1111 .enable()
1112 .set_breakpoint(TestSchedule, second_system)
1113 .step_frame();
1114
1115 assert_schedule_runs!(&schedule, &mut stepping, first_system);
1118 stepping.step_frame();
1119 assert_schedule_runs!(&schedule, &mut stepping, second_system);
1120
1121 stepping.step_frame();
1124 assert_schedule_runs!(&schedule, &mut stepping, first_system);
1125
1126 assert_schedule_runs!(&schedule, &mut stepping,);
1128 }
1129
1130 #[test]
1131 fn continue_breakpoint() {
1132 let (schedule, _world) = setup();
1133
1134 let mut stepping = Stepping::new();
1135 stepping
1136 .add_schedule(TestSchedule)
1137 .enable()
1138 .set_breakpoint(TestSchedule, second_system)
1139 .continue_frame();
1140
1141 assert_schedule_runs!(&schedule, &mut stepping, first_system);
1142 stepping.continue_frame();
1143 assert_schedule_runs!(&schedule, &mut stepping, second_system);
1144 stepping.continue_frame();
1145 assert_schedule_runs!(&schedule, &mut stepping, first_system);
1146 }
1147
1148 #[test]
1151 fn continue_step_continue_with_breakpoint() {
1152 let mut world = World::new();
1153 let mut schedule = Schedule::new(TestSchedule);
1154 schedule.add_systems((first_system, second_system, third_system).chain());
1155 schedule.initialize(&mut world).unwrap();
1156
1157 let mut stepping = Stepping::new();
1158 stepping
1159 .add_schedule(TestSchedule)
1160 .enable()
1161 .set_breakpoint(TestSchedule, second_system);
1162
1163 stepping.continue_frame();
1164 assert_schedule_runs!(&schedule, &mut stepping, first_system);
1165
1166 stepping.step_frame();
1167 assert_schedule_runs!(&schedule, &mut stepping, second_system);
1168
1169 stepping.continue_frame();
1170 assert_schedule_runs!(&schedule, &mut stepping, third_system);
1171 }
1172
1173 #[test]
1174 fn clear_breakpoint() {
1175 let (schedule, _world) = setup();
1176
1177 let mut stepping = Stepping::new();
1178 stepping
1179 .add_schedule(TestSchedule)
1180 .enable()
1181 .set_breakpoint(TestSchedule, second_system)
1182 .continue_frame();
1183
1184 assert_schedule_runs!(&schedule, &mut stepping, first_system);
1185 stepping.continue_frame();
1186 assert_schedule_runs!(&schedule, &mut stepping, second_system);
1187
1188 stepping.clear_breakpoint(TestSchedule, second_system);
1189 stepping.continue_frame();
1190 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1191 }
1192
1193 #[test]
1194 fn clear_system() {
1195 let (schedule, _world) = setup();
1196
1197 let mut stepping = Stepping::new();
1198 stepping
1199 .add_schedule(TestSchedule)
1200 .enable()
1201 .never_run(TestSchedule, second_system)
1202 .continue_frame();
1203 assert_schedule_runs!(&schedule, &mut stepping, first_system);
1204
1205 stepping.clear_system(TestSchedule, second_system);
1206 stepping.continue_frame();
1207 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1208 }
1209
1210 #[test]
1211 fn clear_schedule() {
1212 let (schedule, _world) = setup();
1213
1214 let mut stepping = Stepping::new();
1215 stepping
1216 .add_schedule(TestSchedule)
1217 .enable()
1218 .never_run(TestSchedule, first_system)
1219 .never_run(TestSchedule, second_system)
1220 .continue_frame();
1221 assert_schedule_runs!(&schedule, &mut stepping,);
1222
1223 stepping.clear_schedule(TestSchedule);
1224 stepping.continue_frame();
1225 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1226 }
1227
1228 #[test]
1231 fn set_behavior_then_clear_schedule() {
1232 let (schedule, _world) = setup();
1233
1234 let mut stepping = Stepping::new();
1235 stepping
1236 .add_schedule(TestSchedule)
1237 .enable()
1238 .continue_frame();
1239 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1240
1241 stepping.never_run(TestSchedule, first_system);
1242 stepping.clear_schedule(TestSchedule);
1243 stepping.continue_frame();
1244 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1245 }
1246
1247 #[test]
1250 fn clear_schedule_then_set_behavior() {
1251 let (schedule, _world) = setup();
1252
1253 let mut stepping = Stepping::new();
1254 stepping
1255 .add_schedule(TestSchedule)
1256 .enable()
1257 .continue_frame();
1258 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1259
1260 stepping.clear_schedule(TestSchedule);
1261 stepping.never_run(TestSchedule, first_system);
1262 stepping.continue_frame();
1263 assert_schedule_runs!(&schedule, &mut stepping, second_system);
1264 }
1265
1266 #[test]
1270 fn multiple_calls_per_frame_continue() {
1271 let (schedule, _world) = setup();
1272
1273 let mut stepping = Stepping::new();
1274 stepping
1275 .add_schedule(TestSchedule)
1276 .enable()
1277 .always_run(TestSchedule, second_system)
1278 .continue_frame();
1279
1280 stepping.next_frame();
1283 assert_systems_run!(
1284 &schedule,
1285 stepping.skipped_systems(&schedule),
1286 first_system,
1287 second_system
1288 );
1289 assert_systems_run!(
1290 &schedule,
1291 stepping.skipped_systems(&schedule),
1292 second_system
1293 );
1294 }
1295 #[test]
1296 fn multiple_calls_per_frame_step() {
1297 let (schedule, _world) = setup();
1298
1299 let mut stepping = Stepping::new();
1300 stepping.add_schedule(TestSchedule).enable().step_frame();
1301
1302 stepping.next_frame();
1305 assert_systems_run!(&schedule, stepping.skipped_systems(&schedule), first_system);
1306 assert_systems_run!(&schedule, stepping.skipped_systems(&schedule),);
1307 }
1308
1309 #[test]
1310 fn step_duplicate_systems() {
1311 let mut world = World::new();
1312 let mut schedule = Schedule::new(TestSchedule);
1313 schedule.add_systems((first_system, first_system, second_system).chain());
1314 schedule.initialize(&mut world).unwrap();
1315
1316 let mut stepping = Stepping::new();
1317 stepping.add_schedule(TestSchedule).enable();
1318
1319 let system_names = vec!["first_system", "first_system", "second_system"];
1321 for system_index in 0..3 {
1324 let mut expected = FixedBitSet::with_capacity(3);
1327 expected.set_range(.., true);
1328 expected.set(system_index, false);
1329
1330 stepping.step_frame();
1332 stepping.next_frame();
1333 let skip_list = stepping
1334 .skipped_systems(&schedule)
1335 .expect("TestSchedule has been added to Stepping");
1336
1337 assert_skip_list_eq!(skip_list, expected, &system_names);
1338 }
1339 }
1340
1341 #[test]
1342 fn step_run_if_false() {
1343 let mut world = World::new();
1344 let mut schedule = Schedule::new(TestSchedule);
1345
1346 let first_system: fn() = move || {
1353 panic!("first_system should not be run");
1354 };
1355
1356 #[derive(Resource)]
1360 struct RunCount(usize);
1361 world.insert_resource(RunCount(0));
1362 let second_system = |mut run_count: ResMut<RunCount>| {
1363 println!("I have run!");
1364 run_count.0 += 1;
1365 };
1366
1367 schedule.add_systems((first_system.run_if(|| false), second_system).chain());
1370 schedule.initialize(&mut world).unwrap();
1371
1372 let mut stepping = Stepping::new();
1374 stepping.add_schedule(TestSchedule).enable();
1375 world.insert_resource(stepping);
1376
1377 let mut stepping = world.resource_mut::<Stepping>();
1381 stepping.step_frame();
1382 stepping.next_frame();
1383 schedule.run(&mut world);
1384 assert_eq!(
1385 world.resource::<RunCount>().0,
1386 0,
1387 "second_system should not have run"
1388 );
1389
1390 let mut stepping = world.resource_mut::<Stepping>();
1392 stepping.step_frame();
1393 stepping.next_frame();
1394 schedule.run(&mut world);
1395 assert_eq!(
1396 world.resource::<RunCount>().0,
1397 1,
1398 "second_system should have run"
1399 );
1400 }
1401
1402 #[test]
1403 fn remove_schedule() {
1404 let (schedule, _world) = setup();
1405 let mut stepping = Stepping::new();
1406 stepping.add_schedule(TestSchedule).enable();
1407
1408 assert_schedule_runs!(&schedule, &mut stepping,);
1410 assert!(!stepping.schedules().unwrap().is_empty());
1411
1412 stepping.remove_schedule(TestSchedule);
1414 assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1415 assert!(stepping.schedules().unwrap().is_empty());
1416 }
1417
1418 #[test]
1420 fn schedules() {
1421 let mut world = World::new();
1422
1423 let mut schedule_a = Schedule::new(TestScheduleA);
1425 schedule_a.initialize(&mut world).unwrap();
1426 let mut schedule_b = Schedule::new(TestScheduleB);
1427 schedule_b.initialize(&mut world).unwrap();
1428 let mut schedule_c = Schedule::new(TestScheduleC);
1429 schedule_c.initialize(&mut world).unwrap();
1430 let mut schedule_d = Schedule::new(TestScheduleD);
1431 schedule_d.initialize(&mut world).unwrap();
1432
1433 let mut stepping = Stepping::new();
1435 stepping
1436 .add_schedule(TestScheduleA)
1437 .add_schedule(TestScheduleB)
1438 .add_schedule(TestScheduleC)
1439 .add_schedule(TestScheduleD)
1440 .enable()
1441 .next_frame();
1442
1443 assert!(stepping.schedules().is_err());
1444
1445 stepping.skipped_systems(&schedule_b);
1446 assert!(stepping.schedules().is_err());
1447 stepping.skipped_systems(&schedule_a);
1448 assert!(stepping.schedules().is_err());
1449 stepping.skipped_systems(&schedule_c);
1450 assert!(stepping.schedules().is_err());
1451
1452 stepping.skipped_systems(&schedule_d);
1455 assert!(stepping.schedules().is_ok());
1456
1457 assert_eq!(
1458 *stepping.schedules().unwrap(),
1459 vec![
1460 TestScheduleB.intern(),
1461 TestScheduleA.intern(),
1462 TestScheduleC.intern(),
1463 TestScheduleD.intern(),
1464 ]
1465 );
1466 }
1467
1468 #[test]
1469 fn verify_cursor() {
1470 fn cursor(schedule: &Schedule, index: usize) -> (InternedScheduleLabel, NodeId) {
1472 let node_id = schedule.executable().system_ids[index];
1473 (schedule.label(), node_id)
1474 }
1475
1476 let mut world = World::new();
1477
1478 let mut schedule_a = Schedule::new(TestScheduleA);
1480 schedule_a.add_systems((|| {}, || {}, || {}, || {}).chain());
1481 schedule_a.initialize(&mut world).unwrap();
1482 let mut schedule_b = Schedule::new(TestScheduleB);
1483 schedule_b.add_systems((|| {}, || {}, || {}, || {}).chain());
1484 schedule_b.initialize(&mut world).unwrap();
1485
1486 let mut stepping = Stepping::new();
1488 stepping
1489 .add_schedule(TestScheduleA)
1490 .add_schedule(TestScheduleB)
1491 .enable();
1492
1493 assert!(stepping.cursor().is_none());
1494
1495 let mut cursors = Vec::new();
1498 for _ in 0..9 {
1499 stepping.step_frame().next_frame();
1500 cursors.push(stepping.cursor());
1501 stepping.skipped_systems(&schedule_a);
1502 stepping.skipped_systems(&schedule_b);
1503 cursors.push(stepping.cursor());
1504 }
1505
1506 #[rustfmt::skip]
1507 assert_eq!(
1508 cursors,
1509 vec![
1510 None, Some(cursor(&schedule_a, 1)),
1512 Some(cursor(&schedule_a, 1)), Some(cursor(&schedule_a, 2)),
1513 Some(cursor(&schedule_a, 2)), Some(cursor(&schedule_a, 3)),
1514 Some(cursor(&schedule_a, 3)), Some(cursor(&schedule_b, 0)),
1515 Some(cursor(&schedule_b, 0)), Some(cursor(&schedule_b, 1)),
1516 Some(cursor(&schedule_b, 1)), Some(cursor(&schedule_b, 2)),
1517 Some(cursor(&schedule_b, 2)), Some(cursor(&schedule_b, 3)),
1518 Some(cursor(&schedule_b, 3)), None,
1519 Some(cursor(&schedule_a, 0)), Some(cursor(&schedule_a, 1)),
1520 ]
1521 );
1522
1523 stepping
1527 .disable()
1529 .enable()
1530 .set_breakpoint_node(TestScheduleA, NodeId::System(1))
1531 .always_run_node(TestScheduleA, NodeId::System(3))
1532 .never_run_node(TestScheduleB, NodeId::System(0));
1533
1534 let mut cursors = Vec::new();
1535 for _ in 0..9 {
1536 stepping.step_frame().next_frame();
1537 cursors.push(stepping.cursor());
1538 stepping.skipped_systems(&schedule_a);
1539 stepping.skipped_systems(&schedule_b);
1540 cursors.push(stepping.cursor());
1541 }
1542
1543 #[rustfmt::skip]
1544 assert_eq!(
1545 cursors,
1546 vec![
1547 Some(cursor(&schedule_a, 0)), Some(cursor(&schedule_a, 1)),
1549 Some(cursor(&schedule_a, 1)), Some(cursor(&schedule_a, 2)),
1550 Some(cursor(&schedule_a, 2)), Some(cursor(&schedule_b, 1)),
1551 Some(cursor(&schedule_b, 1)), Some(cursor(&schedule_b, 2)),
1552 Some(cursor(&schedule_b, 2)), Some(cursor(&schedule_b, 3)),
1553 Some(cursor(&schedule_b, 3)), None,
1554 Some(cursor(&schedule_a, 0)), Some(cursor(&schedule_a, 1)),
1555 Some(cursor(&schedule_a, 1)), Some(cursor(&schedule_a, 2)),
1556 Some(cursor(&schedule_a, 2)), Some(cursor(&schedule_b, 1)),
1557 ]
1558 );
1559 }
1560}