Skip to content

Commit

Permalink
Merge pull request #89 from cdunster/feat/toggleable-output-pin
Browse files Browse the repository at this point in the history
Add support for ToggleableOutputPin/StatefulOutputPin for pin::Mock
  • Loading branch information
dbrgn committed May 30, 2024
2 parents 989059f + 2654fc4 commit 7ea451b
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 9 deletions.
54 changes: 50 additions & 4 deletions src/eh0/digital.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
//! Mock digital [`InputPin`] and [`OutputPin`] v2 implementations
//! Mock digital [`InputPin`], [`OutputPin`], and [`ToggleableOutputPin`] v2 implementations
//!
//! [`InputPin`]: https://docs.rs/embedded-hal/0.2/embedded_hal/digital/v2/trait.InputPin.html
//! [`OutputPin`]: https://docs.rs/embedded-hal/0.2/embedded_hal/digital/v2/trait.OutputPin.html
//! [`ToggleableOutputPin`]: https://docs.rs/embedded-hal/0.2/embedded_hal/digital/v2/trait.ToggleableOutputPin.html
//!
//! ```
//! # use eh0 as embedded_hal;
//! use std::io::ErrorKind;
//!
//! use embedded_hal::digital::v2::{InputPin, OutputPin};
//! use embedded_hal::digital::v2::{InputPin, OutputPin, ToggleableOutputPin};
//! use embedded_hal_mock::eh0::{
//! digital::{Mock as PinMock, State as PinState, Transaction as PinTransaction},
//! MockError,
Expand All @@ -21,6 +22,7 @@
//! PinTransaction::get(PinState::High),
//! PinTransaction::set(PinState::Low),
//! PinTransaction::set(PinState::High).with_error(err.clone()),
//! PinTransaction::toggle(),
//! ];
//!
//! // Create pin
Expand All @@ -33,6 +35,8 @@
//! pin.set_low().unwrap();
//! pin.set_high().expect_err("expected error return");
//!
//! pin.toggle().unwrap();
//!
//! pin.done();
//!
//! // Update expectations
Expand All @@ -43,7 +47,7 @@

use eh0 as embedded_hal;
use embedded_hal::{
digital::v2::{InputPin, OutputPin},
digital::v2::{InputPin, OutputPin, ToggleableOutputPin},
PwmPin,
};

Expand Down Expand Up @@ -84,6 +88,11 @@ impl Transaction {
Transaction::new(TransactionKind::Get(state))
}

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

/// Create a new get transaction
pub fn set(state: State) -> Transaction {
Transaction::new(TransactionKind::Set(state))
Expand Down Expand Up @@ -137,6 +146,8 @@ pub enum TransactionKind {
Set(State),
/// Get the pin state
Get(State),
/// Toggle the pin state
Toggle,
/// Disable a [`PwmPin`] using [`PwmPin::disable`]
Disable,
/// Enable a [`PwmPin`] using [`PwmPin::enable`]
Expand All @@ -160,7 +171,7 @@ impl TransactionKind {
/// Specifies whether the actual API returns a [`Result`] (= supports errors) or not.
fn supports_errors(&self) -> bool {
match self {
TransactionKind::Set(_) | TransactionKind::Get(_) => true,
TransactionKind::Set(_) | TransactionKind::Get(_) | TransactionKind::Toggle => true,
_ => false,
}
}
Expand Down Expand Up @@ -246,6 +257,24 @@ impl InputPin for Mock {
}
}

/// Single digital output pin that can be toggled between high and low states
impl ToggleableOutputPin for Mock {
/// Error type
type Error = MockError;

/// 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(()),
}
}
}

impl PwmPin for Mock {
type Duty = PwmDuty;

Expand Down Expand Up @@ -351,6 +380,23 @@ mod test {
pin.done();
}

#[test]
fn test_toggleable_output_pin() {
let expectations = [
Transaction::new(Toggle),
Transaction::toggle(),
Transaction::new(Toggle).with_error(MockError::Io(ErrorKind::NotConnected)),
];
let mut pin = Mock::new(&expectations);

pin.toggle().unwrap();
pin.toggle().unwrap();

pin.toggle().expect_err("expected error return");

pin.done();
}

#[test]
fn test_pwm_pin() {
let expected_duty = 10_000;
Expand Down
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 7ea451b

Please sign in to comment.