Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ eh0 = ["dep:eh0", "dep:nb"]
eh1 = ["dep:eh1", "dep:embedded-hal-nb"]

embedded-time = ["dep:embedded-time", "dep:void"]
embedded-hal-async = ["dep:embedded-hal-async"]
embedded-hal-async = ["dep:embedded-hal-async","dep:futures"]

default = ["eh1", "embedded-time"]

Expand All @@ -33,12 +33,13 @@ eh0 = { package = "embedded-hal", version = "0.2.7", features = ["unproven"], op
eh1 = { package = "embedded-hal", version = "1.0", optional = true }
embedded-hal-nb = { version = "1.0", optional = true }
embedded-hal-async = { version = "1.0", optional = true }
futures = { version = "0.3.31", default-features = false, optional = true }
embedded-time = { version = "0.12", optional = true }
nb = { version = "1.1", optional = true }
void = { version = "^1.0", optional = true }

[dev-dependencies]
tokio = { version = "1.21.1", features = ["rt", "macros"] }
tokio = { version = "1.21.1", features = ["rt", "macros", "time"] }

[package.metadata.docs.rs]
all-features = true
Expand Down
161 changes: 136 additions & 25 deletions src/eh1/digital.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,28 @@ impl Transaction {
Transaction::new(TransactionKind::WaitForState(state))
}

/// Create a new wait_for_state transaction,
/// which returns a forever-Pending future when called.
/// This simulates waiting on a pin that never changes.
#[cfg(feature = "embedded-hal-async")]
pub fn wait_for_state_forever(state: State) -> Transaction {
Transaction::new(TransactionKind::WaitForStateForever(state))
}

/// Crate a new wait_for_edge transaction
#[cfg(feature = "embedded-hal-async")]
pub fn wait_for_edge(edge: Edge) -> Transaction {
Transaction::new(TransactionKind::WaitForEdge(edge))
}

/// Crate a new wait_for_edge transaction,
/// which returns a forever-Pending future when called.
/// This simulates waiting on a pin that never changes.
#[cfg(feature = "embedded-hal-async")]
pub fn wait_for_edge_forever(edge: Edge) -> Transaction {
Transaction::new(TransactionKind::WaitForEdgeForever(edge))
}

/// Add an error return to a transaction
///
/// This is used to mock failure behaviours.
Expand Down Expand Up @@ -157,9 +173,15 @@ pub enum TransactionKind {
/// Wait for the given pin state
#[cfg(feature = "embedded-hal-async")]
WaitForState(State),
/// Wait for the given pin state, returning Pending to simulate a never-changing pin
#[cfg(feature = "embedded-hal-async")]
WaitForStateForever(State),
/// Wait for the given pin edge
#[cfg(feature = "embedded-hal-async")]
WaitForEdge(Edge),
/// Wait for the given pin edge, returning Pending to simulate a never-changing pin
#[cfg(feature = "embedded-hal-async")]
WaitForEdgeForever(Edge),
}

impl TransactionKind {
Expand Down Expand Up @@ -309,6 +331,9 @@ impl StatefulOutputPin for Mock {
}
}

#[cfg(feature = "embedded-hal-async")]
use futures::future::pending;

#[cfg(feature = "embedded-hal-async")]
impl embedded_hal_async::digital::Wait for Mock {
/// Wait for the pin to go high
Expand All @@ -320,14 +345,18 @@ impl embedded_hal_async::digital::Wait for Mock {
.expect("no expectation for pin::wait_for_high call");

assert!(
matches!(kind, TransactionKind::WaitForState(State::High)),
matches!(
kind,
TransactionKind::WaitForState(State::High)
| TransactionKind::WaitForStateForever(State::High)
),
"got call to wait_for_high"
);

if let Some(e) = err {
Err(e)
} else {
Ok(())
match (kind, err) {
(_, Some(e)) => Err(e),
(TransactionKind::WaitForState(State::High), _) => Ok(()),
_ => pending().await,
}
}

Expand All @@ -339,14 +368,18 @@ impl embedded_hal_async::digital::Wait for Mock {
s.next().expect("no expectation for pin::wait_for_low call");

assert!(
matches!(kind, TransactionKind::WaitForState(State::Low)),
matches!(
kind,
TransactionKind::WaitForState(State::Low)
| TransactionKind::WaitForStateForever(State::Low)
),
"got call to wait_for_low"
);

if let Some(e) = err {
Err(e)
} else {
Ok(())
match (kind, err) {
(_, Some(e)) => Err(e),
(TransactionKind::WaitForState(State::Low), _) => Ok(()),
_ => pending().await,
}
}

Expand All @@ -359,14 +392,18 @@ impl embedded_hal_async::digital::Wait for Mock {
.expect("no expectation for pin::wait_for_rising_edge call");

assert!(
matches!(kind, TransactionKind::WaitForEdge(Edge::Rising)),
matches!(
kind,
TransactionKind::WaitForEdge(Edge::Rising)
| TransactionKind::WaitForEdgeForever(Edge::Rising)
),
"got call to wait_for_rising_edge"
);

if let Some(e) = err {
Err(e)
} else {
Ok(())
match (kind, err) {
(_, Some(e)) => Err(e),
(TransactionKind::WaitForEdge(Edge::Rising), _) => Ok(()),
_ => pending().await,
}
}

Expand All @@ -379,14 +416,18 @@ impl embedded_hal_async::digital::Wait for Mock {
.expect("no expectation for pin::wait_for_falling_edge call");

assert!(
matches!(kind, TransactionKind::WaitForEdge(Edge::Falling)),
matches!(
kind,
TransactionKind::WaitForEdge(Edge::Falling)
| TransactionKind::WaitForEdgeForever(Edge::Falling)
),
"got call to wait_for_falling_edge"
);

if let Some(e) = err {
Err(e)
} else {
Ok(())
match (kind, err) {
(_, Some(e)) => Err(e),
(TransactionKind::WaitForEdge(Edge::Falling), _) => Ok(()),
_ => pending().await,
}
}

Expand All @@ -399,24 +440,32 @@ impl embedded_hal_async::digital::Wait for Mock {
.expect("no expectation for pin::wait_for_any_edge call");

assert!(
matches!(kind, TransactionKind::WaitForEdge(Edge::Any)),
matches!(
kind,
TransactionKind::WaitForEdge(Edge::Any)
| TransactionKind::WaitForEdgeForever(Edge::Any)
),
"got call to wait_for_any_edge"
);

if let Some(e) = err {
Err(e)
} else {
Ok(())
match (kind, err) {
(_, Some(e)) => Err(e),
(TransactionKind::WaitForEdge(Edge::Any), _) => Ok(()),
_ => pending().await,
}
}
}

#[cfg(test)]
mod test {
use std::io::ErrorKind;
#[cfg(feature = "embedded-hal-async")]
use std::time::Duration;

use eh1 as embedded_hal;
use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin};
#[cfg(feature = "embedded-hal-async")]
use tokio::time::timeout;

use super::{
super::error::MockError,
Expand Down Expand Up @@ -528,6 +577,35 @@ mod test {
pin.done();
}

#[tokio::test]
#[cfg(feature = "embedded-hal-async")]
async fn test_can_wait_for_state_forever() {
use embedded_hal_async::digital::Wait;

let expectations = [
Transaction::new(TransactionKind::WaitForStateForever(State::High)),
Transaction::new(TransactionKind::WaitForStateForever(State::Low)),
Transaction::new(TransactionKind::WaitForStateForever(State::High))
.with_error(MockError::Io(ErrorKind::NotConnected)),
];
let mut pin = Mock::new(&expectations);

// we should not return Ok while asking to wait forever!
timeout(Duration::from_millis(10), pin.wait_for_high())
.await
.expect_err("expected wait_for_high timeout");
timeout(Duration::from_millis(10), pin.wait_for_low())
.await
.expect_err("expected wait_for_low timeout");

// errors are still returned, since maybe awaiting on the pin failed for some reason
pin.wait_for_high()
.await
.expect_err("expected error return");

pin.done();
}

#[tokio::test]
#[cfg(feature = "embedded-hal-async")]
async fn test_can_wait_for_edge() {
Expand All @@ -553,6 +631,39 @@ mod test {
pin.done();
}

#[tokio::test]
#[cfg(feature = "embedded-hal-async")]
async fn test_can_wait_for_edge_forever() {
use embedded_hal_async::digital::Wait;

let expectations = [
Transaction::new(TransactionKind::WaitForEdgeForever(Edge::Rising)),
Transaction::new(TransactionKind::WaitForEdgeForever(Edge::Falling)),
Transaction::new(TransactionKind::WaitForEdgeForever(Edge::Any)),
Transaction::new(TransactionKind::WaitForEdgeForever(Edge::Rising))
.with_error(MockError::Io(ErrorKind::NotConnected)),
];
let mut pin = Mock::new(&expectations);

// we should not return Ok while asking to wait forever!
timeout(Duration::from_millis(10), pin.wait_for_rising_edge())
.await
.expect_err("expected wait_for_rising_edge timeout");
timeout(Duration::from_millis(10), pin.wait_for_falling_edge())
.await
.expect_err("expected wait_for_falling_edge timeout");
timeout(Duration::from_millis(10), pin.wait_for_any_edge())
.await
.expect_err("expected wait_for_any_edge timeout");

// errors are still returned, since maybe awaiting on the pin failed for some reason
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")]
Expand Down
Loading