Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check if actual calls exceed the expected number on mock object drop #472

Merged
merged 8 commits into from
Mar 26, 2023
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Static methods' expectations will now be cleared during a panic.
([#443](https://github.com/asomers/mockall/pull/443))

- The `checkpoint` method now works correctly even after a panic due to too many
method calls.
([#472](https://github.com/asomers/mockall/pull/472))

- Methods with unknown size type bounds can now be mocked.
([#421](https://github.com/asomers/mockall/pull/421))

Expand Down
29 changes: 25 additions & 4 deletions mockall/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1554,6 +1554,14 @@ impl From<RangeToInclusive<usize>> for TimesRange {
}
}

#[derive(PartialEq)]
#[doc(hidden)]
pub enum ExpectedCalls {
Satisfied,
TooMany,
TooFew,
}

#[derive(Debug, Default)]
#[doc(hidden)]
pub struct Times{
Expand Down Expand Up @@ -1602,10 +1610,23 @@ impl Times {
(self.range.0.end - self.range.0.start) == 1
}

/// Has this expectation already been called the minimum required number of
/// times?
pub fn is_satisfied(&self) -> bool {
self.count.load(Ordering::Relaxed) >= self.range.0.start
/// Has this expectation already been called the expected number of times?
/// If not, was it too many or too few?
pub fn is_satisfied(&self) -> ExpectedCalls {
let satisfied_lower_bound = self.count.load(Ordering::Relaxed) >= self.range.0.start;
let satisfied_upper_bound = self.count.load(Ordering::Relaxed) < self.range.0.end;
if satisfied_lower_bound && satisfied_upper_bound {
ExpectedCalls::Satisfied
} else if satisfied_lower_bound {
ExpectedCalls::TooMany
} else {
ExpectedCalls::TooFew
}
}

/// The maximum number of times that this expectation must be called
pub fn maximum(&self) -> usize {
self.range.0.end - 1
}

/// The minimum number of times that this expectation must be called
Expand Down
15 changes: 15 additions & 0 deletions mockall/tests/mock_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ mock!{
}

mod checkpoint {
use std::panic;
use super::*;

#[test]
Expand Down Expand Up @@ -52,6 +53,20 @@ mod checkpoint {
panic!("Shouldn't get here!");
}

#[test]
#[should_panic(expected =
"MockFoo::foo: Expectation(<anything>) called 1 time(s) which is more than expected 0")]
fn too_many_calls() {
let mut mock = MockFoo::default();
mock.expect_foo()
.returning(|_| 42)
.times(0);
let _ = panic::catch_unwind(|| {
mock.foo(0);
});
mock.checkpoint();
panic!("Shouldn't get here!");
}

#[test]
fn ok() {
Expand Down
31 changes: 21 additions & 10 deletions mockall_derive/src/mock_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,9 +416,9 @@ pub(crate) struct MockFunction {
/// Types used for Predicates. Will be almost the same as args, but every
/// type will be a non-reference type.
predty: Vec<Type>,
/// Does the function return a non-'static reference?
/// Does the function return a non-'static reference?
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do not mix formatting changes and content changes in the same commit.

return_ref: bool,
/// Does the function return a mutable reference?
/// Does the function return a mutable reference?
return_refmut: bool,
/// References to every type in `predty`.
refpredty: Vec<Type>,
Expand Down Expand Up @@ -913,7 +913,7 @@ impl<'a> ToTokens for Common<'a> {
m);
});
self.verify_sequence(desc);
if self.times.is_satisfied() {
if ::mockall::ExpectedCalls::TooFew != self.times.is_satisfied() {
self.satisfy_sequence()
}
}
Expand Down Expand Up @@ -986,15 +986,26 @@ impl<'a> ToTokens for Common<'a> {

impl #ig Drop for Common #tg #wc {
fn drop(&mut self) {
if !::std::thread::panicking() && !self.times.is_satisfied()
{
if !::std::thread::panicking() {
let desc = std::format!(
"{}", self.matcher.lock().unwrap());
panic!("{}: Expectation({}) called {} time(s) which is fewer than expected {}",
#funcname,
desc,
self.times.count(),
self.times.minimum());
match self.times.is_satisfied() {
::mockall::ExpectedCalls::TooFew => {
panic!("{}: Expectation({}) called {} time(s) which is fewer than expected {}",
#funcname,
desc,
self.times.count(),
self.times.minimum());
},
::mockall::ExpectedCalls::TooMany => {
panic!("{}: Expectation({}) called {} time(s) which is more than expected {}",
#funcname,
desc,
self.times.count(),
self.times.maximum());
},
_ => ()
}
}
}
}
Expand Down