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}