bevy_ecs/schedule/
stepping.rs

1use fixedbitset::FixedBitSet;
2use std::any::TypeId;
3use std::collections::HashMap;
4
5use crate::{
6    schedule::{InternedScheduleLabel, NodeId, Schedule, ScheduleLabel},
7    system::{IntoSystem, ResMut, Resource},
8};
9use bevy_utils::{
10    tracing::{error, info, warn},
11    TypeIdMap,
12};
13use thiserror::Error;
14
15#[cfg(test)]
16use bevy_utils::tracing::debug;
17
18use crate as bevy_ecs;
19
20#[derive(Debug, Default, PartialEq, Eq, Copy, Clone)]
21enum Action {
22    /// Stepping is disabled; run all systems
23    #[default]
24    RunAll,
25
26    /// Stepping is enabled, but we're only running required systems this frame
27    Waiting,
28
29    /// Stepping is enabled; run all systems until the end of the frame, or
30    /// until we encounter a system marked with [`SystemBehavior::Break`] or all
31    /// systems in the frame have run.
32    Continue,
33
34    /// stepping is enabled; only run the next system in our step list
35    Step,
36}
37
38#[derive(Debug, Copy, Clone)]
39enum SystemBehavior {
40    /// System will always run regardless of stepping action
41    AlwaysRun,
42
43    /// System will never run while stepping is enabled
44    NeverRun,
45
46    /// When [`Action::Waiting`] this system will not be run
47    /// When [`Action::Step`] this system will be stepped
48    /// When [`Action::Continue`] system execution will stop before executing
49    /// this system unless its the first system run when continuing
50    Break,
51
52    /// When [`Action::Waiting`] this system will not be run
53    /// When [`Action::Step`] this system will be stepped
54    /// When [`Action::Continue`] this system will be run
55    Continue,
56}
57
58// schedule_order index, and schedule start point
59#[derive(Debug, Default, Clone, Copy)]
60struct Cursor {
61    /// index within `Stepping::schedule_order`
62    pub schedule: usize,
63    /// index within the schedule's system list
64    pub system: usize,
65}
66
67// Two methods of referring to Systems, via TypeId, or per-Schedule NodeId
68enum SystemIdentifier {
69    Type(TypeId),
70    Node(NodeId),
71}
72
73/// Updates to [`Stepping.schedule_states`] that will be applied at the start
74/// of the next render frame
75enum Update {
76    /// Set the action stepping will perform for this render frame
77    SetAction(Action),
78    /// Enable stepping for this schedule
79    AddSchedule(InternedScheduleLabel),
80    /// Disable stepping for this schedule
81    RemoveSchedule(InternedScheduleLabel),
82    /// Clear any system-specific behaviors for this schedule
83    ClearSchedule(InternedScheduleLabel),
84    /// Set a system-specific behavior for this schedule & system
85    SetBehavior(InternedScheduleLabel, SystemIdentifier, SystemBehavior),
86    /// Clear any system-specific behavior for this schedule & system
87    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)]
95/// Resource for controlling system stepping behavior
96pub struct Stepping {
97    // [`ScheduleState`] for each [`Schedule`] with stepping enabled
98    schedule_states: HashMap<InternedScheduleLabel, ScheduleState>,
99
100    // dynamically generated [`Schedule`] order
101    schedule_order: Vec<InternedScheduleLabel>,
102
103    // current position in the stepping frame
104    cursor: Cursor,
105
106    // index in [`schedule_order`] of the last schedule to call `skipped_systems()`
107    previous_schedule: Option<usize>,
108
109    // Action to perform during this render frame
110    action: Action,
111
112    // Updates apply at the start of the next render frame
113    updates: Vec<Update>,
114}
115
116impl std::fmt::Debug for Stepping {
117    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::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    /// Create a new instance of the `Stepping` resource.
138    pub fn new() -> Self {
139        Stepping::default()
140    }
141
142    /// System to call denoting that a new render frame has begun
143    ///
144    /// Note: This system is automatically added to the default `MainSchedule`.
145    pub fn begin_frame(stepping: Option<ResMut<Self>>) {
146        if let Some(mut stepping) = stepping {
147            stepping.next_frame();
148        }
149    }
150
151    /// Return the list of schedules with stepping enabled in the order
152    /// they are executed in.
153    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    /// Return our current position within the stepping frame
162    ///
163    /// NOTE: This function **will** return `None` during normal execution with
164    /// stepping enabled.  This can happen at the end of the stepping frame
165    /// after the last system has been run, but before the start of the next
166    /// render frame.
167    pub fn cursor(&self) -> Option<(InternedScheduleLabel, NodeId)> {
168        if self.action == Action::RunAll {
169            return None;
170        }
171        let label = match self.schedule_order.get(self.cursor.schedule) {
172            None => return None,
173            Some(label) => label,
174        };
175        let state = match self.schedule_states.get(label) {
176            None => return None,
177            Some(state) => state,
178        };
179        state
180            .node_ids
181            .get(self.cursor.system)
182            .map(|node_id| (*label, *node_id))
183    }
184
185    /// Enable stepping for the provided schedule
186    pub fn add_schedule(&mut self, schedule: impl ScheduleLabel) -> &mut Self {
187        self.updates.push(Update::AddSchedule(schedule.intern()));
188        self
189    }
190
191    /// Disable stepping for the provided schedule
192    ///
193    /// NOTE: This function will also clear any system-specific behaviors that
194    /// may have been configured.
195    pub fn remove_schedule(&mut self, schedule: impl ScheduleLabel) -> &mut Self {
196        self.updates.push(Update::RemoveSchedule(schedule.intern()));
197        self
198    }
199
200    /// Clear behavior set for all systems in the provided [`Schedule`]
201    pub fn clear_schedule(&mut self, schedule: impl ScheduleLabel) -> &mut Self {
202        self.updates.push(Update::ClearSchedule(schedule.intern()));
203        self
204    }
205
206    /// Begin stepping at the start of the next frame
207    pub fn enable(&mut self) -> &mut Self {
208        #[cfg(feature = "bevy_debug_stepping")]
209        self.updates.push(Update::SetAction(Action::Waiting));
210        #[cfg(not(feature = "bevy_debug_stepping"))]
211        error!(
212            "Stepping cannot be enabled; \
213            bevy was compiled without the bevy_debug_stepping feature"
214        );
215        self
216    }
217
218    /// Disable stepping, resume normal systems execution
219    pub fn disable(&mut self) -> &mut Self {
220        self.updates.push(Update::SetAction(Action::RunAll));
221        self
222    }
223
224    /// Check if stepping is enabled
225    pub fn is_enabled(&self) -> bool {
226        self.action != Action::RunAll
227    }
228
229    /// Run the next system during the next render frame
230    ///
231    /// NOTE: This will have no impact unless stepping has been enabled
232    pub fn step_frame(&mut self) -> &mut Self {
233        self.updates.push(Update::SetAction(Action::Step));
234        self
235    }
236
237    /// Run all remaining systems in the stepping frame during the next render
238    /// frame
239    ///
240    /// NOTE: This will have no impact unless stepping has been enabled
241    pub fn continue_frame(&mut self) -> &mut Self {
242        self.updates.push(Update::SetAction(Action::Continue));
243        self
244    }
245
246    /// Ensure this system always runs when stepping is enabled
247    ///
248    /// Note: if the system is run multiple times in the [`Schedule`], this
249    /// will apply for all instances of the system.
250    pub fn always_run<Marker>(
251        &mut self,
252        schedule: impl ScheduleLabel,
253        system: impl IntoSystem<(), (), Marker>,
254    ) -> &mut Self {
255        let type_id = system.system_type_id();
256        self.updates.push(Update::SetBehavior(
257            schedule.intern(),
258            SystemIdentifier::Type(type_id),
259            SystemBehavior::AlwaysRun,
260        ));
261
262        self
263    }
264
265    /// Ensure this system instance always runs when stepping is enabled
266    pub fn always_run_node(&mut self, schedule: impl ScheduleLabel, node: NodeId) -> &mut Self {
267        self.updates.push(Update::SetBehavior(
268            schedule.intern(),
269            SystemIdentifier::Node(node),
270            SystemBehavior::AlwaysRun,
271        ));
272        self
273    }
274
275    /// Ensure this system never runs when stepping is enabled
276    pub fn never_run<Marker>(
277        &mut self,
278        schedule: impl ScheduleLabel,
279        system: impl IntoSystem<(), (), Marker>,
280    ) -> &mut Self {
281        let type_id = system.system_type_id();
282        self.updates.push(Update::SetBehavior(
283            schedule.intern(),
284            SystemIdentifier::Type(type_id),
285            SystemBehavior::NeverRun,
286        ));
287
288        self
289    }
290
291    /// Ensure this system instance never runs when stepping is enabled
292    pub fn never_run_node(&mut self, schedule: impl ScheduleLabel, node: NodeId) -> &mut Self {
293        self.updates.push(Update::SetBehavior(
294            schedule.intern(),
295            SystemIdentifier::Node(node),
296            SystemBehavior::NeverRun,
297        ));
298        self
299    }
300
301    /// Add a breakpoint for system
302    pub fn set_breakpoint<Marker>(
303        &mut self,
304        schedule: impl ScheduleLabel,
305        system: impl IntoSystem<(), (), Marker>,
306    ) -> &mut Self {
307        let type_id = system.system_type_id();
308        self.updates.push(Update::SetBehavior(
309            schedule.intern(),
310            SystemIdentifier::Type(type_id),
311            SystemBehavior::Break,
312        ));
313
314        self
315    }
316
317    /// Add a breakpoint for system instance
318    pub fn set_breakpoint_node(&mut self, schedule: impl ScheduleLabel, node: NodeId) -> &mut Self {
319        self.updates.push(Update::SetBehavior(
320            schedule.intern(),
321            SystemIdentifier::Node(node),
322            SystemBehavior::Break,
323        ));
324        self
325    }
326
327    /// Clear a breakpoint for the system
328    pub fn clear_breakpoint<Marker>(
329        &mut self,
330        schedule: impl ScheduleLabel,
331        system: impl IntoSystem<(), (), Marker>,
332    ) -> &mut Self {
333        self.clear_system(schedule, system);
334
335        self
336    }
337
338    /// clear a breakpoint for system instance
339    pub fn clear_breakpoint_node(
340        &mut self,
341        schedule: impl ScheduleLabel,
342        node: NodeId,
343    ) -> &mut Self {
344        self.clear_node(schedule, node);
345        self
346    }
347
348    /// Clear any behavior set for the system
349    pub fn clear_system<Marker>(
350        &mut self,
351        schedule: impl ScheduleLabel,
352        system: impl IntoSystem<(), (), Marker>,
353    ) -> &mut Self {
354        let type_id = system.system_type_id();
355        self.updates.push(Update::ClearBehavior(
356            schedule.intern(),
357            SystemIdentifier::Type(type_id),
358        ));
359
360        self
361    }
362
363    /// clear a breakpoint for system instance
364    pub fn clear_node(&mut self, schedule: impl ScheduleLabel, node: NodeId) -> &mut Self {
365        self.updates.push(Update::ClearBehavior(
366            schedule.intern(),
367            SystemIdentifier::Node(node),
368        ));
369        self
370    }
371
372    /// lookup the first system for the supplied schedule index
373    fn first_system_index_for_schedule(&self, index: usize) -> usize {
374        let label = match self.schedule_order.get(index) {
375            None => return 0,
376            Some(label) => label,
377        };
378        let state = match self.schedule_states.get(label) {
379            None => return 0,
380            Some(state) => state,
381        };
382        state.first.unwrap_or(0)
383    }
384
385    /// Move the cursor to the start of the first schedule
386    fn reset_cursor(&mut self) {
387        self.cursor = Cursor {
388            schedule: 0,
389            system: self.first_system_index_for_schedule(0),
390        };
391    }
392
393    /// Advance schedule states for the next render frame
394    fn next_frame(&mut self) {
395        // if stepping is enabled; reset our internal state for the start of
396        // the next frame
397        if self.action != Action::RunAll {
398            self.action = Action::Waiting;
399            self.previous_schedule = None;
400
401            // if the cursor passed the last schedule, reset it
402            if self.cursor.schedule >= self.schedule_order.len() {
403                self.reset_cursor();
404            }
405        }
406
407        if self.updates.is_empty() {
408            return;
409        }
410
411        let mut reset_cursor = false;
412        for update in self.updates.drain(..) {
413            match update {
414                Update::SetAction(Action::RunAll) => {
415                    self.action = Action::RunAll;
416                    reset_cursor = true;
417                }
418                Update::SetAction(action) => {
419                    // This match block is really just to filter out invalid
420                    // transitions, and add debugging messages for permitted
421                    // transitions.  Any action transition that falls through
422                    // this match block will be performed.
423                    match (self.action, action) {
424                        // ignore non-transition updates, and prevent a call to
425                        // enable() from overwriting a step or continue call
426                        (Action::RunAll, Action::RunAll)
427                        | (Action::Waiting, Action::Waiting)
428                        | (Action::Continue, Action::Continue)
429                        | (Action::Step, Action::Step)
430                        | (Action::Continue, Action::Waiting)
431                        | (Action::Step, Action::Waiting) => continue,
432
433                        // when stepping is disabled
434                        (Action::RunAll, Action::Waiting) => info!("enabled stepping"),
435                        (Action::RunAll, _) => {
436                            warn!(
437                                "stepping not enabled; call Stepping::enable() \
438                                before step_frame() or continue_frame()"
439                            );
440                            continue;
441                        }
442
443                        // stepping enabled; waiting
444                        (Action::Waiting, Action::RunAll) => info!("disabled stepping"),
445                        (Action::Waiting, Action::Continue) => info!("continue frame"),
446                        (Action::Waiting, Action::Step) => info!("step frame"),
447
448                        // stepping enabled; continue frame
449                        (Action::Continue, Action::RunAll) => info!("disabled stepping"),
450                        (Action::Continue, Action::Step) => {
451                            warn!("ignoring step_frame(); already continuing next frame");
452                            continue;
453                        }
454
455                        // stepping enabled; step frame
456                        (Action::Step, Action::RunAll) => info!("disabled stepping"),
457                        (Action::Step, Action::Continue) => {
458                            warn!("ignoring continue_frame(); already stepping next frame");
459                            continue;
460                        }
461                    }
462
463                    // permitted action transition; make the change
464                    self.action = action;
465                }
466                Update::AddSchedule(l) => {
467                    self.schedule_states.insert(l, ScheduleState::default());
468                }
469                Update::RemoveSchedule(label) => {
470                    self.schedule_states.remove(&label);
471                    if let Some(index) = self.schedule_order.iter().position(|l| l == &label) {
472                        self.schedule_order.remove(index);
473                    }
474                    reset_cursor = true;
475                }
476                Update::ClearSchedule(label) => match self.schedule_states.get_mut(&label) {
477                    Some(state) => state.clear_behaviors(),
478                    None => {
479                        warn!(
480                            "stepping is not enabled for schedule {:?}; \
481                            use `.add_stepping({:?})` to enable stepping",
482                            label, label
483                        );
484                    }
485                },
486                Update::SetBehavior(label, system, behavior) => {
487                    match self.schedule_states.get_mut(&label) {
488                        Some(state) => state.set_behavior(system, behavior),
489                        None => {
490                            warn!(
491                                "stepping is not enabled for schedule {:?}; \
492                                use `.add_stepping({:?})` to enable stepping",
493                                label, label
494                            );
495                        }
496                    }
497                }
498                Update::ClearBehavior(label, system) => {
499                    match self.schedule_states.get_mut(&label) {
500                        Some(state) => state.clear_behavior(system),
501                        None => {
502                            warn!(
503                                "stepping is not enabled for schedule {:?}; \
504                                use `.add_stepping({:?})` to enable stepping",
505                                label, label
506                            );
507                        }
508                    }
509                }
510            }
511        }
512
513        if reset_cursor {
514            self.reset_cursor();
515        }
516    }
517
518    /// get the list of systems this schedule should skip for this render
519    /// frame
520    pub fn skipped_systems(&mut self, schedule: &Schedule) -> Option<FixedBitSet> {
521        if self.action == Action::RunAll {
522            return None;
523        }
524
525        // grab the label and state for this schedule
526        let label = schedule.label();
527        let state = self.schedule_states.get_mut(&label)?;
528
529        // Stepping is enabled, and this schedule is supposed to be stepped.
530        //
531        // We need to maintain a list of schedules in the order that they call
532        // this function. We'll check the ordered list now to see if this
533        // schedule is present. If not, we'll add it after the last schedule
534        // that called this function. Finally we want to save off the index of
535        // this schedule in the ordered schedule list. This is used to
536        // determine if this is the schedule the cursor is pointed at.
537        let index = self.schedule_order.iter().position(|l| *l == label);
538        let index = match (index, self.previous_schedule) {
539            (Some(index), _) => index,
540            (None, None) => {
541                self.schedule_order.insert(0, label);
542                0
543            }
544            (None, Some(last)) => {
545                self.schedule_order.insert(last + 1, label);
546                last + 1
547            }
548        };
549        // Update the index of the previous schedule to be the index of this
550        // schedule for the next call
551        self.previous_schedule = Some(index);
552
553        #[cfg(test)]
554        debug!(
555            "cursor {:?}, index {}, label {:?}",
556            self.cursor, index, label
557        );
558
559        // if the stepping frame cursor is pointing at this schedule, we'll run
560        // the schedule with the current stepping action.  If this is not the
561        // cursor schedule, we'll run the schedule with the waiting action.
562        let cursor = self.cursor;
563        let (skip_list, next_system) = if index == cursor.schedule {
564            let (skip_list, next_system) =
565                state.skipped_systems(schedule, cursor.system, self.action);
566
567            // if we just stepped this schedule, then we'll switch the action
568            // to be waiting
569            if self.action == Action::Step {
570                self.action = Action::Waiting;
571            }
572            (skip_list, next_system)
573        } else {
574            // we're not supposed to run any systems in this schedule, so pull
575            // the skip list, but ignore any changes it makes to the cursor.
576            let (skip_list, _) = state.skipped_systems(schedule, 0, Action::Waiting);
577            (skip_list, Some(cursor.system))
578        };
579
580        // update the stepping frame cursor based on if there are any systems
581        // remaining to be run in the schedule
582        // Note: Don't try to detect the end of the render frame here using the
583        // schedule index.  We don't know all schedules have been added to the
584        // schedule_order, so only next_frame() knows its safe to reset the
585        // cursor.
586        match next_system {
587            Some(i) => self.cursor.system = i,
588            None => {
589                let index = cursor.schedule + 1;
590                self.cursor = Cursor {
591                    schedule: index,
592                    system: self.first_system_index_for_schedule(index),
593                };
594
595                #[cfg(test)]
596                debug!("advanced schedule index: {} -> {}", cursor.schedule, index);
597            }
598        }
599
600        Some(skip_list)
601    }
602}
603
604#[derive(Default)]
605struct ScheduleState {
606    /// per-system [`SystemBehavior`]
607    behaviors: HashMap<NodeId, SystemBehavior>,
608
609    /// order of [`NodeId`]s in the schedule
610    ///
611    /// This is a cached copy of `SystemExecutable::system_ids`. We need it
612    /// available here to be accessed by [`Stepping::cursor()`] so we can return
613    /// [`NodeId`]s to the caller.
614    node_ids: Vec<NodeId>,
615
616    /// changes to system behavior that should be applied the next time
617    /// [`ScheduleState::skipped_systems()`] is called
618    behavior_updates: TypeIdMap<Option<SystemBehavior>>,
619
620    /// This field contains the first steppable system in the schedule.
621    first: Option<usize>,
622}
623
624impl ScheduleState {
625    // set the stepping behavior for a system in this schedule
626    fn set_behavior(&mut self, system: SystemIdentifier, behavior: SystemBehavior) {
627        self.first = None;
628        match system {
629            SystemIdentifier::Node(node_id) => {
630                self.behaviors.insert(node_id, behavior);
631            }
632            // Behaviors are indexed by NodeId, but we cannot map a system
633            // TypeId to a NodeId without the `Schedule`.  So queue this update
634            // to be processed the next time `skipped_systems()` is called.
635            SystemIdentifier::Type(type_id) => {
636                self.behavior_updates.insert(type_id, Some(behavior));
637            }
638        }
639    }
640
641    // clear the stepping behavior for a system in this schedule
642    fn clear_behavior(&mut self, system: SystemIdentifier) {
643        self.first = None;
644        match system {
645            SystemIdentifier::Node(node_id) => {
646                self.behaviors.remove(&node_id);
647            }
648            // queue TypeId updates to be processed later when we have Schedule
649            SystemIdentifier::Type(type_id) => {
650                self.behavior_updates.insert(type_id, None);
651            }
652        }
653    }
654
655    // clear all system behaviors
656    fn clear_behaviors(&mut self) {
657        self.behaviors.clear();
658        self.behavior_updates.clear();
659        self.first = None;
660    }
661
662    // apply system behavior updates by looking up the node id of the system in
663    // the schedule, and updating `systems`
664    fn apply_behavior_updates(&mut self, schedule: &Schedule) {
665        // Systems may be present multiple times within a schedule, so we
666        // iterate through all systems in the schedule, and check our behavior
667        // updates for the system TypeId.
668        // PERF: If we add a way to efficiently query schedule systems by their TypeId, we could remove the full
669        // system scan here
670        for (node_id, system) in schedule.systems().unwrap() {
671            let behavior = self.behavior_updates.get(&system.type_id());
672            match behavior {
673                None => continue,
674                Some(None) => {
675                    self.behaviors.remove(&node_id);
676                }
677                Some(Some(behavior)) => {
678                    self.behaviors.insert(node_id, *behavior);
679                }
680            }
681        }
682        self.behavior_updates.clear();
683
684        #[cfg(test)]
685        debug!("apply_updates(): {:?}", self.behaviors);
686    }
687
688    fn skipped_systems(
689        &mut self,
690        schedule: &Schedule,
691        start: usize,
692        mut action: Action,
693    ) -> (FixedBitSet, Option<usize>) {
694        use std::cmp::Ordering;
695
696        // if our NodeId list hasn't been populated, copy it over from the
697        // schedule
698        if self.node_ids.len() != schedule.systems_len() {
699            self.node_ids.clone_from(&schedule.executable().system_ids);
700        }
701
702        // Now that we have the schedule, apply any pending system behavior
703        // updates.  The schedule is required to map from system `TypeId` to
704        // `NodeId`.
705        if !self.behavior_updates.is_empty() {
706            self.apply_behavior_updates(schedule);
707        }
708
709        // if we don't have a first system set, set it now
710        if self.first.is_none() {
711            for (i, (node_id, _)) in schedule.systems().unwrap().enumerate() {
712                match self.behaviors.get(&node_id) {
713                    Some(SystemBehavior::AlwaysRun | SystemBehavior::NeverRun) => continue,
714                    Some(_) | None => {
715                        self.first = Some(i);
716                        break;
717                    }
718                }
719            }
720        }
721
722        let mut skip = FixedBitSet::with_capacity(schedule.systems_len());
723        let mut pos = start;
724
725        for (i, (node_id, _system)) in schedule.systems().unwrap().enumerate() {
726            let behavior = self
727                .behaviors
728                .get(&node_id)
729                .unwrap_or(&SystemBehavior::Continue);
730
731            #[cfg(test)]
732            debug!(
733                "skipped_systems(): systems[{}], pos {}, Action::{:?}, Behavior::{:?}, {}",
734                i,
735                pos,
736                action,
737                behavior,
738                _system.name()
739            );
740
741            match (action, behavior) {
742                // regardless of which action we're performing, if the system
743                // is marked as NeverRun, add it to the skip list.
744                // Also, advance the cursor past this system if it is our
745                // current position
746                (_, SystemBehavior::NeverRun) => {
747                    skip.insert(i);
748                    if i == pos {
749                        pos += 1;
750                    }
751                }
752                // similarly, ignore any system marked as AlwaysRun; they should
753                // never be added to the skip list
754                // Also, advance the cursor past this system if it is our
755                // current position
756                (_, SystemBehavior::AlwaysRun) => {
757                    if i == pos {
758                        pos += 1;
759                    }
760                }
761                // if we're waiting, no other systems besides AlwaysRun should
762                // be run, so add systems to the skip list
763                (Action::Waiting, _) => skip.insert(i),
764
765                // If we're stepping, the remaining behaviors don't matter,
766                // we're only going to run the system at our cursor.  Any system
767                // prior to the cursor is skipped.  Once we encounter the system
768                // at the cursor, we'll advance the cursor, and set behavior to
769                // Waiting to skip remaining systems.
770                (Action::Step, _) => match i.cmp(&pos) {
771                    Ordering::Less => skip.insert(i),
772                    Ordering::Equal => {
773                        pos += 1;
774                        action = Action::Waiting;
775                    }
776                    Ordering::Greater => unreachable!(),
777                },
778                // If we're continuing, and the step behavior is continue, we
779                // want to skip any systems prior to our start position.  That's
780                // where the stepping frame left off last time we ran anything.
781                (Action::Continue, SystemBehavior::Continue) => {
782                    if i < start {
783                        skip.insert(i);
784                    }
785                }
786                // If we're continuing, and we encounter a breakpoint we may
787                // want to stop before executing the system.  To do this we
788                // skip this system and set the action to Waiting.
789                //
790                // Note: if the cursor is pointing at this system, we will run
791                // it anyway.  This allows the user to continue, hit a
792                // breakpoint, then continue again to run the breakpoint system
793                // and any following systems.
794                (Action::Continue, SystemBehavior::Break) => {
795                    if i != start {
796                        skip.insert(i);
797
798                        // stop running systems if the breakpoint isn't the
799                        // system under the cursor.
800                        if i > start {
801                            action = Action::Waiting;
802                        }
803                    }
804                }
805                // should have never gotten into this method if stepping is
806                // disabled
807                (Action::RunAll, _) => unreachable!(),
808            }
809
810            // If we're at the cursor position, and not waiting, advance the
811            // cursor.
812            if i == pos && action != Action::Waiting {
813                pos += 1;
814            }
815        }
816
817        // output is the skip list, and the index of the next system to run in
818        // this schedule.
819        if pos >= schedule.systems_len() {
820            (skip, None)
821        } else {
822            (skip, Some(pos))
823        }
824    }
825}
826
827#[cfg(all(test, feature = "bevy_debug_stepping"))]
828mod tests {
829    use super::*;
830    use crate::prelude::*;
831    use crate::schedule::ScheduleLabel;
832
833    pub use crate as bevy_ecs;
834
835    #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
836    struct TestSchedule;
837
838    #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
839    struct TestScheduleA;
840
841    #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
842    struct TestScheduleB;
843
844    #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
845    struct TestScheduleC;
846
847    #[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash)]
848    struct TestScheduleD;
849
850    fn first_system() {}
851    fn second_system() {}
852    fn third_system() {}
853
854    fn setup() -> (Schedule, World) {
855        let mut world = World::new();
856        let mut schedule = Schedule::new(TestSchedule);
857        schedule.add_systems((first_system, second_system).chain());
858        schedule.initialize(&mut world).unwrap();
859        (schedule, world)
860    }
861
862    // Helper for verifying skip_lists are equal, and if not, printing a human
863    // readable message.
864    macro_rules! assert_skip_list_eq {
865        ($actual:expr, $expected:expr, $system_names:expr) => {
866            let actual = $actual;
867            let expected = $expected;
868            let systems: &Vec<&str> = $system_names;
869
870            if (actual != expected) {
871                use std::fmt::Write as _;
872
873                // mismatch, let's construct a human-readable message of what
874                // was returned
875                let mut msg = format!(
876                    "Schedule:\n    {:9} {:16}{:6} {:6} {:6}\n",
877                    "index", "name", "expect", "actual", "result"
878                );
879                for (i, name) in systems.iter().enumerate() {
880                    let _ = write!(msg, "    system[{:1}] {:16}", i, name);
881                    match (expected.contains(i), actual.contains(i)) {
882                        (true, true) => msg.push_str("skip   skip   pass\n"),
883                        (true, false) => {
884                            msg.push_str("skip   run    FAILED; system should not have run\n")
885                        }
886                        (false, true) => {
887                            msg.push_str("run    skip   FAILED; system should have run\n")
888                        }
889                        (false, false) => msg.push_str("run    run    pass\n"),
890                    }
891                }
892                assert_eq!(actual, expected, "{}", msg);
893            }
894        };
895    }
896
897    // Helper for verifying that a set of systems will be run for a given skip
898    // list
899    macro_rules! assert_systems_run {
900        ($schedule:expr, $skipped_systems:expr, $($system:expr),*) => {
901            // pull an ordered list of systems in the schedule, and save the
902            // system TypeId, and name.
903            let systems: Vec<(TypeId, std::borrow::Cow<'static, str>)> = $schedule.systems().unwrap()
904                .map(|(_, system)| {
905                    (system.type_id(), system.name())
906                })
907            .collect();
908
909            // construct a list of systems that are expected to run
910            let mut expected = FixedBitSet::with_capacity(systems.len());
911            $(
912                let sys = IntoSystem::into_system($system);
913                for (i, (type_id, _)) in systems.iter().enumerate() {
914                    if sys.type_id() == *type_id {
915                        expected.insert(i);
916                    }
917                }
918            )*
919
920            // flip the run list to get our skip list
921            expected.toggle_range(..);
922
923            // grab the list of skipped systems
924            let actual = match $skipped_systems {
925                None => FixedBitSet::with_capacity(systems.len()),
926                Some(b) => b,
927            };
928            let system_names: Vec<&str> = systems
929                .iter()
930                .map(|(_,n)| n.rsplit_once("::").unwrap().1)
931                .collect();
932
933            assert_skip_list_eq!(actual, expected, &system_names);
934        };
935    }
936
937    // Helper for verifying the expected systems will be run by the schedule
938    //
939    // This macro will construct an expected FixedBitSet for the systems that
940    // should be skipped, and compare it with the results from stepping the
941    // provided schedule.  If they don't match, it generates a human-readable
942    // error message and asserts.
943    macro_rules! assert_schedule_runs {
944        ($schedule:expr, $stepping:expr, $($system:expr),*) => {
945            // advance stepping to the next frame, and build the skip list for
946            // this schedule
947            $stepping.next_frame();
948            assert_systems_run!($schedule, $stepping.skipped_systems($schedule), $($system),*);
949        };
950    }
951
952    #[test]
953    fn stepping_disabled() {
954        let (schedule, _world) = setup();
955
956        let mut stepping = Stepping::new();
957        stepping.add_schedule(TestSchedule).disable().next_frame();
958
959        assert!(stepping.skipped_systems(&schedule).is_none());
960        assert!(stepping.cursor().is_none());
961    }
962
963    #[test]
964    fn unknown_schedule() {
965        let (schedule, _world) = setup();
966
967        let mut stepping = Stepping::new();
968        stepping.enable().next_frame();
969
970        assert!(stepping.skipped_systems(&schedule).is_none());
971    }
972
973    #[test]
974    fn disabled_always_run() {
975        let (schedule, _world) = setup();
976
977        let mut stepping = Stepping::new();
978        stepping
979            .add_schedule(TestSchedule)
980            .disable()
981            .always_run(TestSchedule, first_system);
982
983        assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
984    }
985
986    #[test]
987    fn waiting_always_run() {
988        let (schedule, _world) = setup();
989
990        let mut stepping = Stepping::new();
991        stepping
992            .add_schedule(TestSchedule)
993            .enable()
994            .always_run(TestSchedule, first_system);
995
996        assert_schedule_runs!(&schedule, &mut stepping, first_system);
997    }
998
999    #[test]
1000    fn step_always_run() {
1001        let (schedule, _world) = setup();
1002
1003        let mut stepping = Stepping::new();
1004        stepping
1005            .add_schedule(TestSchedule)
1006            .enable()
1007            .always_run(TestSchedule, first_system)
1008            .step_frame();
1009
1010        assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1011    }
1012
1013    #[test]
1014    fn continue_always_run() {
1015        let (schedule, _world) = setup();
1016
1017        let mut stepping = Stepping::new();
1018        stepping
1019            .add_schedule(TestSchedule)
1020            .enable()
1021            .always_run(TestSchedule, first_system)
1022            .continue_frame();
1023
1024        assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1025    }
1026
1027    #[test]
1028    fn disabled_never_run() {
1029        let (schedule, _world) = setup();
1030
1031        let mut stepping = Stepping::new();
1032        stepping
1033            .add_schedule(TestSchedule)
1034            .never_run(TestSchedule, first_system)
1035            .disable();
1036        assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1037    }
1038
1039    #[test]
1040    fn waiting_never_run() {
1041        let (schedule, _world) = setup();
1042
1043        let mut stepping = Stepping::new();
1044        stepping
1045            .add_schedule(TestSchedule)
1046            .enable()
1047            .never_run(TestSchedule, first_system);
1048
1049        assert_schedule_runs!(&schedule, &mut stepping,);
1050    }
1051
1052    #[test]
1053    fn step_never_run() {
1054        let (schedule, _world) = setup();
1055
1056        let mut stepping = Stepping::new();
1057        stepping
1058            .add_schedule(TestSchedule)
1059            .enable()
1060            .never_run(TestSchedule, first_system)
1061            .step_frame();
1062
1063        assert_schedule_runs!(&schedule, &mut stepping, second_system);
1064    }
1065
1066    #[test]
1067    fn continue_never_run() {
1068        let (schedule, _world) = setup();
1069
1070        let mut stepping = Stepping::new();
1071        stepping
1072            .add_schedule(TestSchedule)
1073            .enable()
1074            .never_run(TestSchedule, first_system)
1075            .continue_frame();
1076
1077        assert_schedule_runs!(&schedule, &mut stepping, second_system);
1078    }
1079
1080    #[test]
1081    fn disabled_breakpoint() {
1082        let (schedule, _world) = setup();
1083
1084        let mut stepping = Stepping::new();
1085        stepping
1086            .add_schedule(TestSchedule)
1087            .disable()
1088            .set_breakpoint(TestSchedule, second_system);
1089
1090        assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1091    }
1092
1093    #[test]
1094    fn waiting_breakpoint() {
1095        let (schedule, _world) = setup();
1096
1097        let mut stepping = Stepping::new();
1098        stepping
1099            .add_schedule(TestSchedule)
1100            .enable()
1101            .set_breakpoint(TestSchedule, second_system);
1102
1103        assert_schedule_runs!(&schedule, &mut stepping,);
1104    }
1105
1106    #[test]
1107    fn step_breakpoint() {
1108        let (schedule, _world) = setup();
1109
1110        let mut stepping = Stepping::new();
1111        stepping
1112            .add_schedule(TestSchedule)
1113            .enable()
1114            .set_breakpoint(TestSchedule, second_system)
1115            .step_frame();
1116
1117        // since stepping stops at every system, breakpoints are ignored during
1118        // stepping
1119        assert_schedule_runs!(&schedule, &mut stepping, first_system);
1120        stepping.step_frame();
1121        assert_schedule_runs!(&schedule, &mut stepping, second_system);
1122
1123        // let's go again to verify that we wrap back around to the start of
1124        // the frame
1125        stepping.step_frame();
1126        assert_schedule_runs!(&schedule, &mut stepping, first_system);
1127
1128        // should be back in a waiting state now that it ran first_system
1129        assert_schedule_runs!(&schedule, &mut stepping,);
1130    }
1131
1132    #[test]
1133    fn continue_breakpoint() {
1134        let (schedule, _world) = setup();
1135
1136        let mut stepping = Stepping::new();
1137        stepping
1138            .add_schedule(TestSchedule)
1139            .enable()
1140            .set_breakpoint(TestSchedule, second_system)
1141            .continue_frame();
1142
1143        assert_schedule_runs!(&schedule, &mut stepping, first_system);
1144        stepping.continue_frame();
1145        assert_schedule_runs!(&schedule, &mut stepping, second_system);
1146        stepping.continue_frame();
1147        assert_schedule_runs!(&schedule, &mut stepping, first_system);
1148    }
1149
1150    /// regression test for issue encountered while writing `system_stepping`
1151    /// example
1152    #[test]
1153    fn continue_step_continue_with_breakpoint() {
1154        let mut world = World::new();
1155        let mut schedule = Schedule::new(TestSchedule);
1156        schedule.add_systems((first_system, second_system, third_system).chain());
1157        schedule.initialize(&mut world).unwrap();
1158
1159        let mut stepping = Stepping::new();
1160        stepping
1161            .add_schedule(TestSchedule)
1162            .enable()
1163            .set_breakpoint(TestSchedule, second_system);
1164
1165        stepping.continue_frame();
1166        assert_schedule_runs!(&schedule, &mut stepping, first_system);
1167
1168        stepping.step_frame();
1169        assert_schedule_runs!(&schedule, &mut stepping, second_system);
1170
1171        stepping.continue_frame();
1172        assert_schedule_runs!(&schedule, &mut stepping, third_system);
1173    }
1174
1175    #[test]
1176    fn clear_breakpoint() {
1177        let (schedule, _world) = setup();
1178
1179        let mut stepping = Stepping::new();
1180        stepping
1181            .add_schedule(TestSchedule)
1182            .enable()
1183            .set_breakpoint(TestSchedule, second_system)
1184            .continue_frame();
1185
1186        assert_schedule_runs!(&schedule, &mut stepping, first_system);
1187        stepping.continue_frame();
1188        assert_schedule_runs!(&schedule, &mut stepping, second_system);
1189
1190        stepping.clear_breakpoint(TestSchedule, second_system);
1191        stepping.continue_frame();
1192        assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1193    }
1194
1195    #[test]
1196    fn clear_system() {
1197        let (schedule, _world) = setup();
1198
1199        let mut stepping = Stepping::new();
1200        stepping
1201            .add_schedule(TestSchedule)
1202            .enable()
1203            .never_run(TestSchedule, second_system)
1204            .continue_frame();
1205        assert_schedule_runs!(&schedule, &mut stepping, first_system);
1206
1207        stepping.clear_system(TestSchedule, second_system);
1208        stepping.continue_frame();
1209        assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1210    }
1211
1212    #[test]
1213    fn clear_schedule() {
1214        let (schedule, _world) = setup();
1215
1216        let mut stepping = Stepping::new();
1217        stepping
1218            .add_schedule(TestSchedule)
1219            .enable()
1220            .never_run(TestSchedule, first_system)
1221            .never_run(TestSchedule, second_system)
1222            .continue_frame();
1223        assert_schedule_runs!(&schedule, &mut stepping,);
1224
1225        stepping.clear_schedule(TestSchedule);
1226        stepping.continue_frame();
1227        assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1228    }
1229
1230    /// This was discovered in code-review, ensure that `clear_schedule` also
1231    /// clears any pending changes too.
1232    #[test]
1233    fn set_behavior_then_clear_schedule() {
1234        let (schedule, _world) = setup();
1235
1236        let mut stepping = Stepping::new();
1237        stepping
1238            .add_schedule(TestSchedule)
1239            .enable()
1240            .continue_frame();
1241        assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1242
1243        stepping.never_run(TestSchedule, first_system);
1244        stepping.clear_schedule(TestSchedule);
1245        stepping.continue_frame();
1246        assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1247    }
1248
1249    /// Ensure that if they `clear_schedule` then make further changes to the
1250    /// schedule, those changes after the clear are applied.
1251    #[test]
1252    fn clear_schedule_then_set_behavior() {
1253        let (schedule, _world) = setup();
1254
1255        let mut stepping = Stepping::new();
1256        stepping
1257            .add_schedule(TestSchedule)
1258            .enable()
1259            .continue_frame();
1260        assert_schedule_runs!(&schedule, &mut stepping, first_system, second_system);
1261
1262        stepping.clear_schedule(TestSchedule);
1263        stepping.never_run(TestSchedule, first_system);
1264        stepping.continue_frame();
1265        assert_schedule_runs!(&schedule, &mut stepping, second_system);
1266    }
1267
1268    // Schedules such as FixedUpdate can be called multiple times in a single
1269    // render frame.  Ensure we only run steppable systems the first time the
1270    // schedule is run
1271    #[test]
1272    fn multiple_calls_per_frame_continue() {
1273        let (schedule, _world) = setup();
1274
1275        let mut stepping = Stepping::new();
1276        stepping
1277            .add_schedule(TestSchedule)
1278            .enable()
1279            .always_run(TestSchedule, second_system)
1280            .continue_frame();
1281
1282        // start a new frame, then run the schedule two times; first system
1283        // should only run on the first one
1284        stepping.next_frame();
1285        assert_systems_run!(
1286            &schedule,
1287            stepping.skipped_systems(&schedule),
1288            first_system,
1289            second_system
1290        );
1291        assert_systems_run!(
1292            &schedule,
1293            stepping.skipped_systems(&schedule),
1294            second_system
1295        );
1296    }
1297    #[test]
1298    fn multiple_calls_per_frame_step() {
1299        let (schedule, _world) = setup();
1300
1301        let mut stepping = Stepping::new();
1302        stepping.add_schedule(TestSchedule).enable().step_frame();
1303
1304        // start a new frame, then run the schedule two times; first system
1305        // should only run on the first one
1306        stepping.next_frame();
1307        assert_systems_run!(&schedule, stepping.skipped_systems(&schedule), first_system);
1308        assert_systems_run!(&schedule, stepping.skipped_systems(&schedule),);
1309    }
1310
1311    #[test]
1312    fn step_duplicate_systems() {
1313        let mut world = World::new();
1314        let mut schedule = Schedule::new(TestSchedule);
1315        schedule.add_systems((first_system, first_system, second_system).chain());
1316        schedule.initialize(&mut world).unwrap();
1317
1318        let mut stepping = Stepping::new();
1319        stepping.add_schedule(TestSchedule).enable();
1320
1321        // needed for assert_skip_list_eq!
1322        let system_names = vec!["first_system", "first_system", "second_system"];
1323        // we're going to step three times, and each system in order should run
1324        // only once
1325        for system_index in 0..3 {
1326            // build the skip list by setting all bits, then clearing our the
1327            // one system that should run this step
1328            let mut expected = FixedBitSet::with_capacity(3);
1329            expected.set_range(.., true);
1330            expected.set(system_index, false);
1331
1332            // step the frame and get the skip list
1333            stepping.step_frame();
1334            stepping.next_frame();
1335            let skip_list = stepping
1336                .skipped_systems(&schedule)
1337                .expect("TestSchedule has been added to Stepping");
1338
1339            assert_skip_list_eq!(skip_list, expected, &system_names);
1340        }
1341    }
1342
1343    #[test]
1344    fn step_run_if_false() {
1345        let mut world = World::new();
1346        let mut schedule = Schedule::new(TestSchedule);
1347
1348        // This needs to be a system test to confirm the interaction between
1349        // the skip list and system conditions in Schedule::run().  That means
1350        // all of our systems need real bodies that do things.
1351        //
1352        // first system will be configured as `run_if(|| false)`, so it can
1353        // just panic if called
1354        let first_system = move || panic!("first_system should not be run");
1355
1356        // The second system, we need to know when it has been called, so we'll
1357        // add a resource for tracking if it has been run.  The system will
1358        // increment the run count.
1359        #[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        // build our schedule; first_system should never run, followed by
1368        // second_system.
1369        schedule.add_systems((first_system.run_if(|| false), second_system).chain());
1370        schedule.initialize(&mut world).unwrap();
1371
1372        // set up stepping
1373        let mut stepping = Stepping::new();
1374        stepping.add_schedule(TestSchedule).enable();
1375        world.insert_resource(stepping);
1376
1377        // if we step, and the run condition is false, we should not run
1378        // second_system.  The stepping cursor is at first_system, and if
1379        // first_system wasn't able to run, that's ok.
1380        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        // now on the next step, second_system should run
1391        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        // run the schedule once and verify all systems are skipped
1409        assert_schedule_runs!(&schedule, &mut stepping,);
1410        assert!(!stepping.schedules().unwrap().is_empty());
1411
1412        // remove the test schedule
1413        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    // verify that Stepping can construct an ordered list of schedules
1419    #[test]
1420    fn schedules() {
1421        let mut world = World::new();
1422
1423        // build & initialize a few schedules
1424        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        // setup stepping and add all the schedules
1434        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        // when we call the last schedule, Stepping should have enough data to
1453        // return an ordered list of schedules
1454        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        // helper to build a cursor tuple for the supplied schedule
1471        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        // create two schedules with a number of systems in them
1479        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        // setup stepping and add all schedules
1487        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        // step the system nine times, and verify the cursor before & after
1496        // each step
1497        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                // before render frame        // after render frame
1511                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        // reset our cursor (disable/enable), and update stepping to test if the
1524        // cursor properly skips over AlwaysRun & NeverRun systems.  Also set
1525        // a Break system to ensure that shows properly in the cursor
1526        stepping
1527            // disable/enable to reset cursor
1528            .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                // before render frame        // after render frame
1548                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}