use std::sync::{Arc, Mutex};
use std::vec::Vec;
use std::{panic, vec};
use crate::{Busy, Error, PinState, Ready, Reset};
use embedded_hal::blocking::delay::{DelayMs, DelayUs};
use embedded_hal::blocking::spi;
use embedded_hal::digital::{InputPin, OutputPin};
pub struct Mock {
inner: Arc<Mutex<Inner>>,
count: Id,
}
pub type Id = u32;
#[derive(Clone, Debug)]
pub struct Spi {
id: Id,
inner: Arc<Mutex<Inner>>,
}
#[derive(Clone, Debug)]
pub struct Pin {
id: Id,
inner: Arc<Mutex<Inner>>,
}
#[derive(Clone, Debug)]
pub struct Delay {
id: Id,
inner: Arc<Mutex<Inner>>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum MockTransaction {
None,
SpiWrite(Id, Vec<u8>, Vec<u8>),
SpiRead(Id, Vec<u8>, Vec<u8>),
SpiExec(Id, Vec<MockExec>),
Busy(Id, PinState),
Ready(Id, PinState),
Reset(Id, PinState),
Write(Id, Vec<u8>),
Transfer(Id, Vec<u8>, Vec<u8>),
IsHigh(Id, bool),
IsLow(Id, bool),
SetHigh(Id),
SetLow(Id),
DelayMs(u32),
DelayUs(u32),
}
impl MockTransaction {
pub fn spi_write<A, B>(spi: &Spi, prefix: A, outgoing: B) -> Self
where
A: AsRef<[u8]>,
B: AsRef<[u8]>,
{
MockTransaction::SpiWrite(spi.id, prefix.as_ref().to_vec(), outgoing.as_ref().to_vec())
}
pub fn spi_read<A, B>(spi: &Spi, prefix: A, incoming: B) -> Self
where
A: AsRef<[u8]>,
B: AsRef<[u8]>,
{
MockTransaction::SpiRead(spi.id, prefix.as_ref().to_vec(), incoming.as_ref().to_vec())
}
pub fn spi_exec<O>(spi: &Spi, o: O) -> Self
where
O: AsRef<[MockExec]>,
{
MockTransaction::SpiExec(spi.id, o.as_ref().to_vec())
}
pub fn busy(spi: &Spi, value: PinState) -> Self {
MockTransaction::Busy(spi.id, value)
}
pub fn ready(spi: &Spi, value: PinState) -> Self {
MockTransaction::Ready(spi.id, value)
}
pub fn reset(spi: &Spi, value: PinState) -> Self {
MockTransaction::Reset(spi.id, value)
}
pub fn delay_ms(v: u32) -> Self {
MockTransaction::DelayMs(v)
}
pub fn write<B>(spi: &Spi, outgoing: B) -> Self
where
B: AsRef<[u8]>,
{
MockTransaction::Write(spi.id, outgoing.as_ref().to_vec())
}
pub fn transfer<B>(spi: &Spi, outgoing: B, incoming: B) -> Self
where
B: AsRef<[u8]>,
{
MockTransaction::Transfer(
spi.id,
outgoing.as_ref().to_vec(),
incoming.as_ref().to_vec(),
)
}
pub fn is_high(pin: &Pin, value: bool) -> Self {
MockTransaction::IsHigh(pin.id, value)
}
pub fn is_low(pin: &Pin, value: bool) -> Self {
MockTransaction::IsLow(pin.id, value)
}
pub fn set_high(pin: &Pin) -> Self {
MockTransaction::SetHigh(pin.id)
}
pub fn set_low(pin: &Pin) -> Self {
MockTransaction::SetLow(pin.id)
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum MockExec {
SpiWrite(Vec<u8>),
SpiTransfer(Vec<u8>, Vec<u8>),
}
impl<'a> From<&spi::Operation<'a, u8>> for MockExec {
fn from(t: &spi::Operation<'a, u8>) -> Self {
match t {
spi::Operation::Write(ref d) => MockExec::SpiWrite(d.to_vec()),
spi::Operation::Transfer(ref d) => {
MockExec::SpiTransfer(d.to_vec(), vec![0u8; d.len()])
}
}
}
}
#[derive(Clone, Debug, PartialEq)]
struct Inner {
index: usize,
expected: Vec<MockTransaction>,
actual: Vec<MockTransaction>,
}
impl Inner {
fn finalise(&mut self) {
assert_eq!(self.expected, self.actual);
}
}
impl Mock {
pub fn new() -> Self {
Self {
inner: Arc::new(Mutex::new(Inner {
index: 0,
expected: Vec::new(),
actual: Vec::new(),
})),
count: 0,
}
}
pub fn expect<T>(&mut self, transactions: T)
where
T: AsRef<[MockTransaction]>,
{
let expected: Vec<_> = transactions.as_ref().to_vec();
let actual = vec![];
let i = Inner {
index: 0,
expected,
actual,
};
*self.inner.lock().unwrap() = i;
}
pub fn spi(&mut self) -> Spi {
let id = self.count;
self.count += 1;
Spi {
inner: self.inner.clone(),
id,
}
}
pub fn pin(&mut self) -> Pin {
let id = self.count;
self.count += 1;
Pin {
inner: self.inner.clone(),
id,
}
}
pub fn delay(&mut self) -> Delay {
let id = self.count;
self.count += 1;
Delay {
inner: self.inner.clone(),
id,
}
}
pub fn finalise(&self) {
let mut i = self.inner.lock().unwrap();
i.finalise();
}
}
impl Busy for Spi {
type Error = Error<(), (), ()>;
fn get_busy(&mut self) -> Result<PinState, Self::Error> {
let mut i = self.inner.lock().unwrap();
let index = i.index;
let state = match &i.expected.get(index) {
Some(MockTransaction::Busy(_id, state)) => state.clone(),
_ => PinState::Low,
};
i.actual.push(MockTransaction::Busy(self.id, state.clone()));
i.index += 1;
Ok(state)
}
}
impl Ready for Spi {
type Error = Error<(), (), ()>;
fn get_ready(&mut self) -> Result<PinState, Self::Error> {
let mut i = self.inner.lock().unwrap();
let index = i.index;
let state = match &i.expected.get(index) {
Some(MockTransaction::Ready(_id, state)) => state.clone(),
_ => PinState::Low,
};
i.actual
.push(MockTransaction::Ready(self.id, state.clone()));
i.index += 1;
Ok(state)
}
}
impl Reset for Spi {
type Error = Error<(), (), ()>;
fn set_reset(&mut self, state: PinState) -> Result<(), Self::Error> {
let mut i = self.inner.lock().unwrap();
i.actual.push(MockTransaction::Reset(self.id, state));
i.index += 1;
Ok(())
}
}
impl DelayMs<u32> for Spi {
type Error = ();
fn try_delay_ms(&mut self, t: u32) -> Result<(), Self::Error> {
let mut i = self.inner.lock().unwrap();
i.actual.push(MockTransaction::DelayMs(t));
i.index += 1;
Ok(())
}
}
impl DelayUs<u32> for Spi {
type Error = ();
fn try_delay_us(&mut self, t: u32) -> Result<(), Self::Error> {
let mut i = self.inner.lock().unwrap();
i.actual.push(MockTransaction::DelayUs(t));
i.index += 1;
Ok(())
}
}
impl spi::Transfer<u8> for Spi {
type Error = Error<(), (), ()>;
fn try_transfer<'w>(&mut self, data: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
let mut i = self.inner.lock().unwrap();
let index = i.index;
let incoming: Vec<_> = data.into();
match &i.expected.get(index) {
Some(MockTransaction::Transfer(_id, _outgoing, incoming)) => {
if incoming.len() == data.len() {
data.copy_from_slice(&incoming);
}
}
_ => (),
};
i.actual
.push(MockTransaction::Transfer(self.id, incoming, data.into()));
i.index += 1;
Ok(data)
}
}
impl spi::Write<u8> for Spi {
type Error = Error<(), (), ()>;
fn try_write<'w>(&mut self, data: &[u8]) -> Result<(), Self::Error> {
let mut i = self.inner.lock().unwrap();
i.actual.push(MockTransaction::Write(self.id, data.into()));
i.index += 1;
Ok(())
}
}
impl spi::Transactional<u8> for Spi {
type Error = Error<(), (), ()>;
fn try_exec<'a>(
&mut self,
operations: &mut [spi::Operation<'a, u8>],
) -> Result<(), Self::Error> {
let mut i = self.inner.lock().unwrap();
let index = i.index;
let t: Vec<MockExec> = operations
.as_mut()
.iter()
.map(|ref v| MockExec::from(*v))
.collect();
i.actual.push(MockTransaction::SpiExec(self.id, t));
let transactions = operations.as_mut();
if let MockTransaction::SpiExec(_id, e) = &i.expected[index] {
for i in 0..transactions.len() {
let t = &mut transactions[i];
let x = e.get(i);
match (t, x) {
(
spi::Operation::Transfer(ref mut t_in),
Some(MockExec::SpiTransfer(_x_out, x_in)),
) => t_in.copy_from_slice(&x_in),
(spi::Operation::Write(ref _t_out), Some(MockExec::SpiWrite(ref _x_out))) => {
}
_ => (),
}
}
}
i.index += 1;
Ok(())
}
}
impl InputPin for Pin {
type Error = ();
fn try_is_high(&self) -> Result<bool, Self::Error> {
let mut i = self.inner.lock().unwrap();
let index = i.index;
let v = match &i.expected.get(index) {
Some(MockTransaction::IsHigh(_id, v)) => *v,
_ => false,
};
i.actual.push(MockTransaction::IsHigh(self.id, v));
i.index += 1;
Ok(v)
}
fn try_is_low(&self) -> Result<bool, Self::Error> {
let mut i = self.inner.lock().unwrap();
let index = i.index;
let v = match &i.expected.get(index) {
Some(MockTransaction::IsLow(_id, v)) => *v,
_ => false,
};
i.actual.push(MockTransaction::IsLow(self.id, v));
i.index += 1;
Ok(v)
}
}
impl OutputPin for Pin {
type Error = ();
fn try_set_high(&mut self) -> Result<(), Self::Error> {
let mut i = self.inner.lock().unwrap();
i.actual.push(MockTransaction::SetHigh(self.id));
i.index += 1;
Ok(())
}
fn try_set_low(&mut self) -> Result<(), Self::Error> {
let mut i = self.inner.lock().unwrap();
i.actual.push(MockTransaction::SetLow(self.id));
i.index += 1;
Ok(())
}
}
impl DelayMs<u32> for Delay {
type Error = ();
fn try_delay_ms(&mut self, t: u32) -> Result<(), Self::Error> {
let mut i = self.inner.lock().unwrap();
i.actual.push(MockTransaction::DelayMs(t));
i.index += 1;
Ok(())
}
}
impl DelayUs<u32> for Delay {
type Error = ();
fn try_delay_us(&mut self, t: u32) -> Result<(), Self::Error> {
let mut i = self.inner.lock().unwrap();
i.actual.push(MockTransaction::DelayUs(t));
i.index += 1;
Ok(())
}
}
#[cfg(test)]
mod test {
use std::*;
use std::{panic, vec};
use super::*;
use crate::{PrefixRead, PrefixWrite};
#[test]
#[ignore]
fn test_transactional_read() {
let mut m = Mock::new();
let mut s = m.spi();
let prefix = vec![0xFF];
let data = vec![0xAA, 0xBB];
m.expect(vec![MockTransaction::spi_exec(
&s,
&[
MockExec::SpiWrite(prefix.clone()),
MockExec::SpiTransfer(vec![0u8; 2], data.clone()),
],
)]);
let mut d = [0u8; 2];
s.try_prefix_read(&prefix, &mut d).expect("read failure");
m.finalise();
assert_eq!(&data, &d);
}
#[test]
#[should_panic]
fn test_transactional_read_expect_write() {
let mut m = Mock::new();
let mut s = m.spi();
let prefix = vec![0xFF];
let data = vec![0xAA, 0xBB];
m.expect(vec![MockTransaction::spi_write(
&s,
prefix.clone(),
data.clone(),
)]);
let mut d = [0u8; 2];
s.try_prefix_read(&prefix, &mut d).expect("read failure");
m.finalise();
assert_eq!(&data, &d);
}
#[test]
#[ignore]
fn test_transactional_write() {
let mut m = Mock::new();
let mut s = m.spi();
let prefix = vec![0xFF];
let data = vec![0xAA, 0xBB];
m.expect(vec![MockTransaction::spi_write(
&s,
prefix.clone(),
data.clone(),
)]);
s.try_prefix_write(&prefix, &data).expect("write failure");
m.finalise();
}
#[test]
#[should_panic]
fn test_transactional_write_expect_read() {
let mut m = Mock::new();
let mut s = m.spi();
let prefix = vec![0xFF];
let data = vec![0xAA, 0xBB];
m.expect(vec![MockTransaction::spi_read(
&s,
prefix.clone(),
data.clone(),
)]);
s.try_prefix_write(&prefix, &data).expect("write failure");
m.finalise();
}
#[test]
fn test_standard_write() {
use embedded_hal::blocking::spi::Write;
let mut m = Mock::new();
let mut s = m.spi();
let data = vec![0xAA, 0xBB];
m.expect(vec![MockTransaction::write(&s, data.clone())]);
s.try_write(&data).expect("write failure");
m.finalise();
}
#[test]
fn test_standard_transfer() {
use embedded_hal::blocking::spi::Transfer;
let mut m = Mock::new();
let mut s = m.spi();
let outgoing = vec![0xAA, 0xBB];
let incoming = vec![0xCC, 0xDD];
m.expect(vec![MockTransaction::transfer(
&s,
outgoing.clone(),
incoming.clone(),
)]);
let mut d = outgoing.clone();
s.try_transfer(&mut d).expect("read failure");
m.finalise();
assert_eq!(&incoming, &d);
}
#[test]
fn test_pins() {
use embedded_hal::digital::{InputPin, OutputPin};
let mut m = Mock::new();
let mut p = m.pin();
m.expect(vec![
MockTransaction::is_high(&p, true),
MockTransaction::is_low(&p, false),
MockTransaction::set_high(&p),
MockTransaction::set_low(&p),
]);
assert_eq!(true, p.try_is_high().unwrap());
assert_eq!(false, p.try_is_low().unwrap());
p.try_set_high().unwrap();
p.try_set_low().unwrap();
m.finalise();
}
#[test]
#[should_panic]
fn test_incorrect_pin() {
use embedded_hal::digital::InputPin;
let mut m = Mock::new();
let p1 = m.pin();
let p2 = m.pin();
m.expect(vec![MockTransaction::is_high(&p1, true)]);
p2.try_is_high().unwrap();
m.finalise();
}
}