mockers/clone.rs
1use super::CallMatch0;
2
3pub trait CloneMock<Mock>: Sized {
4 fn clone(&self) -> CallMatch0<Mock>;
5}
6
7/// Implements `Clone` for mock object.
8///
9/// Sometimes it is needed for mock to be clonable. Say you have
10/// following trait and function accepting this trait you need to test:
11///
12/// ```rust,ignore
13/// #[mocked]
14/// pub trait A {
15/// fn foo(&self, a: u32);
16/// }
17///
18/// fn target<AC: A + Clone>(a: AC) {
19/// let clone = a.clone();
20/// clone.foo(2);
21/// }
22/// ```
23///
24/// There are two forms of macro invokation.
25/// First one with mock struct name as single argument mocks `clone` method,
26/// so you may specify arbitrary expectations and reactions on it:
27///
28/// ```rust,ignore
29/// mock_clone!(AMock);
30///
31/// #[test]
32/// fn test_clone_mock() {
33/// let scenario = Scenario::new();
34/// let mock = scenario.create_mock_for::<dyn A>();
35/// let mock_clone = scenario.create_mock_for::<dyn A>();
36///
37/// scenario.expect(mock_clone.foo(2).and_return_default().times(1));
38/// scenario.expect(mock.clone().and_return(mock_clone));
39///
40/// target(mock);
41/// }
42/// ```
43///
44/// Please note that you must specify mock name, not name of mocked trait. This is
45/// limitation of current macro_rules system. If you mocked trait using `derive`
46/// attribute, then just append "Mock" to trait name.
47///
48/// Second form accepts one additional parameter - strategy, which specifies how
49/// cloned mock should behave. Currently there is only one strategy - "share_expectations",
50/// which means that all mock clones are indistinguishable and expectations set on one
51/// of them may be satisfied by calls made on another one. This is very useful when
52/// mocked trait behaves like handle to some real implementation.
53///
54/// ```rust,ignore
55/// mock_clone!(AMock, share_expectations);
56///
57/// #[test]
58/// fn test_shared() {
59/// let scenario = Scenario::new();
60/// let mock = scenario.create_mock_for::<dyn A>();
61///
62/// scenario.expect(mock.foo(2).and_return_default().times(1));
63///
64/// target(mock);
65/// }
66/// ```
67#[macro_export]
68macro_rules! mock_clone {
69 ($mock_name:ident, $handle_name:ident) => {
70 #[cfg(test)]
71 impl Clone for $mock_name {
72 fn clone(&self) -> Self {
73 use std::borrow::BorrowMut as _;
74 let method_data = ::mockers::MethodData {
75 mock_id: self.mock_id,
76 mock_type_id: 0usize,
77 method_name: "clone",
78 type_param_ids: vec![],
79 };
80 let mut lock = self.scenario.lock().unwrap();
81 let action = lock.borrow_mut().verify0(method_data);
82 drop(lock);
83
84 action()
85 }
86 }
87
88 #[cfg(test)]
89 impl $crate::CloneMock<$mock_name> for $handle_name {
90 #[allow(dead_code)]
91 fn clone(&self) -> ::mockers::CallMatch0<$mock_name> {
92 ::mockers::CallMatch0::new(self.mock_id, 0usize, "clone", vec![])
93 }
94 }
95 };
96
97 ($mock_name:ident, $handle_name:ident, share_expectations) => {
98 #[cfg(test)]
99 impl Clone for $mock_name {
100 fn clone(&self) -> Self {
101 use $crate::Mock;
102 $mock_name::new(self.mock_id, self.scenario.clone())
103 }
104 }
105 };
106}