Skip to main content

cu_profiler_core/backend/
recorded.rs

1//! Deterministic backend that replays recorded logs.
2//!
3//! This is the backbone of testing and the `inspect` command: it depends on no
4//! Solana runtime, so parser, budget, and CI logic can be developed and tested
5//! in isolation.
6
7use std::collections::HashMap;
8
9use crate::Result;
10use crate::backend::{ExecutionBackend, SimulationOutput};
11use crate::error::Error;
12use crate::metadata::BackendKind;
13use crate::scenario::Scenario;
14
15/// A backend that returns pre-recorded logs keyed by scenario name.
16#[derive(Debug, Default, Clone)]
17pub struct RecordedLogsBackend {
18    by_scenario: HashMap<String, SimulationOutput>,
19}
20
21impl RecordedLogsBackend {
22    /// An empty backend.
23    #[must_use]
24    pub fn new() -> Self {
25        Self::default()
26    }
27
28    /// Register logs for a scenario. `success` reflects the simulated outcome.
29    pub fn insert(&mut self, scenario: impl Into<String>, logs: Vec<String>, success: bool) {
30        self.by_scenario
31            .insert(scenario.into(), SimulationOutput { logs, success });
32    }
33
34    /// Register logs from a raw multi-line blob (splitting on newlines).
35    pub fn insert_blob(&mut self, scenario: impl Into<String>, blob: &str, success: bool) {
36        let logs = blob.lines().map(str::to_string).collect();
37        self.insert(scenario, logs, success);
38    }
39}
40
41impl ExecutionBackend for RecordedLogsBackend {
42    fn kind(&self) -> BackendKind {
43        BackendKind::Recorded
44    }
45
46    fn run(&self, scenario: &Scenario) -> Result<SimulationOutput> {
47        self.by_scenario
48            .get(&scenario.name)
49            .cloned()
50            .ok_or_else(|| {
51                Error::Simulation(format!(
52                    "no recorded logs registered for scenario `{}`",
53                    scenario.name
54                ))
55            })
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62
63    #[test]
64    fn replays_registered_logs() {
65        let mut backend = RecordedLogsBackend::new();
66        backend.insert_blob("swap", "Program X invoke [1]\nProgram X success", true);
67        let out = backend.run(&Scenario::new("swap")).unwrap();
68        assert_eq!(out.logs.len(), 2);
69        assert!(out.success);
70    }
71
72    #[test]
73    fn missing_scenario_errors_clearly() {
74        let backend = RecordedLogsBackend::new();
75        let err = backend.run(&Scenario::new("nope")).unwrap_err();
76        assert!(err.to_string().contains("no recorded logs"));
77    }
78}