Skip to content

Commit

Permalink
Better support for non-Send types:
Browse files Browse the repository at this point in the history
* Added `return_const_st` for returning non-`Send` constants, similar to
  `returning_st`.

* Added `return_once_st` for static methods.  It was already available for
  non-static methods.

Fixes #80
  • Loading branch information
asomers committed Aug 23, 2020
1 parent c75e659 commit 833fa78
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 77 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ This project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased] - ReleaseDate
### Added
- Better support for non-Send types:
* Added `return_const_st` for returning non-`Send` constants, similar to
`returning_st`.
* Added `return_once_st` for static methods. It was already available for
non-static methods.
([#178](https://github.com/asomers/mockall/pull/178))

- Support mocking methods with arbitrary receivers like `self: Box<Self`
([#176](https://github.com/asomers/mockall/pull/176))

Expand Down
3 changes: 2 additions & 1 deletion mockall/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@
//!
//! Mock objects are always `Send`. If you need to use a return type that
//! isn't, you can use the
//! [`returning_st`](https://docs.rs/mockall_examples/latest/mockall_examples/__mock_Foo_Foo/foo/struct.Expectation.html#method.returning_st)
//! [`return_const_st`](https://docs.rs/mockall_examples/latest/mockall_examples/__mock_Foo_Foo/foo/struct.Expectation.html#method.return_const_st),
//! [`returning_st`](https://docs.rs/mockall_examples/latest/mockall_examples/__mock_Foo_Foo/foo/struct.Expectation.html#method.returning_st),
//! or
//! [`return_once_st`](https://docs.rs/mockall_examples/latest/mockall_examples/__mock_Foo_Foo/foo/struct.Expectation.html#method.return_once_st)
//! methods. If you need to match arguments that are not `Send`, you can use the
Expand Down
23 changes: 0 additions & 23 deletions mockall/tests/automock_neither_send_nor_clone.rs

This file was deleted.

170 changes: 121 additions & 49 deletions mockall/tests/automock_nonsend.rs
Original file line number Diff line number Diff line change
@@ -1,66 +1,138 @@
// vim: tw=80
//! A method may have non-Send arguments and/or return values
//! A method may have non-Send arguments and/or return values.
#![deny(warnings)]

use mockall::*;
// Rc is not Send
use std::rc::Rc;
use std::sync::Mutex;

#[automock]
trait Foo {
// Rc is not Send
fn foo(&self, x: Rc<u32>) -> Rc<u32>;
// Neither Send nor Clone
pub struct NonSend(Rc<u32>);

// Rc is not Send
fn bar(x: Rc<u32>) -> Rc<u32>;
}
mod normal_method {
use super::*;

#[automock]
trait Foo {
fn foo(&self, x: Rc<u32>);
fn bar(&self) -> Rc<u32>;
fn baz(&self) -> NonSend;
}

#[test]
fn return_once_st() {
let mut mock = MockFoo::new();
let r = NonSend(Rc::new(42u32));
mock.expect_baz()
.return_once_st(move || r);
assert_eq!(42, *mock.baz().0);
}

lazy_static! {
static ref BAR_MTX: Mutex<()> = Mutex::new(());
#[test]
fn return_const_st() {
let mut mock = MockFoo::new();
mock.expect_bar()
.return_const_st(Rc::new(43u32));
assert_eq!(43, *mock.bar());
}

#[test]
fn returning_st() {
let mut mock = MockFoo::new();
mock.expect_bar()
.returning_st(|| Rc::new(43u32));
assert_eq!(43, *mock.bar());
}

#[test]
fn withf_st() {
let mut mock = MockFoo::new();
mock.expect_foo()
.withf_st(|x| **x == 42)
.return_const(());
mock.foo(Rc::new(42));
}
}

#[test]
fn returning_st() {
let mut mock = MockFoo::new();
let y = Rc::new(43u32);
mock.expect_foo()
.returning_st(move |_| y.clone());
let x = Rc::new(42u32);
assert_eq!(43, *mock.foo(x).as_ref());
mod ref_method {
// ref methods don't have return_once_st because they don't return owned
// values, and they don't have returning_st because they don't have
// returning. Instead, they only have return_const, which does not require
// Send.
// fn foo(&self) -> &Rc<u32>;
}

#[test]
fn returning_st_static() {
let _m = BAR_MTX.lock().unwrap();
mod refmut_method {
use super::*;

let mock = MockFoo::bar_context();
let y = Rc::new(43u32);
mock.expect()
.returning_st(move |_| y.clone());
let x = Rc::new(42u32);
assert_eq!(43, *MockFoo::bar(x).as_ref());
}
#[automock]
trait Foo {
fn foo(&mut self, x: Rc<u32>);
fn bar(&mut self) -> &mut Rc<u32>;
}

#[test]
fn withf_st() {
let mut mock = MockFoo::new();
let x = Rc::new(42u32);
let argument = x.clone();
mock.expect_foo()
.withf_st(move |x| *x == argument)
.returning_st(|_| Rc::new(43u32));
assert_eq!(43, *mock.foo(x).as_ref());
// refmut methods don't have return_once_st because they don't return owned
// values.
#[test]
fn returning_st() {
let mut mock = MockFoo::new();
mock.expect_bar()
.returning_st(|| Rc::new(43u32));
assert_eq!(43, **mock.bar());
}

#[test]
fn withf_st() {
let mut mock = MockFoo::new();
mock.expect_foo()
.withf_st(|x| **x == 42)
.return_const(());
mock.foo(Rc::new(42));
}
}

#[test]
fn withf_st_static() {
let _m = BAR_MTX.lock().unwrap();

let mock = MockFoo::bar_context();
let x = Rc::new(42u32);
let argument = x.clone();
mock.expect()
.withf_st(move |x| *x == argument)
.returning_st(|_| Rc::new(43u32));
assert_eq!(43, *MockFoo::bar(x).as_ref());
pub mod static_method {
#![allow(unused)] // https://github.com/asomers/mockall/issues/177
use super::*;

#[automock]
trait Foo {
fn foo(x: Rc<u32>);
fn bar() -> Rc<u32>;
fn baz() -> NonSend;
}

#[test]
fn return_once_st() {
let ctx = MockFoo::baz_context();
let r = NonSend(Rc::new(42u32));
ctx.expect()
.return_once_st(move || r);
assert_eq!(42, *MockFoo::baz().0);
}

#[test]
fn returning_st() {
let ctx = MockFoo::bar_context();
ctx.expect()
.returning_st(|| Rc::new(42));
assert_eq!(42, *MockFoo::bar());
}

#[test]
fn return_const_st() {
let ctx = MockFoo::bar_context();
ctx.expect()
.return_const_st(Rc::new(42));
assert_eq!(42, *MockFoo::bar());
}

#[test]
fn withf_st() {
let ctx = MockFoo::foo_context();
ctx.expect()
.withf_st(|x| **x == 42)
.return_const(());
MockFoo::foo(Rc::new(42));
}
}
54 changes: 50 additions & 4 deletions mockall_derive/src/mock_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,16 @@ impl<'a> ToTokens for ExpectationGuardCommonMethods<'a> {
#expectations.0[self.i].return_const(__mockall_c)
}

/// Just like
/// [`Expectation::return_const_st`](struct.Expectation.html#method.return_const_st)
#v fn return_const_st<MockallOutput>
(&mut self, __mockall_c: MockallOutput)
-> &mut Expectation #tg
where MockallOutput: Clone + Into<#output> + 'static
{
#expectations.0[self.i].return_const_st(__mockall_c)
}

/// Just like
/// [`Expectation::returning`](struct.Expectation.html#method.returning)
#v fn returning<MockallF>(&mut self, __mockall_f: MockallF)
Expand All @@ -1144,6 +1154,17 @@ impl<'a> ToTokens for ExpectationGuardCommonMethods<'a> {
#expectations.0[self.i].return_once(__mockall_f)
}

/// Just like
/// [`Expectation::return_once_st`](struct.Expectation.html#method.return_once_st)
#v fn return_once_st<MockallF>(&mut self, __mockall_f: MockallF)
-> &mut Expectation #tg
where MockallF: #hrtb FnOnce(#(#argty, )*)
-> #output + 'static
{
#expectations.0[self.i].return_once_st(__mockall_f)
}


/// Just like
/// [`Expectation::returning_st`](struct.Expectation.html#method.returning_st)
#v fn returning_st<MockallF>(&mut self, __mockall_f: MockallF)
Expand Down Expand Up @@ -1961,20 +1982,45 @@ impl<'a> ToTokens for StaticExpectation<'a> {
/// Return a constant value from the `Expectation`
///
/// The output type must be `Clone`. The compiler can't always
/// infer the proper type to use with this method; you will usually
/// need to specify it explicitly. i.e. `return_const(42i32)`
/// instead of `return_const(42)`.
/// infer the proper type to use with this method; you will
/// usually need to specify it explicitly. i.e.
/// `return_const(42i32)` instead of `return_const(42)`.
// We must use Into<#output> instead of #output because where
// clauses don't accept equality constraints.
// https://github.com/rust-lang/rust/issues/20041
#[allow(unused_variables)]
#v fn return_const<MockallOutput>(&mut self, __mockall_c: MockallOutput)
#v fn return_const<MockallOutput>(&mut self,
__mockall_c: MockallOutput)
-> &mut Self
where MockallOutput: Clone + Into<#output> + Send + 'static
{
self.returning(move |#(#argnames, )*| __mockall_c.clone().into())
}

/// Single-threaded version of
/// [`return_const`](#method.return_const). This is useful for
/// return types that are not `Send`.
///
/// The output type must be `Clone`. The compiler can't always
/// infer the proper type to use with this method; you will
/// usually need to specify it explicitly. i.e.
/// `return_const(42i32)` instead of `return_const(42)`.
///
/// It is a runtime error to call the mock method from a
/// different thread than the one that originally called this
/// method.
// We must use Into<#output> instead of #output because where
// clauses don't accept equality constraints.
// https://github.com/rust-lang/rust/issues/20041
#[allow(unused_variables)]
#v fn return_const_st<MockallOutput>(&mut self,
__mockall_c: MockallOutput)
-> &mut Self
where MockallOutput: Clone + Into<#output> + 'static
{
self.returning_st(move |#(#argnames, )*| __mockall_c.clone().into())
}

/// Supply an `FnOnce` closure that will provide the return
/// value for this Expectation. This is useful for return types
/// that aren't `Clone`. It will be an error to call this
Expand Down

0 comments on commit 833fa78

Please sign in to comment.