use embedded_hal::blocking::serial::write;
use embedded_hal::serial;
use crate::error::MockError;
use std::collections::VecDeque;
use std::sync::{Arc, Mutex};
#[derive(Debug, Clone)]
enum Mode<Word> {
Read(Word),
ReadError(nb::Error<MockError>),
Write(Word),
WriteError(Word, nb::Error<MockError>),
Flush,
FlushError(nb::Error<MockError>),
}
pub struct Transaction<Word> {
mode: Vec<Mode<Word>>,
}
impl<Word> Transaction<Word>
where
Word: Clone,
{
pub fn read(word: Word) -> Self {
Transaction {
mode: vec![Mode::Read(word)],
}
}
pub fn read_many<Ws>(words: Ws) -> Self
where
Ws: AsRef<[Word]>,
{
Transaction {
mode: words.as_ref().iter().cloned().map(Mode::Read).collect(),
}
}
pub fn read_error(error: nb::Error<MockError>) -> Self {
Transaction {
mode: vec![Mode::ReadError(error)],
}
}
pub fn write(word: Word) -> Self {
Transaction {
mode: vec![Mode::Write(word)],
}
}
pub fn write_many<Ws>(words: Ws) -> Self
where
Ws: AsRef<[Word]>,
{
Transaction {
mode: words.as_ref().iter().cloned().map(Mode::Write).collect(),
}
}
pub fn write_error(word: Word, error: nb::Error<MockError>) -> Self {
Transaction {
mode: vec![Mode::WriteError(word, error)],
}
}
pub fn flush() -> Self {
Transaction {
mode: vec![Mode::Flush],
}
}
pub fn flush_error(error: nb::Error<MockError>) -> Self {
Transaction {
mode: vec![Mode::FlushError(error)],
}
}
}
#[derive(Clone)]
pub struct Mock<Word> {
expected_modes: Arc<Mutex<Option<VecDeque<Mode<Word>>>>>,
}
impl<Word: Clone> Mock<Word> {
pub fn new(transactions: &[Transaction<Word>]) -> Self {
let mut ser = Mock {
expected_modes: Arc::new(Mutex::new(None)),
};
ser.expect(transactions);
ser
}
pub fn expect(&mut self, transactions: &[Transaction<Word>]) {
let mut lock = self
.expected_modes
.lock()
.expect("unable to lock serial mock in call to expect");
*lock = Some(
transactions
.iter()
.fold(VecDeque::new(), |mut modes, transaction| {
modes.extend(transaction.mode.clone());
modes
}),
);
}
pub fn done(&mut self) {
let mut lock = self
.expected_modes
.lock()
.expect("unable to lock serial mock in call to done");
let modes = lock.take().expect("attempted to take None from Optional");
assert!(
modes.is_empty(),
"serial mock has unsatisfied expectations after call to done"
);
}
fn pop(&mut self) -> Option<Mode<Word>> {
let mut lock = self
.expected_modes
.lock()
.expect("unable to lock serial mock in call to pop");
let queue = lock
.as_mut()
.expect("attempt to get queue reference from a None");
queue.pop_front()
}
}
impl<Word> serial::Read<Word> for Mock<Word>
where
Word: Clone + std::fmt::Debug,
{
type Error = MockError;
fn read(&mut self) -> nb::Result<Word, Self::Error> {
let t = self.pop().expect("called serial::read with no expectation");
match t {
Mode::Read(word) => Ok(word.clone()),
Mode::ReadError(error) => Err(error),
other => panic!(
"expected to perform a serial transaction '{:?}', but instead did a read",
other
),
}
}
}
impl<Word> serial::Write<Word> for Mock<Word>
where
Word: PartialEq + std::fmt::Debug + Clone,
{
type Error = MockError;
fn write(&mut self, word: Word) -> nb::Result<(), Self::Error> {
let t = self
.pop()
.expect("called serial::write with no expectation");
let assert_write = |expectation: Word| {
assert_eq!(
expectation, word,
"serial::write expected to write {:?} but actually wrote {:?}",
expectation, word
);
};
match t {
Mode::Write(expectation) => {
assert_write(expectation);
Ok(())
}
Mode::WriteError(expectation, error) => {
assert_write(expectation);
Err(error)
}
other => panic!(
"expected to perform a serial transaction '{:?}' but instead did a write of {:?}",
other, word
),
}
}
fn flush(&mut self) -> nb::Result<(), Self::Error> {
let t = self
.pop()
.expect("called serial::flush with no expectation");
match t {
Mode::Flush => Ok(()),
Mode::FlushError(error) => Err(error),
mode => panic!(
"expected to perform a serial transaction '{:?}' but instead did a flush",
mode
),
}
}
}
impl<Word> write::Default<Word> for Mock<Word> where Word: PartialEq + std::fmt::Debug + Clone {}
#[cfg(test)]
mod test {
use std::io;
use embedded_hal::{
blocking::serial::Write as BWrite,
serial::{Read, Write},
};
use super::{Mock, MockError, Transaction};
#[test]
fn test_serial_mock_read() {
let ts = [Transaction::read(0x54)];
let mut ser = Mock::new(&ts);
let r = ser.read().expect("failed to read");
assert_eq!(r, 0x54);
ser.done();
}
#[test]
fn test_serial_mock_write_single_value_nonblocking() {
let ts = [Transaction::write(0xAB)];
let mut ser = Mock::new(&ts);
ser.write(0xAB).unwrap();
ser.done();
}
#[test]
fn test_serial_mock_write_many_values_nonblocking() {
let ts = [Transaction::write_many([0xAB, 0xCD, 0xEF])];
let mut ser = Mock::new(&ts);
ser.write(0xAB).unwrap();
ser.write(0xCD).unwrap();
ser.write(0xEF).unwrap();
ser.done();
}
#[test]
fn test_serial_mock_blocking_write() {
let ts = [Transaction::write_many([0xAB, 0xCD, 0xEF])];
let mut ser = Mock::new(&ts);
ser.bwrite_all(&[0xAB, 0xCD, 0xEF]).unwrap();
ser.done();
}
#[test]
#[should_panic(expected = "called serial::write with no expectation")]
fn test_serial_mock_blocking_write_more_than_expected() {
let ts = [Transaction::write_many([0xAB, 0xCD])];
let mut ser = Mock::new(&ts);
ser.bwrite_all(&[0xAB, 0xCD, 0xEF]).unwrap();
ser.done();
}
#[test]
#[should_panic(expected = "unsatisfied expectations")]
fn test_serial_mock_blocking_write_not_enough() {
let ts = [Transaction::write_many([0xAB, 0xCD, 0xEF, 0x00])];
let mut ser = Mock::new(&ts);
ser.bwrite_all(&[0xAB, 0xCD, 0xEF]).unwrap();
ser.done();
}
#[test]
#[should_panic(expected = "serial::write expected to write")]
fn test_serial_mock_wrong_write() {
let ts = [Transaction::write(0x12)];
let mut ser = Mock::new(&ts);
ser.write(0x14).unwrap();
}
#[test]
fn test_serial_mock_flush() {
let ts = [Transaction::flush()];
let mut ser: Mock<u8> = Mock::new(&ts);
ser.flush().unwrap();
ser.done();
}
#[test]
fn test_serial_mock_blocking_flush() {
let ts = [Transaction::flush()];
let mut ser: Mock<u8> = Mock::new(&ts);
ser.bflush().unwrap();
ser.done();
}
#[test]
#[should_panic(expected = "unsatisfied expectations")]
fn test_serial_mock_pending_transactions() {
let ts = [Transaction::read(0x54)];
let mut ser = Mock::new(&ts);
ser.done();
}
#[test]
#[should_panic(expected = "unsatisfied expectations")]
fn test_serial_mock_reuse_pending_transactions() {
let ts = [Transaction::read(0x54)];
let mut ser = Mock::new(&ts);
let r = ser.read().expect("failed to read");
assert_eq!(r, 0x54);
ser.done();
ser.expect(&ts);
ser.done();
}
#[test]
#[should_panic(expected = "expected to perform a serial transaction 'Read(")]
fn test_serial_mock_expected_read() {
let ts = [Transaction::read(0x54)];
let mut ser = Mock::new(&ts);
ser.bwrite_all(&[0x77]).unwrap();
}
#[test]
#[should_panic(expected = "expected to perform a serial transaction 'Write(")]
fn test_serial_mock_expected_write() {
let ts = [Transaction::write(0x54)];
let mut ser = Mock::new(&ts);
ser.flush().unwrap();
}
#[test]
#[should_panic(expected = "expected to perform a serial transaction 'Flush'")]
fn test_serial_mock_expected_flush() {
let ts = [Transaction::flush()];
let mut ser: Mock<u128> = Mock::new(&ts);
ser.read().unwrap();
}
#[test]
fn test_serial_mock_read_error() {
let error = nb::Error::WouldBlock;
let ts = [Transaction::read_error(error.clone())];
let mut ser: Mock<u8> = Mock::new(&ts);
assert_eq!(ser.read().unwrap_err(), error);
}
#[test]
fn test_serial_mock_write_error() {
let error = nb::Error::Other(MockError::Io(io::ErrorKind::NotConnected));
let ts = [Transaction::write_error(42, error.clone())];
let mut ser: Mock<u8> = Mock::new(&ts);
assert_eq!(ser.write(42).unwrap_err(), error);
}
#[test]
#[should_panic(expected = "serial::write expected to write 42 but actually wrote 23")]
fn test_serial_mock_write_error_wrong_data() {
let error = nb::Error::Other(MockError::Io(io::ErrorKind::NotConnected));
let ts = [Transaction::write_error(42, error.clone())];
let mut ser: Mock<u8> = Mock::new(&ts);
ser.write(23).unwrap();
}
#[test]
fn test_serial_mock_flush_error() {
let error = nb::Error::Other(MockError::Io(io::ErrorKind::TimedOut));
let ts = [Transaction::flush_error(error.clone())];
let mut ser: Mock<u8> = Mock::new(&ts);
assert_eq!(ser.flush().unwrap_err(), error);
}
}