1pub use ffi_mock_macro::mock;
2
3use core::panic;
4pub use lazy_static::lazy_static;
5use std::{collections::VecDeque, sync::Mutex};
6
7unsafe impl<Tin, Tout> Send for FunctionMockInner<Tin, Tout>
8where
9 Tin: Sized + 'static + Clone,
10 Tout: Sized + 'static + Clone,
11{
12}
13pub struct FunctionMockInner<Tin: Sized + 'static + Clone, Tout: Sized + 'static + Clone> {
14 pub call_history: Vec<Tin>,
15 pub return_val: VecDeque<Tout>,
16 pub default_ret_val: Option<Tout>,
17}
18
19impl<Tin, Tout> FunctionMockInner<Tin, Tout>
20where
21 Tin: Sized + 'static + Clone,
22 Tout: Sized + 'static + Clone,
23{
24 pub fn new() -> Self {
25 FunctionMockInner {
26 call_history: Vec::new(),
27 return_val: VecDeque::new(),
28 default_ret_val: None,
29 }
30 }
31
32 pub fn get_next_return(&mut self) -> Tout {
33 match self.return_val.pop_front() {
34 Some(v) => v,
35 None => match &self.default_ret_val {
36 Some(v) => v.clone(),
37 None => panic!("Unexpected call"),
38 },
39 }
40 }
41}
42
43pub struct FunctionMock<'a, Tin: Sized + 'static + Clone, Tout: Sized + 'static + Clone> {
44 inner: &'a Mutex<FunctionMockInner<Tin, Tout>>,
45}
46
47impl<'a, Tin, Tout> FunctionMock<'a, Tin, Tout>
48where
49 Tin: Sized + 'static + Clone,
50 Tout: Sized + 'static + Clone,
51{
52 pub fn new(inner: &'a Mutex<FunctionMockInner<Tin, Tout>>) -> Self {
53 FunctionMock { inner }
54 }
55
56 pub fn calls(self) -> Vec<Tin> {
57 self.inner.lock().unwrap().call_history.clone()
58 }
59
60 pub fn add_return(&self, val: Tout) {
61 self.inner.lock().unwrap().return_val.push_back(val);
62 }
63
64 pub fn set_default_return(&self, val: Tout) {
65 let mut inner = self.inner.lock().unwrap();
66 inner.default_ret_val = Some(val);
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use std::ptr::null;
73
74 use super::*;
75 use crate as ffi_mock;
76
77 #[derive(Clone)]
78 struct TestStruct {
79 a: u32,
80 b: *const std::ffi::c_void,
81 c: usize,
82 }
83 extern "C" {
84 fn test_fun(a: u32, b: *const std::ffi::c_void, c: TestStruct) -> TestStruct;
85 }
86
87 fn test_indirection() -> u32 {
88 let res = unsafe {
89 test_fun(
90 10,
91 null(),
92 TestStruct {
93 a: 0,
94 b: null(),
95 c: 0,
96 },
97 )
98 };
99
100 res.a
101 }
102
103 #[test]
104 fn basic_setup() {
105 let mock = mock!(
106 fn test_fun(a: u32, b: *const std::ffi::c_void, c: TestStruct) -> TestStruct
107 );
108
109 mock.add_return(TestStruct {
110 a: 0xbeef,
111 b: null(),
112 c: 0,
113 });
114
115 let res = test_indirection();
116 assert_eq!(res, 0xbeef);
117 }
118}