Skip to content

Commit

Permalink
Add StatefulOutputPin support for eh1::digital::Mock
Browse files Browse the repository at this point in the history
  • Loading branch information
cdunster authored and dbrgn committed May 30, 2024
1 parent 9024ffb commit 2654fc4
Showing 1 changed file with 111 additions and 5 deletions.
116 changes: 111 additions & 5 deletions src/eh1/digital.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
//! Mock digital [`InputPin`] and [`OutputPin`] implementations
//! Mock digital [`InputPin`], [`OutputPin`], and [`StatefulOutputPin`] implementations
//! Also mock calls to [`Wait`], assuming the `embedded-hal-async` feature is enabled.
//!
//! [`InputPin`]: https://docs.rs/embedded-hal/1/embedded_hal/digital/trait.InputPin.html
//! [`OutputPin`]: https://docs.rs/embedded-hal/1/embedded_hal/digital/trait.OutputPin.html
//! [`StatefulOutputPin`]: https://docs.rs/embedded-hal/1/embedded_hal/digital/trait.StatefulOutputPin.html
//! [`Wait`]: https://docs.rs/embedded-hal-async/1/embedded_hal_async/digital/trait.Wait.html
//!
//! ```
//! # use eh1 as embedded_hal;
//! use std::io::ErrorKind;
//!
//! use embedded_hal::digital::{InputPin, OutputPin};
//! use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin};
//! use embedded_hal_mock::eh1::{
//! digital::{Mock as PinMock, State as PinState, Transaction as PinTransaction},
//! MockError,
Expand All @@ -23,6 +24,9 @@
//! PinTransaction::get(PinState::High),
//! PinTransaction::set(PinState::Low),
//! PinTransaction::set(PinState::High).with_error(err.clone()),
//! PinTransaction::get_state(PinState::High),
//! PinTransaction::toggle(),
//! PinTransaction::get_state(PinState::Low),
//! ];
//!
//! // Create pin
Expand All @@ -35,6 +39,10 @@
//! pin.set_low().unwrap();
//! pin.set_high().expect_err("expected error return");
//!
//! pin.is_set_high().unwrap();
//! pin.toggle().unwrap();
//! pin.is_set_low().unwrap();
//!
//! pin.done();
//!
//! // Update expectations
Expand All @@ -44,7 +52,7 @@
//! ```
use eh1 as embedded_hal;
use embedded_hal::digital::{ErrorType, InputPin, OutputPin};
use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin};

use crate::{common::Generic, eh1::error::MockError};

Expand Down Expand Up @@ -96,6 +104,16 @@ impl Transaction {
Transaction::new(TransactionKind::Set(state))
}

/// Create a new toggle transaction
pub fn toggle() -> Transaction {
Transaction::new(TransactionKind::Toggle)
}

/// Create a new get stateful pin state transaction
pub fn get_state(state: State) -> Transaction {
Transaction::new(TransactionKind::GetState(state))
}

/// Create a new wait_for_state transaction
#[cfg(feature = "embedded-hal-async")]
pub fn wait_for_state(state: State) -> Transaction {
Expand Down Expand Up @@ -132,6 +150,10 @@ pub enum TransactionKind {
Set(State),
/// Get the pin state
Get(State),
/// Toggle the pin state
Toggle,
/// Get the set state of the stateful pin
GetState(State),
/// Wait for the given pin state
#[cfg(feature = "embedded-hal-async")]
WaitForState(State),
Expand Down Expand Up @@ -232,6 +254,61 @@ impl InputPin for Mock {
}
}

/// Single digital output pin that remembers its state and can be toggled between high and low states
impl StatefulOutputPin for Mock {
/// Toggle the pin low to high or high to low
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(()),
}
}

/// Is the output pin set high?
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!();
}
}

/// Is the output pin set low?
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 {
/// Wait for the pin to go high
Expand Down Expand Up @@ -339,11 +416,11 @@ mod test {
use std::io::ErrorKind;

use eh1 as embedded_hal;
use embedded_hal::digital::{InputPin, OutputPin};
use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin};

use super::{
super::error::MockError,
TransactionKind::{Get, Set},
TransactionKind::{Get, GetState, Set, Toggle},
*,
};

Expand Down Expand Up @@ -385,6 +462,35 @@ mod test {
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() {
Expand Down

0 comments on commit 2654fc4

Please sign in to comment.