Skip to main content

perfgate_fake/
builder.rs

1//! Builder pattern for creating mock process results.
2
3use crate::RunResult;
4
5/// Builder for creating mock `RunResult` instances.
6///
7/// This provides a fluent API for constructing `RunResult` values
8/// with sensible defaults for testing.
9///
10/// # Defaults
11///
12/// - `wall_ms`: 0
13/// - `exit_code`: 0
14/// - `timed_out`: false
15/// - `cpu_ms`: None
16/// - `page_faults`: None
17/// - `ctx_switches`: None
18/// - `max_rss_kb`: None
19/// - `binary_bytes`: None
20/// - `stdout`: empty
21/// - `stderr`: empty
22///
23/// # Example
24///
25/// ```
26/// use perfgate_fake::MockProcessBuilder;
27///
28/// let result = MockProcessBuilder::new()
29///     .exit_code(0)
30///     .wall_ms(100)
31///     .stdout(b"hello world\n".to_vec())
32///     .cpu_ms(50)
33///     .max_rss_kb(2048)
34///     .build();
35///
36/// assert_eq!(result.exit_code, 0);
37/// assert_eq!(result.wall_ms, 100);
38/// assert_eq!(result.stdout, b"hello world\n");
39/// assert_eq!(result.cpu_ms, Some(50));
40/// assert_eq!(result.max_rss_kb, Some(2048));
41/// ```
42#[derive(Debug, Clone, Default)]
43pub struct MockProcessBuilder {
44    wall_ms: u64,
45    exit_code: i32,
46    timed_out: bool,
47    cpu_ms: Option<u64>,
48    page_faults: Option<u64>,
49    ctx_switches: Option<u64>,
50    max_rss_kb: Option<u64>,
51    binary_bytes: Option<u64>,
52    stdout: Vec<u8>,
53    stderr: Vec<u8>,
54}
55
56impl MockProcessBuilder {
57    /// Create a new builder with default values.
58    pub fn new() -> Self {
59        Self::default()
60    }
61
62    /// Create a builder pre-configured for a successful result.
63    ///
64    /// Equivalent to `new().exit_code(0)`.
65    pub fn success() -> Self {
66        Self::new().exit_code(0)
67    }
68
69    /// Create a builder pre-configured for a failed result.
70    ///
71    /// Equivalent to `new().exit_code(1)`.
72    pub fn failure() -> Self {
73        Self::new().exit_code(1)
74    }
75
76    /// Create a builder pre-configured for a timed-out result.
77    ///
78    /// Equivalent to `new().exit_code(-1).timed_out(true)`.
79    pub fn timeout() -> Self {
80        Self::new().exit_code(-1).timed_out(true)
81    }
82
83    /// Set the wall clock time in milliseconds.
84    pub fn wall_ms(mut self, ms: u64) -> Self {
85        self.wall_ms = ms;
86        self
87    }
88
89    /// Set the process exit code.
90    pub fn exit_code(mut self, code: i32) -> Self {
91        self.exit_code = code;
92        self
93    }
94
95    /// Set whether the process timed out.
96    pub fn timed_out(mut self, timed_out: bool) -> Self {
97        self.timed_out = timed_out;
98        self
99    }
100
101    /// Set the CPU time in milliseconds.
102    pub fn cpu_ms(mut self, ms: u64) -> Self {
103        self.cpu_ms = Some(ms);
104        self
105    }
106
107    /// Set the number of major page faults.
108    pub fn page_faults(mut self, faults: u64) -> Self {
109        self.page_faults = Some(faults);
110        self
111    }
112
113    /// Set the number of context switches.
114    pub fn ctx_switches(mut self, switches: u64) -> Self {
115        self.ctx_switches = Some(switches);
116        self
117    }
118
119    /// Set the peak RSS in kilobytes.
120    pub fn max_rss_kb(mut self, kb: u64) -> Self {
121        self.max_rss_kb = Some(kb);
122        self
123    }
124
125    /// Set the binary size in bytes.
126    pub fn binary_bytes(mut self, bytes: u64) -> Self {
127        self.binary_bytes = Some(bytes);
128        self
129    }
130
131    /// Set the stdout content.
132    pub fn stdout(mut self, output: Vec<u8>) -> Self {
133        self.stdout = output;
134        self
135    }
136
137    /// Set the stdout content from a string.
138    pub fn stdout_str(mut self, output: &str) -> Self {
139        self.stdout = output.as_bytes().to_vec();
140        self
141    }
142
143    /// Set the stderr content.
144    pub fn stderr(mut self, output: Vec<u8>) -> Self {
145        self.stderr = output;
146        self
147    }
148
149    /// Set the stderr content from a string.
150    pub fn stderr_str(mut self, output: &str) -> Self {
151        self.stderr = output.as_bytes().to_vec();
152        self
153    }
154
155    /// Build the final `RunResult`.
156    pub fn build(self) -> RunResult {
157        RunResult {
158            wall_ms: self.wall_ms,
159            exit_code: self.exit_code,
160            timed_out: self.timed_out,
161            cpu_ms: self.cpu_ms,
162            page_faults: self.page_faults,
163            ctx_switches: self.ctx_switches,
164            max_rss_kb: self.max_rss_kb,
165            io_read_bytes: None,
166            io_write_bytes: None,
167            network_packets: None,
168            energy_uj: None,
169            binary_bytes: self.binary_bytes,
170            stdout: self.stdout,
171            stderr: self.stderr,
172        }
173    }
174}
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179
180    #[test]
181    fn new_builder_has_defaults() {
182        let result = MockProcessBuilder::new().build();
183
184        assert_eq!(result.wall_ms, 0);
185        assert_eq!(result.exit_code, 0);
186        assert!(!result.timed_out);
187        assert!(result.cpu_ms.is_none());
188        assert!(result.page_faults.is_none());
189        assert!(result.ctx_switches.is_none());
190        assert!(result.max_rss_kb.is_none());
191        assert!(result.binary_bytes.is_none());
192        assert!(result.stdout.is_empty());
193        assert!(result.stderr.is_empty());
194    }
195
196    #[test]
197    fn success_preset() {
198        let result = MockProcessBuilder::success().build();
199        assert_eq!(result.exit_code, 0);
200    }
201
202    #[test]
203    fn failure_preset() {
204        let result = MockProcessBuilder::failure().build();
205        assert_eq!(result.exit_code, 1);
206    }
207
208    #[test]
209    fn timeout_preset() {
210        let result = MockProcessBuilder::timeout().build();
211        assert_eq!(result.exit_code, -1);
212        assert!(result.timed_out);
213    }
214
215    #[test]
216    fn fluent_configuration() {
217        let result = MockProcessBuilder::new()
218            .wall_ms(500)
219            .exit_code(42)
220            .timed_out(false)
221            .cpu_ms(200)
222            .page_faults(100)
223            .ctx_switches(50)
224            .max_rss_kb(4096)
225            .binary_bytes(8192)
226            .stdout(b"out".to_vec())
227            .stderr(b"err".to_vec())
228            .build();
229
230        assert_eq!(result.wall_ms, 500);
231        assert_eq!(result.exit_code, 42);
232        assert!(!result.timed_out);
233        assert_eq!(result.cpu_ms, Some(200));
234        assert_eq!(result.page_faults, Some(100));
235        assert_eq!(result.ctx_switches, Some(50));
236        assert_eq!(result.max_rss_kb, Some(4096));
237        assert_eq!(result.binary_bytes, Some(8192));
238        assert_eq!(result.stdout, b"out");
239        assert_eq!(result.stderr, b"err");
240    }
241
242    #[test]
243    fn stdout_str_and_stderr_str() {
244        let result = MockProcessBuilder::new()
245            .stdout_str("hello\n")
246            .stderr_str("warning\n")
247            .build();
248
249        assert_eq!(result.stdout, b"hello\n");
250        assert_eq!(result.stderr, b"warning\n");
251    }
252
253    #[test]
254    fn builder_can_be_reused() {
255        let base = MockProcessBuilder::new().wall_ms(100);
256
257        let result1 = base.clone().exit_code(0).build();
258        let result2 = base.exit_code(1).build();
259
260        assert_eq!(result1.wall_ms, 100);
261        assert_eq!(result1.exit_code, 0);
262        assert_eq!(result2.wall_ms, 100);
263        assert_eq!(result2.exit_code, 1);
264    }
265}