Skip to main content

windjammer_runtime/
mock.rs

1//! Simple mocking utilities for Windjammer tests
2//!
3//! Provides basic mocking without requiring complex external dependencies.
4//! For advanced mocking, consider using mockall or similar crates.
5
6use std::sync::{Arc, Mutex};
7
8/// A simple mock call tracker
9///
10/// # Example
11/// ```
12/// use windjammer_runtime::mock::MockTracker;
13///
14/// let tracker = MockTracker::new();
15/// tracker.record_call("my_function", vec!["arg1".to_string()]);
16/// assert_eq!(tracker.call_count("my_function"), 1);
17/// assert!(tracker.was_called("my_function"));
18/// ```
19pub struct MockTracker {
20    #[allow(clippy::type_complexity)]
21    calls: Arc<Mutex<Vec<(String, Vec<String>)>>>,
22}
23
24impl MockTracker {
25    /// Create a new mock tracker
26    pub fn new() -> Self {
27        Self {
28            calls: Arc::new(Mutex::new(Vec::new())),
29        }
30    }
31
32    /// Record a function call with arguments
33    pub fn record_call(&self, function: &str, args: Vec<String>) {
34        let mut calls = self.calls.lock().unwrap();
35        calls.push((function.to_string(), args));
36    }
37
38    /// Check if a function was called
39    pub fn was_called(&self, function: &str) -> bool {
40        let calls = self.calls.lock().unwrap();
41        calls.iter().any(|(name, _)| name == function)
42    }
43
44    /// Get the number of times a function was called
45    pub fn call_count(&self, function: &str) -> usize {
46        let calls = self.calls.lock().unwrap();
47        calls.iter().filter(|(name, _)| name == function).count()
48    }
49
50    /// Get all calls to a specific function
51    pub fn get_calls(&self, function: &str) -> Vec<Vec<String>> {
52        let calls = self.calls.lock().unwrap();
53        calls
54            .iter()
55            .filter(|(name, _)| name == function)
56            .map(|(_, args)| args.clone())
57            .collect()
58    }
59
60    /// Clear all recorded calls
61    pub fn reset(&self) {
62        let mut calls = self.calls.lock().unwrap();
63        calls.clear();
64    }
65
66    /// Verify that a function was called a specific number of times
67    pub fn verify_call_count(&self, function: &str, expected: usize) {
68        let actual = self.call_count(function);
69        if actual != expected {
70            panic!(
71                "Mock verification failed: expected {} to be called {} time(s), but was called {} time(s)",
72                function, expected, actual
73            );
74        }
75    }
76
77    /// Verify that a function was called at least once
78    pub fn verify_called(&self, function: &str) {
79        if !self.was_called(function) {
80            panic!(
81                "Mock verification failed: expected {} to be called at least once",
82                function
83            );
84        }
85    }
86
87    /// Verify that a function was never called
88    pub fn verify_not_called(&self, function: &str) {
89        if self.was_called(function) {
90            panic!(
91                "Mock verification failed: expected {} to never be called, but was called {} time(s)",
92                function,
93                self.call_count(function)
94            );
95        }
96    }
97}
98
99impl Default for MockTracker {
100    fn default() -> Self {
101        Self::new()
102    }
103}
104
105/// A simple mock return value
106pub struct MockReturn<T> {
107    values: Arc<Mutex<Vec<T>>>,
108    index: Arc<Mutex<usize>>,
109}
110
111impl<T: Clone> MockReturn<T> {
112    /// Create a new mock return value
113    pub fn new(values: Vec<T>) -> Self {
114        Self {
115            values: Arc::new(Mutex::new(values)),
116            index: Arc::new(Mutex::new(0)),
117        }
118    }
119
120    /// Get the next return value
121    pub fn next(&self) -> Option<T> {
122        let mut index = self.index.lock().unwrap();
123        let values = self.values.lock().unwrap();
124
125        if *index < values.len() {
126            let value = values[*index].clone();
127            *index += 1;
128            Some(value)
129        } else {
130            None
131        }
132    }
133
134    /// Reset to the first return value
135    pub fn reset(&self) {
136        let mut index = self.index.lock().unwrap();
137        *index = 0;
138    }
139}
140
141#[cfg(test)]
142mod tests {
143    use super::*;
144
145    #[test]
146    fn test_mock_tracker_basic() {
147        let tracker = MockTracker::new();
148
149        tracker.record_call("foo", vec!["arg1".to_string()]);
150
151        assert!(tracker.was_called("foo"));
152        assert!(!tracker.was_called("bar"));
153        assert_eq!(tracker.call_count("foo"), 1);
154        assert_eq!(tracker.call_count("bar"), 0);
155    }
156
157    #[test]
158    fn test_mock_tracker_multiple_calls() {
159        let tracker = MockTracker::new();
160
161        tracker.record_call("foo", vec!["1".to_string()]);
162        tracker.record_call("foo", vec!["2".to_string()]);
163        tracker.record_call("bar", vec!["3".to_string()]);
164
165        assert_eq!(tracker.call_count("foo"), 2);
166        assert_eq!(tracker.call_count("bar"), 1);
167
168        let foo_calls = tracker.get_calls("foo");
169        assert_eq!(foo_calls.len(), 2);
170        assert_eq!(foo_calls[0], vec!["1".to_string()]);
171        assert_eq!(foo_calls[1], vec!["2".to_string()]);
172    }
173
174    #[test]
175    fn test_mock_tracker_reset() {
176        let tracker = MockTracker::new();
177
178        tracker.record_call("foo", vec![]);
179        assert_eq!(tracker.call_count("foo"), 1);
180
181        tracker.reset();
182        assert_eq!(tracker.call_count("foo"), 0);
183    }
184
185    #[test]
186    fn test_mock_tracker_verify_called() {
187        let tracker = MockTracker::new();
188        tracker.record_call("foo", vec![]);
189        tracker.verify_called("foo");
190    }
191
192    #[test]
193    #[should_panic(expected = "Mock verification failed")]
194    fn test_mock_tracker_verify_called_fails() {
195        let tracker = MockTracker::new();
196        tracker.verify_called("foo");
197    }
198
199    #[test]
200    fn test_mock_tracker_verify_not_called() {
201        let tracker = MockTracker::new();
202        tracker.verify_not_called("foo");
203    }
204
205    #[test]
206    #[should_panic(expected = "Mock verification failed")]
207    fn test_mock_tracker_verify_not_called_fails() {
208        let tracker = MockTracker::new();
209        tracker.record_call("foo", vec![]);
210        tracker.verify_not_called("foo");
211    }
212
213    #[test]
214    fn test_mock_return() {
215        let mock = MockReturn::new(vec![1, 2, 3]);
216
217        assert_eq!(mock.next(), Some(1));
218        assert_eq!(mock.next(), Some(2));
219        assert_eq!(mock.next(), Some(3));
220        assert_eq!(mock.next(), None);
221
222        mock.reset();
223        assert_eq!(mock.next(), Some(1));
224    }
225}