Skip to main content

rs_teststand/station/
execution_mask.rs

1//! Execution and tracing option flags.
2
3bitflags::bitflags! {
4    /// Execution options (`ExecMask_*`), the mask behind the Execution page of
5    /// the Station Options dialog.
6    ///
7    /// Each checkbox on that page is one bit here, so the mask is how a headless
8    /// host configures debugging and tracing without a sequence editor.
9    ///
10    /// Tracing has two independent controls, and confusing them is the usual
11    /// mistake:
12    ///
13    /// * **Whether** tracing happens, [`Self::TRACING_ENABLED`] plus the
14    ///   `TRACE_INTO_*` bits that widen its reach.
15    /// * **How fast** it runs, not in this mask at all. That is the Speed
16    ///   slider, which is
17    ///   [`StationOptions::ui_message_delay`](crate::StationOptions::ui_message_delay):
18    ///   milliseconds between trace postings. See that method for the scale.
19    ///
20    /// ```
21    /// use rs_teststand::ExecutionMask;
22    ///
23    /// // Headless: keep tracing, drop everything that can stop an execution.
24    /// let unattended = ExecutionMask::DEFAULT.difference(
25    ///     ExecutionMask::BREAKPOINTS_ENABLED
26    ///         | ExecutionMask::BREAK_ON_RUN_TIME_ERROR
27    ///         | ExecutionMask::BREAK_WHILE_TERMINATING
28    ///         | ExecutionMask::ALLOW_BREAK_WHILE_IN_CODE_MODULES,
29    /// );
30    /// assert!(!unattended.contains(ExecutionMask::BREAKPOINTS_ENABLED));
31    /// ```
32    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
33    pub struct ExecutionMask: i32 {
34        /// Breakpoints are honoured (`ExecMask_BreakpointsEnabled`).
35        ///
36        /// A breakpoint suspends the execution until an operator resumes it, so
37        /// this bit is the first to clear on an unattended station.
38        const BREAKPOINTS_ENABLED = 1;
39        /// Breakpoints are still honoured while terminating
40        /// (`ExecMask_BreakWhileTerminating`).
41        const BREAK_WHILE_TERMINATING = 2;
42        /// A run-time error breaks into the debugger
43        /// (`ExecMask_BreakOnRunTimeError`).
44        ///
45        /// Clear this on an unattended station and let
46        /// [`StationOptions::set_rte_option`](crate::StationOptions::set_rte_option)
47        /// decide what happens instead.
48        const BREAK_ON_RUN_TIME_ERROR = 4;
49        /// Tracing is on (`ExecMask_TracingEnabled`).
50        const TRACING_ENABLED = 8;
51        /// Trace into Setup and Cleanup step groups
52        /// (`ExecMask_TraceIntoSetupCleanup`).
53        const TRACE_INTO_SETUP_CLEANUP = 16;
54        /// Trace into pre- and post-step callbacks
55        /// (`ExecMask_TraceIntoPrePostCallbacks`).
56        const TRACE_INTO_PRE_POST_CALLBACKS = 32;
57        /// Trace into post-action callbacks
58        /// (`ExecMask_TraceIntoPostActionCallbacks`).
59        const TRACE_INTO_POST_ACTION_CALLBACKS = 64;
60        /// Trace into separate-execution callbacks
61        /// (`ExecMask_TraceIntoSeparateExecutionCallbacks`).
62        const TRACE_INTO_SEPARATE_EXECUTION_CALLBACKS = 128;
63        /// Trace into entry points (`ExecMask_TraceIntoEntryPoints`).
64        const TRACE_INTO_ENTRY_POINTS = 256;
65        /// Trace into sequence calls whose tracing is switched off
66        /// (`ExecMask_TraceIntoSequenceCallsMarkedAsTraceOff`).
67        const TRACE_INTO_SEQUENCE_CALLS_MARKED_AS_TRACE_OFF = 512;
68        /// Keep tracing while an execution terminates
69        /// (`ExecMask_TraceWhileTerminating`).
70        const TRACE_WHILE_TERMINATING = 1024;
71        /// Trace every thread (`ExecMask_TraceAllThreads`).
72        ///
73        /// The most expensive tracing bit on a parallel station: every thread
74        /// posts trace messages, and each posting is paced by the Speed setting.
75        const TRACE_ALL_THREADS = 2048;
76        /// Interactive executions record results
77        /// (`ExecMask_InteractiveRecordResults`).
78        const INTERACTIVE_RECORD_RESULTS = 4096;
79        /// Interactive executions run Setup and Cleanup
80        /// (`ExecMask_InteractiveRunSetupCleanup`).
81        const INTERACTIVE_RUN_SETUP_CLEANUP = 8192;
82        /// Interactive executions evaluate preconditions
83        /// (`ExecMask_InteractiveEvaluatePreconditions`).
84        const INTERACTIVE_EVALUATE_PRECONDITIONS = 16384;
85        /// Allow breaking while inside a code module
86        /// (`ExecMask_AllowBreakWhileInCodeModules`).
87        const ALLOW_BREAK_WHILE_IN_CODE_MODULES = 32768;
88
89        /// The engine's default combination (`ExecMask_DefaultExecutionMask`).
90        ///
91        /// 32797 = breakpoints + run-time-error break + tracing + trace into
92        /// setup/cleanup + break inside code modules.
93        const DEFAULT = 32797;
94
95        /// Every bit that breaks into the debugger.
96        ///
97        /// Not an engine constant: the union this crate treats as unsafe for an
98        /// unattended host: each one suspends the execution until an operator acts.
99        const BREAKS = Self::BREAKPOINTS_ENABLED.bits()
100            | Self::BREAK_WHILE_TERMINATING.bits()
101            | Self::BREAK_ON_RUN_TIME_ERROR.bits()
102            | Self::ALLOW_BREAK_WHILE_IN_CODE_MODULES.bits();
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::ExecutionMask;
109
110    #[test]
111    fn the_engine_default_decomposes_into_named_bits() {
112        // 32797 is a composite; proving it equals the sum of named bits is what
113        // stops a typo in any one constant going unnoticed.
114        let expected = ExecutionMask::BREAKPOINTS_ENABLED
115            | ExecutionMask::BREAK_ON_RUN_TIME_ERROR
116            | ExecutionMask::TRACING_ENABLED
117            | ExecutionMask::TRACE_INTO_SETUP_CLEANUP
118            | ExecutionMask::ALLOW_BREAK_WHILE_IN_CODE_MODULES;
119        assert_eq!(ExecutionMask::DEFAULT, expected);
120        assert_eq!(ExecutionMask::DEFAULT.bits(), 32797);
121    }
122
123    #[test]
124    fn the_default_mask_would_halt_an_unattended_station() {
125        // The out-of-the-box configuration contains breakpoint and break-on-error
126        // bits, which is precisely why a headless host must clear them.
127        assert!(ExecutionMask::DEFAULT.intersects(ExecutionMask::BREAKS));
128    }
129
130    #[test]
131    fn clearing_the_halting_bits_keeps_tracing_intact() {
132        let unattended = ExecutionMask::DEFAULT.difference(ExecutionMask::BREAKS);
133        assert!(unattended.contains(ExecutionMask::TRACING_ENABLED));
134        assert!(unattended.contains(ExecutionMask::TRACE_INTO_SETUP_CLEANUP));
135        assert!(!unattended.intersects(ExecutionMask::BREAKS));
136    }
137
138    #[test]
139    fn unknown_bits_from_a_newer_engine_survive() {
140        let unknown = ExecutionMask::from_bits_retain(1 << 24);
141        let cleared = (unknown | ExecutionMask::BREAKS).difference(ExecutionMask::BREAKS);
142        assert_eq!(cleared.bits(), 1 << 24);
143    }
144}