1use std::sync::{Arc, Mutex};
2use std::vec;
3use std::vec::Vec;
4
5use crate::{Busy, PinState, Ready, Reset};
6
7use embedded_hal::spi::Operation as SpiOperation;
8
9#[derive(Clone, Debug)]
10pub struct PinError;
11
12impl embedded_hal::digital::Error for PinError {
13 fn kind(&self) -> embedded_hal::digital::ErrorKind {
14 embedded_hal::digital::ErrorKind::Other
15 }
16}
17
18#[derive(Clone, Debug)]
19pub struct SpiError;
20
21#[derive(Clone, Debug)]
22pub struct MockError {}
23impl embedded_hal::spi::Error for MockError {
24 fn kind(&self) -> embedded_hal::spi::ErrorKind {
25 embedded_hal::spi::ErrorKind::Other
26 }
27}
28
29pub struct Mock {
31 inner: Arc<Mutex<Inner>>,
32 count: Id,
33}
34
35pub type Id = u32;
36
37#[derive(Clone, Debug)]
39pub struct Spi {
40 id: Id,
41 inner: Arc<Mutex<Inner>>,
42}
43
44#[derive(Clone, Debug)]
46pub struct Pin {
47 id: Id,
48 inner: Arc<Mutex<Inner>>,
49}
50
51#[derive(Clone, Debug)]
53pub struct Delay {
54 id: Id,
55 inner: Arc<Mutex<Inner>>,
56}
57
58#[derive(Clone, Debug, PartialEq)]
60pub enum MockTransaction {
61 None,
62
63 SpiWrite(Id, Vec<u8>, Vec<u8>),
64 SpiRead(Id, Vec<u8>, Vec<u8>),
65
66 SpiExec(Id, Vec<MockExec>),
67
68 Busy(Id, PinState),
69 Ready(Id, PinState),
70 Reset(Id, PinState),
71
72 Write(Id, Vec<u8>),
73 Transfer(Id, Vec<u8>, Vec<u8>),
74
75 IsHigh(Id, bool),
76 IsLow(Id, bool),
77 SetHigh(Id),
78 SetLow(Id),
79
80 DelayNs(u32),
81}
82
83impl MockTransaction {
84 pub fn spi_write<A, B>(spi: &Spi, prefix: A, outgoing: B) -> Self
85 where
86 A: AsRef<[u8]>,
87 B: AsRef<[u8]>,
88 {
89 MockTransaction::SpiWrite(spi.id, prefix.as_ref().to_vec(), outgoing.as_ref().to_vec())
90 }
91
92 pub fn spi_read<A, B>(spi: &Spi, prefix: A, incoming: B) -> Self
93 where
94 A: AsRef<[u8]>,
95 B: AsRef<[u8]>,
96 {
97 MockTransaction::SpiRead(spi.id, prefix.as_ref().to_vec(), incoming.as_ref().to_vec())
98 }
99
100 pub fn spi_exec<O>(spi: &Spi, o: O) -> Self
101 where
102 O: AsRef<[MockExec]>,
103 {
104 MockTransaction::SpiExec(spi.id, o.as_ref().to_vec())
105 }
106
107 pub fn busy(spi: &Spi, value: PinState) -> Self {
108 MockTransaction::Busy(spi.id, value)
109 }
110
111 pub fn ready(spi: &Spi, value: PinState) -> Self {
112 MockTransaction::Ready(spi.id, value)
113 }
114
115 pub fn reset(spi: &Spi, value: PinState) -> Self {
116 MockTransaction::Reset(spi.id, value)
117 }
118
119 pub fn delay_ms(v: u32) -> Self {
120 MockTransaction::DelayNs(v * 1000)
121 }
122
123 pub fn write<B>(spi: &Spi, outgoing: B) -> Self
124 where
125 B: AsRef<[u8]>,
126 {
127 MockTransaction::Write(spi.id, outgoing.as_ref().to_vec())
128 }
129
130 pub fn transfer<B>(spi: &Spi, outgoing: B, incoming: B) -> Self
131 where
132 B: AsRef<[u8]>,
133 {
134 MockTransaction::Transfer(
135 spi.id,
136 outgoing.as_ref().to_vec(),
137 incoming.as_ref().to_vec(),
138 )
139 }
140
141 pub fn is_high(pin: &Pin, value: bool) -> Self {
142 MockTransaction::IsHigh(pin.id, value)
143 }
144
145 pub fn is_low(pin: &Pin, value: bool) -> Self {
146 MockTransaction::IsLow(pin.id, value)
147 }
148
149 pub fn set_high(pin: &Pin) -> Self {
150 MockTransaction::SetHigh(pin.id)
151 }
152
153 pub fn set_low(pin: &Pin) -> Self {
154 MockTransaction::SetLow(pin.id)
155 }
156}
157
158#[derive(Clone, Debug, PartialEq)]
160pub enum MockExec {
161 SpiWrite(Vec<u8>),
162 SpiTransfer(Vec<u8>, Vec<u8>),
163}
164
165impl<'a> From<&SpiOperation<'a, u8>> for MockExec {
166 fn from(t: &SpiOperation<'a, u8>) -> Self {
167 match t {
168 SpiOperation::Write(ref d) => MockExec::SpiWrite(d.to_vec()),
169 SpiOperation::TransferInPlace(ref d) => {
170 MockExec::SpiTransfer(d.to_vec(), vec![0u8; d.len()])
171 }
172 _ => todo!(),
173 }
174 }
175}
176
177#[derive(Clone, Debug, PartialEq)]
178struct Inner {
179 index: usize,
180 expected: Vec<MockTransaction>,
181 actual: Vec<MockTransaction>,
182}
183
184impl Inner {
185 fn finalise(&mut self) {
186 assert_eq!(self.expected, self.actual);
187 }
188}
189
190impl Mock {
191 pub fn new() -> Self {
193 Self {
194 inner: Arc::new(Mutex::new(Inner {
195 index: 0,
196 expected: Vec::new(),
197 actual: Vec::new(),
198 })),
199 count: 0,
200 }
201 }
202
203 pub fn expect<T>(&mut self, transactions: T)
205 where
206 T: AsRef<[MockTransaction]>,
207 {
208 let expected: Vec<_> = transactions.as_ref().to_vec();
209 let actual = vec![];
210
211 let i = Inner {
212 index: 0,
213 expected,
214 actual,
215 };
216
217 *self.inner.lock().unwrap() = i;
218 }
219
220 pub fn spi(&mut self) -> Spi {
221 let id = self.count;
222 self.count += 1;
223 Spi {
224 inner: self.inner.clone(),
225 id,
226 }
227 }
228
229 pub fn pin(&mut self) -> Pin {
230 let id = self.count;
231 self.count += 1;
232 Pin {
233 inner: self.inner.clone(),
234 id,
235 }
236 }
237
238 pub fn delay(&mut self) -> Delay {
239 let id = self.count;
240 self.count += 1;
241 Delay {
242 inner: self.inner.clone(),
243 id,
244 }
245 }
246
247 pub fn finalise(&self) {
250 let mut i = self.inner.lock().unwrap();
251 i.finalise();
252 }
253}
254
255impl Busy for Spi {
256 type Error = PinError;
257
258 fn get_busy(&mut self) -> Result<PinState, Self::Error> {
260 let mut i = self.inner.lock().unwrap();
261 let index = i.index;
262
263 let state = match &i.expected.get(index) {
264 Some(MockTransaction::Busy(_id, state)) => state.clone(),
265 _ => PinState::Low,
266 };
267
268 i.actual.push(MockTransaction::Busy(self.id, state.clone()));
269
270 i.index += 1;
271
272 Ok(state)
273 }
274}
275
276impl Ready for Spi {
277 type Error = PinError;
278
279 fn get_ready(&mut self) -> Result<PinState, Self::Error> {
281 let mut i = self.inner.lock().unwrap();
282 let index = i.index;
283
284 let state = match &i.expected.get(index) {
285 Some(MockTransaction::Ready(_id, state)) => state.clone(),
286 _ => PinState::Low,
287 };
288
289 i.actual
290 .push(MockTransaction::Ready(self.id, state.clone()));
291
292 i.index += 1;
293
294 Ok(state)
295 }
296}
297
298impl Reset for Spi {
299 type Error = PinError;
300
301 fn set_reset(&mut self, state: PinState) -> Result<(), Self::Error> {
303 let mut i = self.inner.lock().unwrap();
304
305 i.actual.push(MockTransaction::Reset(self.id, state));
306
307 i.index += 1;
308
309 Ok(())
310 }
311}
312
313impl embedded_hal::delay::DelayNs for Spi {
314 fn delay_ns(&mut self, t: u32) {
315 let mut i = self.inner.lock().unwrap();
316
317 i.actual.push(MockTransaction::DelayNs(t));
319
320 i.index += 1;
322 }
323}
324
325impl embedded_hal::spi::SpiDevice<u8> for Spi {
326 fn transaction(&mut self, operations: &mut [SpiOperation<'_, u8>]) -> Result<(), Self::Error> {
327 let mut i = self.inner.lock().unwrap();
328 let index = i.index;
329
330 let t: Vec<MockExec> = operations
332 .as_mut()
333 .iter()
334 .map(|ref v| MockExec::from(*v))
335 .collect();
336 i.actual.push(MockTransaction::SpiExec(self.id, t));
337
338 let transactions = operations.as_mut();
339
340 if let MockTransaction::SpiExec(_id, e) = &i.expected[index] {
342 for i in 0..transactions.len() {
343 let t = &mut transactions[i];
344 let x = e.get(i);
345
346 match (t, x) {
347 (
348 SpiOperation::TransferInPlace(ref mut t_in),
349 Some(MockExec::SpiTransfer(_x_out, x_in)),
350 ) => t_in.copy_from_slice(&x_in),
351 (SpiOperation::Write(ref _t_out), Some(MockExec::SpiWrite(ref _x_out))) => {
352 }
354 _ => (),
355 }
356 }
357 }
358
359 i.index += 1;
361
362 Ok(())
363 }
364
365 fn write<'w>(&mut self, data: &[u8]) -> Result<(), Self::Error> {
366 let mut i = self.inner.lock().unwrap();
367
368 i.actual.push(MockTransaction::Write(self.id, data.into()));
370
371 i.index += 1;
373
374 Ok(())
375 }
376 fn transfer_in_place<'w>(&mut self, data: &'w mut [u8]) -> Result<(), Self::Error> {
377 let mut i = self.inner.lock().unwrap();
378 let index = i.index;
379
380 let incoming: Vec<_> = data.into();
381
382 match &i.expected.get(index) {
384 Some(MockTransaction::Transfer(_id, _outgoing, incoming)) => {
385 if incoming.len() == data.len() {
386 data.copy_from_slice(&incoming);
387 }
388 }
389 _ => (),
390 };
391
392 i.actual
394 .push(MockTransaction::Transfer(self.id, incoming, data.into()));
395
396 i.index += 1;
398
399 Ok(())
400 }
401}
402
403impl embedded_hal::spi::ErrorType for Spi {
404 type Error = MockError;
405}
406
407impl embedded_hal::digital::InputPin for Pin {
408 fn is_high(&mut self) -> Result<bool, Self::Error> {
409 let mut i = self.inner.lock().unwrap();
410 let index = i.index;
411
412 let v = match &i.expected.get(index) {
414 Some(MockTransaction::IsHigh(_id, v)) => *v,
415 _ => false,
416 };
417
418 i.actual.push(MockTransaction::IsHigh(self.id, v));
420
421 i.index += 1;
423
424 Ok(v)
425 }
426
427 fn is_low(&mut self) -> Result<bool, Self::Error> {
428 let mut i = self.inner.lock().unwrap();
429 let index = i.index;
430
431 let v = match &i.expected.get(index) {
433 Some(MockTransaction::IsLow(_id, v)) => *v,
434 _ => false,
435 };
436
437 i.actual.push(MockTransaction::IsLow(self.id, v));
439
440 i.index += 1;
442
443 Ok(v)
444 }
445}
446
447impl embedded_hal::digital::OutputPin for Pin {
448 fn set_high(&mut self) -> Result<(), Self::Error> {
449 let mut i = self.inner.lock().unwrap();
450
451 i.actual.push(MockTransaction::SetHigh(self.id));
453
454 i.index += 1;
456
457 Ok(())
458 }
459
460 fn set_low(&mut self) -> Result<(), Self::Error> {
461 let mut i = self.inner.lock().unwrap();
462
463 i.actual.push(MockTransaction::SetLow(self.id));
465
466 i.index += 1;
468
469 Ok(())
470 }
471}
472
473impl embedded_hal::digital::ErrorType for Pin {
474 type Error = PinError;
475}
476
477impl embedded_hal::delay::DelayNs for Delay {
478 fn delay_ns(&mut self, t: u32) {
479 let mut i = self.inner.lock().unwrap();
480
481 i.actual.push(MockTransaction::DelayNs(t));
483
484 i.index += 1;
486 }
487}
488
489#[cfg(test)]
490mod test {
491 use std::*;
492 use std::{panic, vec};
493
494 use embedded_hal::delay::*;
495 use embedded_hal::digital::*;
496 use embedded_hal::spi::*;
497
498 use super::*;
499 use crate::{PrefixRead, PrefixWrite};
500
501 #[test]
503 #[ignore]
504 fn test_transactional_read() {
505 let mut m = Mock::new();
506 let mut s = m.spi();
507
508 let prefix = vec![0xFF];
509 let data = vec![0xAA, 0xBB];
510
511 m.expect(vec![MockTransaction::spi_exec(
512 &s,
513 &[
514 MockExec::SpiWrite(prefix.clone()),
515 MockExec::SpiTransfer(vec![0u8; 2], data.clone()),
516 ],
517 )]);
518
519 let mut d = [0u8; 2];
520 s.prefix_read(&prefix, &mut d).expect("read failure");
521
522 m.finalise();
523 assert_eq!(&data, &d);
524 }
525
526 #[test]
527 #[should_panic]
528 fn test_transactional_read_expect_write() {
529 let mut m = Mock::new();
530 let mut s = m.spi();
531
532 let prefix = vec![0xFF];
533 let data = vec![0xAA, 0xBB];
534
535 m.expect(vec![MockTransaction::spi_write(
536 &s,
537 prefix.clone(),
538 data.clone(),
539 )]);
540
541 let mut d = [0u8; 2];
542 s.prefix_read(&prefix, &mut d).expect("read failure");
543
544 m.finalise();
545 assert_eq!(&data, &d);
546 }
547
548 #[test]
550 #[ignore]
551 fn test_transactional_write() {
552 let mut m = Mock::new();
553 let mut s = m.spi();
554
555 let prefix = vec![0xFF];
556 let data = vec![0xAA, 0xBB];
557
558 m.expect(vec![MockTransaction::spi_write(
559 &s,
560 prefix.clone(),
561 data.clone(),
562 )]);
563
564 s.prefix_write(&prefix, &data).expect("write failure");
565
566 m.finalise();
567 }
568
569 #[test]
570 #[should_panic]
571 fn test_transactional_write_expect_read() {
572 let mut m = Mock::new();
573 let mut s = m.spi();
574
575 let prefix = vec![0xFF];
576 let data = vec![0xAA, 0xBB];
577
578 m.expect(vec![MockTransaction::spi_read(
579 &s,
580 prefix.clone(),
581 data.clone(),
582 )]);
583
584 s.prefix_write(&prefix, &data).expect("write failure");
585
586 m.finalise();
587 }
588
589 #[test]
590 fn test_standard_write() {
591 let mut m = Mock::new();
592 let mut s = m.spi();
593
594 let data = vec![0xAA, 0xBB];
595
596 m.expect(vec![MockTransaction::write(&s, data.clone())]);
597
598 s.write(&data).expect("write failure");
599
600 m.finalise();
601 }
602
603 #[test]
604 fn test_standard_transfer() {
605 let mut m = Mock::new();
606 let mut s = m.spi();
607
608 let outgoing = vec![0xAA, 0xBB];
609 let incoming = vec![0xCC, 0xDD];
610
611 m.expect(vec![MockTransaction::transfer(
612 &s,
613 outgoing.clone(),
614 incoming.clone(),
615 )]);
616
617 let mut d = outgoing.clone();
618 s.transfer_in_place(&mut d).expect("read failure");
619
620 m.finalise();
621 assert_eq!(&incoming, &d);
622 }
623
624 #[test]
625 fn test_pins() {
626 let mut m = Mock::new();
627 let mut p = m.pin();
628
629 m.expect(vec![
630 MockTransaction::is_high(&p, true),
631 MockTransaction::is_low(&p, false),
632 MockTransaction::set_high(&p),
633 MockTransaction::set_low(&p),
634 ]);
635
636 assert_eq!(true, p.is_high().unwrap());
637 assert_eq!(false, p.is_low().unwrap());
638
639 p.set_high().unwrap();
640 p.set_low().unwrap();
641
642 m.finalise();
643 }
644
645 #[test]
646 #[should_panic]
647 fn test_incorrect_pin() {
648 let mut m = Mock::new();
649 let p1 = m.pin();
650 let mut p2 = m.pin();
651
652 m.expect(vec![MockTransaction::is_high(&p1, true)]);
653
654 p2.is_high().unwrap();
655
656 m.finalise();
657 }
658}