use std::{
collections::VecDeque,
sync::{Arc, Mutex},
};
use embedded_hal_nb::{
nb, serial,
serial::{ErrorKind, ErrorType},
};
use crate::common::DoneCallDetector;
#[derive(Debug, Clone)]
enum Mode<Word> {
Read(Word),
ReadError(nb::Error<ErrorKind>),
Write(Word),
WriteError(Word, nb::Error<ErrorKind>),
Flush,
FlushError(nb::Error<ErrorKind>),
}
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<ErrorKind>) -> 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<ErrorKind>) -> Self {
Transaction {
mode: vec![Mode::WriteError(word, error)],
}
}
pub fn flush() -> Self {
Transaction {
mode: vec![Mode::Flush],
}
}
pub fn flush_error(error: nb::Error<ErrorKind>) -> Self {
Transaction {
mode: vec![Mode::FlushError(error)],
}
}
}
#[derive(Clone)]
pub struct Mock<Word> {
expected_modes: Arc<Mutex<VecDeque<Mode<Word>>>>,
done_called: Arc<Mutex<DoneCallDetector>>,
}
impl<Word: Clone> Mock<Word> {
pub fn new(transactions: &[Transaction<Word>]) -> Self {
let mut ser = Mock {
expected_modes: Arc::new(Mutex::new(VecDeque::new())),
done_called: Arc::new(Mutex::new(DoneCallDetector::new())),
};
ser.update_expectations(transactions);
ser
}
pub fn update_expectations(&mut self, transactions: &[Transaction<Word>]) {
self.done_impl(false);
let mut expected = self.expected_modes.lock().unwrap();
let mut done_called = self.done_called.lock().unwrap();
*expected = transactions
.iter()
.fold(VecDeque::new(), |mut modes, transaction| {
modes.extend(transaction.mode.clone());
modes
});
done_called.reset();
}
#[deprecated(
since = "0.10.0",
note = "The method 'expect' was renamed to 'update_expectations'"
)]
pub fn expect<E>(&mut self, transactions: &[Transaction<Word>]) {
self.update_expectations(transactions)
}
pub fn done(&mut self) {
self.done_impl(true);
}
fn done_impl(&mut self, panic_if_already_done: bool) {
self.done_called
.lock()
.unwrap()
.mark_as_called(panic_if_already_done);
let modes = self
.expected_modes
.lock()
.expect("unable to lock serial mock in call to done");
assert!(
modes.is_empty(),
"serial mock has unsatisfied expectations after call to done"
);
}
fn pop(&mut self) -> Option<Mode<Word>> {
self.expected_modes
.lock()
.expect("unable to lock serial mock in call to pop")
.pop_front()
}
}
impl<Word> ErrorType for Mock<Word> {
type Error = ErrorKind;
}
impl<Word> serial::Read<Word> for Mock<Word>
where
Word: Copy + Clone + std::fmt::Debug,
{
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),
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 + Copy + Clone,
{
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
),
}
}
}
#[cfg(test)]
mod test {
use embedded_hal_nb::serial::{ErrorKind, Read, Write};
use super::*;
#[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_read_many_values_nonblocking() {
let ts = [Transaction::read_many([0xAB, 0xCD, 0xEF])];
let mut ser = Mock::new(&ts);
assert_eq!(0xAB, ser.read().unwrap());
assert_eq!(0xCD, ser.read().unwrap());
assert_eq!(0xEF, ser.read().unwrap());
ser.done();
}
#[test]
#[should_panic(expected = "serial::write expected to write 18 but actually wrote 20")]
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]
#[should_panic(expected = "serial mock has unsatisfied expectations after call to done")]
fn test_serial_mock_pending_transactions() {
let ts = [Transaction::read(0x54)];
let mut ser = Mock::new(&ts);
ser.done();
}
#[test]
#[should_panic(expected = "serial mock has unsatisfied expectations after call to done")]
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.update_expectations(&ts);
ser.done();
}
#[test]
#[should_panic(
expected = "expected to perform a serial transaction 'Write(84)' but instead did a flush"
)]
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', but instead did a read"
)]
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);
ser.done();
}
#[test]
fn test_serial_mock_write_error() {
let error = nb::Error::Other(ErrorKind::Parity);
let ts = [Transaction::write_error(42, error.clone())];
let mut ser: Mock<u8> = Mock::new(&ts);
assert_eq!(ser.write(42).unwrap_err(), error);
ser.done();
}
#[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(ErrorKind::Parity);
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(ErrorKind::Overrun);
let ts = [Transaction::flush_error(error.clone())];
let mut ser: Mock<u8> = Mock::new(&ts);
assert_eq!(ser.flush().unwrap_err(), error);
ser.done();
}
}