Skip to main content

perfgate_adapters/
fake.rs

1//! Fake process runner for deterministic testing.
2
3use crate::{AdapterError, CommandSpec, ProcessRunner, RunResult};
4use std::collections::HashMap;
5use std::sync::{Arc, Mutex};
6
7/// A process runner that returns pre-configured results for specific commands.
8#[derive(Debug, Default, Clone)]
9pub struct FakeProcessRunner {
10    /// Map from joined command argv to result
11    results: Arc<Mutex<HashMap<String, RunResult>>>,
12    /// Fallback result if command not found
13    fallback: Arc<Mutex<Option<RunResult>>>,
14    /// History of executed commands
15    history: Arc<Mutex<Vec<CommandSpec>>>,
16}
17
18impl FakeProcessRunner {
19    pub fn new() -> Self {
20        Self::default()
21    }
22
23    /// Configure a result for a specific command.
24    pub fn set_result(&self, argv: &[&str], result: RunResult) {
25        let key = argv.join(" ");
26        self.results.lock().expect("lock").insert(key, result);
27    }
28
29    /// Configure a fallback result.
30    pub fn set_fallback(&self, result: RunResult) {
31        *self.fallback.lock().expect("lock") = Some(result);
32    }
33
34    /// Get history of executed commands.
35    pub fn history(&self) -> Vec<CommandSpec> {
36        self.history.lock().expect("lock").clone()
37    }
38}
39
40impl ProcessRunner for FakeProcessRunner {
41    fn run(&self, spec: &CommandSpec) -> Result<RunResult, AdapterError> {
42        self.history.lock().expect("lock").push(spec.clone());
43
44        let key = spec.argv.join(" ");
45        let results = self.results.lock().expect("lock");
46        if let Some(res) = results.get(&key) {
47            return Ok(res.clone());
48        }
49
50        let fallback = self.fallback.lock().expect("lock");
51        if let Some(res) = &*fallback {
52            return Ok(res.clone());
53        }
54
55        Err(AdapterError::Other(format!(
56            "FakeProcessRunner: no result configured for command: {:?}",
57            spec.argv
58        )))
59    }
60}