embedded_hal_mock/eh0/
spi.rs

1//! SPI mock implementations.
2//!
3//! This mock supports the specification and checking of expectations to allow
4//! automated testing of SPI based drivers. Mismatches between expected and
5//! real SPI transactions will cause runtime assertions to assist with locating
6//! faults.
7//!
8//! ## Usage
9//!
10//! ```
11//! # use eh0 as embedded_hal;
12//! use embedded_hal::{
13//!     blocking::spi::{Transfer, Write},
14//!     spi::FullDuplex,
15//! };
16//! use embedded_hal_mock::eh0::spi::{Mock as SpiMock, Transaction as SpiTransaction};
17//!
18//! // Configure expectations
19//! let expectations = [
20//!     SpiTransaction::send(0x09),
21//!     SpiTransaction::read(0x0A),
22//!     SpiTransaction::send(0xFE),
23//!     SpiTransaction::read(0xFF),
24//!     SpiTransaction::write(vec![1, 2]),
25//!     SpiTransaction::transfer(vec![3, 4], vec![5, 6]),
26//! ];
27//!
28//! let mut spi = SpiMock::new(&expectations);
29//! // FullDuplex transfers
30//! spi.send(0x09);
31//! assert_eq!(spi.read().unwrap(), 0x0A);
32//! spi.send(0xFE);
33//! assert_eq!(spi.read().unwrap(), 0xFF);
34//!
35//! // Writing
36//! spi.write(&vec![1, 2]).unwrap();
37//!
38//! // Transferring
39//! let mut buf = vec![3, 4];
40//! spi.transfer(&mut buf).unwrap();
41//! assert_eq!(buf, vec![5, 6]);
42//!
43//! // Finalise expectations
44//! spi.done();
45//! ```
46use eh0 as embedded_hal;
47use embedded_hal::{blocking::spi, spi::FullDuplex};
48
49use super::error::MockError;
50use crate::common::Generic;
51
52/// SPI Transaction mode
53#[derive(Clone, Debug, PartialEq, Eq)]
54pub enum Mode {
55    /// Write transaction
56    Write,
57    /// Write and read transaction
58    Transfer,
59    /// Send transaction
60    Send,
61    /// After a send transaction in real HW a Read is available
62    Read,
63}
64
65/// SPI transaction type
66///
67/// Models an SPI write or transfer (with response)
68#[derive(Clone, Debug, PartialEq, Eq)]
69pub struct Transaction {
70    expected_mode: Mode,
71    expected_data: Vec<u8>,
72    response: Vec<u8>,
73}
74
75impl Transaction {
76    /// Create a write transaction
77    pub fn write(expected: Vec<u8>) -> Transaction {
78        Transaction {
79            expected_mode: Mode::Write,
80            expected_data: expected,
81            response: Vec::new(),
82        }
83    }
84
85    /// Create a transfer transaction
86    pub fn transfer(expected: Vec<u8>, response: Vec<u8>) -> Transaction {
87        Transaction {
88            expected_mode: Mode::Transfer,
89            expected_data: expected,
90            response,
91        }
92    }
93
94    /// Create a transfer transaction
95    pub fn send(expected: u8) -> Transaction {
96        Transaction {
97            expected_mode: Mode::Send,
98            expected_data: [expected].to_vec(),
99            response: Vec::new(),
100        }
101    }
102
103    /// Create a transfer transaction
104    pub fn read(response: u8) -> Transaction {
105        Transaction {
106            expected_mode: Mode::Read,
107            expected_data: Vec::new(),
108            response: [response].to_vec(),
109        }
110    }
111}
112
113/// Mock SPI implementation
114///
115/// This supports the specification and checking of expectations to allow
116/// automated testing of SPI based drivers. Mismatches between expected and
117/// real SPI transactions will cause runtime assertions to assist with locating
118/// faults.
119///
120/// See the usage section in the module level docs for an example.
121pub type Mock = Generic<Transaction>;
122
123impl spi::Write<u8> for Mock {
124    type Error = MockError;
125
126    /// spi::Write implementation for Mock
127    ///
128    /// This will cause an assertion if the write call does not match the next expectation
129    fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
130        let w = self.next().expect("no expectation for spi::write call");
131        assert_eq!(w.expected_mode, Mode::Write, "spi::write unexpected mode");
132        assert_eq!(
133            &w.expected_data, &buffer,
134            "spi::write data does not match expectation"
135        );
136        Ok(())
137    }
138}
139
140impl FullDuplex<u8> for Mock {
141    type Error = MockError;
142    /// spi::FullDuplex implementeation for Mock
143    ///
144    /// This will call the nonblocking read/write primitives.
145    fn send(&mut self, buffer: u8) -> nb::Result<(), Self::Error> {
146        let data = self.next().expect("no expectation for spi::send call");
147        assert_eq!(data.expected_mode, Mode::Send, "spi::send unexpected mode");
148        assert_eq!(
149            data.expected_data[0], buffer,
150            "spi::send data does not match expectation"
151        );
152        Ok(())
153    }
154
155    /// spi::FullDuplex implementeation for Mock
156    ///
157    /// This will call the nonblocking read/write primitives.
158    fn read(&mut self) -> nb::Result<u8, Self::Error> {
159        let w = self.next().expect("no expectation for spi::read call");
160        assert_eq!(w.expected_mode, Mode::Read, "spi::Read unexpected mode");
161        assert_eq!(
162            1,
163            w.response.len(),
164            "mismatched response length for spi::read"
165        );
166        let buffer: u8 = w.response[0];
167        Ok(buffer)
168    }
169}
170
171impl spi::Transfer<u8> for Mock {
172    type Error = MockError;
173
174    /// spi::Transfer implementation for Mock
175    ///
176    /// This writes the provided response to the buffer and will cause an assertion if the written data does not match the next expectation
177    fn transfer<'w>(&mut self, buffer: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
178        let w = self.next().expect("no expectation for spi::transfer call");
179        assert_eq!(
180            w.expected_mode,
181            Mode::Transfer,
182            "spi::transfer unexpected mode"
183        );
184        assert_eq!(
185            &w.expected_data, &buffer,
186            "spi::transfer write data does not match expectation"
187        );
188        assert_eq!(
189            buffer.len(),
190            w.response.len(),
191            "mismatched response length for spi::transfer"
192        );
193        buffer.copy_from_slice(&w.response);
194        Ok(buffer)
195    }
196}
197
198impl spi::WriteIter<u8> for Mock {
199    type Error = MockError;
200
201    fn write_iter<WI>(&mut self, words: WI) -> Result<(), Self::Error>
202    where
203        WI: IntoIterator<Item = u8>,
204    {
205        let w = self
206            .next()
207            .expect("no expectation for spi::write_iter call");
208        let buffer = words.into_iter().collect::<Vec<_>>();
209        assert_eq!(
210            w.expected_mode,
211            Mode::Write,
212            "spi::write_iter unexpected mode"
213        );
214        assert_eq!(
215            &w.expected_data, &buffer,
216            "spi::write_iter data does not match expectation"
217        );
218        Ok(())
219    }
220}
221
222#[cfg(test)]
223mod test {
224    use eh0 as embedded_hal;
225    use embedded_hal::blocking::spi::{Transfer, Write, WriteIter};
226
227    use super::*;
228
229    #[test]
230    fn test_spi_mock_send() {
231        let mut spi = Mock::new(&[Transaction::send(10)]);
232
233        let _ = spi.send(10).unwrap();
234
235        spi.done();
236    }
237
238    #[test]
239    fn test_spi_mock_read() {
240        let mut spi = Mock::new(&[Transaction::read(10)]);
241
242        let ans = spi.read().unwrap();
243
244        assert_eq!(ans, 10);
245
246        spi.done();
247    }
248
249    #[test]
250    fn test_spi_mock_multiple1() {
251        let expectations = [
252            Transaction::write(vec![1, 2]),
253            Transaction::send(9),
254            Transaction::read(10),
255            Transaction::send(0xFE),
256            Transaction::read(0xFF),
257            Transaction::transfer(vec![3, 4], vec![5, 6]),
258        ];
259        let mut spi = Mock::new(&expectations);
260
261        spi.write(&vec![1, 2]).unwrap();
262
263        let _ = spi.send(0x09);
264        assert_eq!(spi.read().unwrap(), 0x0a);
265        let _ = spi.send(0xfe);
266        assert_eq!(spi.read().unwrap(), 0xFF);
267        let mut v = vec![3, 4];
268        spi.transfer(&mut v).unwrap();
269
270        assert_eq!(v, vec![5, 6]);
271
272        spi.done();
273    }
274
275    #[test]
276    fn test_spi_mock_write() {
277        let expectations = [Transaction::write(vec![10, 12])];
278        let mut spi = Mock::new(&expectations);
279
280        spi.write(&vec![10, 12]).unwrap();
281
282        spi.done();
283    }
284
285    #[test]
286    fn test_spi_mock_write_iter() {
287        let expectations = [Transaction::write(vec![10, 12])];
288        let mut spi = Mock::new(&expectations);
289
290        spi.write_iter(vec![10, 12u8]).unwrap();
291
292        spi.done();
293    }
294
295    #[test]
296    fn test_spi_mock_transfer() {
297        let expectations = [Transaction::transfer(vec![10, 12], vec![12, 13])];
298        let mut spi = Mock::new(&expectations);
299
300        let mut v = vec![10, 12];
301        spi.transfer(&mut v).unwrap();
302
303        assert_eq!(v, vec![12, 13]);
304
305        spi.done();
306    }
307
308    #[test]
309    fn test_spi_mock_multiple() {
310        let expectations = [
311            Transaction::write(vec![1, 2]),
312            Transaction::transfer(vec![3, 4], vec![5, 6]),
313        ];
314        let mut spi = Mock::new(&expectations);
315
316        spi.write(&vec![1, 2]).unwrap();
317
318        let mut v = vec![3, 4];
319        spi.transfer(&mut v).unwrap();
320
321        assert_eq!(v, vec![5, 6]);
322
323        spi.done();
324    }
325
326    #[test]
327    #[should_panic(expected = "spi::write data does not match expectation")]
328    fn test_spi_mock_write_err() {
329        let expectations = [Transaction::write(vec![10, 12])];
330        let mut spi = Mock::new(&expectations);
331        spi.write(&vec![10, 12, 12]).unwrap();
332    }
333
334    #[test]
335    #[should_panic(expected = "spi::write_iter data does not match expectation")]
336    fn test_spi_mock_write_iter_err() {
337        let expectations = [Transaction::write(vec![10, 12])];
338        let mut spi = Mock::new(&expectations);
339        spi.write_iter(vec![10, 12, 12u8]).unwrap();
340    }
341
342    #[test]
343    #[should_panic(expected = "spi::transfer write data does not match expectation")]
344    fn test_spi_mock_transfer_err() {
345        let expectations = [Transaction::transfer(vec![10, 12], vec![12, 15])];
346        let mut spi = Mock::new(&expectations);
347        spi.transfer(&mut vec![10, 13]).unwrap();
348    }
349
350    #[test]
351    #[should_panic(expected = "spi::write data does not match expectation")]
352    fn test_spi_mock_multiple_transaction_err() {
353        let expectations = [
354            Transaction::write(vec![10, 12]),
355            Transaction::write(vec![10, 12]),
356        ];
357        let mut spi = Mock::new(&expectations);
358        spi.write(&vec![10, 12, 10]).unwrap();
359    }
360
361    #[test]
362    #[should_panic(expected = "spi::write unexpected mode")]
363    fn test_spi_mock_mode_err() {
364        let expectations = [Transaction::transfer(vec![10, 12], vec![])];
365        let mut spi = Mock::new(&expectations);
366        // Write instead of transfer
367        spi.write(&vec![10, 12, 12]).unwrap();
368    }
369}