use eh1 as embedded_hal;
use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin};
use crate::{common::Generic, eh1::error::MockError};
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct Transaction {
kind: TransactionKind,
err: Option<MockError>,
}
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum State {
Low,
High,
}
#[cfg(feature = "embedded-hal-async")]
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum Edge {
Rising,
Falling,
Any,
}
impl Transaction {
pub fn new(kind: TransactionKind) -> Transaction {
Transaction { kind, err: None }
}
pub fn get(state: State) -> Transaction {
Transaction::new(TransactionKind::Get(state))
}
pub fn set(state: State) -> Transaction {
Transaction::new(TransactionKind::Set(state))
}
pub fn toggle() -> Transaction {
Transaction::new(TransactionKind::Toggle)
}
pub fn get_state(state: State) -> Transaction {
Transaction::new(TransactionKind::GetState(state))
}
#[cfg(feature = "embedded-hal-async")]
pub fn wait_for_state(state: State) -> Transaction {
Transaction::new(TransactionKind::WaitForState(state))
}
#[cfg(feature = "embedded-hal-async")]
pub fn wait_for_edge(edge: Edge) -> Transaction {
Transaction::new(TransactionKind::WaitForEdge(edge))
}
pub fn with_error(mut self, error: MockError) -> Self {
assert!(
self.kind.supports_errors(),
"the transaction kind supports errors"
);
self.err = Some(error);
self
}
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum TransactionKind {
Set(State),
Get(State),
Toggle,
GetState(State),
#[cfg(feature = "embedded-hal-async")]
WaitForState(State),
#[cfg(feature = "embedded-hal-async")]
WaitForEdge(Edge),
}
impl TransactionKind {
fn is_get(&self) -> bool {
match self {
TransactionKind::Get(_) => true,
_ => false,
}
}
fn supports_errors(&self) -> bool {
true
}
}
pub type Mock = Generic<Transaction>;
impl ErrorType for Mock {
type Error = MockError;
}
impl OutputPin for Mock {
fn set_low(&mut self) -> Result<(), Self::Error> {
let Transaction { kind, err } = self.next().expect("no expectation for pin::set_low call");
assert_eq!(
kind,
TransactionKind::Set(State::Low),
"expected pin::set_low"
);
match err {
Some(e) => Err(e),
None => Ok(()),
}
}
fn set_high(&mut self) -> Result<(), Self::Error> {
let Transaction { kind, err } = self.next().expect("no expectation for pin::set_high call");
assert_eq!(
kind,
TransactionKind::Set(State::High),
"expected pin::set_high"
);
match err {
Some(e) => Err(e),
None => Ok(()),
}
}
}
impl InputPin for Mock {
fn is_high(&mut self) -> Result<bool, Self::Error> {
let mut s = self.clone();
let Transaction { kind, err } = s.next().expect("no expectation for pin::is_high call");
assert!(kind.is_get(), "expected pin::get");
if let Some(e) = err {
Err(e)
} else if let TransactionKind::Get(v) = kind {
Ok(v == State::High)
} else {
unreachable!();
}
}
fn is_low(&mut self) -> Result<bool, Self::Error> {
let mut s = self.clone();
let Transaction { kind, err } = s.next().expect("no expectation for pin::is_low call");
assert!(kind.is_get(), "expected pin::get");
if let Some(e) = err {
Err(e)
} else if let TransactionKind::Get(v) = kind {
Ok(v == State::Low)
} else {
unreachable!();
}
}
}
impl StatefulOutputPin for Mock {
fn toggle(&mut self) -> Result<(), Self::Error> {
let Transaction { kind, err } = self.next().expect("no expectation for pin::toggle call");
assert_eq!(kind, TransactionKind::Toggle, "expected pin::toggle");
match err {
Some(e) => Err(e),
None => Ok(()),
}
}
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
let mut s = self.clone();
let Transaction { kind, err } = s.next().expect("no expectation for pin::is_set_high call");
assert!(
matches!(kind, TransactionKind::GetState(_)),
"expected pin::is_set_high"
);
if let Some(e) = err {
Err(e)
} else if let TransactionKind::GetState(v) = kind {
Ok(v == State::High)
} else {
unreachable!();
}
}
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
let mut s = self.clone();
let Transaction { kind, err } = s.next().expect("no expectation for pin::is_set_low call");
assert!(
matches!(kind, TransactionKind::GetState(_)),
"expected pin::is_set_low"
);
if let Some(e) = err {
Err(e)
} else if let TransactionKind::GetState(v) = kind {
Ok(v == State::Low)
} else {
unreachable!();
}
}
}
#[cfg(feature = "embedded-hal-async")]
impl embedded_hal_async::digital::Wait for Mock {
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
let mut s = self.clone();
let Transaction { kind, err } = s
.next()
.expect("no expectation for pin::wait_for_high call");
assert!(
matches!(kind, TransactionKind::WaitForState(State::High)),
"got call to wait_for_high"
);
if let Some(e) = err {
Err(e)
} else {
Ok(())
}
}
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
let mut s = self.clone();
let Transaction { kind, err } =
s.next().expect("no expectation for pin::wait_for_low call");
assert!(
matches!(kind, TransactionKind::WaitForState(State::Low)),
"got call to wait_for_low"
);
if let Some(e) = err {
Err(e)
} else {
Ok(())
}
}
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
let mut s = self.clone();
let Transaction { kind, err } = s
.next()
.expect("no expectation for pin::wait_for_rising_edge call");
assert!(
matches!(kind, TransactionKind::WaitForEdge(Edge::Rising)),
"got call to wait_for_rising_edge"
);
if let Some(e) = err {
Err(e)
} else {
Ok(())
}
}
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
let mut s = self.clone();
let Transaction { kind, err } = s
.next()
.expect("no expectation for pin::wait_for_falling_edge call");
assert!(
matches!(kind, TransactionKind::WaitForEdge(Edge::Falling)),
"got call to wait_for_falling_edge"
);
if let Some(e) = err {
Err(e)
} else {
Ok(())
}
}
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
let mut s = self.clone();
let Transaction { kind, err } = s
.next()
.expect("no expectation for pin::wait_for_any_edge call");
assert!(
matches!(kind, TransactionKind::WaitForEdge(Edge::Any)),
"got call to wait_for_any_edge"
);
if let Some(e) = err {
Err(e)
} else {
Ok(())
}
}
}
#[cfg(test)]
mod test {
use std::io::ErrorKind;
use eh1 as embedded_hal;
use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin};
use super::{
super::error::MockError,
TransactionKind::{Get, GetState, Set, Toggle},
*,
};
#[test]
fn test_input_pin() {
let expectations = [
Transaction::new(Get(State::High)),
Transaction::new(Get(State::High)),
Transaction::new(Get(State::Low)),
Transaction::new(Get(State::Low)),
Transaction::new(Get(State::High)).with_error(MockError::Io(ErrorKind::NotConnected)),
];
let mut pin = Mock::new(&expectations);
assert_eq!(pin.is_high().unwrap(), true);
assert_eq!(pin.is_low().unwrap(), false);
assert_eq!(pin.is_high().unwrap(), false);
assert_eq!(pin.is_low().unwrap(), true);
pin.is_low().expect_err("expected error return");
pin.done();
}
#[test]
fn test_output_pin() {
let expectations = [
Transaction::new(Set(State::High)),
Transaction::new(Set(State::Low)),
Transaction::new(Set(State::High)).with_error(MockError::Io(ErrorKind::NotConnected)),
];
let mut pin = Mock::new(&expectations);
pin.set_high().unwrap();
pin.set_low().unwrap();
pin.set_high().expect_err("expected error return");
pin.done();
}
#[test]
fn test_stateful_output_pin() {
let expectations = [
Transaction::new(GetState(State::Low)),
Transaction::get_state(State::Low),
Transaction::new(Toggle),
Transaction::get_state(State::High),
Transaction::get_state(State::High),
Transaction::toggle(),
Transaction::get_state(State::Low).with_error(MockError::Io(ErrorKind::NotConnected)),
Transaction::toggle().with_error(MockError::Io(ErrorKind::NotConnected)),
];
let mut pin = Mock::new(&expectations);
assert!(pin.is_set_low().unwrap());
assert!(!pin.is_set_high().unwrap());
pin.toggle().unwrap();
assert!(pin.is_set_high().unwrap());
assert!(!pin.is_set_low().unwrap());
pin.toggle().unwrap();
pin.is_set_low()
.expect_err("expected an error when getting state");
pin.toggle()
.expect_err("expected an error when toggling state");
pin.done();
}
#[tokio::test]
#[cfg(feature = "embedded-hal-async")]
async fn test_can_wait_for_state() {
use embedded_hal_async::digital::Wait;
let expectations = [
Transaction::new(TransactionKind::WaitForState(State::High)),
Transaction::new(TransactionKind::WaitForState(State::Low)),
Transaction::new(TransactionKind::WaitForState(State::High))
.with_error(MockError::Io(ErrorKind::NotConnected)),
];
let mut pin = Mock::new(&expectations);
pin.wait_for_high().await.unwrap();
pin.wait_for_low().await.unwrap();
pin.wait_for_high()
.await
.expect_err("expected error return");
pin.done();
}
#[tokio::test]
#[should_panic(expected = "got call to wait_for_high")]
#[cfg(feature = "embedded-hal-async")]
async fn test_wait_for_wrong_state() {
use embedded_hal_async::digital::Wait;
let expectations = [Transaction::wait_for_state(State::Low)];
let mut pin = Mock::new(&expectations);
pin.wait_for_high().await.unwrap();
pin.done();
}
#[tokio::test]
#[cfg(feature = "embedded-hal-async")]
async fn test_can_wait_for_edge() {
use embedded_hal_async::digital::Wait;
let expectations = [
Transaction::new(TransactionKind::WaitForEdge(Edge::Rising)),
Transaction::new(TransactionKind::WaitForEdge(Edge::Falling)),
Transaction::new(TransactionKind::WaitForEdge(Edge::Any)),
Transaction::new(TransactionKind::WaitForEdge(Edge::Rising))
.with_error(MockError::Io(ErrorKind::NotConnected)),
];
let mut pin = Mock::new(&expectations);
pin.wait_for_rising_edge().await.unwrap();
pin.wait_for_falling_edge().await.unwrap();
pin.wait_for_any_edge().await.unwrap();
pin.wait_for_rising_edge()
.await
.expect_err("expected error return");
pin.done();
}
#[tokio::test]
#[should_panic(expected = "got call to wait_for_rising_edge")]
#[cfg(feature = "embedded-hal-async")]
async fn test_wait_for_wrong_edge() {
use embedded_hal_async::digital::Wait;
let expectations = [Transaction::wait_for_edge(Edge::Falling)];
let mut pin = Mock::new(&expectations);
pin.wait_for_rising_edge().await.unwrap();
pin.done();
}
}