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

The times method now accepts range arguments #14

Merged
merged 1 commit into from
Aug 5, 2019
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
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased] - ReleaseDate
### Added
### Changed

- The `times` method now accepts ranges as arguments. `types_any` and
`times_range` are deprecated.
([#14](https://github.com/asomers/mockall/pull/14))

### Fixed
### Removed

## [0.2.1] - 3 August 2019

### Fixed
Expand Down
98 changes: 78 additions & 20 deletions mockall/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,11 +292,8 @@
//! ```
//!
//! See also
//! [`never`](https://docs.rs/mockall_examples/latest/mockall_examples/__mock_Foo_Foo/foo/struct.Expectation.html#method.never),
//! [`times`](https://docs.rs/mockall_examples/latest/mockall_examples/__mock_Foo_Foo/foo/struct.Expectation.html#method.times),
//! [`times_any`](https://docs.rs/mockall_examples/latest/mockall_examples/__mock_Foo_Foo/foo/struct.Expectation.html#method.times_any),
//! and
//! [`times_range`](https://docs.rs/mockall_examples/latest/mockall_examples/__mock_Foo_Foo/foo/struct.Expectation.html#method.times_range).
//! [`never`](https://docs.rs/mockall_examples/latest/mockall_examples/__mock_Foo_Foo/foo/struct.Expectation.html#method.never) and
//! [`times`](https://docs.rs/mockall_examples/latest/mockall_examples/__mock_Foo_Foo/foo/struct.Expectation.html#method.times).
//!
//! ## Sequences
//!
Expand Down Expand Up @@ -1020,7 +1017,8 @@ use downcast::*;
use std::{
any,
marker::PhantomData,
ops::Range,
ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo,
RangeToInclusive},
sync::{
Arc,
atomic::{AtomicUsize, Ordering}
Expand Down Expand Up @@ -1073,80 +1071,140 @@ pub struct DefaultReturner<O: 'static>(PhantomData<O>);
}
}

// Though it's not entirely correct, we treat usize::max_value() as
// approximately infinity.
#[derive(Debug)]
#[doc(hidden)]
pub struct TimesRange(Range<usize>);

impl Default for TimesRange {
fn default() -> TimesRange {
TimesRange(0..usize::max_value())
}
}

impl From<usize> for TimesRange {
fn from(n: usize) -> TimesRange {
TimesRange(n..(n+1))
}
}

impl From<Range<usize>> for TimesRange {
fn from(r: Range<usize>) -> TimesRange {
assert!(r.end > r.start, "Backwards range");
TimesRange(r)
}
}

impl From<RangeFrom<usize>> for TimesRange {
fn from(r: RangeFrom<usize>) -> TimesRange {
TimesRange(r.start..usize::max_value())
}
}

impl From<RangeFull> for TimesRange {
fn from(_: RangeFull) -> TimesRange {
TimesRange(0..usize::max_value())
}
}

impl From<RangeInclusive<usize>> for TimesRange {
fn from(r: RangeInclusive<usize>) -> TimesRange {
assert!(r.end() >= r.start(), "Backwards range");
TimesRange(*r.start()..*r.end() + 1)
}
}

impl From<RangeTo<usize>> for TimesRange {
fn from(r: RangeTo<usize>) -> TimesRange {
TimesRange(0..r.end)
}
}

impl From<RangeToInclusive<usize>> for TimesRange {
fn from(r: RangeToInclusive<usize>) -> TimesRange {
TimesRange(0..r.end + 1)
}
}

#[derive(Debug)]
#[doc(hidden)]
pub struct Times{
/// How many times has the expectation already been called?
count: AtomicUsize,
range: Range<usize>
range: TimesRange
}

impl Times {
pub fn call(&self) {
let count = self.count.fetch_add(1, Ordering::Relaxed) + 1;
if count >= self.range.end {
if self.range.end == 1 {
if count >= self.range.0.end {
if self.range.0.end == 1 {
panic!("Expectation should not have been called");
} else {
let lim = self.range.end - 1;
let lim = self.range.0.end - 1;
panic!("Expectation called more than {} times", lim);
}
}
}

pub fn any(&mut self) {
self.range = 0..usize::max_value();
self.range.0 = 0..usize::max_value();
}

/// Has this expectation already been called the maximum allowed number of
/// times?
pub fn is_done(&self) -> bool {
self.count.load(Ordering::Relaxed) >= self.range.end - 1
self.count.load(Ordering::Relaxed) >= self.range.0.end - 1
}

/// Is it required that this expectation be called an exact number of times,
/// or may it be satisfied by a range of call counts?
pub fn is_exact(&self) -> bool {
(self.range.end - self.range.start) == 1
(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.start
self.count.load(Ordering::Relaxed) >= self.range.0.start
}

// https://github.com/rust-lang/rust-clippy/issues/3307
#[allow(clippy::range_plus_one)]
pub fn n(&mut self, n: usize) {
self.range = n..(n+1);
self.range.0 = n..(n+1);
}

pub fn never(&mut self) {
self.range = 0..1;
self.range.0 = 0..1;
}

pub fn range(&mut self, range: Range<usize>) {
assert!(range.end > range.start, "Backwards range");
self.range = range;
self.range.0 = range;
}

pub fn times<T: Into<TimesRange>>(&mut self, t: T) {
self.range = t.into();
}
}

impl Default for Times {
fn default() -> Self {
// By default, allow any number of calls
let count = AtomicUsize::default();
let range = 0..usize::max_value();
let range = TimesRange::default();
Times{count, range}
}
}

impl Drop for Times {
fn drop(&mut self) {
let count = self.count.load(Ordering::Relaxed);
if !thread::panicking() && (count < self.range.start) {
panic!("Expectation called fewer than {} times", self.range.start);
if !thread::panicking() && (count < self.range.0.start) {
panic!("Expectation called fewer than {} times",
self.range.0.start);
}
}
}
Expand Down
21 changes: 8 additions & 13 deletions mockall/tests/mock_reference_arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,39 +83,34 @@ mod times {
// Verify that we panic quickly and don't reach code below this point.
panic!("Shouldn't get here!");
}
}

mod times_range {
use super::*;
const X: u32 = 42;

#[test]
fn ok() {
fn range_ok() {
let mut mock = MockFoo::new();
mock.expect_foo()
.returning(|_| 0)
.times_range(2..4);
.times(2..4);
mock.foo(&X);
mock.foo(&X);
}

#[test]
#[should_panic(expected = "Expectation called fewer than 2 times")]
fn too_few() {
fn range_too_few() {
let mut mock = MockFoo::new();
mock.expect_foo()
.returning(|_| 0)
.times_range(2..4);
.times(2..4);
mock.foo(&X);
}

#[test]
#[should_panic(expected = "Expectation called more than 3 times")]
fn too_many() {
fn range_too_many() {
let mut mock = MockFoo::new();
mock.expect_foo()
.returning(|_| 0)
.times_range(2..4);
.times(2..4);
mock.foo(&X);
mock.foo(&X);
mock.foo(&X);
Expand All @@ -126,13 +121,13 @@ mod times_range {
}

#[test]
fn times_any() {
fn times_full() {
const X: u32 = 42;
let mut mock = MockFoo::new();
mock.expect_foo()
.returning(|_| 0)
.times(1)
.times_any();
.times(..);
mock.foo(&X);
mock.foo(&X);
}
2 changes: 1 addition & 1 deletion mockall/tests/mock_return_reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ mod sequence {
let mut seq = Sequence::new();
let mut mock = MockFoo::new();
mock.expect_foo()
.times_range(1..3)
.times(1..3)
.in_sequence(&mut seq);
mock.foo();
}
Expand Down
Loading