diff --git a/CHANGELOG.md b/CHANGELOG.md index a7fec30c..bba783dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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); - -#[automock] -trait Foo { - fn foo(&self, x: MyType) -> MyType; -} - -#[test] -fn return_once_st() { - let mut mock = MockFoo::new(); - let y = MyType(Rc::new(43u32)); - mock.expect_foo() - .return_once_st(move |_| y); - let x = MyType(Rc::new(42u32)); - assert_eq!(43, *mock.foo(x).0.as_ref()); -} diff --git a/mockall/tests/automock_nonsend.rs b/mockall/tests/automock_nonsend.rs index 9cd98291..79b6db97 100644 --- a/mockall/tests/automock_nonsend.rs +++ b/mockall/tests/automock_nonsend.rs @@ -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) -> Rc; +// Neither Send nor Clone +pub struct NonSend(Rc); - // Rc is not Send - fn bar(x: Rc) -> Rc; -} +mod normal_method { + use super::*; + + #[automock] + trait Foo { + fn foo(&self, x: Rc); + fn bar(&self) -> Rc; + 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; } -#[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); + fn bar(&mut self) -> &mut Rc; + } -#[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); + fn bar() -> Rc; + 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)); + } } diff --git a/mockall_derive/src/mock_function.rs b/mockall_derive/src/mock_function.rs index fd1610d1..10cb6435 100644 --- a/mockall_derive/src/mock_function.rs +++ b/mockall_derive/src/mock_function.rs @@ -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 + (&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(&mut self, __mockall_f: MockallF) @@ -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(&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(&mut self, __mockall_f: MockallF) @@ -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(&mut self, __mockall_c: MockallOutput) + #v fn return_const(&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(&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