From 881e105ea2424dc23b614ab810ed55f302034062 Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Sun, 2 Jul 2023 16:34:42 +0200 Subject: [PATCH] add `SetDutyCycle` mock this is the 1.0 equivalent of the old `PwmPin` trait (the mock for this has been added in #52). note that the test coverage is fully handled by the doc test which is why there's no additional `mod test` in this file. this fixes #73 --- CHANGELOG.md | 2 +- src/lib.rs | 1 + src/pin.rs | 6 +-- src/pwm.rs | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 src/pwm.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fbbd95..6cfb050 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Print a warning to stderr and fail test if a mock is dropped without having calling `.done()` on it, or if `.done()` is called twice (#59, #61) +- Implement mock for `embedded_hal::pwm::SetDutyCycle` ### Fixed @@ -39,7 +40,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Switch to Rust 2021 edition (#55) - Switch from CircleCI to GitHub Actions (#50) - ## 0.8.0 - 2021-08-16 ### Added diff --git a/src/lib.rs b/src/lib.rs index e89d6c9..16b7a92 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,5 +30,6 @@ pub mod common; pub mod delay; pub mod i2c; pub mod pin; +pub mod pwm; pub mod serial; pub mod spi; diff --git a/src/pin.rs b/src/pin.rs index 2c40c75..17c7969 100644 --- a/src/pin.rs +++ b/src/pin.rs @@ -1,7 +1,7 @@ -//! Mock digi:set ignorecase smartcasetal [`InputPin`] and [`OutputPin`] v2 implementations +//! Mock digital [`InputPin`] and [`OutputPin`] implementations //! -//! [`InputPin`]: https://docs.rs/embedded-hal/1.0.0-alpha.6/embedded_hal/digital/trait.InputPin.html -//! [`OutputPin`]: https://docs.rs/embedded-hal/1.0.0-alpha.6/embedded_hal/digital/trait.OutputPin.html +//! [`InputPin`]: https://docs.rs/embedded-hal/1.0.0-alpha.10/embedded_hal/digital/trait.InputPin.html +//! [`OutputPin`]: https://docs.rs/embedded-hal/1.0.0-alpha.10/embedded_hal/digital/trait.OutputPin.html //! //! ``` //! use std::io::ErrorKind; diff --git a/src/pwm.rs b/src/pwm.rs new file mode 100644 index 0000000..6371353 --- /dev/null +++ b/src/pwm.rs @@ -0,0 +1,130 @@ +//! Mock implementations for [`embedded_hal::pwm`]. +//! +//! Usage example: +//! ``` +//! use std::io::ErrorKind; +//! +//! use embedded_hal_mock::MockError; +//! use embedded_hal_mock::pwm::{Transaction as PwmTransaction, Mock as PwmMock}; +//! use embedded_hal::pwm::SetDutyCycle; +//! +//! let err = MockError::Io(ErrorKind::NotConnected); +//! +//! // Configure expectations +//! let expectations = [ +//! PwmTransaction::get_max_duty_cycle(100), +//! PwmTransaction::set_duty_cycle(50), +//! PwmTransaction::set_duty_cycle(101).with_error(err.clone()), +//! ]; +//! +//! // Create pin +//! let mut pwm = PwmMock::new(&expectations); +//! +//! // Run and test +//! pwm.set_duty_cycle_percent(50).unwrap(); +//! pwm.set_duty_cycle(101).expect_err("expected error return"); +//! +//! pwm.done(); +//! +//! // Update expectations +//! pwm.expect(&[]); +//! // ... +//! pwm.done(); +//! ``` + +use crate::common::Generic; +use crate::error::MockError; +use embedded_hal::pwm::{ErrorKind, ErrorType, SetDutyCycle}; + +/// MockPwm transaction +#[derive(PartialEq, Clone, Debug)] +pub struct Transaction { + /// Kind is the transaction kind (and data) expected + kind: TransactionKind, + /// Err is an optional error return for a transaction. + /// This is in addition to kind to allow validation that the transaction kind + /// is correct prior to returning the error. + err: Option, +} + +impl Transaction { + /// Create a new PWM transaction + pub fn new(kind: TransactionKind) -> Transaction { + Transaction { kind, err: None } + } + + /// Create a new [`TransactionKind::GetMaxDutyCycle`] transaction for [`SetDutyCycle::get_max_duty_cycle`]. + pub fn get_max_duty_cycle(duty: u16) -> Transaction { + Transaction::new(TransactionKind::GetMaxDutyCycle(duty)) + } + + /// Create a new [`TransactionKind::SetDutyCycle`] transaction for [`SetDutyCycle::set_duty_cycle`]. + pub fn set_duty_cycle(duty: u16) -> Transaction { + Transaction::new(TransactionKind::SetDutyCycle(duty)) + } + + /// Add an error return to a transaction + /// + /// This is used to mock failure behaviours. + pub fn with_error(mut self, error: MockError) -> Self { + self.err = Some(error); + self + } +} + +/// MockPwm transaction kind +#[derive(PartialEq, Clone, Debug)] +pub enum TransactionKind { + /// [`SetDutyCycle::get_max_duty_cycle`] which will return the defined duty. + GetMaxDutyCycle(u16), + /// [`SetDutyCycle::set_duty_cycle`] with the expected duty. + SetDutyCycle(u16), +} + +/// Mock SetDutyCycle implementation +pub type Mock = Generic; + +impl embedded_hal::pwm::Error for MockError { + fn kind(&self) -> ErrorKind { + ErrorKind::Other + } +} + +impl ErrorType for Mock { + type Error = MockError; +} + +impl SetDutyCycle for Mock { + fn get_max_duty_cycle(&self) -> u16 { + let mut s = self.clone(); + + let Transaction { kind, err } = s + .next() + .expect("no expectation for get_max_duty_cycle call"); + + assert_eq!(err, None, "error not supported by get_max_duty_cycle!"); + + if let TransactionKind::GetMaxDutyCycle(duty) = kind { + duty + } else { + unreachable!(); + } + } + + fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { + let Transaction { kind, err } = + self.next().expect("no expectation for set_duty_cycle call"); + + assert_eq!( + kind, + TransactionKind::SetDutyCycle(duty), + "expected set_duty_cycle" + ); + + if let Some(e) = err { + Err(e) + } else { + Ok(()) + } + } +}