Skip to content

Commit

Permalink
Merge pull request #63 from dbrgn/expect-call-done
Browse files Browse the repository at this point in the history
Generic mock: When calling expect, call done internally
  • Loading branch information
dbrgn authored Nov 23, 2023
2 parents 0c2bbf5 + cc7aa53 commit d58c836
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 77 deletions.
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## Unreleased

### Added

- Implement mock for `embedded_hal::pwm::SetDutyCycle`

### Fixed

### Changed

- Renamed `.expect(...)` method to `.update_expectations(...)` to avoid
confusion with the expect method in `Option` and `Result` (#63)
- When updating expectations on a mock by calling `.expect(...)` /
`.update_expectations(...)` on it, assert that previous expectations have
been consumed (#63)


## 0.10.0-rc.1 - 2023-11-01

Expand Down Expand Up @@ -86,7 +93,7 @@ contributions!

### Fixed

- Fix link to digital pin docs (#28)
- Fix link to digital pin docs (#28)


## 0.7.0 - 2019-05-22
Expand Down
55 changes: 43 additions & 12 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::{

/// Generic mock implementation.
///
/// ⚠️ **Do not use this directly as end user! This is only a building block
/// ⚠️ **Do not create this directly as end user! This is only a building block
/// for creating mocks.**
///
/// This type supports the specification and evaluation of expectations to
Expand Down Expand Up @@ -42,31 +42,60 @@ where
done_called: Arc::new(Mutex::new(DoneCallDetector::new())),
};

g.expect(expected);
g.update_expectations(expected);

g
}

/// Set expectations on the interface
/// Update expectations on the interface
///
/// This is a list of transactions to be executed in order. Note that
/// setting this will overwrite any existing expectations.
pub fn expect<E>(&mut self, expected: E)
/// When this method is called, first it is ensured that existing
/// expectations are all consumed by calling [`done()`](#method.done)
/// internally (if not called already). Afterwards, the new expectations
/// are set.
pub fn update_expectations<E>(&mut self, expected: E)
where
E: IntoIterator<Item = &'a T>,
{
let v: VecDeque<T> = expected.into_iter().cloned().collect();
// Ensure that existing expectations are consumed
self.done_impl(false);

// Collect new expectations into vector
let new_expectations: VecDeque<T> = expected.into_iter().cloned().collect();

// Lock internal state
let mut expected = self.expected.lock().unwrap();
let mut done_called = self.done_called.lock().unwrap();
*expected = v;

// Update expectations
*expected = new_expectations;

// Reset done call detector
done_called.reset();
}

/// Deprecated alias of `update_expectations`.
#[deprecated(
since = "0.10.0",
note = "The method 'expect' was renamed to 'update_expectations'"
)]
pub fn expect<E>(&mut self, expected: E)
where
E: IntoIterator<Item = &'a T>,
{
self.update_expectations(expected)
}

/// Assert that all expectations on a given mock have been consumed.
pub fn done(&mut self) {
self.done_called.lock().unwrap().mark_as_called();
self.done_impl(true);
}

fn done_impl(&mut self, panic_if_already_done: bool) {
self.done_called
.lock()
.unwrap()
.mark_as_called(panic_if_already_done);
let e = self.expected.lock().unwrap();
assert!(e.is_empty(), "Not all expectations consumed");
}
Expand Down Expand Up @@ -97,9 +126,11 @@ impl DoneCallDetector {
/// Mark the `.done()` method as called.
///
/// Note: When calling this method twice, an assertion failure will be
/// triggered.
pub(crate) fn mark_as_called(&mut self) {
assert!(!self.called, "The `.done()` method was called twice!");
/// triggered if `panic_if_already_done` is true.
pub(crate) fn mark_as_called(&mut self, panic_if_already_done: bool) {
if panic_if_already_done {
assert!(!self.called, "The `.done()` method was called twice!");
}
self.called = true;
}

Expand Down
2 changes: 1 addition & 1 deletion src/eh0/pin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
//! pin.done();
//!
//! // Update expectations
//! pin.expect(&[]);
//! pin.update_expectations(&[]);
//! // ...
//! pin.done();
//!
Expand Down
76 changes: 45 additions & 31 deletions src/eh0/serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,54 +298,72 @@ where
/// if desired.
#[derive(Clone)]
pub struct Mock<Word> {
/// The expected operations upon the mock
///
/// It's in an arc to maintain shared state, and in a mutex
/// to make it thread safe. It's then wrapped in an `Option`
/// so that we can take it in the call to `done()`.
expected_modes: Arc<Mutex<Option<VecDeque<Mode<Word>>>>>,
expected_modes: Arc<Mutex<VecDeque<Mode<Word>>>>,
done_called: Arc<Mutex<DoneCallDetector>>,
}

impl<Word: Clone> Mock<Word> {
/// Create a serial mock that will expect the provided transactions
pub fn new(transactions: &[Transaction<Word>]) -> Self {
let mut ser = Mock {
expected_modes: Arc::new(Mutex::new(None)),
expected_modes: Arc::new(Mutex::new(VecDeque::new())),
done_called: Arc::new(Mutex::new(DoneCallDetector::new())),
};
ser.expect(transactions);
ser.update_expectations(transactions);
ser
}

/// Set expectations on the interface
/// Update expectations on the interface
///
/// This is a list of transactions to be executed in order.
/// Note that setting this will overwrite any existing expectations
pub fn expect(&mut self, transactions: &[Transaction<Word>]) {
/// When this method is called, first it is ensured that existing
/// expectations are all consumed by calling [`done()`](#method.done)
/// internally (if not called already). Afterwards, the new expectations
/// are set.
pub fn update_expectations(&mut self, transactions: &[Transaction<Word>]) {
// Ensure that existing expectations are consumed
self.done_impl(false);

// Lock internal state
let mut expected = self.expected_modes.lock().unwrap();
let mut done_called = self.done_called.lock().unwrap();
*expected = Some(
transactions
.iter()
.fold(VecDeque::new(), |mut modes, transaction| {
modes.extend(transaction.mode.clone());
modes
}),
);

// Update expectations
*expected = transactions
.iter()
.fold(VecDeque::new(), |mut modes, transaction| {
modes.extend(transaction.mode.clone());
modes
});

// Reset done call detector
done_called.reset();
}

/// Deprecated alias of `update_expectations`.
#[deprecated(
since = "0.10.0",
note = "The method 'expect' was renamed to 'update_expectations'"
)]
pub fn expect(&mut self, transactions: &[Transaction<Word>]) {
self.update_expectations(transactions)
}

/// Asserts that all expectations up to this point were satisfied.
/// Panics if there are unsatisfied expectations.
pub fn done(&mut self) {
self.done_called.lock().unwrap().mark_as_called();
self.done_impl(true);
}

let mut lock = self
fn done_impl(&mut self, panic_if_already_done: bool) {
self.done_called
.lock()
.unwrap()
.mark_as_called(panic_if_already_done);

let modes = self
.expected_modes
.lock()
.expect("unable to lock serial mock in call to done");
let modes = lock.take().expect("attempted to take None from Optional");
assert!(
modes.is_empty(),
"serial mock has unsatisfied expectations after call to done"
Expand All @@ -354,14 +372,10 @@ impl<Word: Clone> Mock<Word> {

/// Pop the next transaction out of the queue
fn pop(&mut self) -> Option<Mode<Word>> {
let mut lock = self
.expected_modes
self.expected_modes
.lock()
.expect("unable to lock serial mock in call to pop");
let queue = lock
.as_mut()
.expect("attempt to get queue reference from a None");
queue.pop_front()
.expect("unable to lock serial mock in call to pop")
.pop_front()
}
}

Expand Down Expand Up @@ -563,7 +577,7 @@ mod test {
let r = ser.read().expect("failed to read");
assert_eq!(r, 0x54);
ser.done();
ser.expect(&ts);
ser.update_expectations(&ts);
ser.done();
}

Expand Down
2 changes: 1 addition & 1 deletion src/eh1/pin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
//! pin.done();
//!
//! // Update expectations
//! pin.expect(&[]);
//! pin.update_expectations(&[]);
//! // ...
//! pin.done();
//!
Expand Down
76 changes: 45 additions & 31 deletions src/eh1/serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,54 +258,72 @@ where
/// if desired.
#[derive(Clone)]
pub struct Mock<Word> {
/// The expected operations upon the mock
///
/// It's in an arc to maintain shared state, and in a mutex
/// to make it thread safe. It's then wrapped in an `Option`
/// so that we can take it in the call to `done()`.
expected_modes: Arc<Mutex<Option<VecDeque<Mode<Word>>>>>,
expected_modes: Arc<Mutex<VecDeque<Mode<Word>>>>,
done_called: Arc<Mutex<DoneCallDetector>>,
}

impl<Word: Clone> Mock<Word> {
/// Create a serial mock that will expect the provided transactions
pub fn new(transactions: &[Transaction<Word>]) -> Self {
let mut ser = Mock {
expected_modes: Arc::new(Mutex::new(None)),
expected_modes: Arc::new(Mutex::new(VecDeque::new())),
done_called: Arc::new(Mutex::new(DoneCallDetector::new())),
};
ser.expect(transactions);
ser.update_expectations(transactions);
ser
}

/// Set expectations on the interface
/// Update expectations on the interface
///
/// This is a list of transactions to be executed in order.
/// Note that setting this will overwrite any existing expectations
pub fn expect(&mut self, transactions: &[Transaction<Word>]) {
/// When this method is called, first it is ensured that existing
/// expectations are all consumed by calling [`done()`](#method.done)
/// internally (if not called already). Afterwards, the new expectations
/// are set.
pub fn update_expectations(&mut self, transactions: &[Transaction<Word>]) {
// Ensure that existing expectations are consumed
self.done_impl(false);

// Lock internal state
let mut expected = self.expected_modes.lock().unwrap();
let mut done_called = self.done_called.lock().unwrap();
*expected = Some(
transactions
.iter()
.fold(VecDeque::new(), |mut modes, transaction| {
modes.extend(transaction.mode.clone());
modes
}),
);

// Update expectations
*expected = transactions
.iter()
.fold(VecDeque::new(), |mut modes, transaction| {
modes.extend(transaction.mode.clone());
modes
});

// Reset done call detector
done_called.reset();
}

/// Deprecated alias of `update_expectations`.
#[deprecated(
since = "0.10.0",
note = "The method 'expect' was renamed to 'update_expectations'"
)]
pub fn expect<E>(&mut self, transactions: &[Transaction<Word>]) {
self.update_expectations(transactions)
}

/// Asserts that all expectations up to this point were satisfied.
/// Panics if there are unsatisfied expectations.
pub fn done(&mut self) {
self.done_called.lock().unwrap().mark_as_called();
self.done_impl(true);
}

let mut lock = self
fn done_impl(&mut self, panic_if_already_done: bool) {
self.done_called
.lock()
.unwrap()
.mark_as_called(panic_if_already_done);

let modes = self
.expected_modes
.lock()
.expect("unable to lock serial mock in call to done");
let modes = lock.take().expect("attempted to take None from Optional");
assert!(
modes.is_empty(),
"serial mock has unsatisfied expectations after call to done"
Expand All @@ -314,14 +332,10 @@ impl<Word: Clone> Mock<Word> {

/// Pop the next transaction out of the queue
fn pop(&mut self) -> Option<Mode<Word>> {
let mut lock = self
.expected_modes
self.expected_modes
.lock()
.expect("unable to lock serial mock in call to pop");
let queue = lock
.as_mut()
.expect("attempt to get queue reference from a None");
queue.pop_front()
.expect("unable to lock serial mock in call to pop")
.pop_front()
}
}

Expand Down Expand Up @@ -469,7 +483,7 @@ mod test {
let r = ser.read().expect("failed to read");
assert_eq!(r, 0x54);
ser.done();
ser.expect(&ts);
ser.update_expectations(&ts);
ser.done();
}

Expand Down

0 comments on commit d58c836

Please sign in to comment.