mry/mock/
mod.rs

1mod log;
2use std::{iter::repeat, sync::Arc};
3
4pub use log::*;
5
6use parking_lot::Mutex;
7
8use crate::{times::Times, Behavior, Matcher, MockableRet, Output, Rule};
9
10pub struct Mock<I, O> {
11    pub name: &'static str,
12    pub log: Logs<I>,
13    rules: Vec<Rule<I, O>>,
14}
15
16impl<I, O> Mock<I, O> {
17    pub fn new(name: &'static str) -> Self {
18        Self {
19            name,
20            log: Default::default(),
21            rules: Default::default(),
22        }
23    }
24}
25
26impl<I, O> Mock<I, O> {
27    pub(crate) fn returns_with(
28        &mut self,
29        matcher: Arc<Mutex<Matcher<I>>>,
30        behavior: Behavior<I, O>,
31    ) {
32        self.rules.push(Rule { matcher, behavior });
33    }
34
35    pub(crate) fn returns_once(&mut self, matcher: Arc<Mutex<Matcher<I>>>, ret: O) {
36        self.returns_with(matcher, Behavior::Once(Mutex::new(Some(ret))))
37    }
38
39    pub(crate) fn calls_real_impl(&mut self, matcher: Arc<Mutex<Matcher<I>>>) {
40        self.rules.push(Rule {
41            matcher,
42            behavior: Behavior::CallsRealImpl,
43        })
44    }
45}
46
47impl<I, O> Mock<I, O> {
48    #[track_caller]
49    pub(crate) fn assert_called(&self, matcher: &Matcher<I>, times: Times) {
50        self.log.assert_called(self.name, matcher, times);
51    }
52
53    pub(crate) fn record_call(&mut self, input: Arc<Mutex<I>>) {
54        self.log.push(input);
55    }
56}
57
58impl<I, O> Mock<I, O> {
59    #[track_caller]
60    pub(crate) fn find_mock_output(&mut self, input: &I) -> Option<O> {
61        for rule in &mut self.rules {
62            if !rule.matches(input) {
63                continue;
64            }
65            return match rule.call_behavior(input) {
66                Output::Found(output) => Some(output),
67                Output::CallsRealImpl => None,
68                Output::ErrorCalledOnce => {
69                    panic!("{} was called more than once", self.name)
70                }
71            };
72        }
73        panic!("mock not found for {}", self.name)
74    }
75}
76
77impl<I, O> Mock<I, O>
78where
79    O: MockableRet + Clone,
80{
81    pub(crate) fn returns(&mut self, matcher: Arc<Mutex<Matcher<I>>>, ret: O) {
82        self.returns_with(matcher, Behavior::Const(Mutex::new(Box::new(repeat(ret)))))
83    }
84}
85
86#[cfg(test)]
87mod test {
88    use super::*;
89    use crate::Behavior1;
90
91    #[test]
92    fn returns_with() {
93        let mut mock = Mock::<(usize,), String>::new("a");
94        mock.returns_with(
95            Matcher::any().wrapped(),
96            Behavior1::from(|a| "a".repeat(a)).into(),
97        );
98
99        assert_eq!(mock.find_mock_output(&(3,)), "aaa".to_string().into());
100    }
101
102    #[test]
103    fn returns() {
104        let mut mock = Mock::<(usize,), String>::new("a");
105        mock.returns(Matcher::any().wrapped(), "a".repeat(3));
106
107        assert_eq!(mock.find_mock_output(&(3,)), "aaa".to_string().into());
108
109        // allows called multiple times
110        assert_eq!(mock.find_mock_output(&(3,)), "aaa".to_string().into());
111    }
112
113    #[test]
114    #[should_panic(expected = "mock not found for a")]
115    fn returns_with_never() {
116        let mut mock = Mock::<(usize,), String>::new("a");
117        mock.returns_with(
118            Matcher::never().wrapped(),
119            Behavior1::from(|a| "a".repeat(a)).into(),
120        );
121
122        mock.find_mock_output(&(3,));
123    }
124
125    #[test]
126    fn returns_with_always() {
127        let mut mock = Mock::<(usize,), String>::new("a");
128        mock.returns_with(
129            Matcher::any().wrapped(),
130            Behavior1::from(|a| "a".repeat(a)).into(),
131        );
132
133        assert_eq!(mock.find_mock_output(&(3,)), "aaa".to_string().into());
134    }
135
136    #[test]
137    #[should_panic(expected = "mock not found for a")]
138    fn returns_never() {
139        let mut mock = Mock::<(usize,), String>::new("a");
140        mock.returns(Matcher::never().wrapped(), "a".repeat(3));
141
142        mock.find_mock_output(&(3,));
143    }
144
145    #[test]
146    fn returns_always() {
147        let mut mock = Mock::<(usize,), String>::new("a");
148        mock.returns(Matcher::any().wrapped(), "a".repeat(3));
149
150        assert_eq!(mock.find_mock_output(&(3,)), "aaa".to_string().into());
151    }
152
153    #[test]
154    fn calls_real_impl() {
155        let mut mock = Mock::<(usize,), String>::new("a");
156        mock.calls_real_impl(Arc::new(Mutex::new(Matcher::new_eq((3,)))));
157
158        assert_eq!(mock.find_mock_output(&(3,)), None);
159    }
160
161    #[test]
162    #[should_panic(expected = "mock not found for a")]
163    fn calls_real_impl_never() {
164        let mut mock = Mock::<(usize,), String>::new("a");
165        mock.calls_real_impl(Arc::new(Mutex::new(Matcher::new_eq((3,)))));
166
167        mock.find_mock_output(&(2,));
168    }
169
170    #[test]
171    #[should_panic(expected = "a was called more than once")]
172    fn panic_on_once_called_multiple_time() {
173        let mut mock = Mock::<(usize,), String>::new("a");
174        mock.returns_once(Matcher::any().wrapped(), "a".repeat(3));
175
176        mock.find_mock_output(&(3,));
177        mock.find_mock_output(&(3,));
178    }
179}