From 9024ffbee6e592cbd6abab24b6173e983fadaeb4 Mon Sep 17 00:00:00 2001 From: Callum Dunster Date: Thu, 11 Jan 2024 11:17:06 +0100 Subject: [PATCH 1/2] Add ToggleableOutputPin support for eh0::digital::Mock --- src/eh0/digital.rs | 54 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/src/eh0/digital.rs b/src/eh0/digital.rs index 0b0962b..34d7ba0 100644 --- a/src/eh0/digital.rs +++ b/src/eh0/digital.rs @@ -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, @@ -21,6 +22,7 @@ //! PinTransaction::get(PinState::High), //! PinTransaction::set(PinState::Low), //! PinTransaction::set(PinState::High).with_error(err.clone()), +//! PinTransaction::toggle(), //! ]; //! //! // Create pin @@ -33,6 +35,8 @@ //! pin.set_low().unwrap(); //! pin.set_high().expect_err("expected error return"); //! +//! pin.toggle().unwrap(); +//! //! pin.done(); //! //! // Update expectations @@ -43,7 +47,7 @@ use eh0 as embedded_hal; use embedded_hal::{ - digital::v2::{InputPin, OutputPin}, + digital::v2::{InputPin, OutputPin, ToggleableOutputPin}, PwmPin, }; @@ -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)) @@ -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`] @@ -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, } } @@ -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; @@ -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; From 2654fc4fb5c2500ad4d1b613a91ae693a8e6cea3 Mon Sep 17 00:00:00 2001 From: Callum Dunster Date: Thu, 11 Jan 2024 11:17:26 +0100 Subject: [PATCH 2/2] Add StatefulOutputPin support for eh1::digital::Mock --- src/eh1/digital.rs | 116 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 111 insertions(+), 5 deletions(-) diff --git a/src/eh1/digital.rs b/src/eh1/digital.rs index c7f4bec..d76f7ef 100644 --- a/src/eh1/digital.rs +++ b/src/eh1/digital.rs @@ -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, @@ -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 @@ -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 @@ -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}; @@ -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 { @@ -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), @@ -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 { + 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 { + 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 @@ -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}, *, }; @@ -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() {