Skip to main content

hojicha_core/testing/
test_backend.rs

1//! Test backend for capturing program output
2
3use std::io::{self, Write};
4use std::sync::{Arc, Mutex};
5
6/// A test backend that captures all output
7#[derive(Clone)]
8pub struct TestBackend {
9    output: Arc<Mutex<Vec<u8>>>,
10    lines: Arc<Mutex<Vec<String>>>,
11}
12
13impl TestBackend {
14    /// Create a new test backend
15    pub fn new() -> Self {
16        Self {
17            output: Arc::new(Mutex::new(Vec::new())),
18            lines: Arc::new(Mutex::new(Vec::new())),
19        }
20    }
21
22    /// Get all captured output as bytes
23    pub fn get_output(&self) -> Vec<u8> {
24        self.output.lock().unwrap().clone()
25    }
26
27    /// Get all captured output as a string
28    pub fn get_output_string(&self) -> String {
29        String::from_utf8_lossy(&self.get_output()).to_string()
30    }
31
32    /// Get captured lines
33    pub fn get_lines(&self) -> Vec<String> {
34        self.lines.lock().unwrap().clone()
35    }
36
37    /// Check if output contains a string
38    pub fn contains(&self, text: &str) -> bool {
39        self.get_output_string().contains(text)
40    }
41
42    /// Clear captured output
43    pub fn clear(&self) {
44        self.output.lock().unwrap().clear();
45        self.lines.lock().unwrap().clear();
46    }
47}
48
49impl Write for TestBackend {
50    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
51        let mut output = self.output.lock().unwrap();
52        output.extend_from_slice(buf);
53
54        // Also track lines for easier testing
55        if let Ok(s) = std::str::from_utf8(buf) {
56            let mut lines = self.lines.lock().unwrap();
57            for line in s.lines() {
58                lines.push(line.to_string());
59            }
60        }
61
62        Ok(buf.len())
63    }
64
65    fn flush(&mut self) -> io::Result<()> {
66        Ok(())
67    }
68}
69
70impl Default for TestBackend {
71    fn default() -> Self {
72        Self::new()
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn test_backend_capture() {
82        let mut backend = TestBackend::new();
83
84        write!(backend, "Hello, ").unwrap();
85        write!(backend, "World!").unwrap();
86
87        assert_eq!(backend.get_output_string(), "Hello, World!");
88        assert!(backend.contains("World"));
89    }
90
91    #[test]
92    fn test_backend_lines() {
93        let mut backend = TestBackend::new();
94
95        writeln!(backend, "Line 1").unwrap();
96        writeln!(backend, "Line 2").unwrap();
97
98        let lines = backend.get_lines();
99        assert_eq!(lines.len(), 2);
100        assert_eq!(lines[0], "Line 1");
101        assert_eq!(lines[1], "Line 2");
102    }
103}