ffi_mock/
lib.rs

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}