This patch restores the tests and examples that were removed from mockall when it was bundled for distribution. They are required to enable testing of the built packages. --- diff --git a/examples/ffi.rs b/examples/ffi.rs new file mode 100644 index 0000000..6b7c9d0 --- /dev/null +++ b/examples/ffi.rs @@ -0,0 +1,34 @@ +// vim: tw=80 +//! An example of unit testing involving mocked FFI functions + +#[cfg(test)] +use mockall::automock; +use mockall_double::double; + +#[cfg_attr(test, automock)] +pub mod mockable_ffi { + extern "C" { + pub fn abs(i: i32) -> i32; + } +} + +#[double] +use mockable_ffi as ffi; + +fn abs(i: i32) -> i32 { + unsafe { ffi::abs(i) } +} + +fn main() { + let i: i32 = -42; + println!("abs({}) = {}", i, abs(i) ); +} + + +#[test] +fn time_test() { + let ctx = ffi::abs_context(); + ctx.expect() + .return_const(42); + assert_eq!(42, abs(-42)) +} diff --git a/examples/serde.rs b/examples/serde.rs new file mode 100644 index 0000000..4498449 --- /dev/null +++ b/examples/serde.rs @@ -0,0 +1,102 @@ +// vim: tw=80 +//! Mock a struct that implements Serde traits +//! +//! Serde defines to popular traits that look like this: +//! ```ignore +//! trait Serialize { +//! fn serialize(&self, serializer: S) -> Result; +//! } +//! trait Deserialize { +//! fn deserialize(deserializer: D) -> Result; +//! } +//! ``` +//! +//! Mockall can usually implement traits with generic methods. However, +//! `serde::Serializer` isn't `'static`, and the definition of +//! `Serialize::serialize` doesn't require `S` to be static. That's a problem, +//! because Mockall requires that all generic methods' generic types be +//! `'static` so that they can implement `std::any::Any`. +//! +//! There's no getting around that requirement. But there's still hope! You +//! can mock a struct that implements Serde traits by manually implementing +//! `Serialize` and `Deserialize` in terms of non-generic methods, using a +//! surrogate object for the expectations. +#![deny(warnings)] + +use mockall::*; +use serde::{Deserialize, Deserializer}; +use serde_derive::*; + +/// A serializable surrogate for `Thing`. It should serialize and deserialize +/// in exactly the same way as `Thing`. In fact, it may even be `Thing`. +#[derive(Deserialize, Serialize)] +pub struct SurrogateThing { + x: u32 +} + +mock! { + pub Thing { + // MockThing's private deserialize method. The name is not important, + // but you probably don't want to call it `deserialize` because then + // you'll have to call the real deserialize method using + // `::deserialize` syntax. + // + // This method must always succeed (or panic), because `Deserialize`'s + // error type is neither `'static` nor `Default`. + fn private_deserialize(deserializable: Result) -> Self; + // MockThing's private serialize method. The name is not important, + // but you probably don't want to call it `serialize` because then + // you'll have to call the real serialize method using + // `::serialize` syntax. + // + // This method must always succeed (or panic), because `Serialize`'s + // error type is neither `'static` nor `Default`. + fn private_serialize(&self) -> SurrogateThing; + } +} + +// Manually implement Serialize for MockThing +impl serde::Serialize for MockThing { + fn serialize(&self, s: S) -> Result { + self.private_serialize().serialize(s) + } +} + +// Manually implement Deserialize for MockThing +impl<'de> Deserialize<'de> for MockThing { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let serializable = SurrogateThing::deserialize(deserializer) + .map_err(|_| ()); + Ok(MockThing::private_deserialize(serializable)) + } +} + +// In your tests, set an expectation for `private_serialize` and return a +// suitable `SurrogateThing` +#[test] +fn serialize() { + let mut mock = MockThing::default(); + mock.expect_private_serialize() + .returning(|| SurrogateThing{x: 42} ); + + let json = serde_json::to_string(&mock).unwrap(); + assert_eq!("{\"x\":42}", json); +} + +// In your tests, set an expectation for `private_deserialize`, which will +// receive an already-deserialized `SurrogateThing` object. +#[test] +fn deserialize() { + let ctx = MockThing::private_deserialize_context(); + ctx.expect() + .withf(|st: &Result| + st.as_ref().unwrap().x == 42 + ).once() + .returning(|_| MockThing::default()); + + let json = "{\"x\":42}"; + let _thing: MockThing = serde_json::from_str(json).unwrap(); +} diff --git a/examples/synchronization.rs b/examples/synchronization.rs new file mode 100644 index 0000000..a943b88 --- /dev/null +++ b/examples/synchronization.rs @@ -0,0 +1,64 @@ +// vim: tw=80 +//! Add synchronization to multiple tests that are accessing the same mock +//! +//! When mockall mocks a function or static method, it does so globally. This +//! can cause hard to debug and non-deterministic failures when one test +//! overwrites the mock that another test is depending on. The solution to this +//! is to add some form of synchronization so that tests that depend on a +//! specific mock will not run in parallel. This is easily achieved using a +//! Mutex. +#![deny(warnings)] + +use mockall_double::double; + +pub mod my_mock { + #[cfg(test)] + use mockall::automock; + + pub struct Thing; + #[cfg_attr(test, automock)] + impl Thing { + pub fn one() -> u32 { + 1 + } + } +} + +#[double] +use my_mock::Thing; + +fn main() { + println!("1 == {}", Thing::one()); +} + +#[cfg(test)] +mod test { + use crate::my_mock::MockThing; + use std::sync::Mutex; + + static MTX: Mutex<()> = Mutex::new(()); + + #[test] + fn test_1() { + // The mutex might be poisoned if another test fails. But we don't + // care, because it doesn't hold any data. So don't unwrap the Result + // object; whether it's poisoned or not, we'll still hold the + // MutexGuard. + let _m = MTX.lock(); + + let ctx = MockThing::one_context(); + ctx.expect().returning(|| 1); + let expected = 1; + assert_eq!(expected, MockThing::one()) + } + + #[test] + fn test_2() { + let _m = MTX.lock(); + + let ctx = MockThing::one_context(); + ctx.expect().returning(|| 2); + let expected = 2; + assert_eq!(expected, MockThing::one()) + } +} diff --git a/tests/anyhow.rs b/tests/anyhow.rs new file mode 100644 index 0000000..f4f0fd4 --- /dev/null +++ b/tests/anyhow.rs @@ -0,0 +1,86 @@ +// vim: tw=80 +//! Mockall should be compatible with crates like Anyhow that redefine `Ok`. +#![deny(warnings)] + +use mockall::*; + +// Define Error, Result, and Ok similarly to how anyhow defines them +pub struct Error(); +impl Error { + pub fn new(_e: E) -> Self { + Self() + } +} +#[allow(non_snake_case)] +pub fn Ok(t: T) -> Result { + Result::Ok(t) +} +pub type Result = std::result::Result; + +#[automock] +pub trait Foo { + fn foo(&self) -> Result<(), Error>; + fn reffoo(&self) -> &Result<(), Error>; + fn refmutfoo(&mut self) -> &mut Result<(), Error>; + fn staticfoo() -> Result<(), Error>; +} + +mod static_method { + use super::*; + + #[test] + fn ok() { + let mut foo = MockFoo::new(); + foo.expect_foo() + .returning(|| Ok(())); + assert!(foo.foo().is_ok()); + } + + #[test] + fn err() { + let mut foo = MockFoo::new(); + foo.expect_foo() + .returning(|| Err(Error::new(std::io::Error::last_os_error()))); + assert!(foo.foo().is_err()); + } +} + +mod ref_method { + use super::*; + + #[test] + fn ok() { + let mut foo = MockFoo::new(); + foo.expect_reffoo() + .return_const(Ok(())); + assert!(foo.reffoo().is_ok()); + } + + #[test] + fn err() { + let mut foo = MockFoo::new(); + foo.expect_reffoo() + .return_const(Err(Error::new(std::io::Error::last_os_error()))); + assert!(foo.reffoo().is_err()); + } +} + +mod refmut_method { + use super::*; + + #[test] + fn ok() { + let mut foo = MockFoo::new(); + foo.expect_refmutfoo() + .return_var(Ok(())); + assert!(foo.refmutfoo().is_ok()); + } + + #[test] + fn err() { + let mut foo = MockFoo::new(); + foo.expect_refmutfoo() + .return_var(Err(Error::new(std::io::Error::last_os_error()))); + assert!(foo.refmutfoo().is_err()); + } +} diff --git a/tests/automock_associated_const.rs b/tests/automock_associated_const.rs new file mode 100644 index 0000000..b7c14cf --- /dev/null +++ b/tests/automock_associated_const.rs @@ -0,0 +1,56 @@ +// vim: tw=80 +//! A trait with an associated constant +//! +//! It's not possible to automock the trait, like: +//! ``` +//! #[automock] +//! trait Foo { +//! const X: i32; +//! } +//! ``` +//! because there's no way to set the value of X on MockFoo. +//! +//! But it _is_ possible to automock the trait implementation, like this: +//! ``` +//! struct Bar {} +//! #[automock] +//! impl Foo for Bar { +//! const X: i32; +//! } +//! ``` +//! +//! https://github.com/asomers/mockall/issues/97 +#![deny(warnings)] + +use mockall::*; + +trait Foo { + const X: i32; + + fn x_plus_one(&self) -> i32 { + Self::X + 1 + } +} + +pub struct Bar {} + +#[automock] +impl Foo for Bar { + const X: i32 = 42; +} + +pub struct Baz {} +#[automock] +impl Baz { + pub const Y: i32 = 69; +} + +#[test] +fn default_method() { + assert_eq!(MockBar::new().x_plus_one(), 43); +} + +#[test] +fn on_a_struct() { + assert_eq!(MockBaz::Y, 69); +} diff --git a/tests/automock_associated_type_constructor.rs b/tests/automock_associated_type_constructor.rs new file mode 100644 index 0000000..c9a5a1a --- /dev/null +++ b/tests/automock_associated_type_constructor.rs @@ -0,0 +1,36 @@ +// vim: tw=80 +//! A constructor that returns Self as an associated type of some other trait. +//! This is very useful when working with Futures. +#![deny(warnings)] + +use mockall::*; + +pub trait MyIterator { + type Item; +} + +pub struct Foo{} + +#[automock] +impl Foo { + pub fn open() -> impl MyIterator { + struct Bar {} + impl MyIterator for Bar { + type Item=Foo; + } + Bar{} + } +} + +#[test] +fn returning() { + let ctx = MockFoo::open_context(); + ctx.expect().returning(|| { + struct Baz {} + impl MyIterator for Baz { + type Item = MockFoo; + } + Box::new(Baz{}) + }); + let _a = MockFoo::open(); +} diff --git a/tests/automock_associated_types.rs b/tests/automock_associated_types.rs new file mode 100644 index 0000000..82ffa78 --- /dev/null +++ b/tests/automock_associated_types.rs @@ -0,0 +1,19 @@ +// vim: tw=80 +//! automatic-style mocking with associated types +#![deny(warnings)] + +use mockall::*; + +#[automock(type T=u32;)] +trait A { + type T: Clone; + fn foo(&self, x: Self::T) -> Self::T; +} + +#[test] +fn returning() { + let mut mock = MockA::new(); + mock.expect_foo() + .returning(|x| x); + assert_eq!(4, mock.foo(4)); +} diff --git a/tests/automock_associated_types_with_qself.rs b/tests/automock_associated_types_with_qself.rs new file mode 100644 index 0000000..937e5b4 --- /dev/null +++ b/tests/automock_associated_types_with_qself.rs @@ -0,0 +1,23 @@ +// vim: tw=80 +//! automatic-style mocking with associated types, with QSelf +#![deny(warnings)] + +use mockall::*; + +trait SomeTrait{} +struct Foo {} +impl SomeTrait for Foo {} + +#[automock(type T=u32;)] +trait A { + type T: Clone; + fn baz(&self) -> Box::T>>; +} + +#[test] +fn returning() { + let mut mock = MockA::new(); + mock.expect_baz() + .returning(|| Box::new(Foo{})); + mock.baz(); +} diff --git a/tests/automock_async_trait.rs b/tests/automock_async_trait.rs new file mode 100644 index 0000000..4e642b5 --- /dev/null +++ b/tests/automock_async_trait.rs @@ -0,0 +1,31 @@ +// vim: tw=80 +//! An async trait, for use with Futures +#![deny(warnings)] + +use async_trait::async_trait; +use futures::executor::block_on; +use mockall::*; + +#[automock] +#[async_trait] +pub trait Foo { + async fn foo(&self) -> u32; + async fn bar() -> u32; +} + + +#[test] +fn return_const() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .return_const(42u32); + assert_eq!(block_on(mock.foo()), 42); +} + +#[test] +fn static_method() { + let ctx = MockFoo::bar_context(); + ctx.expect() + .return_const(42u32); + assert_eq!(block_on(MockFoo::bar()), 42); +} diff --git a/tests/automock_attrs.rs b/tests/automock_attrs.rs new file mode 100644 index 0000000..bbae260 --- /dev/null +++ b/tests/automock_attrs.rs @@ -0,0 +1,57 @@ +// vim: tw=80 +//! Attributes are applied to the mock object, too. +#![deny(warnings)] + +use mockall::*; + +#[automock] +pub mod m { + #[cfg(target_os = "multics")] + pub fn bloob(x: DoesNotExist) -> i64 {unimplemented!()} + #[cfg(not(target_os = "multics"))] + pub fn blarg(_x: i32) -> i64 {unimplemented!()} +} + +#[test] +fn returning() { + let ctx = mock_m::blarg_context(); + ctx.expect() + .returning(|x| i64::from(x) + 1); + assert_eq!(5, mock_m::blarg(4)); +} + +pub struct A{} +#[automock] +impl A { + // Neither A::foo nor MockA::foo should be defined + #[cfg(target_os = "multics")] pub fn foo(&self, x: DoesNotExist) {} + // Both A::bar and MockA::bar should be defined + #[cfg(not(target_os = "multics"))] pub fn bar(&self, _x: i32) -> i32 {0} +} + +#[automock] +pub mod ffi { + extern "C" { + // mock_ffi::baz should not be defined + #[cfg(target_os = "multics")] + pub fn baz(x: DoesNotExist) -> i64; + // mock_ffi::bean should be defined + #[cfg(not(target_os = "multics"))] + pub fn bean(x: u32) -> i64; + } +} + +#[test] +fn method() { + let mut mock = MockA::new(); + mock.expect_bar() + .returning(|x| x); + assert_eq!(4, mock.bar(4)); +} + +#[test] +fn foreign() { + let ctx = mock_ffi::bean_context(); + ctx.expect().returning(i64::from); + assert_eq!(42, unsafe{mock_ffi::bean(42)}); +} diff --git a/tests/automock_boxed_constructor.rs b/tests/automock_boxed_constructor.rs new file mode 100644 index 0000000..f0d13c9 --- /dev/null +++ b/tests/automock_boxed_constructor.rs @@ -0,0 +1,17 @@ +// vim: tw=80 +//! A trait with a constructor method that returns Box +#![deny(warnings)] + +use mockall::*; + +#[automock] +pub trait A { + fn new() -> Box; +} + +#[test] +fn returning() { + let ctx = MockA::new_context(); + ctx.expect().returning(Box::default); + let _a: Box = ::new(); +} diff --git a/tests/automock_concretize.rs b/tests/automock_concretize.rs new file mode 100644 index 0000000..f169a20 --- /dev/null +++ b/tests/automock_concretize.rs @@ -0,0 +1,50 @@ +// vim: tw=80 +//! #[concretize] works with #[automock], too. +#![deny(warnings)] + +use mockall::*; +use std::path::{Path, PathBuf}; + +#[automock] +trait Foo { + #[concretize] + fn foo>(&self, x: P); +} + +#[automock] +pub mod mymod { + #[mockall::concretize] + pub fn bang>(_x: P) { unimplemented!() } +} + +mod generic_arg { + use super::*; + + #[test] + fn withf() { + let mut foo = MockFoo::new(); + foo.expect_foo() + .withf(|p| p.as_ref() == Path::new("/tmp")) + .times(3) + .return_const(()); + foo.foo(Path::new("/tmp")); + foo.foo(PathBuf::from(Path::new("/tmp"))); + foo.foo("/tmp"); + } +} + +mod module { + use super::*; + + #[test] + fn withf() { + let ctx = mock_mymod::bang_context(); + ctx.expect() + .withf(|p| p.as_ref() == Path::new("/tmp")) + .times(3) + .return_const(()); + mock_mymod::bang(Path::new("/tmp")); + mock_mymod::bang(PathBuf::from(Path::new("/tmp"))); + mock_mymod::bang("/tmp"); + } +} diff --git a/tests/automock_constructor_impl_trait.rs b/tests/automock_constructor_impl_trait.rs new file mode 100644 index 0000000..7384e11 --- /dev/null +++ b/tests/automock_constructor_impl_trait.rs @@ -0,0 +1,30 @@ +// vim: tw=80 +//! A trait with a constructor method that returns impl Trait +#![deny(warnings)] + +use mockall::*; + +pub trait Foo {} + +pub struct A{} + +struct Bar {} +impl Foo for Bar {} + +#[automock] +impl A { + pub fn build() -> impl Foo { + Bar{} + } +} + +#[test] +fn returning() { + let ctx = MockA::build_context(); + ctx.expect().returning(|| { + struct Baz {} + impl Foo for Baz {} + Box::new(Baz{}) + }); + let _a = MockA::build(); +} diff --git a/tests/automock_constructor_in_generic_trait.rs b/tests/automock_constructor_in_generic_trait.rs new file mode 100644 index 0000000..550fcb3 --- /dev/null +++ b/tests/automock_constructor_in_generic_trait.rs @@ -0,0 +1,21 @@ +// vim: tw=80 +//! A generic trait with a non-generic constructor method. +#![deny(warnings)] + +use mockall::*; + +#[automock] +trait Foo { + fn new(t: T) -> Self; +} + +#[test] +fn return_once() { + let mock = MockFoo::::default(); + + let ctx = MockFoo::::new_context(); + ctx.expect() + .return_once(move |_| mock); + + let _mock = MockFoo::new(5u32); +} diff --git a/tests/automock_constructor_in_struct.rs b/tests/automock_constructor_in_struct.rs new file mode 100644 index 0000000..1c3a895 --- /dev/null +++ b/tests/automock_constructor_in_struct.rs @@ -0,0 +1,22 @@ +// vim: tw=80 +//! A struct with a constructor method +#![deny(warnings)] + +use mockall::*; + +pub struct A {} + +#[automock] +#[allow(clippy::new_without_default)] +impl A { + pub fn new() -> Self { + unimplemented!() + } +} + +#[test] +fn returning() { + let ctx = MockA::new_context(); + ctx.expect().returning(MockA::default); + let _a: MockA = MockA::new(); +} diff --git a/tests/automock_constructor_in_trait.rs b/tests/automock_constructor_in_trait.rs new file mode 100644 index 0000000..1bd0189 --- /dev/null +++ b/tests/automock_constructor_in_trait.rs @@ -0,0 +1,17 @@ +// vim: tw=80 +//! A trait with a constructor method +#![deny(warnings)] + +use mockall::*; + +#[automock] +pub trait A { + fn new() -> Self; +} + +#[test] +fn returning() { + let ctx = MockA::new_context(); + ctx.expect().returning(MockA::default); + let _a: MockA = ::new(); +} diff --git a/tests/automock_constructor_in_trait_with_args.rs b/tests/automock_constructor_in_trait_with_args.rs new file mode 100644 index 0000000..f7373ba --- /dev/null +++ b/tests/automock_constructor_in_trait_with_args.rs @@ -0,0 +1,23 @@ +// vim: tw=80 +//! A struct with a constructor method named "new" that has arguments. +//! mockall should mock the provided method, and not autogenerate a 0-argument +//! "new" method. +#![deny(warnings)] + +use mockall::*; + +#[automock] +trait Foo { + fn new(x: u32) -> Self; +} + +#[test] +fn return_once() { + let mock = MockFoo::default(); + let ctx = MockFoo::new_context(); + + ctx.expect() + .return_once(|_| mock); + + let _mock = MockFoo::new(5); +} diff --git a/tests/automock_constructor_with_args.rs b/tests/automock_constructor_with_args.rs new file mode 100644 index 0000000..5a6f563 --- /dev/null +++ b/tests/automock_constructor_with_args.rs @@ -0,0 +1,25 @@ +// vim: tw=80 +//! A struct with a constructor method named "new" that has arguments. +//! mockall should mock the provided method, and not autogenerate a 0-argument +//! "new" method. +#![deny(warnings)] + +use mockall::*; + +pub struct Foo{} + +#[automock] +impl Foo { + pub fn new(_x: u32) -> Self {unimplemented!()} +} + +#[test] +fn return_once() { + let mock = MockFoo::default(); + let ctx = MockFoo::new_context(); + + ctx.expect() + .return_once(|_| mock); + + let _mock = MockFoo::new(5); +} diff --git a/tests/automock_consume_arguments.rs b/tests/automock_consume_arguments.rs new file mode 100644 index 0000000..eb47fe8 --- /dev/null +++ b/tests/automock_consume_arguments.rs @@ -0,0 +1,19 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; + +struct NonCopy{} + +#[automock] +trait T { + fn foo(&self, x: NonCopy); +} + +#[test] +fn returning() { + let mut mock = MockT::new(); + mock.expect_foo() + .returning(|_x: NonCopy| ()); + mock.foo(NonCopy{}); +} diff --git a/tests/automock_custom_result.rs b/tests/automock_custom_result.rs new file mode 100644 index 0000000..89f7591 --- /dev/null +++ b/tests/automock_custom_result.rs @@ -0,0 +1,42 @@ +// vim: tw=80 +//! It should be possible to use a custom Result type in the signature of a +//! mocked method. Regression test for +//! https://github.com/asomers/mockall/issues/73 +#![deny(warnings)] + +use mockall::*; + +pub type Result = std::result::Result; + +pub struct MyStruct {} + +#[automock] +impl MyStruct { + pub fn ret_static(&self) -> Result { unimplemented!() } + pub fn ret_ref(&self) -> &Result { unimplemented!() } + pub fn ret_refmut(&mut self) -> &mut Result { unimplemented!() } +} + +#[test] +fn ret_ref() { + let mut s = MockMyStruct::new(); + s.expect_ret_ref() + .return_const(Ok(42)); + assert_eq!(Ok(42), *s.ret_ref()); +} + +#[test] +fn ret_ref_mut() { + let mut s = MockMyStruct::new(); + s.expect_ret_refmut() + .return_var(Ok(42)); + assert_eq!(Ok(42), *s.ret_refmut()); +} + +#[test] +fn ret_static() { + let mut s = MockMyStruct::new(); + s.expect_ret_static() + .return_const(Ok(42)); + assert_eq!(Ok(42), s.ret_static()); +} diff --git a/tests/automock_debug.rs b/tests/automock_debug.rs new file mode 100644 index 0000000..6341c75 --- /dev/null +++ b/tests/automock_debug.rs @@ -0,0 +1,35 @@ +// vim: tw=80 +//! A mocked struct should implement Debug +#![deny(warnings)] + +use mockall::*; + +#[automock] +pub trait Foo {} + +pub trait Bar {} +pub struct Baz {} +#[automock] +impl Bar for Baz{} + +pub struct Bean {} +#[automock] +impl Bean{} + +#[test] +fn automock_trait() { + let foo = MockFoo::new(); + assert_eq!("MockFoo", format!("{foo:?}")); +} + +#[test] +fn automock_struct_impl() { + let bean = MockBean::new(); + assert_eq!("MockBean", format!("{bean:?}")); +} + +#[test] +fn automock_trait_impl() { + let baz = MockBaz::new(); + assert_eq!("MockBaz", format!("{baz:?}")); +} diff --git a/tests/automock_deref.rs b/tests/automock_deref.rs new file mode 100644 index 0000000..61c55b3 --- /dev/null +++ b/tests/automock_deref.rs @@ -0,0 +1,74 @@ +// vim: tw=80 +//! A method that returns a type which is a common target for std::ops::Deref +#![deny(warnings)] + +use mockall::*; +use std::{ + ffi::{CStr, CString, OsStr, OsString}, + path::{Path, PathBuf}, +}; + +#[automock] +trait Foo { + fn name(&self) -> &CStr; + fn alias(&self) -> &str; + fn desc(&self) -> &OsStr; + fn path(&self) -> &Path; + fn text(&self) -> &'static str; + fn slice(&self) -> &[i32]; +} + +mod return_const { + use super::*; + + #[test] + fn cstr() { + let mut mock = MockFoo::new(); + let name = CString::new("abcd").unwrap(); + mock.expect_name().return_const(name.clone()); + assert_eq!(name.as_c_str(), mock.name()); + } + + #[test] + fn osstr() { + let mut mock = MockFoo::new(); + let desc = OsString::from("abcd"); + mock.expect_desc().return_const(desc.clone()); + assert_eq!(desc.as_os_str(), mock.desc()); + } + + #[test] + fn path() { + let mut mock = MockFoo::new(); + let mut pb = PathBuf::new(); + pb.push("foo"); + pb.push("bar"); + pb.push("baz"); + mock.expect_path().return_const(pb.clone()); + assert_eq!(pb.as_path(), mock.path()); + } + + #[test] + fn str() { + let mut mock = MockFoo::new(); + mock.expect_alias().return_const("abcd".to_owned()); + assert_eq!("abcd", mock.alias()); + } + + #[allow(clippy::redundant_static_lifetimes)] + #[test] + fn static_str() { + const TEXT: &'static str = "abcd"; + let mut mock = MockFoo::new(); + mock.expect_text().return_const(TEXT); + assert_eq!("abcd", mock.text()); + } + + #[test] + fn slice() { + let r = vec![1, 2, 3]; + let mut mock = MockFoo::new(); + mock.expect_slice().return_const(r); + assert_eq!(&[1, 2, 3], mock.slice()); + } +} diff --git a/tests/automock_extern_std.rs b/tests/automock_extern_std.rs new file mode 100644 index 0000000..3b67c68 --- /dev/null +++ b/tests/automock_extern_std.rs @@ -0,0 +1,19 @@ +// vim: tw=80 +//! automocking a trait when std is only an extern crate (eg., as a testing +//! support mod for a no_std library). This setup requires some extra "use"s +//! to make, eg., Box, available. + +#![no_std] +extern crate std; + +use mockall::*; + +#[automock] +pub trait SimpleTrait { + fn foo(&self, x: u32) -> u32; +} + +#[test] +fn creating() { + let _ = MockSimpleTrait::new(); +} diff --git a/tests/automock_foreign_c.rs b/tests/automock_foreign_c.rs new file mode 100644 index 0000000..07eb47d --- /dev/null +++ b/tests/automock_foreign_c.rs @@ -0,0 +1,52 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; +use std::sync::Mutex; + +#[automock] +mod ffi { + extern "C" { + pub(super) fn foo(x: u32) -> i64; + } +} + +static FOO_MTX: Mutex<()> = Mutex::new(()); + +// Ensure we can still use the original mocked function +pub fn normal_usage() { + let _m = FOO_MTX.lock(); + unsafe { + ffi::foo(42); + } +} + +#[test] +#[should_panic(expected = "mock_ffi::foo(5): No matching expectation found")] +fn with_no_matches() { + let _m = FOO_MTX.lock(); + let ctx = mock_ffi::foo_context(); + ctx.expect() + .with(predicate::eq(4)) + .returning(i64::from); + unsafe{ mock_ffi::foo(5) }; +} + +#[test] +fn returning() { + let _m = FOO_MTX.lock(); + let ctx = mock_ffi::foo_context(); + ctx.expect().returning(i64::from); + assert_eq!(42, unsafe{mock_ffi::foo(42)}); +} + +/// Ensure that the mock function can be called from C by casting it to a C +/// function pointer. +#[test] +fn c_abi() { + let _m = FOO_MTX.lock(); + let ctx = mock_ffi::foo_context(); + ctx.expect().returning(i64::from); + let p: unsafe extern "C" fn(u32) -> i64 = mock_ffi::foo; + assert_eq!(42, unsafe{p(42)}); +} diff --git a/tests/automock_foreign_c_variadic.rs b/tests/automock_foreign_c_variadic.rs new file mode 100644 index 0000000..71a62ed --- /dev/null +++ b/tests/automock_foreign_c_variadic.rs @@ -0,0 +1,23 @@ +// vim: tw=80 +#![cfg_attr(feature = "nightly", feature(c_variadic))] +#![deny(warnings)] + +#[cfg(feature = "nightly")] +use mockall::*; + +#[automock] +#[cfg(feature = "nightly")] +pub mod ffi { + extern "C" { + pub fn foo(x: i32, y: i32, ...) -> i32; + } +} + +#[test] +#[cfg(feature = "nightly")] +#[cfg_attr(miri, ignore)] +fn mocked_c_variadic() { + let ctx = mock_ffi::foo_context(); + ctx.expect().returning(|x, y| x * y); + assert_eq!(6, unsafe{mock_ffi::foo(2, 3, 1, 4, 1)}); +} diff --git a/tests/automock_foreign_rust.rs b/tests/automock_foreign_rust.rs new file mode 100644 index 0000000..ccc7ee5 --- /dev/null +++ b/tests/automock_foreign_rust.rs @@ -0,0 +1,18 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; + +#[automock] +pub mod ffi { + extern "Rust" { + pub fn foo(_x: u32) -> i64; + } +} + +#[test] +fn returning() { + let ctx = mock_ffi::foo_context(); + ctx.expect().returning(i64::from); + assert_eq!(42, unsafe{mock_ffi::foo(42)}); +} diff --git a/tests/automock_gat.rs b/tests/automock_gat.rs new file mode 100644 index 0000000..4fd4938 --- /dev/null +++ b/tests/automock_gat.rs @@ -0,0 +1,35 @@ +#! vim: tw=80 +//! automock a trait with Generic Associated Types +#![deny(warnings)] + +use cfg_if::cfg_if; + +cfg_if! { + if #[cfg(feature = "nightly")] { + use mockall::*; + + // The lifetime must have the same name as in the next() method. + #[automock(type Item=&'a u32;)] + trait LendingIterator { + type Item<'a> where Self: 'a; + + // Clippy doesn't know that Mockall will need the lifetime when it + // expands the macro. + #[allow(clippy::needless_lifetimes)] + fn next<'a>(&'a mut self) -> Option>; + } + + // It isn't possible to safely set an expectation for a non-'static + // return value (because the mock object doesn't have any lifetime + // parameters itself), but unsafely setting such an expectation is a + // common use case. + #[test] + fn return_const() { + let mut mock = MockLendingIterator::new(); + let x = 42u32; + let xstatic: &'static u32 = unsafe{ std::mem::transmute(&x) }; + mock.expect_next().return_const(Some(xstatic)); + assert_eq!(42u32, *mock.next().unwrap()); + } + } +} diff --git a/tests/automock_generic_arguments.rs b/tests/automock_generic_arguments.rs new file mode 100644 index 0000000..5b8a9e6 --- /dev/null +++ b/tests/automock_generic_arguments.rs @@ -0,0 +1,21 @@ +// vim: tw=80 +//! generic methods with generic arguments +#![deny(warnings)] + +use mockall::*; + +#[automock] +trait A { + fn foo(&self, t: T); +} + +#[test] +fn returning() { + let mut mock = MockA::new(); + mock.expect_foo::() + .returning(|_x: u32| ()); + mock.expect_foo::() + .returning(|_x: i16| ()); + mock.foo(5u32); + mock.foo(-1i16); +} diff --git a/tests/automock_generic_arguments_returning_references.rs b/tests/automock_generic_arguments_returning_references.rs new file mode 100644 index 0000000..de1f5c1 --- /dev/null +++ b/tests/automock_generic_arguments_returning_references.rs @@ -0,0 +1,17 @@ +// vim: tw=80 +//! generic methods with generic arguments returning immutable references +#![deny(warnings)] + +use mockall::*; + +#[automock] +trait A { + fn foo(&self, t: T) -> &u32; +} + +#[test] +fn return_const() { + let mut mock = MockA::new(); + mock.expect_foo::().return_const(5); + assert_eq!(5, *mock.foo(42u32)); +} diff --git a/tests/automock_generic_arguments_with_where_clause.rs b/tests/automock_generic_arguments_with_where_clause.rs new file mode 100644 index 0000000..2d619d9 --- /dev/null +++ b/tests/automock_generic_arguments_with_where_clause.rs @@ -0,0 +1,21 @@ +// vim: tw=80 +//! A method with generic arguments bounded by a where clause +#![deny(warnings)] + +use mockall::*; + +#[automock] +trait A { + fn foo(&self, t: T) where T: Clone + 'static; +} + +#[test] +fn returning() { + let mut mock = MockA::new(); + mock.expect_foo::() + .returning(|_x: u32| ()); + mock.expect_foo::() + .returning(|_x: i16| ()); + mock.foo(5u32); + mock.foo(-1i16); +} diff --git a/tests/automock_generic_constructor.rs b/tests/automock_generic_constructor.rs new file mode 100644 index 0000000..d9f7286 --- /dev/null +++ b/tests/automock_generic_constructor.rs @@ -0,0 +1,19 @@ +// vim: tw=80 +//! A non-generic struct can have a generic constructor method +#![deny(warnings)] + +use mockall::*; + +#[automock] +trait Foo { + fn build(t: T) -> Self; +} + +#[test] +fn returning_once() { + let ctx = MockFoo::build_context(); + ctx.expect::() + .return_once(|_| MockFoo::default()); + + let _mock: MockFoo = MockFoo::build::(-1); +} diff --git a/tests/automock_generic_future.rs b/tests/automock_generic_future.rs new file mode 100644 index 0000000..de26d92 --- /dev/null +++ b/tests/automock_generic_future.rs @@ -0,0 +1,36 @@ +// vim: tw=80 +//! A generic mock object that implements Future +//! +//! This is tricky because the Context object has a lifetime parameter, yet the +//! poll method must not be treated as a generic method. +#![deny(warnings)] + +use futures::executor::block_on; +use mockall::*; +use std::{ + future::Future, + pin::Pin, + task::{Context, Poll}, +}; + +struct Foo(T); + +#[automock] +impl Future for Foo { + type Output = (); + + fn poll<'a>(self: Pin<&mut Self>, _cx: &mut Context<'a>) + -> Poll + { + unimplemented!() + } +} + +#[test] +fn ready() { + let mut mock = MockFoo::::new(); + mock.expect_poll() + .once() + .return_const(Poll::Ready(())); + block_on(mock); +} diff --git a/tests/automock_generic_future_with_where_clause.rs b/tests/automock_generic_future_with_where_clause.rs new file mode 100644 index 0000000..45e80d8 --- /dev/null +++ b/tests/automock_generic_future_with_where_clause.rs @@ -0,0 +1,36 @@ +// vim: tw=80 +//! A generic mock object with a method that has only lifetime generic +//! parameters, and a where clause that bounds a generic type not used by the +//! method. +//! +//! Mockall must not emit the where clause for the method's Expectation. +#![deny(warnings)] +#![allow(clippy::needless_lifetimes)] + +use mockall::*; + +struct Foo((T, V)); +trait MyTrait { + type Item; + + fn myfunc(&self, cx: &NonStatic) -> Self::Item; +} +#[allow(dead_code)] +pub struct NonStatic<'ns>(&'ns i32); + +#[automock] +impl MyTrait for Foo where T: Clone { + type Item = V; + + fn myfunc<'a>(&self, _cx: &NonStatic<'a>) -> V { unimplemented!() } +} + +#[test] +fn return_const() { + let mut mock = MockFoo::::new(); + let x = 5i32; + let ns = NonStatic(&x); + mock.expect_myfunc() + .return_const(42u32); + assert_eq!(42u32, mock.myfunc(&ns)); +} diff --git a/tests/automock_generic_method_with_bounds.rs b/tests/automock_generic_method_with_bounds.rs new file mode 100644 index 0000000..6bc68b1 --- /dev/null +++ b/tests/automock_generic_method_with_bounds.rs @@ -0,0 +1,23 @@ +// vim: tw=80 +//! generic methods with bounds on their generic parameters +#![deny(warnings)] + +use mockall::*; +use std::fmt::Debug; + +struct X(T); + +#[automock] +trait Foo { + fn foo(&self, x: X); +} + +#[test] +fn withf() { + let mut mock = MockFoo::new(); + mock.expect_foo::() + .withf(|x| x.0 == 42u32) + .return_const(()); + + mock.foo(X(42u32)); +} diff --git a/tests/automock_generic_method_with_lifetime_parameter.rs b/tests/automock_generic_method_with_lifetime_parameter.rs new file mode 100644 index 0000000..0c1b65d --- /dev/null +++ b/tests/automock_generic_method_with_lifetime_parameter.rs @@ -0,0 +1,48 @@ +// vim: tw=80 +//! A generic method whose only generic parameter is a lifetime parameter is, +//! from Mockall's perspective, pretty much the same as a non-generic method. +#![deny(warnings)] + +use mockall::*; + +#[derive(Debug, Eq)] +struct X<'a>(&'a u32); + +impl<'a> PartialEq for X<'a> { + fn eq(&self, other: &X<'a>) -> bool { + self.0 == other.0 + } +} + +#[automock] +trait Foo { + fn foo<'a>(&self, x: &'a X<'a>) -> u32; +} + +#[test] +fn return_const() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .return_const(42u32); + let x = X(&5); + assert_eq!(42, mock.foo(&x)); +} + +#[test] +fn returning() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .returning(|f| *f.0); + let x = X(&5); + assert_eq!(5, mock.foo(&x)); +} + +#[test] +fn withf() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .withf(|f| *f.0 == 5) + .return_const(42u32); + let x = X(&5); + assert_eq!(42, mock.foo(&x)); +} diff --git a/tests/automock_generic_method_with_unknown_size_bounds.rs b/tests/automock_generic_method_with_unknown_size_bounds.rs new file mode 100644 index 0000000..4182b2e --- /dev/null +++ b/tests/automock_generic_method_with_unknown_size_bounds.rs @@ -0,0 +1,34 @@ +// vim: tw=80 +//! generic methods with unknown size bounds on their generic parameters +#![deny(warnings)] + +use mockall::*; + +#[automock] +trait Foo { + fn foo(&self, input: Box); +} + +trait Bar { + fn get(&self) -> u32; +} + +struct Foobar { + value: u32, +} + +impl Bar for Foobar { + fn get(&self) -> u32 { + self.value + } +} + +#[test] +fn withf() { + let mut mock = MockFoo::new(); + mock.expect_foo::() + .withf(|x| x.get() == 42) + .return_const(()); + + mock.foo::(Box::new(Foobar { value: 42 })); +} diff --git a/tests/automock_generic_method_without_generic_args_or_return.rs b/tests/automock_generic_method_without_generic_args_or_return.rs new file mode 100644 index 0000000..63b297b --- /dev/null +++ b/tests/automock_generic_method_without_generic_args_or_return.rs @@ -0,0 +1,44 @@ +// vim: tw=80 +//! generic methods whose generic parameters appear in neither inputs nor return +//! types should still be mockable, and it should be possible to set +//! simultaneous expectations for different generic types on the same object. +#![deny(warnings)] + +use mockall::*; + +pub struct Foo{} + +#[automock] +impl Foo { + #[allow(clippy::extra_unused_type_parameters)] + pub fn foo(&self) -> i32 { + unimplemented!() + } + /// A static method + #[allow(clippy::extra_unused_type_parameters)] + pub fn bar() -> i32 { + unimplemented!() + } +} + +#[test] +fn return_const() { + let mut mock = MockFoo::new(); + mock.expect_foo::() + .return_const(42); + mock.expect_foo::() + .return_const(69); + assert_eq!(42, mock.foo::()); + assert_eq!(69, mock.foo::()); +} + +#[test] +fn static_method() { + let ctx = MockFoo::bar_context(); + ctx.expect::() + .return_const(42); + ctx.expect::() + .return_const(69); + assert_eq!(42, MockFoo::bar::()); + assert_eq!(69, MockFoo::bar::()); +} diff --git a/tests/automock_generic_return.rs b/tests/automock_generic_return.rs new file mode 100644 index 0000000..9ea8944 --- /dev/null +++ b/tests/automock_generic_return.rs @@ -0,0 +1,21 @@ +// vim: tw=80 +//! generic methods with generic return values +#![deny(warnings)] + +use mockall::*; + +#[automock] +trait A { + fn foo(&self, t: T) -> T; +} + +#[test] +fn returning() { + let mut mock = MockA::new(); + mock.expect_foo::() + .returning(|_x: u32| 42u32); + mock.expect_foo::() + .returning(|_x: i16| 42i16); + assert_eq!(42u32, mock.foo(5u32)); + assert_eq!(42i16, mock.foo(-1i16)); +} diff --git a/tests/automock_generic_static_method.rs b/tests/automock_generic_static_method.rs new file mode 100644 index 0000000..3afc95e --- /dev/null +++ b/tests/automock_generic_static_method.rs @@ -0,0 +1,33 @@ +// vim: tw=80 +//! generic static methods with generic arguments +#![deny(warnings)] + +use mockall::*; +use std::sync::Mutex; + +static A_MTX: Mutex<()> = Mutex::new(()); + +#[automock] +trait A { + fn bar(t: T) -> u32; +} + +#[test] +fn returning() { + let _m = A_MTX.lock().unwrap(); + + let ctx = MockA::bar_context(); + ctx.expect::() + .returning(|_| 42); + assert_eq!(42, MockA::bar(-1i16)); +} + +#[test] +fn return_const() { + let _m = A_MTX.lock().unwrap(); + + let ctx = MockA::bar_context(); + ctx.expect::() + .return_const(42u32); + assert_eq!(42, MockA::bar(-1i16)); +} diff --git a/tests/automock_generic_struct.rs b/tests/automock_generic_struct.rs new file mode 100644 index 0000000..2efaba3 --- /dev/null +++ b/tests/automock_generic_struct.rs @@ -0,0 +1,24 @@ +// vim: tw=80 +//! generic structs +#![deny(warnings)] + +use mockall::*; + +pub struct GenericStruct { + _t: T, + _v: V +} +#[automock] +impl GenericStruct { + pub fn foo(&self, _x: u32) -> i64 { + 42 + } +} + +#[test] +fn returning() { + let mut mock = MockGenericStruct::::new(); + mock.expect_foo() + .returning(|x| i64::from(x) + 1); + assert_eq!(5, mock.foo(4)); +} diff --git a/tests/automock_generic_struct_with_bounds.rs b/tests/automock_generic_struct_with_bounds.rs new file mode 100644 index 0000000..b7ba3b4 --- /dev/null +++ b/tests/automock_generic_struct_with_bounds.rs @@ -0,0 +1,24 @@ +// vim: tw=80 +//! generic structs with bounds on their generic parameters +#![deny(warnings)] + +use mockall::*; + +pub struct GenericStruct { + _t: T, + _v: V +} +#[automock] +impl GenericStruct { + pub fn foo(&self, _x: u32) -> i64 { + 42 + } +} + +#[test] +fn returning() { + let mut mock = MockGenericStruct::::new(); + mock.expect_foo() + .returning(|x| i64::from(x) + 1); + assert_eq!(5, mock.foo(4)); +} diff --git a/tests/automock_generic_struct_with_static_method.rs b/tests/automock_generic_struct_with_static_method.rs new file mode 100644 index 0000000..1410113 --- /dev/null +++ b/tests/automock_generic_struct_with_static_method.rs @@ -0,0 +1,20 @@ +// vim: tw=80 +//! static non-generic methods of generic structs shouldn't require any special +//! treatment when mocking. Prior to version 0.3.0, the struct's generic +//! parameters had to be duplicated as generic parameters of the method. +#![deny(warnings)] + +use mockall::*; + +#[automock] +trait Foo { + fn foo(t: T); +} + +#[test] +fn returning() { + let ctx = MockFoo::::foo_context(); + ctx.expect() + .returning(|_| ()); + MockFoo::foo(42u32); +} diff --git a/tests/automock_generic_struct_with_where_clause.rs b/tests/automock_generic_struct_with_where_clause.rs new file mode 100644 index 0000000..f62996f --- /dev/null +++ b/tests/automock_generic_struct_with_where_clause.rs @@ -0,0 +1,26 @@ +// vim: tw=80 +//! A struct with generic parameters bounded by a where clause +#![deny(warnings)] + +use mockall::*; + +pub struct GenericStruct { + _t: T, +} +#[automock] +impl GenericStruct + where T: Clone + Default +{ + #[allow(clippy::redundant_clone)] + pub fn foo(&self, x: T) -> T { + x.clone() + } +} + +#[test] +fn returning() { + let mut mock = MockGenericStruct::::default(); + mock.expect_foo() + .returning(|x| x); + assert_eq!(4, mock.foo(4u8)); +} diff --git a/tests/automock_generic_trait.rs b/tests/automock_generic_trait.rs new file mode 100644 index 0000000..499ef08 --- /dev/null +++ b/tests/automock_generic_trait.rs @@ -0,0 +1,30 @@ +// vim: tw=80 +//! generic traits +#![deny(warnings)] + +use mockall::*; + +#[automock] +trait A { + fn foo(&self, t: T); + fn bar(&self) -> T; +} + +#[test] +fn generic_arguments() { + let mut mock = MockA::::new(); + mock.expect_foo() + .with(mockall::predicate::eq(16u32)) + .once() + .returning(|_| ()); + mock.foo(16u32); +} + +#[test] +fn generic_return() { + let mut mock = MockA::::new(); + mock.expect_bar() + .returning(|| 42u32); + assert_eq!(42u32, mock.bar()); +} + diff --git a/tests/automock_generic_trait_with_bounds.rs b/tests/automock_generic_trait_with_bounds.rs new file mode 100644 index 0000000..a99e824 --- /dev/null +++ b/tests/automock_generic_trait_with_bounds.rs @@ -0,0 +1,18 @@ +// vim: tw=80 +//! generic traits with bounds on the generic parameters +#![deny(warnings)] + +use mockall::*; + +#[automock] +trait A { + fn foo(&self); +} + +#[test] +fn returning() { + let mut mock = MockA::::new(); + mock.expect_foo() + .returning(|| ()); + mock.foo(); +} diff --git a/tests/automock_generic_trait_with_where_clause.rs b/tests/automock_generic_trait_with_where_clause.rs new file mode 100644 index 0000000..e12be15 --- /dev/null +++ b/tests/automock_generic_trait_with_where_clause.rs @@ -0,0 +1,18 @@ +// vim: tw=80 +//! A trait with generic parameters bounded by a where clause +#![deny(warnings)] + +use mockall::*; + +#[automock] +trait Foo where T: Clone + Copy { + fn foo(&self); +} + +#[test] +fn returning() { + let mut mock = MockFoo::::default(); + mock.expect_foo() + .returning(|| ()); + mock.foo(); +} diff --git a/tests/automock_impl_future.rs b/tests/automock_impl_future.rs new file mode 100644 index 0000000..26086af --- /dev/null +++ b/tests/automock_impl_future.rs @@ -0,0 +1,50 @@ +// vim: tw=80 +//! A trait with a constructor method that returns impl Future<...>. +//! +//! This needs special handling, because Box> is pretty useless. +//! You need Pin>> instead. +#![deny(warnings)] + +use futures::{Future, FutureExt, Stream, StreamExt, future, stream}; +use mockall::*; + +pub struct Foo{} + +#[automock] +impl Foo { + pub fn foo(&self) -> impl Future + { + future::ready(42) + } + + pub fn bar(&self) -> impl Stream + { + stream::empty() + } +} + +#[test] +fn returning_future() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .returning(|| { + Box::pin(future::ready(42)) + }); + mock.foo() + .now_or_never() + .unwrap(); +} + +#[test] +fn returning_stream() { + let mut mock = MockFoo::new(); + mock.expect_bar() + .returning(|| { + Box::pin(stream::iter(vec![42])) + }); + let all = mock.bar() + .collect::>() + .now_or_never() + .unwrap(); + assert_eq!(&all[..], &[42][..]); +} diff --git a/tests/automock_impl_generic_trait_for.rs b/tests/automock_impl_generic_trait_for.rs new file mode 100644 index 0000000..6bd218a --- /dev/null +++ b/tests/automock_impl_generic_trait_for.rs @@ -0,0 +1,28 @@ +// vim: tw=80 +//! A generic struct that implements a generic trait +#![deny(warnings)] + +use mockall::*; + +trait Foo { + fn foo(&self, t: T) -> T; +} + +pub struct SomeStruct { + _t: std::marker::PhantomData +} + +#[automock] +impl Foo for SomeStruct { + fn foo(&self, t: T) -> T { + t + } +} + +#[test] +fn returning() { + let mut mock = MockSomeStruct::::new(); + mock.expect_foo() + .returning(|t| t); + assert_eq!(4, as Foo>::foo(&mock, 4)); +} diff --git a/tests/automock_impl_trait.rs b/tests/automock_impl_trait.rs new file mode 100644 index 0000000..eb14a13 --- /dev/null +++ b/tests/automock_impl_trait.rs @@ -0,0 +1,20 @@ +// vim: tw=80 +//! A method that returns "impl Trait" +#![deny(warnings)] + +use mockall::*; +use std::fmt::Debug; + +pub struct Foo {} + +#[automock] +impl Foo { + pub fn foo(&self) -> impl Debug + Send { unimplemented!()} +} + +#[test] +fn returning() { + let mut mock = MockFoo::new(); + mock.expect_foo().returning(|| Box::new(4)); + format!("{:?}", mock.foo()); +} diff --git a/tests/automock_impl_trait_for.rs b/tests/automock_impl_trait_for.rs new file mode 100644 index 0000000..6dc153a --- /dev/null +++ b/tests/automock_impl_trait_for.rs @@ -0,0 +1,26 @@ +// vim: tw=80 +//! A struct that implements a trait +#![deny(warnings)] + +use mockall::*; + +pub trait Foo { + fn foo(&self, x: u32) -> i64; +} + +pub struct SomeStruct {} + +#[automock] +impl Foo for SomeStruct { + fn foo(&self, _x: u32) -> i64 { + 42 + } +} + +#[test] +fn returning() { + let mut mock = MockSomeStruct::new(); + mock.expect_foo() + .returning(|x| i64::from(x) + 1); + assert_eq!(5, ::foo(&mock, 4)); +} diff --git a/tests/automock_impl_trait_for_generic.rs b/tests/automock_impl_trait_for_generic.rs new file mode 100644 index 0000000..7d50ef4 --- /dev/null +++ b/tests/automock_impl_trait_for_generic.rs @@ -0,0 +1,28 @@ +// vim: tw=80 +//! A generic struct that implements a trait +#![deny(warnings)] + +use mockall::*; + +trait Foo { + fn foo(&self, x: u32) -> i64; +} + +pub struct SomeStruct { + _t: std::marker::PhantomData +} + +#[automock] +impl Foo for SomeStruct { + fn foo(&self, _x: u32) -> i64 { + 42 + } +} + +#[test] +fn returning() { + let mut mock = MockSomeStruct::::new(); + mock.expect_foo() + .returning(|x| i64::from(x) + 1); + assert_eq!(5, mock.foo(4)); +} diff --git a/tests/automock_impl_trait_with_associated_types_for.rs b/tests/automock_impl_trait_with_associated_types_for.rs new file mode 100644 index 0000000..17bcca7 --- /dev/null +++ b/tests/automock_impl_trait_with_associated_types_for.rs @@ -0,0 +1,22 @@ +// vim: tw=80 +//! A struct implements a trait with associated types +#![deny(warnings)] + +use mockall::*; + +pub struct Foo {} +#[automock] +impl Iterator for Foo { + type Item = u32; + + fn next(&mut self) -> Option { + unimplemented!() + } +} + +#[test] +fn returning() { + let mut mock = MockFoo::new(); + mock.expect_next().returning(|| None); + assert!(mock.next().is_none()); +} diff --git a/tests/automock_inline.rs b/tests/automock_inline.rs new file mode 100644 index 0000000..6ba2991 --- /dev/null +++ b/tests/automock_inline.rs @@ -0,0 +1,52 @@ +// vim: tw=80 +//! Mockall should ignore and not emit attributes like "inline" that affect +//! code generation. +#![deny(warnings)] + +use mockall::*; + +pub struct Foo {} + +#[automock] +impl Foo { + #[inline] + pub fn foo(&self) -> i32 {unimplemented!()} + #[inline] + pub fn bar() -> i32 {unimplemented!()} + #[cold] + pub fn baz(&self) -> i32 {unimplemented!()} + #[cold] + pub fn bean() -> i32 {unimplemented!()} +} + +#[test] +fn inline_method() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .return_const(42i32); + assert_eq!(mock.foo(), 42); +} + +#[test] +fn inline_static_method() { + let ctx = MockFoo::bar_context(); + ctx.expect() + .return_const(42i32); + assert_eq!(MockFoo::bar(), 42); +} + +#[test] +fn cold_method() { + let mut mock = MockFoo::new(); + mock.expect_baz() + .return_const(42i32); + assert_eq!(mock.baz(), 42); +} + +#[test] +fn cold_static_method() { + let ctx = MockFoo::bean_context(); + ctx.expect() + .return_const(42i32); + assert_eq!(MockFoo::bean(), 42); +} diff --git a/tests/automock_instrument.rs b/tests/automock_instrument.rs new file mode 100644 index 0000000..4f210ff --- /dev/null +++ b/tests/automock_instrument.rs @@ -0,0 +1,22 @@ +// vim: tw=80 +//! A trait that uses tracing::instrument should be automockable. The mock +//! method won't be instrumented, though. +#![deny(warnings)] + +use mockall::*; +use tracing::instrument; + +#[derive(Debug)] +pub struct Foo {} + +#[automock] +impl Foo { + #[instrument] + fn foo(&self) {} + #[instrument] + fn bar() {} + #[tracing::instrument] + fn fooz(&self) {} + #[tracing::instrument] + fn barz() {} +} diff --git a/tests/automock_many_args.rs b/tests/automock_many_args.rs new file mode 100644 index 0000000..8393130 --- /dev/null +++ b/tests/automock_many_args.rs @@ -0,0 +1,81 @@ +// vim: tw=80 +//! mockall should be able to mock methods with at least 16 arguments +#![allow(clippy::too_many_arguments)] // Good job, Clippy! +#![allow(clippy::type_complexity)] +#![deny(warnings)] + +use mockall::{automock, predicate::*}; + +#[automock] +trait ManyArgs { + fn foo(&self, _a0: u8, _a1: u8, _a2: u8, _a3: u8, _a4: u8, _a5: u8, + _a6: u8, _a7: u8, _a8: u8, _a9: u8, _a10: u8, _a11: u8, + _a12: u8, _a13: u8, _a14: u8, _a15: u8); + fn bar(&self, _a0: u8, _a1: u8, _a2: u8, _a3: u8, _a4: u8, _a5: u8, + _a6: u8, _a7: u8, _a8: u8, _a9: u8, _a10: u8, _a11: u8, + _a12: u8, _a13: u8, _a14: u8, _a15: u8) -> &u32; + fn baz(&mut self, _a0: u8, _a1: u8, _a2: u8, _a3: u8, _a4: u8, _a5: u8, + _a6: u8, _a7: u8, _a8: u8, _a9: u8, _a10: u8, _a11: u8, + _a12: u8, _a13: u8, _a14: u8, _a15: u8) -> &mut u32; + fn bean(_a0: u8, _a1: u8, _a2: u8, _a3: u8, _a4: u8, _a5: u8, + _a6: u8, _a7: u8, _a8: u8, _a9: u8, _a10: u8, _a11: u8, + _a12: u8, _a13: u8, _a14: u8, _a15: u8); +} + +#[test] +#[should_panic( + expected = "MockManyArgs::foo: Expectation(true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true) called 0 time(s) which is fewer than expected 1" +)] +fn not_yet_satisfied() { + let mut mock = MockManyArgs::new(); + mock.expect_foo() + .with(always(), always(), always(), always(), always(), always(), always(), always(), always(), always(), always(), always(), always(), always(), always(), always(), ) + .times(1) + .returning(|_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _| ()); +} + +#[test] +fn returning() { + let mut mock = MockManyArgs::new(); + mock.expect_foo() + .returning(|_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _| ()); + mock.foo(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); +} + +#[test] +fn return_const() { + let mut mock = MockManyArgs::new(); + mock.expect_bar() + .return_const(42); + mock.bar(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); +} + +#[test] +fn return_var() { + let mut mock = MockManyArgs::new(); + mock.expect_baz() + .return_var(42); + mock.baz(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); +} + +#[test] +fn static_method_returning() { + let ctx = MockManyArgs::bean_context(); + ctx.expect() + .returning(|_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _| ()); + MockManyArgs::bean(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); +} + +#[test] +#[should_panic( + expected = "MockManyArgs::foo: Expectation(true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true) called 2 times which is more than the expected 1" +)] +fn too_many() { + let mut mock = MockManyArgs::new(); + mock.expect_foo() + .with(always(), always(), always(), always(), always(), always(), always(), always(), always(), always(), always(), always(), always(), always(), always(), always(), ) + .times(1) + .returning(|_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _| ()); + mock.foo(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + mock.foo(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); +} diff --git a/tests/automock_module.rs b/tests/automock_module.rs new file mode 100644 index 0000000..ddfe53e --- /dev/null +++ b/tests/automock_module.rs @@ -0,0 +1,50 @@ +// vim: tw=80 +//! Mocking an entire module of functions +#![deny(warnings)] + + +pub mod m { + use std::sync::Mutex; + use mockall::*; + type T = u32; + + static BAR_MTX: Mutex<()> = Mutex::new(()); + + #[automock] + pub mod foo { + use super::T; + pub fn bar(_x: T) -> i64 {unimplemented!()} + // Module functions should be able to use impl Trait, too + pub fn baz() -> impl std::fmt::Debug + Send { unimplemented!()} + // Module functions can use mutable arguments + pub fn bean(mut _x: u32) { unimplemented!() } + } + + #[test] + #[should_panic(expected = "mock_foo::bar(5): No matching expectation found")] + fn with_no_matches() { + let _m = BAR_MTX.lock(); + let ctx = mock_foo::bar_context(); + ctx.expect() + .with(predicate::eq(4)) + .return_const(0); + mock_foo::bar(5); + } + + #[test] + fn returning() { + let _m = BAR_MTX.lock(); + let ctx = mock_foo::bar_context(); + ctx.expect() + .returning(|x| i64::from(x) + 1); + assert_eq!(5, mock_foo::bar(4)); + } + + #[test] + fn impl_trait() { + let ctx = mock_foo::baz_context(); + ctx.expect() + .returning(|| Box::new(4)); + format!("{:?}", mock_foo::baz()); + } +} diff --git a/tests/automock_module_nonpub.rs b/tests/automock_module_nonpub.rs new file mode 100644 index 0000000..d131197 --- /dev/null +++ b/tests/automock_module_nonpub.rs @@ -0,0 +1,45 @@ +// vim: tw=80 +//! bare functions can use non-public types, as long as the object's visibility is compatible. +#![deny(warnings)] +#![allow(dead_code)] + +mod outer { + struct SuperT(); + + mod inner { + use mockall::automock; + + pub(crate) struct PubCrateT(); + struct PrivT(); + + #[automock] + mod m { + use super::*; + + pub(crate) fn foo(_x: PubCrateT) -> PubCrateT { + unimplemented!() + } + pub(super) fn bar(_x: PrivT) -> PrivT { + unimplemented!() + } + pub(in super::super) fn baz(_x: super::super::SuperT) + -> super::super::SuperT + { + unimplemented!() + } + pub(in crate::outer) fn bang(_x: crate::outer::SuperT) + -> crate::outer::SuperT + { + unimplemented!() + } + } + + #[test] + fn returning() { + let ctx = mock_m::foo_context(); + ctx.expect() + .returning(|x| x); + mock_m::foo(PubCrateT()); + } + } +} diff --git a/tests/automock_module_unused_import.rs b/tests/automock_module_unused_import.rs new file mode 100644 index 0000000..7f09b43 --- /dev/null +++ b/tests/automock_module_unused_import.rs @@ -0,0 +1,26 @@ +// vim: tw=80 +//! Mocking modules should not generated "unused_imports" warnings for imports +//! used by the bodies of the mocked functions. +//! https://github.com/asomers/mockall/issues/343 +#![deny(warnings)] + +use mockall::automock; + +#[automock] +pub mod foo { + use std::convert::TryInto; + + pub fn bar() { + let x = 42i32; + let _y: u32 = x.try_into().unwrap_or(0); + } +} + +#[test] +fn return_const() { + let ctx = mock_foo::bar_context(); + ctx.expect() + .once() + .return_const(()); + mock_foo::bar(); +} diff --git a/tests/automock_multiple_lifetime_parameters.rs b/tests/automock_multiple_lifetime_parameters.rs new file mode 100644 index 0000000..8bdae38 --- /dev/null +++ b/tests/automock_multiple_lifetime_parameters.rs @@ -0,0 +1,15 @@ +// vim: tw=80 +//! Methods with multiple generic lifetime parameters should produce their +//! generated code deterministically. +//! +//! This test is designed to work with "--cfg reprocheck" + +#![deny(warnings)] +#![allow(clippy::needless_lifetimes)] + +use mockall::*; + +#[automock] +pub trait Foo { + fn foo<'a, 'b, 'c, 'd, 'e, 'f>(&self, x: &'a &'b &'c &'d &'e &'f i32); +} diff --git a/tests/automock_must_use.rs b/tests/automock_must_use.rs new file mode 100644 index 0000000..d661fd4 --- /dev/null +++ b/tests/automock_must_use.rs @@ -0,0 +1,60 @@ +// vim: tw=80 +//! Mockall should be able to mock #[must_use] methods. The generated code +//! should contain #[must_use] on the mock method, but not the expect method. +#![deny(warnings)] + +use mockall::*; + +pub struct Foo {} + +#[automock] +impl Foo { + #[must_use] + pub fn bloob(&self) -> i32 {unimplemented!()} + #[must_use] + pub fn blarg() -> i32 {unimplemented!()} +} + +// test that basic code generation works with must_use structs and traits. The +// exact output will be verified by the unit tests. +#[must_use] +pub struct MustUseStruct {} +#[automock] +impl MustUseStruct {} +#[automock] +#[must_use] +pub trait MustUseTrait {} + +#[test] +fn must_use_method() { + let mut mock = MockFoo::new(); + mock.expect_bloob() + .return_const(42i32); + assert_eq!(42, mock.bloob()); +} + +#[cfg(feature = "nightly")] +#[test] +fn may_not_use_expectation() { + let mut mock = MockFoo::new(); + // This should not produce a "must_use" warning. + mock.expect_bloob(); +} + +#[test] +fn must_use_static_method() { + let ctx = MockFoo::blarg_context(); + ctx.expect() + .return_const(42i32); + assert_eq!(MockFoo::blarg(), 42); +} + +#[cfg(feature = "nightly")] +#[test] +fn may_not_use_static_expectation() { + let ctx = MockFoo::blarg_context(); + // This should not produce a "must_use" warning. + ctx.expect(); +} + + diff --git a/tests/automock_mutable_args.rs b/tests/automock_mutable_args.rs new file mode 100644 index 0000000..6bdb5f4 --- /dev/null +++ b/tests/automock_mutable_args.rs @@ -0,0 +1,34 @@ +// vim: tw=80 +//! A method with mutable arguments +#![deny(warnings)] + +use mockall::*; + +pub struct Foo {} + +#[automock] +impl Foo { + pub fn foo(&self, mut _x: u32) {} + pub fn bar(&mut self, _x: i32) -> i32 {0} +} + +#[test] +fn mutable_self() { + let mut mock = MockFoo::new(); + let mut count = 0; + mock.expect_bar() + .returning(move |x| { + count += x; + count + }); + assert_eq!(5, mock.bar(5)); + assert_eq!(10, mock.bar(5)); +} + +#[test] +fn returning() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .returning(|_| ()); + mock.foo(42); +} diff --git a/tests/automock_nondebug.rs b/tests/automock_nondebug.rs new file mode 100644 index 0000000..d6e2690 --- /dev/null +++ b/tests/automock_nondebug.rs @@ -0,0 +1,22 @@ +// vim: tw=80 +//! A method may have non-Debug arguments and/or return values. +#![deny(warnings)] + +use mockall::*; + +// Don't derive Debug +pub struct NonDebug{} + +#[automock] +pub trait Foo { + fn foo(&self, x: NonDebug); +} + +#[test] +#[should_panic(expected = "MockFoo::foo(?): No matching expectation found")] +fn with_no_matches() { + let mock = MockFoo::new(); + mock.foo(NonDebug{}); +} + + diff --git a/tests/automock_nonpub_methods.rs b/tests/automock_nonpub_methods.rs new file mode 100644 index 0000000..c534858 --- /dev/null +++ b/tests/automock_nonpub_methods.rs @@ -0,0 +1,22 @@ +// vim: tw=80 +//! Using automock on a struct with private methods should not result in any +//! "dead_code" warnings. Such methods are often private helpers used only by +//! other public methods. +#[deny(dead_code)] + +pub mod mymod { + use mockall::automock; + + pub struct Foo{} + + #[automock] + impl Foo { + fn private_helper(&self) {} + fn static_private_helper() {} + + pub fn pubfunc(&self) { + Self::static_private_helper(); + self.private_helper() + } + } +} diff --git a/tests/automock_nonsend.rs b/tests/automock_nonsend.rs new file mode 100644 index 0000000..059879f --- /dev/null +++ b/tests/automock_nonsend.rs @@ -0,0 +1,146 @@ +// vim: tw=80 +//! A method may have non-Send arguments and/or return values. +#![deny(warnings)] + +use mockall::*; +// Rc is not Send +use std::rc::Rc; + +// Neither Send nor Clone +pub struct NonSend(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); + } + + #[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)); + } +} + +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; +} + +mod refmut_method { + use super::*; + + #[automock] + trait Foo { + fn foo(&mut self, x: Rc); + fn bar(&mut self) -> &mut Rc; + } + + // 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)); + } +} + +pub mod static_method { + use super::*; + use std::sync::Mutex; + + static FOO_MTX: Mutex<()> = Mutex::new(()); + static BAR_MTX: Mutex<()> = Mutex::new(()); + static BAZ_MTX: Mutex<()> = Mutex::new(()); + + #[automock] + trait Foo { + fn foo(x: Rc); + fn bar() -> Rc; + fn baz() -> NonSend; + } + + #[test] + fn return_once_st() { + let _guard = BAZ_MTX.lock().unwrap(); + 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 _guard = BAR_MTX.lock().unwrap(); + let ctx = MockFoo::bar_context(); + ctx.expect() + .returning_st(|| Rc::new(42)); + assert_eq!(42, *MockFoo::bar()); + } + + #[test] + fn return_const_st() { + let _guard = BAR_MTX.lock().unwrap(); + let ctx = MockFoo::bar_context(); + ctx.expect() + .return_const_st(Rc::new(42)); + assert_eq!(42, *MockFoo::bar()); + } + + #[test] + fn withf_st() { + let _guard = FOO_MTX.lock().unwrap(); + let ctx = MockFoo::foo_context(); + ctx.expect() + .withf_st(|x| **x == 42) + .return_const(()); + MockFoo::foo(Rc::new(42)); + } +} diff --git a/tests/automock_nonstatic_struct.rs b/tests/automock_nonstatic_struct.rs new file mode 100644 index 0000000..ecb3399 --- /dev/null +++ b/tests/automock_nonstatic_struct.rs @@ -0,0 +1,53 @@ +// vim: tw=80 +//! Mock a struct with a lifetime parameter +#![deny(warnings)] + +use mockall::*; + +pub struct NonStaticStruct<'nss> { + _x: &'nss i32 +} + +#[automock] +impl<'nss> NonStaticStruct<'nss> { + pub fn foo(&self) -> i64 { + 42 + } + pub fn bar() -> i64{ + 42 + } + // XXX Constructors aren't yet supported for non-static Structs + //pub fn new(x: &'nss i32) -> Self { + //NonStaticStruct{x} + //} +} + +#[test] +fn normal_method() { + // This function serves to define a named lifetime + #[allow(clippy::trivially_copy_pass_by_ref)] + fn has_lt<'a>(_x: &'a i8) { + let mut mock = MockNonStaticStruct::<'a>::default(); + mock.expect_foo() + .returning(|| 5); + assert_eq!(5, mock.foo()); + } + + let x = 42i8; + has_lt(&x); +} + +#[test] +fn static_method() { + // This function serves to define a named lifetime + #[allow(clippy::trivially_copy_pass_by_ref)] + fn has_lt<'a>(_x: &'a i8) { + let ctx = MockNonStaticStruct::<'a>::bar_context(); + ctx.expect() + .returning(|| 5); + assert_eq!(5, MockNonStaticStruct::bar()); + } + + let x = 42i8; + has_lt(&x); +} diff --git a/tests/automock_partial_eq.rs b/tests/automock_partial_eq.rs new file mode 100644 index 0000000..2618b4e --- /dev/null +++ b/tests/automock_partial_eq.rs @@ -0,0 +1,39 @@ +// vim: tw=80 +//! Mockall should deselfify `Self` types, even if they aren't named `self`. +use mockall::*; + +mock! { + #[derive(Debug)] + pub Foo { + fn compare(&self, other: &Self) -> bool; + } + impl PartialEq for Foo { + fn eq(&self, other: &Self) -> bool; + } +} + +#[test] +fn inherent_method() { + let mut x = MockFoo::default(); + let mut y = MockFoo::default(); + x.expect_compare() + .return_const(true); + y.expect_compare() + .return_const(false); + + assert!(x.compare(&y)); + assert!(!y.compare(&x)); +} + +#[test] +fn trait_method() { + let mut x = MockFoo::default(); + let mut y = MockFoo::default(); + x.expect_eq() + .return_const(true); + y.expect_eq() + .return_const(false); + + assert_eq!(x, y); + assert!(y != x); +} diff --git a/tests/automock_qself.rs b/tests/automock_qself.rs new file mode 100644 index 0000000..2b10034 --- /dev/null +++ b/tests/automock_qself.rs @@ -0,0 +1,54 @@ +// vim: tw=80 +//! Using QSelf in an argument, a where clause, or a return type +#![deny(warnings)] + +use mockall::*; + +pub trait Foo { + type Output; +} + +pub struct SendFoo {} +impl Foo for SendFoo { + type Output = u32; +} + +pub struct A{} +#[automock] +impl A { + pub fn foo(&self, _q: ::Output) { + } + pub fn bar(&self, _t: T) -> ::Output { + unimplemented!() + } + pub fn bean(&self, _t: T) + where T: Foo + 'static, + ::Output: Send + { + } +} + +#[test] +fn arguments() { + let mut mock = MockA::new(); + mock.expect_foo::() + .with(predicate::eq(42u32)) + .return_const(()); + mock.foo::(42u32); +} + +#[test] +fn where_clause() { + let mut mock = MockA::new(); + mock.expect_bean::() + .return_const(()); + mock.bean(SendFoo{}); +} + +#[test] +fn return_value() { + let mut mock = MockA::new(); + mock.expect_bar::() + .return_const(42u32); + assert_eq!(42, mock.bar(SendFoo{})); +} diff --git a/tests/automock_refmut_arguments.rs b/tests/automock_refmut_arguments.rs new file mode 100644 index 0000000..d65a00e --- /dev/null +++ b/tests/automock_refmut_arguments.rs @@ -0,0 +1,23 @@ +// vim: tw=80 +//! A method that takes mutable reference arguments, returning information +//! through its arguments like C functions often do. +#![deny(warnings)] + +use mockall::*; + +#[automock] +trait T { + fn foo(&self, x: &mut u32); +} + +#[test] +fn returning() { + let mut mock = MockT::new(); + let mut x = 5; + mock.expect_foo() + .returning(|x: &mut u32| { + *x = 42; + }); + mock.foo(&mut x); + assert_eq!(42, x); +} diff --git a/tests/automock_return_mutable_ref.rs b/tests/automock_return_mutable_ref.rs new file mode 100644 index 0000000..705a3eb --- /dev/null +++ b/tests/automock_return_mutable_ref.rs @@ -0,0 +1,22 @@ +// vim: tw=80 +//! A method that returns a mutable reference +#![deny(warnings)] + +use mockall::*; + +#[automock] +trait A { + fn foo(&mut self) -> &mut u32; +} + +#[test] +fn return_var() { + let mut mock = MockA::new(); + mock.expect_foo().return_var(5); + { + let r = mock.foo(); + assert_eq!(5, *r); + *r = 6; + } + assert_eq!(6, *mock.foo()); +} diff --git a/tests/automock_return_owned.rs b/tests/automock_return_owned.rs new file mode 100644 index 0000000..826d711 --- /dev/null +++ b/tests/automock_return_owned.rs @@ -0,0 +1,32 @@ +// vim: tw=80 +//! A method that returns ownership of a value, rather than returning by Copy +#![deny(warnings)] + +use mockall::*; + +struct NonCopy {} + +#[automock] +trait T { + fn foo(&self) -> NonCopy; +} + +#[test] +fn return_once() { + let mut mock = MockT::new(); + let r = NonCopy{}; + mock.expect_foo() + .return_once(|| r); + mock.foo(); +} + +#[test] +#[should_panic(expected = "MockT::foo: Expectation() called twice, but it returns by move")] +fn return_once_too_many_times() { + let mut mock = MockT::new(); + let r = NonCopy{}; + mock.expect_foo() + .return_once(|| r); + mock.foo(); + mock.foo(); +} diff --git a/tests/automock_return_reference.rs b/tests/automock_return_reference.rs new file mode 100644 index 0000000..f312217 --- /dev/null +++ b/tests/automock_return_reference.rs @@ -0,0 +1,17 @@ +// vim: tw=80 +//! A method that returns an immutable reference +#![deny(warnings)] + +use mockall::*; + +#[automock] +trait A { + fn foo(&self) -> &u32; +} + +#[test] +fn return_const() { + let mut mock = MockA::new(); + mock.expect_foo().return_const(5); + assert_eq!(5, *mock.foo()); +} diff --git a/tests/automock_return_static_ref.rs b/tests/automock_return_static_ref.rs new file mode 100644 index 0000000..1f8c533 --- /dev/null +++ b/tests/automock_return_static_ref.rs @@ -0,0 +1,18 @@ +// vim: tw=80 +//! A method that returns an immutable 'static reference +#![deny(warnings)] + +use mockall::*; + +#[automock] +trait A { + fn foo(&self) -> &'static u32; +} + +#[test] +fn return_const() { + const X: u32 = 5; + let mut mock = MockA::new(); + mock.expect_foo().return_const(&X); + assert_eq!(5, *mock.foo()); +} diff --git a/tests/automock_rpitit.rs b/tests/automock_rpitit.rs new file mode 100644 index 0000000..4bf2974 --- /dev/null +++ b/tests/automock_rpitit.rs @@ -0,0 +1,22 @@ +// vim: tw=80 +//! Return position Impl Trait in Traits +//! https://rust-lang.github.io/rfcs/3425-return-position-impl-trait-in-traits.html +#![cfg(feature = "nightly")] +#![deny(warnings)] + +use std::fmt::Debug; + +use mockall::*; + +#[automock] +trait Foo { + fn bar(&self) -> impl Debug; +} + +#[test] +fn returning() { + let mut foo = MockFoo::default(); + foo.expect_bar() + .returning(|| Box::new(42u32)); + assert_eq!("42", format!("{:?}", foo.bar())); +} diff --git a/tests/automock_self_by_value.rs b/tests/automock_self_by_value.rs new file mode 100644 index 0000000..45af055 --- /dev/null +++ b/tests/automock_self_by_value.rs @@ -0,0 +1,30 @@ +// vim: tw=80 +//! A method that consumes self +#![deny(warnings)] + +use mockall::*; + +pub struct MethodByValue {} + +#[automock] +impl MethodByValue { + pub fn foo(self, _x: u32) -> i64 {0} + #[allow(unused_mut)] + pub fn bar(mut self) {} +} + +#[test] +fn immutable() { + let mut mock = MockMethodByValue::new(); + mock.expect_foo() + .returning(|x| i64::from(x) + 1); + assert_eq!(5, mock.foo(4)); +} + +#[test] +fn mutable() { + let mut mock = MockMethodByValue::new(); + mock.expect_bar() + .returning(|| ()); + mock.bar(); +} diff --git a/tests/automock_send.rs b/tests/automock_send.rs new file mode 100644 index 0000000..1f7d468 --- /dev/null +++ b/tests/automock_send.rs @@ -0,0 +1,17 @@ +// vim: tw=80 +//! A mock object should be Send +#![deny(warnings)] + +use mockall::*; + +#[automock] +pub trait T { + fn foo(&self) -> u32; +} + +#[test] +#[allow(clippy::unnecessary_operation)] // The cast is the whole point +fn cast_to_send() { + let mock = MockT::new(); + let _m = Box::new(mock) as Box; +} diff --git a/tests/automock_seven_args.rs b/tests/automock_seven_args.rs new file mode 100644 index 0000000..6760f02 --- /dev/null +++ b/tests/automock_seven_args.rs @@ -0,0 +1,22 @@ +// vim: tw=80 +//! When mocking static functions just at the threshold of Clippy's type +//! complexity limit, no warnings should be emitted regarding the generated +//! code. +#![deny(warnings)] + +use mockall::automock; + +#[automock] +trait ManyArgs { + fn foo(_a0: u8, _a1: u8, _a2: u8, _a3: u8, _a4: u8, _a5: u8, _a6: u8); +} + +#[test] +fn static_method_returning() { + let ctx = MockManyArgs::foo_context(); + ctx.expect() + .returning(|_, _, _, _, _, _, _| ()); + MockManyArgs::foo(0, 0, 0, 0, 0, 0, 0); +} + + diff --git a/tests/automock_slice_arguments.rs b/tests/automock_slice_arguments.rs new file mode 100644 index 0000000..37044ac --- /dev/null +++ b/tests/automock_slice_arguments.rs @@ -0,0 +1,35 @@ +// vim: tw=80 +//! a method with slice arguments +#![deny(warnings)] + +use mockall::*; + +#[automock] +trait Foo { + fn foo(&self, x: &[u8]); +} + +mod withf { + use super::*; + + #[test] + #[should_panic(expected = "MockFoo::foo([1, 2, 3, 4]): No matching expectation found")] + fn fail() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .withf(|sl| sl == [1, 2, 3]) + .returning(|_| ()); + let x = vec![1, 2, 3, 4]; + mock.foo(&x); + } + + #[test] + fn ok() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .withf(|sl| sl == [1, 2, 3]) + .returning(|_| ()); + let x = vec![1, 2, 3]; + mock.foo(&x); + } +} diff --git a/tests/automock_specializing_method_of_nongeneric_struct.rs b/tests/automock_specializing_method_of_nongeneric_struct.rs new file mode 100644 index 0000000..8f82f57 --- /dev/null +++ b/tests/automock_specializing_method_of_nongeneric_struct.rs @@ -0,0 +1,29 @@ +// vim: tw=80 +//! Non-generic structs can have specializing methods, too. For example, some +//! methods place constraints on Self. It's even possible for a where clause to +//! place a constraint on types that appear nowhere in the struct's signatures. +#![deny(warnings)] + +use mockall::*; + +#[automock] +trait Bar { + fn bar(&self) where Self: Sized; + fn baz() where Self: Sized; +} + +#[test] +fn nonstatic() { + let mut mock = MockBar::default(); + mock.expect_bar() + .return_const(()); + mock.bar(); +} + +#[test] +fn static_method() { + let ctx = MockBar::baz_context(); + ctx.expect() + .return_const(()); + MockBar::baz(); +} diff --git a/tests/automock_specializing_methods.rs b/tests/automock_specializing_methods.rs new file mode 100644 index 0000000..e4f3af4 --- /dev/null +++ b/tests/automock_specializing_methods.rs @@ -0,0 +1,33 @@ +// vim: tw=80 +//! A specializing method is a non-generic method of a generic struct that +//! places additional bounds on the struct's generic types via a where +//! clause. +#![deny(warnings)] + +use mockall::*; + +struct G(T); + +#[derive(Clone, Copy)] +struct NonDefault{} + +#[automock] +trait Foo where T: Copy { + fn foo(&self, t: T) -> G where T: Default + 'static; +} + +#[test] +fn returning() { + let mut mock = MockFoo::::default(); + mock.expect_foo() + .returning(G); + assert_eq!(42u32, mock.foo(42u32).0); + + // It's possible to instantiate a mock object that doesn't satisfy the + // specializing method's requirements: + let _mock2 = MockFoo::::default(); + // But you can't call the specializing method. This won't work: + // _mock2.expect_foo() + // .returning(|h| G(h)); + // _mock2.foo(NonDefault{}); +} diff --git a/tests/automock_static_method.rs b/tests/automock_static_method.rs new file mode 100644 index 0000000..bcdff4d --- /dev/null +++ b/tests/automock_static_method.rs @@ -0,0 +1,18 @@ +// vim: tw=80 +//! automocking a trait with a static method +#![deny(warnings)] + +use mockall::*; + +#[automock] +trait A { + fn bar() -> u32; +} + +#[test] +fn returning() { + let ctx = MockA::bar_context(); + ctx.expect() + .returning(|| 42); + assert_eq!(42, MockA::bar()); +} diff --git a/tests/automock_struct.rs b/tests/automock_struct.rs new file mode 100644 index 0000000..d344167 --- /dev/null +++ b/tests/automock_struct.rs @@ -0,0 +1,22 @@ +// vim: tw=80 +//! automocking a struct +#![deny(warnings)] + +use mockall::*; + +pub struct SimpleStruct {} + +#[automock] +impl SimpleStruct { + pub fn foo(&self, _x: u32) -> i64 { + 42 + } +} + +#[test] +fn returning() { + let mut mock = MockSimpleStruct::new(); + mock.expect_foo() + .returning(|x| i64::from(x) + 1); + assert_eq!(5, mock.foo(4)); +} diff --git a/tests/automock_trait.rs b/tests/automock_trait.rs new file mode 100644 index 0000000..81261d6 --- /dev/null +++ b/tests/automock_trait.rs @@ -0,0 +1,18 @@ +// vim: tw=80 +//! automocking a trait +#![deny(warnings)] + +use mockall::*; + +#[automock] +trait SimpleTrait { + fn foo(&self, x: u32) -> u32; +} + +#[test] +fn returning() { + let mut mock = MockSimpleTrait::new(); + mock.expect_foo() + .returning(|x| x + 1); + assert_eq!(5, mock.foo(4)); +} diff --git a/tests/automock_trait_object.rs b/tests/automock_trait_object.rs new file mode 100644 index 0000000..78f93a0 --- /dev/null +++ b/tests/automock_trait_object.rs @@ -0,0 +1,42 @@ +// vim: tw=80 +//! a method that uses unboxed trait object arguments +#![deny(warnings)] + +use mockall::*; + +#[automock] +trait Foo { + fn foo(&self, x: &dyn PartialEq); +} + +// It's almost impossible to construct a Predicate object that will work with +// trait objects for two reasons: +// * Mockall requires the predicates to be Debug, and any useful predicate will +// also be Eq, Ord, or similar, but Rust only allows up to one non-auto trait +// per trait object. +// * The predicate is requird to meet the HRTB +// for<'a> Predicate<(dyn Trait + 'a)> +// That's not impossible, but Mockall doesn't provide any way to construct +// such a predicate. +// So, use of `with` is pretty much limited to predicates like `always` and +// `function`. +#[test] +fn with() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .with(predicate::always()) + .return_const(()); + mock.foo(&42u32) +} + +/// trait object arguments can't be matched with `predicate::eq`, because +/// `PartialEq` cannot be made into a trait object. But withf still +/// works. +#[test] +fn withf() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .withf(|x| *x == 42u32) + .return_const(()); + mock.foo(&42u32); +} diff --git a/tests/automock_unsafe_trait.rs b/tests/automock_unsafe_trait.rs new file mode 100644 index 0000000..ff64eb7 --- /dev/null +++ b/tests/automock_unsafe_trait.rs @@ -0,0 +1,37 @@ +// vim: ts=80 +#![deny(warnings)] + +use mockall::*; + +#[automock] +#[allow(clippy::missing_safety_doc)] +pub unsafe trait Foo { + fn foo(&self) -> i32; +} + +pub struct Baz{} + +#[automock] +unsafe impl Foo for Baz { + fn foo(&self) -> i32 { + unimplemented!() + } +} + +#[test] +fn automock_trait() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .return_const(42); + + assert_eq!(42, mock.foo()); +} + +#[test] +fn automock_trait_impl() { + let mut mock = MockBaz::new(); + mock.expect_foo() + .return_const(42); + + assert_eq!(42, mock.foo()); +} diff --git a/tests/automock_where_self.rs b/tests/automock_where_self.rs new file mode 100644 index 0000000..eedf34f --- /dev/null +++ b/tests/automock_where_self.rs @@ -0,0 +1,29 @@ +// vim: ts=80 +//! Methods with a "where Self: ..." where clause should be mocked as concrete, +//! not generic. +#![deny(warnings)] +#![allow(clippy::needless_lifetimes)] + +// Enclose the mocked trait within a non-public module. With some versions of +// rustc, that causes "unused method" errors for the generic code, but not the +// concrete code. +// rustc 1.66.0-nightly (e7119a030 2022-09-22) +mod mymod { + + #[mockall::automock] + pub trait Server { + fn version<'a>(&'a self) -> Option<&'static str> where Self: Sized; + } + +} + +use mymod::{MockServer, Server}; + +#[test] +fn return_const() { + let mut mock = MockServer::new(); + mock.expect_version() + .return_const(None); + + mock.version(); +} diff --git a/tests/cfg_attr_concretize.rs b/tests/cfg_attr_concretize.rs new file mode 100644 index 0000000..93af750 --- /dev/null +++ b/tests/cfg_attr_concretize.rs @@ -0,0 +1,26 @@ +// vim: tw=80 +//! #[concretize] can be used inside of #[cfg_attr()]` +#![deny(warnings)] + +use std::path::{Path, PathBuf}; + +use mockall::{automock, concretize}; + +#[automock] +trait Foo { + #[cfg_attr(not(target_os = "ia64-unknown-multics"), concretize)] + fn foo>(&self, p: P); +} + + +#[test] +fn withf() { + let mut foo = MockFoo::new(); + foo.expect_foo() + .withf(|p| p.as_ref() == Path::new("/tmp")) + .times(3) + .return_const(()); + foo.foo(Path::new("/tmp")); + foo.foo(PathBuf::from(Path::new("/tmp"))); + foo.foo("/tmp"); +} diff --git a/tests/clear_expectations_on_panic.rs b/tests/clear_expectations_on_panic.rs new file mode 100644 index 0000000..a70df15 --- /dev/null +++ b/tests/clear_expectations_on_panic.rs @@ -0,0 +1,50 @@ +// vim: tw=80 +//! Static methods' expectations should be dropped during panic. +//! +//! https://github.com/asomers/mockall/issues/442 +#![deny(warnings)] + +use std::panic; + +use mockall::*; + +#[automock] +pub trait Foo { + fn foo() -> i32; + fn bar() -> i32; +} + +#[test] +fn too_few_calls() { + panic::catch_unwind(|| { + let ctx = MockFoo::foo_context(); + ctx.expect() + .times(1) + .return_const(42); + }).unwrap_err(); + + // The previously set expectation should've been cleared during the panic, + // so we must set a new one. + let ctx = MockFoo::foo_context(); + ctx.expect() + .times(1) + .return_const(42); + assert_eq!(42, MockFoo::foo()); +} + +// We shouldn't panic during drop in this case. Regression test for +// https://github.com/asomers/mockall/issues/491 +#[cfg_attr(not(feature = "nightly"), ignore)] +#[test] +fn too_many_calls() { + panic::catch_unwind(|| { + let ctx = MockFoo::bar_context(); + ctx.expect() + .times(0); + MockFoo::bar(); + }).unwrap_err(); + + // This line will panic with a PoisonError, at least until issue #515 is + // complete. + let _ctx = MockFoo::bar_context(); +} diff --git a/tests/link_name.rs b/tests/link_name.rs new file mode 100644 index 0000000..0987cce --- /dev/null +++ b/tests/link_name.rs @@ -0,0 +1,19 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; + +#[automock] +pub mod ffi { + extern "C" { + #[link_name = "foo__extern"] + pub fn foo() -> u32; + } +} + +#[test] +fn return_const() { + let ctx = mock_ffi::foo_context(); + ctx.expect().return_const(42u32); + assert_eq!(42, unsafe{mock_ffi::foo()}); +} diff --git a/tests/mock_associated_const.rs b/tests/mock_associated_const.rs new file mode 100644 index 0000000..eae84ff --- /dev/null +++ b/tests/mock_associated_const.rs @@ -0,0 +1,34 @@ +// vim: tw=80 +//! A trait with an associated constant +//! +//! https://github.com/asomers/mockall/issues/97 +#![deny(warnings)] + +use mockall::*; + +trait Foo { + const X: i32; + + fn x_plus_one(&self) -> i32 { + Self::X + 1 + } +} + +mock! { + Foo { + const Y: i32 = 69; + } + impl Foo for Foo { + const X: i32 = 42; + } +} + +#[test] +fn default_method() { + assert_eq!(MockFoo::new().x_plus_one(), 43); +} + +#[test] +fn on_the_struct() { + assert_eq!(MockFoo::Y, 69); +} diff --git a/tests/mock_associated_types.rs b/tests/mock_associated_types.rs new file mode 100644 index 0000000..8cf7aa9 --- /dev/null +++ b/tests/mock_associated_types.rs @@ -0,0 +1,22 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; + +mock! { + MyIter {} + impl Iterator for MyIter { + type Item=u32; + + fn next(&mut self) -> Option; + } +} + +#[test] +fn returning() { + let mut mock = MockMyIter::new(); + mock.expect_next() + .returning(|| Some(5)); + assert_eq!(5, mock.next().unwrap()); +} + diff --git a/tests/mock_async_fn.rs b/tests/mock_async_fn.rs new file mode 100644 index 0000000..0988559 --- /dev/null +++ b/tests/mock_async_fn.rs @@ -0,0 +1,39 @@ +// vim: tw=80 +//! A struct with an async function +#![deny(warnings)] + +use futures::executor::block_on; +use mockall::*; + +mock! { + pub Foo { + async fn foo(&self) -> u32; + async fn bar() -> u32; + async fn baz(&self, t: T) -> T; + } +} + +#[test] +fn return_const() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .return_const(42u32); + assert_eq!(block_on(mock.foo()), 42); +} + +#[test] +fn static_method() { + let ctx = MockFoo::bar_context(); + ctx.expect() + .return_const(42u32); + assert_eq!(block_on(MockFoo::bar()), 42); +} + +#[test] +fn generic_method() { + let mut mock = MockFoo::new(); + mock.expect_baz() + .with(predicate::eq(69u32)) + .return_const(42u32); + assert_eq!(block_on(mock.baz(69u32)), 42u32); +} diff --git a/tests/mock_async_trait.rs b/tests/mock_async_trait.rs new file mode 100644 index 0000000..7493dd9 --- /dev/null +++ b/tests/mock_async_trait.rs @@ -0,0 +1,28 @@ +// vim: tw=80 +//! An async trait, for use with Futures +#![deny(warnings)] + +use async_trait::async_trait; +use futures::executor::block_on; +use mockall::*; + +#[async_trait] +pub trait Foo { + async fn foo(&self) -> u32; +} + +mock! { + pub Bar { } + #[async_trait] + impl Foo for Bar { + async fn foo(&self) -> u32; + } +} + +#[test] +fn return_const() { + let mut mock = MockBar::new(); + mock.expect_foo() + .return_const(42u32); + assert_eq!(block_on(mock.foo()), 42); +} diff --git a/tests/mock_box_self.rs b/tests/mock_box_self.rs new file mode 100644 index 0000000..8d17754 --- /dev/null +++ b/tests/mock_box_self.rs @@ -0,0 +1,68 @@ +// vim: tw=80 +//! Methods that take receivers like Box instead of &self +#![allow(clippy::borrowed_box, clippy::boxed_local)] +#![deny(warnings)] + +use mockall::*; +use std::sync::Arc; +use std::pin::Pin; +use std::rc::Rc; + +mock! { + Foo { + fn foo(self: &Box); + fn baz(mut self: Box); + fn bar(self: Box); + fn bean(self: Arc); + fn booz(self: Pin>); + fn blez(self: Rc); + } +} + +#[test] +fn arc() { + let mut mock = MockFoo::new(); + mock.expect_bean() + .returning(|| ()); + Arc::new(mock).bean(); +} + +#[test] +fn pin() { + let mut mock = MockFoo::new(); + mock.expect_booz() + .returning(|| ()); + Pin::new(Box::new(mock)).booz(); +} + +#[test] +fn rc() { + let mut mock = MockFoo::new(); + mock.expect_blez() + .returning(|| ()); + Rc::new(mock).blez(); +} + +#[test] +fn ref_box() { + let mut mock = Box::new(MockFoo::new()); + mock.expect_foo() + .returning(|| ()); + mock.foo(); +} + +#[test] +fn mutable() { + let mut mock = Box::new(MockFoo::new()); + mock.expect_baz() + .returning(|| ()); + mock.baz(); +} + +#[test] +fn owned() { + let mut mock = Box::new(MockFoo::new()); + mock.expect_bar() + .returning(|| ()); + mock.bar(); +} diff --git a/tests/mock_cfg.rs b/tests/mock_cfg.rs new file mode 100644 index 0000000..b846a87 --- /dev/null +++ b/tests/mock_cfg.rs @@ -0,0 +1,65 @@ +// vim: tw=80 +//! mock's methods and trait impls can be conditionally compiled +#![deny(warnings)] + +use mockall::*; + +// For this test, use the "nightly" feature as the cfg gate, because it's tested +// both ways in CI. +#[cfg(feature = "nightly")] +pub trait Beez { + fn beez(&self); +} +#[cfg(not(feature = "nightly"))] +pub trait Beez { + fn beez(&self, x: i32) -> i32; +} + +mock! { + pub Foo { + #[cfg(feature = "nightly")] + pub fn foo(&self); + #[cfg(not(feature = "nightly"))] + pub fn foo(&self, x: i32) -> i32; + } + #[cfg(feature = "nightly")] + impl Beez for Foo { + fn beez(&self); + } + #[cfg(not(feature = "nightly"))] + impl Beez for Foo { + fn beez(&self, x: i32) -> i32; + } +} + +#[test] +#[cfg(feature = "nightly")] +fn test_nightly_method() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .returning(|| ()); +} + +#[test] +#[cfg(feature = "nightly")] +fn test_nightly_trait() { + let mut mock = MockFoo::new(); + mock.expect_beez() + .returning(|| ()); +} + +#[test] +#[cfg(not(feature = "nightly"))] +fn test_not_nightly_method() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .returning(|x| x + 1); +} + +#[test] +#[cfg(not(feature = "nightly"))] +fn test_not_nightly_trait() { + let mut mock = MockFoo::new(); + mock.expect_beez() + .returning(|x| x + 1); +} diff --git a/tests/mock_clone.rs b/tests/mock_clone.rs new file mode 100644 index 0000000..8f4a227 --- /dev/null +++ b/tests/mock_clone.rs @@ -0,0 +1,22 @@ +// vim: tw=80 +//! Clone-like methods (non-static method with Self return type) need the return +//! type to be deselfified. +#![deny(warnings)] + +use mockall::*; + +mock! { + pub A {} + impl Clone for A { + fn clone(&self) -> Self; + } +} + +#[allow(clippy::redundant_clone)] +#[test] +fn returning() { + let mut mock0 = MockA::new(); + mock0.expect_clone() + .returning(MockA::new); + let _mock1 = mock0.clone(); +} diff --git a/tests/mock_closure.rs b/tests/mock_closure.rs new file mode 100644 index 0000000..412f0f4 --- /dev/null +++ b/tests/mock_closure.rs @@ -0,0 +1,89 @@ +// vim: tw=80 +//! A method with a closure argument can be mocked, by turning the closure into +//! a Boxed Fn. +#![deny(warnings)] + +use mockall::*; + +struct NonCopy(u32); + +mock!{ + Foo { + fn foo u32 + 'static>(&self, f: F) -> u32; + fn bar u32 + 'static>(&self, f: F) -> u32; + fn baz NonCopy + 'static>(&self, f: F) -> NonCopy; + fn bean u32 + 'static>(f: F) -> u32; + // Not technically a closure, but it should work too + fn bang(&self, f: fn(u32) -> u32) -> u32; + // Identical to foo, but it uses a where clause + fn food(&self, f: F) -> u32 where F: Fn(u32) -> u32 + 'static; + // Identical to foo, but with extra unrelated where predicates + fn foody(&self, f: F, g:G) -> u32 + where F: Fn(u32) -> u32 + 'static, + G: 'static; + } +} + +mod returning { + use super::*; + + #[test] + fn bare_fn() { + let mut mock = MockFoo::new(); + mock.expect_bang() + .returning(|f| f(42)); + assert_eq!(84, mock.bang(|x| 2 * x)); + } + + #[test] + fn immutable() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .returning(|f| f(42)); + assert_eq!(84, mock.foo(|x| 2 * x)); + } + + #[test] + fn mutable() { + let mut mock = MockFoo::new(); + mock.expect_bar() + .returning(|mut f| {f(42); f(5) } ); + let mut counter = 0; + assert_eq!(47, mock.bar(move |x| { counter += x; counter } )); + } + + #[test] + fn once() { + let mut mock = MockFoo::new(); + mock.expect_baz() + .returning(|f| f(42)); + let initial = NonCopy(5); + assert_eq!(47, mock.baz(move |x| NonCopy(initial.0 + x)).0); + } + + #[test] + fn static_method() { + let ctx = MockFoo::bean_context(); + ctx.expect() + .returning(|f| f(42)); + assert_eq!(84, MockFoo::bean(|x| 2 * x)); + } + + // food's where clause should be completely deleted in the mock + // implementation. + #[test] + fn deleted_where_clause() { + let mut mock = MockFoo::new(); + mock.expect_food() + .returning(|f| f(42)); + assert_eq!(84, mock.food(|x| 2 * x)); + } + + #[test] + fn where_clause() { + let mut mock = MockFoo::new(); + mock.expect_foody() + .returning(|f, _g: u32| f(42)); + assert_eq!(84, mock.foody(|x| 2u32 * x, 0u32)); + } +} diff --git a/tests/mock_concrete_struct_with_generic_trait.rs b/tests/mock_concrete_struct_with_generic_trait.rs new file mode 100644 index 0000000..2fd07f5 --- /dev/null +++ b/tests/mock_concrete_struct_with_generic_trait.rs @@ -0,0 +1,24 @@ +// vim: tw=80 +//! A concrete struct that implements a generic trait +#![deny(warnings)] + +use mockall::*; + +trait Foo { + fn foo(&self, x: T) -> T; +} +mock! { + Bar {} + impl Foo for Bar { + fn foo(&self, x: i32) -> i32; + } +} + +#[test] +fn returning() { + let mut mock = MockBar::new(); + mock.expect_foo() + .with(predicate::eq(42)) + .returning(|x| x + 1); + assert_eq!(43, mock.foo(42)); +} diff --git a/tests/mock_concretize.rs b/tests/mock_concretize.rs new file mode 100644 index 0000000..f9d73ef --- /dev/null +++ b/tests/mock_concretize.rs @@ -0,0 +1,140 @@ +// vim: tw=80 +//! Some generic methods with non-`'static` generic parameters can be mocked by +//! transforming the function arguments into trait objects. +#![deny(warnings)] + +use mockall::*; +use std::path::{Path, PathBuf}; + +trait AsRefMut: AsRef + AsMut {} +impl AsRefMut for Q where Q: AsRef + AsMut, T: ?Sized {} + +mock! { + Foo { + /// Base concretized function + #[mockall::concretize] + fn foo>(&self, x: P); + + /// With a where clause + #[mockall::concretize] + fn boom

(&self, x: P) where P: AsRef; + + /// Static function + #[mockall::concretize] + fn bang>(x: P); + + /// Reference argument + #[mockall::concretize] + fn boomref>(&self, x: &P); + + /// Mutable reference argument + #[mockall::concretize] + fn boom_mutref>(&self, x: &mut T); + + /// Slice argument + #[mockall::concretize] + fn boomv

(&self, x: &[P]) where P: AsRef; + + /// Public visiblity + #[mockall::concretize] + pub fn foopub>(&self, x: P); + } +} + +mod generic_arg { + use super::*; + + #[test] + fn withf() { + let mut foo = MockFoo::new(); + foo.expect_foo() + .withf(|p| p.as_ref() == Path::new("/tmp")) + .times(3) + .return_const(()); + foo.foo(Path::new("/tmp")); + foo.foo(PathBuf::from(Path::new("/tmp"))); + foo.foo("/tmp"); + } +} + +mod where_clause { + use super::*; + + #[test] + fn withf() { + let mut foo = MockFoo::new(); + foo.expect_boom() + .withf(|p| p.as_ref() == Path::new("/tmp")) + .times(3) + .return_const(()); + foo.boom(Path::new("/tmp")); + foo.boom(PathBuf::from(Path::new("/tmp"))); + foo.boom("/tmp"); + } +} + +mod mutable_reference_arg { + use super::*; + + #[test] + fn withf() { + let mut foo = MockFoo::new(); + foo.expect_boom_mutref() + .withf(|p| p.as_ref() == "/tmp") + .once() + .returning(|s| s.as_mut().make_ascii_uppercase()); + let mut s = String::from("/tmp"); + foo.boom_mutref(&mut s); + assert_eq!(s, "/TMP"); + } +} + +mod reference_arg { + use super::*; + + #[test] + fn withf() { + let mut foo = MockFoo::new(); + foo.expect_boomref() + .withf(|p| p.as_ref() == Path::new("/tmp")) + .times(3) + .return_const(()); + foo.boomref(&Path::new("/tmp")); + foo.boomref(&PathBuf::from(Path::new("/tmp"))); + foo.boomref(&"/tmp"); + } +} + +mod slice { + use super::*; + + #[test] + fn withf() { + let mut foo = MockFoo::new(); + foo.expect_boomv() + .withf(|v| + v[0].as_ref() == Path::new("/tmp") && + v[1].as_ref() == Path::new("/mnt") + ).times(3) + .return_const(()); + foo.boomv(&[Path::new("/tmp"), Path::new("/mnt")]); + foo.boomv(&[PathBuf::from("/tmp"), PathBuf::from("/mnt")]); + foo.boomv(&["/tmp", "/mnt"]); + } +} + +mod static_method { + use super::*; + + #[test] + fn withf() { + let ctx = MockFoo::bang_context(); + ctx.expect() + .withf(|p| p.as_ref() == Path::new("/tmp")) + .times(3) + .return_const(()); + MockFoo::bang(Path::new("/tmp")); + MockFoo::bang(PathBuf::from(Path::new("/tmp"))); + MockFoo::bang("/tmp"); + } +} diff --git a/tests/mock_concretize_with_bounds.rs b/tests/mock_concretize_with_bounds.rs new file mode 100644 index 0000000..76b55a8 --- /dev/null +++ b/tests/mock_concretize_with_bounds.rs @@ -0,0 +1,135 @@ +// vim: tw=80 +//! Using #[concretize] on generic types with trait bounds +#![deny(warnings)] + +use mockall::*; +use std::path::{Path, PathBuf}; + +trait AsRefMut: AsRef + AsMut {} +impl AsRefMut for Q where Q: AsRef + AsMut, T: ?Sized {} + +mock! { + Foo { + /// Base concretized function + #[mockall::concretize] + fn foo + Send>(&self, x: P); + + /// With a where clause + #[mockall::concretize] + fn boom

(&self, x: P) where P: AsRef + Send; + + /// Static function + #[mockall::concretize] + fn bang + Send>(x: P); + + /// Reference argument + #[mockall::concretize] + fn boomref + Send>(&self, x: &P); + + /// Mutable reference argument + #[mockall::concretize] + fn boom_mutref + Send>(&self, x: &mut T); + + /// Slice argument + #[mockall::concretize] + fn boomv

(&self, x: &[P]) where P: AsRef + Send; + } +} + +mod generic_arg { + use super::*; + + #[test] + fn withf() { + let mut foo = MockFoo::new(); + foo.expect_foo() + .withf(|p| p.as_ref() == Path::new("/tmp")) + .times(3) + .return_const(()); + foo.foo(Path::new("/tmp")); + foo.foo(PathBuf::from(Path::new("/tmp"))); + foo.foo("/tmp"); + } +} + +mod where_clause { + use super::*; + + #[test] + fn withf() { + let mut foo = MockFoo::new(); + foo.expect_boom() + .withf(|p| p.as_ref() == Path::new("/tmp")) + .times(3) + .return_const(()); + foo.boom(Path::new("/tmp")); + foo.boom(PathBuf::from(Path::new("/tmp"))); + foo.boom("/tmp"); + } +} + +mod mutable_reference_arg { + use super::*; + + #[test] + fn withf() { + let mut foo = MockFoo::new(); + foo.expect_boom_mutref() + .withf(|p| p.as_ref() == "/tmp") + .once() + .returning(|s| s.as_mut().make_ascii_uppercase()); + let mut s = String::from("/tmp"); + foo.boom_mutref(&mut s); + assert_eq!(s, "/TMP"); + } +} + +mod reference_arg { + use super::*; + + #[test] + fn withf() { + let mut foo = MockFoo::new(); + foo.expect_boomref() + .withf(|p| p.as_ref() == Path::new("/tmp")) + .times(3) + .return_const(()); + foo.boomref(&Path::new("/tmp")); + foo.boomref(&PathBuf::from(Path::new("/tmp"))); + foo.boomref(&"/tmp"); + } +} + +mod slice { + use super::*; + + #[test] + fn withf() { + let mut foo = MockFoo::new(); + foo.expect_boomv() + .withf(|v| + v[0].as_ref() == Path::new("/tmp") && + v[1].as_ref() == Path::new("/mnt") + ).times(3) + .return_const(()); + foo.boomv(&[Path::new("/tmp"), Path::new("/mnt")]); + foo.boomv(&[PathBuf::from("/tmp"), PathBuf::from("/mnt")]); + foo.boomv(&["/tmp", "/mnt"]); + } +} + +mod static_method { + use super::*; + + #[test] + fn withf() { + let ctx = MockFoo::bang_context(); + ctx.expect() + .withf(|p| p.as_ref() == Path::new("/tmp")) + .times(3) + .return_const(()); + MockFoo::bang(Path::new("/tmp")); + MockFoo::bang(PathBuf::from(Path::new("/tmp"))); + MockFoo::bang("/tmp"); + } +} diff --git a/tests/mock_constructor_with_args.rs b/tests/mock_constructor_with_args.rs new file mode 100644 index 0000000..c12c442 --- /dev/null +++ b/tests/mock_constructor_with_args.rs @@ -0,0 +1,24 @@ +// vim: tw=80 +//! A struct with a constructor method named "new" that has arguments. +//! mockall should mock the provided method, and not autogenerate a 0-argument +//! "new" method. +#![deny(warnings)] + +use mockall::*; + +mock! { + pub Foo { + fn new(x: u32) -> Self; + } +} + +#[test] +fn returning_once() { + let mock = MockFoo::default(); + + let ctx = MockFoo::new_context(); + ctx.expect() + .return_once(|_| mock); + + let _mock = MockFoo::new(5); +} diff --git a/tests/mock_debug.rs b/tests/mock_debug.rs new file mode 100644 index 0000000..81e30cd --- /dev/null +++ b/tests/mock_debug.rs @@ -0,0 +1,40 @@ +// vim: tw=80 +//! A mocked struct should implement Debug +#![deny(warnings)] + +use mockall::*; +use std::fmt::{self, Debug, Formatter}; + +// using derive(Debug) tells mockall to generate the Debug impl automatically +mock!{ + #[derive(Debug)] + pub Bar { } + impl Clone for Bar { + fn clone(&self) -> Self; + } +} + +// With no derive(Debug), mockall won't genetate the debug impl automatically +mock!{ + pub Baz { } + impl Clone for Baz { + fn clone(&self) -> Self; + } +} +impl Debug for MockBaz { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + f.debug_struct("XXX").finish() + } +} + +#[test] +fn automatic() { + let bar = MockBar::new(); + assert_eq!("MockBar", format!("{bar:?}")); +} + +#[test] +fn manual() { + let baz = MockBaz::new(); + assert_eq!("XXX", format!("{baz:?}")); +} diff --git a/tests/mock_deref.rs b/tests/mock_deref.rs new file mode 100644 index 0000000..852b891 --- /dev/null +++ b/tests/mock_deref.rs @@ -0,0 +1,19 @@ +// vim: tw=80 +//! A method that returns a type which is a common target for std::ops::Deref +#![deny(warnings)] + +use mockall::*; + +mock! { + Foo { + fn foo(&self) -> &str; + } +} + +#[test] +fn return_const() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .return_const("Stuff".to_owned()); + assert_eq!("Stuff", mock.foo()); +} diff --git a/tests/mock_deref_args.rs b/tests/mock_deref_args.rs new file mode 100644 index 0000000..d0789d6 --- /dev/null +++ b/tests/mock_deref_args.rs @@ -0,0 +1,120 @@ +// vim: tw=80 +//! A method whose argument is a common `Deref` implementor +#![deny(warnings)] + +use mockall::*; +use std::{ + ffi::{CStr, CString, OsStr, OsString}, + path::{Path, PathBuf}, +}; + +mock! { + Foo { + fn foo(&self, x: Vec); + fn bar(&self, x: String); + fn baz(&self, x: CString); + fn bean(&self, x: OsString); + fn boom(&self, x: PathBuf); + } +} + +mod with { + use super::*; + + #[test] + fn cstr() { + let mut mock = MockFoo::new(); + mock.expect_baz() + .with(predicate::eq(CString::new("xxx").unwrap())) + .return_const(()); + mock.baz(CString::new("xxx").unwrap()); + } + + #[test] + fn osstr() { + let mut mock = MockFoo::new(); + mock.expect_bean() + .with(predicate::eq(OsStr::new("xxx").to_owned())) + .return_const(()); + mock.bean(OsString::from("xxx")); + } + + #[test] + fn path() { + let mut mock = MockFoo::new(); + mock.expect_boom() + .with(predicate::eq(Path::new("dir/file").to_owned())) + .return_const(()); + mock.boom(PathBuf::from("dir/file")); + } + + #[test] + fn string() { + let mut mock = MockFoo::new(); + mock.expect_bar() + .with(predicate::eq("xxx".to_owned())) + .return_const(()); + mock.bar(String::from("xxx")); + } + + #[test] + fn vec() { + let mut mock = MockFoo::new(); + mock.expect_foo::() + .with(predicate::eq(vec![1, 2, 3])) + .return_const(()); + mock.foo(vec![1, 2, 3]); + } +} + +mod withf { + use super::*; + + #[test] + fn cstr() { + let mut mock = MockFoo::new(); + const WANT: [u8; 4] = [120u8, 120, 120, 0]; + let want = CStr::from_bytes_with_nul(&WANT[..]).unwrap(); + mock.expect_baz() + .withf(move |s| s.as_c_str() == want) + .return_const(()); + mock.baz(CString::new("xxx").unwrap()); + } + + #[test] + fn osstr() { + let mut mock = MockFoo::new(); + mock.expect_bean() + .withf(move |s| s.as_os_str() == OsStr::new("xxx")) + .return_const(()); + mock.bean(OsString::from("xxx")); + } + + #[test] + fn path() { + let mut mock = MockFoo::new(); + mock.expect_boom() + .withf(move |s| s.as_path() == Path::new("dir/file")) + .return_const(()); + mock.boom(PathBuf::from("dir/file")); + } + + #[test] + fn string() { + let mut mock = MockFoo::new(); + mock.expect_bar() + .withf(|sl: &String| sl == "xxx") + .return_const(()); + mock.bar(String::from("xxx")); + } + + #[test] + fn vec() { + let mut mock = MockFoo::new(); + mock.expect_foo::() + .withf(|sl: &Vec| sl == &[1, 2, 3]) + .return_const(()); + mock.foo(vec![1, 2, 3]); + } + +} diff --git a/tests/mock_docs.rs b/tests/mock_docs.rs new file mode 100644 index 0000000..f1fd7ce --- /dev/null +++ b/tests/mock_docs.rs @@ -0,0 +1,26 @@ +// vim: tw=80 +#![deny(missing_docs)] +#![deny(warnings)] + +use mockall::*; + +// mock! should allow doc comments in all reasonable positions. This test +// ensures that the code will compile. mockall_derive has a unit test to ensure +// that the doc comments are correctly placed. + +pub trait Tr { + fn bar(&self); +} + +mock!{ + /// Struct docs + pub Foo { + /// Method docs + fn foo(&self); + } + /// Trait docs + impl Tr for Foo { + /// Trait method docs + fn bar(&self); + } +} diff --git a/tests/mock_generic_and_reference_arguments.rs b/tests/mock_generic_and_reference_arguments.rs new file mode 100644 index 0000000..02f3767 --- /dev/null +++ b/tests/mock_generic_and_reference_arguments.rs @@ -0,0 +1,20 @@ +// vim: tw=80 +//! A generic method may have non-generic reference arguments +#![deny(warnings)] + +use mockall::*; + +mock! { + Foo { + fn foo(&self, t: T, x: &i16) -> u32; + } +} + +#[test] +fn with() { + let mut mock = MockFoo::new(); + mock.expect_foo::() + .with(predicate::eq(4), predicate::eq(5)) + .return_const(42u32); + assert_eq!(42, mock.foo(4i32, &5i16)); +} diff --git a/tests/mock_generic_arguments.rs b/tests/mock_generic_arguments.rs new file mode 100644 index 0000000..aa3aaf4 --- /dev/null +++ b/tests/mock_generic_arguments.rs @@ -0,0 +1,68 @@ +// vim: tw=80 +//! A struct with a generic method that has generic arguments +#![deny(warnings)] + +use mockall::*; + +mock! { + Foo { + fn foo(&self, t: T) -> i32; + fn bar(&self, t: T) -> i32; + } +} + +#[test] +fn return_const() { + let mut mock = MockFoo::new(); + mock.expect_foo::() + .return_const(0); + assert_eq!(0, mock.foo(0i16)); +} + +mod with { + use super::*; + + /// Multiple generic methods with the same set of generic parameters are in + /// scope at the same time. Their expectations should not get scrambled. + #[test] + fn multiple_methods() { + let mut mock = MockFoo::new(); + mock.expect_foo::() + .with(predicate::eq(4)) + .return_const(0); + mock.expect_bar::() + .with(predicate::eq(5)) + .return_const(0); + mock.bar(5i16); + mock.foo(4i16); + } + + #[test] + fn ok() { + let mut mock = MockFoo::new(); + mock.expect_foo::() + .with(predicate::eq(4)) + .return_const(0); + mock.foo(4i16); + } + + #[test] + #[should_panic(expected = "MockFoo::foo(4): No matching expectation found")] + fn wrong_generic_type() { + let mut mock = MockFoo::new(); + mock.expect_foo::() + .with(predicate::eq(4)) + .return_const(0); + mock.foo(4i32); + } + + #[test] + #[should_panic(expected = "MockFoo::bar(?): No matching expectation found")] + fn no_debug_trait_bound() { + let mut mock = MockFoo::new(); + mock.expect_bar::() + .with(predicate::eq(4)) + .return_const(0); + mock.bar(4i32); + } +} diff --git a/tests/mock_generic_arguments_returning_reference.rs b/tests/mock_generic_arguments_returning_reference.rs new file mode 100644 index 0000000..ea0ea31 --- /dev/null +++ b/tests/mock_generic_arguments_returning_reference.rs @@ -0,0 +1,22 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; + +trait Foo { + fn foo(&self, t: T) -> &u32; +} + +mock!{ + MyStruct {} + impl Foo for MyStruct { + fn foo(&self, t: T) -> &u32; + } +} + +#[test] +fn returning() { + let mut mock = MockMyStruct::new(); + mock.expect_foo::().return_const(5u32); + assert_eq!(5u32, *mock.foo(99i16)); +} diff --git a/tests/mock_generic_constructor.rs b/tests/mock_generic_constructor.rs new file mode 100644 index 0000000..c81b0cf --- /dev/null +++ b/tests/mock_generic_constructor.rs @@ -0,0 +1,21 @@ +// vim: tw=80 +//! A generic struct with bounds on its generic parameters can have a +//! constructor method +#![deny(warnings)] + +use mockall::*; + +mock! { + pub Foo { + fn build() -> MockFoo; + } +} + +#[test] +fn returning_once() { + let ctx = MockFoo::::build_context(); + ctx.expect() + .return_once(MockFoo::::default); + + let _mock: MockFoo = MockFoo::::build(); +} diff --git a/tests/mock_generic_constructor_with_where_clause.rs b/tests/mock_generic_constructor_with_where_clause.rs new file mode 100644 index 0000000..2298ccc --- /dev/null +++ b/tests/mock_generic_constructor_with_where_clause.rs @@ -0,0 +1,21 @@ +// vim: tw=80 +//! A generic struct with a where clause on its generic parameters can have a +//! constructor method +#![deny(warnings)] + +use mockall::*; + +mock! { + pub Foo where T: Default + 'static { + fn build() -> MockFoo; + } +} + +#[test] +fn returning_once() { + let ctx = MockFoo::::build_context(); + ctx.expect() + .return_once(MockFoo::::default); + + let _mock: MockFoo = MockFoo::::build(); +} diff --git a/tests/mock_generic_method_on_generic_struct_returning_nonstatic.rs b/tests/mock_generic_method_on_generic_struct_returning_nonstatic.rs new file mode 100644 index 0000000..819e70a --- /dev/null +++ b/tests/mock_generic_method_on_generic_struct_returning_nonstatic.rs @@ -0,0 +1,116 @@ +//! Mocking a generic method whose return value's lifetime is a generic +//! parameter on a generic struct requires extra care for the Expectation +//! object's type generics. +//! https://github.com/asomers/mockall/issues/306 +#![deny(warnings)] + +use mockall::*; + +#[derive(Clone)] +struct X<'a>(&'a u32); + +trait Bong { + fn trait_foo<'a>(&self) -> X<'a>; + fn trait_baz<'a>(&self) -> &X<'a>; +} + +mock! { + Thing { + fn foo<'a>(&self) -> X<'a>; + + // XXX static methods don't work yet. + // fn bar<'a>() -> X<'a>; + + fn baz<'a>(&self) -> &X<'a>; + + // Methods returning a mutable reference to a non-static value won't + // work unless 'a is static. I doubt there are any real-life methods + // that fit this pattern; open an issue if you find one. + //fn bang<'a>(&mut self) -> &mut X<'a>; + + // Generic methods can't return non-static values either, because + // Mockall requires generic methods' generic parameters to implement + // std::any::Any, which means they must be 'static. + //fn bean<'a, T: 'static>(&self, t: T) -> X<'a>; + } + // The same types of methods should work if they are Trait methods. + impl Bong for Thing { + fn trait_foo<'a>(&self) -> X<'a>; + fn trait_baz<'a>(&self) -> &X<'a>; + } +} + +#[test] +fn return_static() { + const D: u32 = 42; + let x = X(&D); + let mut thing = MockThing::::new(); + thing.expect_foo() + .return_const(x); + + assert_eq!(42u32, *thing.foo().0); +} + +#[test] +fn return_static_ref() { + const D: u32 = 42; + let x = X(&D); + let mut thing = MockThing::::new(); + thing.expect_baz() + .return_const(x); + + assert_eq!(42u32, *thing.baz().0); +} + +// It isn't possible to safely set an expectation for a non-'static return value +// (because the mock object doesn't have any lifetime parameters itself), but +// unsafely setting such an expectation is a common use case. +#[test] +fn return_nonstatic() { + let d = 42u32; + let x = X(&d); + let xstatic: X<'static> = unsafe{ std::mem::transmute(x) }; + let mut thing = MockThing::::new(); + thing.expect_foo() + .returning(move || xstatic.clone()); + + assert_eq!(42u32, *thing.foo().0); +} + +mod trait_methods { + use super::*; + + #[test] + fn return_static() { + const D: u32 = 42; + let x = X(&D); + let mut thing = MockThing::::new(); + thing.expect_trait_foo() + .return_const(x); + + assert_eq!(42u32, *thing.trait_foo().0); + } + + #[test] + fn return_static_ref() { + const D: u32 = 42; + let x = X(&D); + let mut thing = MockThing::::new(); + thing.expect_trait_baz() + .return_const(x); + + assert_eq!(42u32, *thing.trait_baz().0); + } + + #[test] + fn return_nonstatic() { + let d = 42u32; + let x = X(&d); + let xstatic: X<'static> = unsafe{ std::mem::transmute(x) }; + let mut thing = MockThing::::new(); + thing.expect_trait_foo() + .returning(move || xstatic.clone()); + + assert_eq!(42u32, *thing.trait_foo().0); + } +} diff --git a/tests/mock_generic_method_returning_nonstatic.rs b/tests/mock_generic_method_returning_nonstatic.rs new file mode 100644 index 0000000..7b85ee1 --- /dev/null +++ b/tests/mock_generic_method_returning_nonstatic.rs @@ -0,0 +1,117 @@ +// vim: tw=80 +//! A generic method whose return value's lifetime is a generic parameter is a +//! special case. Mockall can only mock such methods if the expectation is +//! 'static. +//! https://github.com/asomers/mockall/issues/76 +#![deny(warnings)] + +use mockall::*; + +#[derive(Clone)] +struct X<'a>(&'a u32); + +trait Bong { + fn trait_foo<'a>(&self) -> X<'a>; + fn trait_baz<'a>(&self) -> &X<'a>; +} + +mock! { + Thing { + fn foo<'a>(&self) -> X<'a>; + + // XXX static methods don't work yet. + // fn bar<'a>() -> X<'a>; + + fn baz<'a>(&self) -> &X<'a>; + + // Methods returning a mutable reference to a non-static value won't + // work unless 'a is static. I doubt there are any real-life methods + // that fit this pattern; open an issue if you find one. + // fn bang<'a>(&mut self) -> &mut X<'a>; + + // Generic methods can't return non-static values either, because + // Mockall requires generic methods' generic parameters to implement + // std::any::Any, which means they must be 'static. + //fn bean<'a, T: 'static>(&self, t: T) -> X<'a>; + } + // The same types of methods should work if they are Trait methods. + impl Bong for Thing { + fn trait_foo<'a>(&self) -> X<'a>; + fn trait_baz<'a>(&self) -> &X<'a>; + } +} + +#[test] +fn return_static() { + const D: u32 = 42; + let x = X(&D); + let mut thing = MockThing::new(); + thing.expect_foo() + .return_const(x); + + assert_eq!(42u32, *thing.foo().0); +} + +#[test] +fn return_static_ref() { + const D: u32 = 42; + let x = X(&D); + let mut thing = MockThing::new(); + thing.expect_baz() + .return_const(x); + + assert_eq!(42u32, *thing.baz().0); +} + +// It isn't possible to safely set an expectation for a non-'static return value +// (because the mock object doesn't have any lifetime parameters itself), but +// unsafely setting such an expectation is a common use case. +#[test] +fn return_nonstatic() { + let d = 42u32; + let x = X(&d); + let xstatic: X<'static> = unsafe{ std::mem::transmute(x) }; + let mut thing = MockThing::new(); + thing.expect_foo() + .returning(move || xstatic.clone()); + + assert_eq!(42u32, *thing.foo().0); +} + +mod trait_methods { + use super::*; + + #[test] + fn return_static() { + const D: u32 = 42; + let x = X(&D); + let mut thing = MockThing::new(); + thing.expect_trait_foo() + .return_const(x); + + assert_eq!(42u32, *thing.trait_foo().0); + } + + #[test] + fn return_static_ref() { + const D: u32 = 42; + let x = X(&D); + let mut thing = MockThing::new(); + thing.expect_trait_baz() + .return_const(x); + + assert_eq!(42u32, *thing.trait_baz().0); + } + + #[test] + fn return_nonstatic() { + let d = 42u32; + let x = X(&d); + let xstatic: X<'static> = unsafe{ std::mem::transmute(x) }; + let mut thing = MockThing::new(); + thing.expect_trait_foo() + .returning(move || xstatic.clone()); + + assert_eq!(42u32, *thing.trait_foo().0); + } +} diff --git a/tests/mock_generic_method_with_lifetime_parameter.rs b/tests/mock_generic_method_with_lifetime_parameter.rs new file mode 100644 index 0000000..3a3d97d --- /dev/null +++ b/tests/mock_generic_method_with_lifetime_parameter.rs @@ -0,0 +1,65 @@ +// vim: tw=80 +//! A generic method whose only generic parameter is a lifetime parameter is, +//! from Mockall's perspective, pretty much the same as a non-generic method. +#![deny(warnings)] + +use mockall::*; + +#[derive(Debug, Eq)] +struct X<'a>(&'a u32); + +impl<'a> PartialEq for X<'a> { + fn eq(&self, other: &X<'a>) -> bool { + self.0 == other.0 + } +} + +mock!{ + Foo { + fn foo<'a>(&self, x: &'a X<'a>) -> u32; + } +} + +#[test] +fn return_const() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .return_const(42u32); + let x = X(&5); + assert_eq!(42, mock.foo(&x)); +} + +#[test] +fn returning() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .returning(|f| *f.0); + let x = X(&5); + assert_eq!(5, mock.foo(&x)); +} + +// I can't get this to work. How can I create a Predicate that's valid for all +// lifetimes? 'static should be reduceable to any other lifetime, but rustc +// doesn't seem to understand that. +//#[test] +//fn with() { + //const X1: u32 = 5; + //const OTHER: X<'static> = X(&X1); + //let mut mock = MockFoo::new(); + //mock.expect_foo() + //.with(mockall::predicate::eq(&OTHER)) + //.return_const(42u32); + //let inner = 5; + //let x = X(&5); + //assert_eq!(42, mock.foo(&x)); +//} + +#[test] +fn withf() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .withf(|f| *f.0 == 5) + .return_const(42u32); + let x = X(&5); + assert_eq!(42, mock.foo(&x)); +} diff --git a/tests/mock_generic_method_with_where_clause.rs b/tests/mock_generic_method_with_where_clause.rs new file mode 100644 index 0000000..8aaa7cb --- /dev/null +++ b/tests/mock_generic_method_with_where_clause.rs @@ -0,0 +1,43 @@ +// vim: tw=80 +#![deny(warnings)] +use mockall::*; + +struct G where T: Copy {t: T} + +mock! { + Foo { + fn foo(&self, t: T) -> G where T: Copy + 'static; + fn bar(&self, g: G) -> T where T: Copy + 'static; + fn baz(&self) -> &G where T: Copy + 'static; + fn bean(&mut self) -> &mut G where T: Copy + 'static; + } +} + +#[test] +fn returning() { + let mut mock = MockFoo::new(); + mock.expect_foo::() + .returning(|t| G{t}); + assert_eq!(42, mock.foo(42u32).t); + + mock.expect_bar::() + .returning(|g| g.t); + assert_eq!(42u32, mock.bar(G{t: 42})); +} + +#[test] +fn return_const() { + let mut mock = MockFoo::new(); + mock.expect_baz::() + .return_const(G{t: 42}); + assert_eq!(42u32, mock.baz().t); +} + +#[test] +fn return_var() { + let mut mock = MockFoo::new(); + mock.expect_bean::() + .return_var(G{t: 42}); + mock.bean::().t += 1; + assert_eq!(43u32, mock.bean().t); +} diff --git a/tests/mock_generic_methods_returning_mutable_reference.rs b/tests/mock_generic_methods_returning_mutable_reference.rs new file mode 100644 index 0000000..1e708b6 --- /dev/null +++ b/tests/mock_generic_methods_returning_mutable_reference.rs @@ -0,0 +1,18 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; + +mock!{ + MyStruct { + fn foo(&mut self, t: T) -> &mut u32; + } +} + +#[test] +fn return_var() { + let mut mock = MockMyStruct::new(); + mock.expect_foo::().return_var(5u32); + *mock.foo(1i16) += 1; + assert_eq!(6u32, *mock.foo(2i16)); +} diff --git a/tests/mock_generic_return.rs b/tests/mock_generic_return.rs new file mode 100644 index 0000000..8fd2a0a --- /dev/null +++ b/tests/mock_generic_return.rs @@ -0,0 +1,19 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; + +mock! { + Foo { + fn foo(&self) -> O; + } +} + +#[test] +fn returning() { + let mut mock = MockFoo::new(); + mock.expect_foo::().return_const(42i32); + mock.expect_foo::().return_const(69u16); + assert_eq!(42i32, mock.foo()); + assert_eq!(69u16, mock.foo()); +} diff --git a/tests/mock_generic_static_method_with_where_clause.rs b/tests/mock_generic_static_method_with_where_clause.rs new file mode 100644 index 0000000..f70d4c7 --- /dev/null +++ b/tests/mock_generic_static_method_with_where_clause.rs @@ -0,0 +1,24 @@ +// vim: tw=80 +#![deny(warnings)] +// multiple bound locations is deliberate; we want to ensure that Mockall can +// handle it correctly. +#![allow(clippy::multiple_bound_locations)] + +use mockall::*; + +struct G where T: Copy {t: T} + +mock! { + Foo { + fn make_g(x: T) -> G where T: Copy; + } +} + +#[test] +fn returning() { + let ctx = MockFoo::make_g_context(); + ctx.expect::() + .returning(|t| G{t}); + let g = MockFoo::make_g(42i16); + assert_eq!(g.t, 42i16); +} diff --git a/tests/mock_generic_struct.rs b/tests/mock_generic_struct.rs new file mode 100644 index 0000000..0863b44 --- /dev/null +++ b/tests/mock_generic_struct.rs @@ -0,0 +1,33 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; + +#[derive(Clone)] +struct NonCopy(u32); + +// A struct with a definition like this: +// pub struct ExtGenericStruct { +// _x: i16 +// } +// impl ExtGenericStruct { +// fn foo(&self, _x: T) -> T { +// 42 +// } +// } +// Could be mocked like this: +mock!{ + pub ExtGenericStruct { + fn foo(&self, x: T) -> T; + } +} + +#[test] +#[allow(clippy::redundant_clone)] +fn returning() { + let mut mock = MockExtGenericStruct::::new(); + // An explicit .clone() is required so as not to return by move + mock.expect_foo() + .returning(|x| x.clone()); + assert_eq!(5, mock.foo(NonCopy(5)).0); +} diff --git a/tests/mock_generic_struct_with_bounds.rs b/tests/mock_generic_struct_with_bounds.rs new file mode 100644 index 0000000..4f15836 --- /dev/null +++ b/tests/mock_generic_struct_with_bounds.rs @@ -0,0 +1,17 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; + +mock! { + pub Foo { + fn foo(&self, x: u32) -> i64; + } +} + +#[test] +fn returning() { + let mut mock = MockFoo::::new(); + mock.expect_foo().returning(i64::from); + assert_eq!(5, mock.foo(5)); +} diff --git a/tests/mock_generic_struct_with_generic_method.rs b/tests/mock_generic_struct_with_generic_method.rs new file mode 100644 index 0000000..92fc46f --- /dev/null +++ b/tests/mock_generic_struct_with_generic_method.rs @@ -0,0 +1,18 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; + +mock!{ + pub Foo { + fn foo(&self, q: Q) -> T; + } +} + +#[test] +fn return_const() { + let mut mock = MockFoo::::new(); + mock.expect_foo::() + .return_const(100_000u32); + assert_eq!(100_000, mock.foo(-5i16)); +} diff --git a/tests/mock_generic_struct_with_generic_static_method.rs b/tests/mock_generic_struct_with_generic_static_method.rs new file mode 100644 index 0000000..1f93f3f --- /dev/null +++ b/tests/mock_generic_struct_with_generic_static_method.rs @@ -0,0 +1,102 @@ +// vim: tw=80 +//! A generic struct with a generic method on a different parameter +#![deny(warnings)] + +use mockall::*; +use std::sync::Mutex; + +mock! { + Foo { + fn foo(t: T, q: Q) -> u64; + } +} + +static FOO_MTX: Mutex<()> = Mutex::new(()); + +// Checkpointing the mock object should not checkpoint static methods too +#[test] +fn checkpoint() { + let _m = FOO_MTX.lock(); + + let mut mock = MockFoo::::new(); + let ctx = MockFoo::::foo_context(); + ctx.expect::() + .returning(|_, _| 0) + .times(1..3); + mock.checkpoint(); + MockFoo::foo(42u32, 69i16); +} + +// It should also be possible to checkpoint just the context object +#[test] +#[should_panic(expected = + "MockFoo::foo: Expectation() called 0 time(s) which is fewer than expected 1")] +fn ctx_checkpoint() { + let _m = FOO_MTX.lock(); + let ctx = MockFoo::::foo_context(); + ctx.expect::() + .returning(|_, _| 0) + .times(1..3); + ctx.checkpoint(); + panic!("Shouldn't get here!"); +} + +// Expectations should be cleared when a context object drops +#[test] +#[should_panic(expected = "MockFoo::foo(42, 69): No matching expectation found")] +fn ctx_hygiene() { + let _m = FOO_MTX.lock(); + { + let ctx0 = MockFoo::::foo_context(); + ctx0.expect::() + .returning(|_, _| 0); + } + MockFoo::foo(42, 69); +} + +#[cfg_attr(not(feature = "nightly"), ignore)] +#[cfg_attr(not(feature = "nightly"), allow(unused_must_use))] +#[test] +fn return_default() { + let _m = FOO_MTX.lock(); + + let ctx = MockFoo::::foo_context(); + ctx.expect::(); + MockFoo::foo(5u32, 6i16); +} + +#[test] +fn returning() { + let _m = FOO_MTX.lock(); + + let ctx = MockFoo::::foo_context(); + ctx.expect::() + .returning(|_, _| 0); + MockFoo::foo(41u32, 42i16); +} + +#[test] +fn two_matches() { + let _m = FOO_MTX.lock(); + + let ctx = MockFoo::::foo_context(); + ctx.expect::() + .with(predicate::eq(42u32), predicate::eq(0i16)) + .return_const(99u64); + ctx.expect::() + .with(predicate::eq(69u32), predicate::eq(0i16)) + .return_const(101u64); + assert_eq!(101, MockFoo::foo(69u32, 0i16)); + assert_eq!(99, MockFoo::foo(42u32, 0i16)); +} + +#[test] +fn with() { + let _m = FOO_MTX.lock(); + + let ctx = MockFoo::::foo_context(); + ctx.expect::() + .with(predicate::eq(42u32), predicate::eq(99i16)) + .returning(|_, _| 0); + MockFoo::foo(42u32, 99i16); +} diff --git a/tests/mock_generic_struct_with_generic_trait.rs b/tests/mock_generic_struct_with_generic_trait.rs new file mode 100644 index 0000000..c955441 --- /dev/null +++ b/tests/mock_generic_struct_with_generic_trait.rs @@ -0,0 +1,22 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; + +trait Foo { + fn foo(&self, x: T) -> T; +} +mock! { + Bar {} + impl Foo for Bar { + fn foo(&self, x: T) -> T; + } +} + +#[test] +fn returning() { + let mut mock = MockBar::::new(); + mock.expect_foo() + .returning(|x| x); + assert_eq!(5u32, mock.foo(5u32)); +} diff --git a/tests/mock_generic_struct_with_generic_trait_with_different_bounds.rs b/tests/mock_generic_struct_with_generic_trait_with_different_bounds.rs new file mode 100644 index 0000000..efb0d58 --- /dev/null +++ b/tests/mock_generic_struct_with_generic_trait_with_different_bounds.rs @@ -0,0 +1,22 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; + +trait Foo { + fn foo(&self, x: T) -> T; +} +mock! { + Bar {} + impl Foo for Bar { + fn foo(&self, x: T) -> T; + } +} + +#[test] +fn returning() { + let mut mock = MockBar::::new(); + mock.expect_foo() + .returning(|x| x); + assert_eq!(5u32, mock.foo(5u32)); +} diff --git a/tests/mock_generic_struct_with_nondefault_parameter.rs b/tests/mock_generic_struct_with_nondefault_parameter.rs new file mode 100644 index 0000000..40bfd82 --- /dev/null +++ b/tests/mock_generic_struct_with_nondefault_parameter.rs @@ -0,0 +1,37 @@ +// vim: tw=80 +//! mock a generic struct and instantiate it with a parameter type that does not +//! implement Default +#![deny(warnings)] + +use mockall::*; + +struct NonDefault(); + +trait Foo { + fn foo(&self) -> T; +} +mock! { + ExternalStruct {} + impl Foo for ExternalStruct { + fn foo(&self) -> T; + } +} + +#[test] +#[should_panic(expected = + "MockExternalStruct::foo: Expectation() Can only return default values for types that impl std::Default")] +#[cfg_attr(not(feature = "nightly"), ignore)] +#[cfg_attr(not(feature = "nightly"), allow(unused_must_use))] +fn return_default() { + let mut mock = MockExternalStruct::::new(); + mock.expect_foo(); + mock.foo(); +} + +#[test] +fn returning() { + let mut mock = MockExternalStruct::::new(); + mock.expect_foo() + .returning(NonDefault); + mock.foo(); +} diff --git a/tests/mock_generic_struct_with_static_method.rs b/tests/mock_generic_struct_with_static_method.rs new file mode 100644 index 0000000..6b2928b --- /dev/null +++ b/tests/mock_generic_struct_with_static_method.rs @@ -0,0 +1,21 @@ +// vim: tw=80 +//! static non-generic methods of generic structs shouldn't require any special +//! treatment when mocking. Prior to version 0.3.0, the struct's generic +//! parameters had to be duplicated as generic parameters of the method. +#![deny(warnings)] + +use mockall::*; + +mock! { + Foo { + fn foo(t: T); + } +} + +#[test] +fn returning() { + let ctx = MockFoo::::foo_context(); + ctx.expect() + .returning(|_| ()); + MockFoo::foo(42u32); +} diff --git a/tests/mock_generic_struct_with_trait.rs b/tests/mock_generic_struct_with_trait.rs new file mode 100644 index 0000000..a772c99 --- /dev/null +++ b/tests/mock_generic_struct_with_trait.rs @@ -0,0 +1,23 @@ +// vim: ts=80 +#![deny(warnings)] + +use mockall::*; + +trait Foo { + fn foo(&self, x: u32) -> u32; +} + +mock! { + Bar {} + impl Foo for Bar { + fn foo(&self, x: u32) -> u32; + } +} + +#[test] +fn return_const() { + let mut mock = MockBar::::new(); + mock.expect_foo() + .return_const(43u32); + assert_eq!(43, mock.foo(42)); +} diff --git a/tests/mock_generic_struct_with_trait_with_associated_types.rs b/tests/mock_generic_struct_with_trait_with_associated_types.rs new file mode 100644 index 0000000..ced6e9e --- /dev/null +++ b/tests/mock_generic_struct_with_trait_with_associated_types.rs @@ -0,0 +1,20 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; + +mock! { + Foo {} + impl Iterator for Foo { + type Item=T; + fn next(&mut self) -> Option; + } +} + +#[test] +fn return_const() { + let mut mock = MockFoo::::new(); + mock.expect_next() + .return_const(None); + assert!(mock.next().is_none()); +} diff --git a/tests/mock_generic_struct_with_where_clause.rs b/tests/mock_generic_struct_with_where_clause.rs new file mode 100644 index 0000000..14a8e10 --- /dev/null +++ b/tests/mock_generic_struct_with_where_clause.rs @@ -0,0 +1,22 @@ +// vim: tw=80 +//! A generic struct with a where clause +#![deny(warnings)] + +// An explicit clone is required so as not to return by move +#![allow(clippy::clone_on_copy)] + +use mockall::*; + +mock! { + Foo where T:Clone { + fn foo(&self, t: T) -> T; + } +} + +#[test] +fn returning() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .returning(|t: u32| t.clone()); + assert_eq!(5u32, mock.foo(5u32)); +} diff --git a/tests/mock_generic_struct_with_where_clause_and_trait.rs b/tests/mock_generic_struct_with_where_clause_and_trait.rs new file mode 100644 index 0000000..fd9b5ac --- /dev/null +++ b/tests/mock_generic_struct_with_where_clause_and_trait.rs @@ -0,0 +1,31 @@ +// vim: tw=80 +//! A generic struct with a where clause, that also implements a trait +#![deny(warnings)] + +// An explicit clone is required so as not to return by move +#![allow(clippy::clone_on_copy)] + +use mockall::*; + +trait Bar { + fn bar(&self); +} +mock! { + Foo where T: Clone { + fn foo(&self, t: T) -> T; + } + impl Bar for Foo where T: Clone { + fn bar(&self); + } +} + +#[test] +fn returning() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .returning(|t: u32| t.clone()); + mock.expect_bar() + .returning(|| ()); + assert_eq!(5u32, mock.foo(5u32)); + mock.bar(); +} diff --git a/tests/mock_generic_trait.rs b/tests/mock_generic_trait.rs new file mode 100644 index 0000000..24413bc --- /dev/null +++ b/tests/mock_generic_trait.rs @@ -0,0 +1,22 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; + +trait Foo { + fn foo(&self); +} + +mock! { + Bar {} + impl Foo for Bar { + fn foo(&self); + } +} + +#[test] +fn return_const() { + let mut mock = MockBar::::new(); + mock.expect_foo().return_const(()); + mock.foo(); +} diff --git a/tests/mock_impl_generic_trait.rs b/tests/mock_impl_generic_trait.rs new file mode 100644 index 0000000..6819c81 --- /dev/null +++ b/tests/mock_impl_generic_trait.rs @@ -0,0 +1,22 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; + +trait MyTrait {} + +struct MyStruct(T); +impl MyTrait for MyStruct {} + +mock!{ + Foo { + fn foo(&self) -> impl MyTrait; + } +} + +#[test] +fn returning() { + let mut mock = MockFoo::new(); + mock.expect_foo::().returning(|| Box::new(MyStruct(42u32))); + let _mto: Box> = mock.foo(); +} diff --git a/tests/mock_impl_trait.rs b/tests/mock_impl_trait.rs new file mode 100644 index 0000000..fb1bb7a --- /dev/null +++ b/tests/mock_impl_trait.rs @@ -0,0 +1,19 @@ +// vim: tw=80 +//! a method that returns impl Trait +#![deny(warnings)] + +use mockall::*; +use std::fmt::Debug; + +mock!{ + Foo { + fn foo(&self) -> impl Debug + Send; + } +} + +#[test] +fn returning() { + let mut mock = MockFoo::new(); + mock.expect_foo().returning(|| Box::new(4)); + format!("{:?}", mock.foo()); +} diff --git a/tests/mock_inherited_traits.rs b/tests/mock_inherited_traits.rs new file mode 100644 index 0000000..940b822 --- /dev/null +++ b/tests/mock_inherited_traits.rs @@ -0,0 +1,32 @@ +// vim: tw=80 +//! A struct that implements a trait that inherits another trait +#![deny(warnings)] + +use mockall::*; + +trait A { + fn foo(&self); +} + +trait B: A { + fn bar(&self); +} + +mock!{ + B {} + impl A for B { + fn foo(&self); + } + impl B for B { + fn bar(&self); + } +} + +#[test] +fn returning() { + let mut mock = MockB::new(); + mock.expect_foo().returning(|| ()); + mock.expect_bar().returning(|| ()); + mock.foo(); + mock.bar(); +} diff --git a/tests/mock_life0.rs b/tests/mock_life0.rs new file mode 100644 index 0000000..722f78e --- /dev/null +++ b/tests/mock_life0.rs @@ -0,0 +1,16 @@ +// vim: tw=80 +//! mock a method whose self parameter has an explicit lifetime +//! https://github.com/asomers/mockall/issues/95 +#![deny(warnings)] + +use mockall::*; + +mock!{ + Foo { + fn foo<'life0>(&'life0 self, x: u32) -> u32; + } +} + +// TODO: test that the mock method respects the lifetime bound, once Mockall +// supports mocking non-static structs +// (https://github.com/asomers/mockall/issues/4) diff --git a/tests/mock_multiple_generic_arguments.rs b/tests/mock_multiple_generic_arguments.rs new file mode 100644 index 0000000..1221c71 --- /dev/null +++ b/tests/mock_multiple_generic_arguments.rs @@ -0,0 +1,18 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; + + +mock! { + Foo { + fn foo(&self, t: T, q: Q); + } +} + +#[test] +fn return_const() { + let mut mock = MockFoo::new(); + mock.expect_foo::().return_const(()); + mock.foo(0i16, 1i32) +} diff --git a/tests/mock_multiple_traits.rs b/tests/mock_multiple_traits.rs new file mode 100644 index 0000000..df4c0e1 --- /dev/null +++ b/tests/mock_multiple_traits.rs @@ -0,0 +1,21 @@ +// vim: tw=80 +//! A struct that implements multiple traits +#![deny(warnings)] + +use mockall::*; + +trait A {} +trait B {} +mock!{ + MultiTrait {} + impl A for MultiTrait {} + impl B for MultiTrait {} +} + +#[test] +fn new() { + fn foo(_t: T) {} + + let mock = MockMultiTrait::new(); + foo(mock); +} diff --git a/tests/mock_nonlocal_trait.rs b/tests/mock_nonlocal_trait.rs new file mode 100644 index 0000000..5a140c6 --- /dev/null +++ b/tests/mock_nonlocal_trait.rs @@ -0,0 +1,28 @@ +// vim: tw=80 +//! A trait that isn't imported directly into the local namespace +#![deny(warnings)] + +use mockall::*; + +mod my_module { + pub trait Foo { + fn foo(&self) -> i32; + } +} + +mock! { + Bar {} + impl my_module::Foo for Bar { + fn foo(&self) -> i32; + } +} + +#[test] +fn returning() { + use my_module::Foo; + + let mut mock = MockBar::new(); + mock.expect_foo() + .returning(|| 42); + assert_eq!(42, mock.foo()); +} diff --git a/tests/mock_nonpub.rs b/tests/mock_nonpub.rs new file mode 100644 index 0000000..a349175 --- /dev/null +++ b/tests/mock_nonpub.rs @@ -0,0 +1,37 @@ +// vim: tw=80 +//! methods can use non-public types, as long as the object's visibility is +//! compatible. +#![deny(warnings)] + +use mockall::*; + +mod outer { + struct SuperT(); + trait SuperTrait {} + + mod inner { + use super::super::mock; + + pub(crate) struct PubCrateT(); + struct PrivT(); + + mock! { + Foo { + fn foo(&self, x: PubCrateT) -> PubCrateT; + fn bar(&self, x: PrivT) -> PrivT; + fn baz(&self, x: super::SuperT) -> super::SuperT; + fn refbaz(&self, x: super::SuperT) -> &super::SuperT; + fn refmutbaz(&mut self, x: super::SuperT) -> &mut super::SuperT; + fn staticbaz(x: super::SuperT) -> super::SuperT; + fn bang(&self, x: crate::outer::SuperT) -> crate::outer::SuperT; + fn bean(&self, x: self::PrivT) -> self::PrivT; + fn goo(t: T); + fn goo_wc(t: T) where T: super::SuperTrait + 'static; + fn boob super::SuperT + 'static>(&self, f: F) + -> u32; + fn boobwc(&self, f: F) -> u32 + where F: Fn(u32) -> super::SuperT + 'static; + } + } + } +} diff --git a/tests/mock_ref_and_nonref_arguments.rs b/tests/mock_ref_and_nonref_arguments.rs new file mode 100644 index 0000000..b5330df --- /dev/null +++ b/tests/mock_ref_and_nonref_arguments.rs @@ -0,0 +1,34 @@ +// vim: tw=80 +//! A method that has both a reference and a nonreference argument +#![deny(warnings)] + +use mockall::*; + +mock!{ + Foo { + fn foo(&self, i0: i32, i1: &u16) -> i32; + } +} + +#[test] +fn with() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .with(predicate::eq(42), predicate::eq(1)) + .returning(|x, y| x + i32::from(*y)); + let x = 42i32; + let y = 1u16; + assert_eq!(43i32, mock.foo(x, &y)); +} + +#[test] +fn withf() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .withf(|x, y| *x == i32::from(*y)) + .returning(|x, y| x + i32::from(*y)); + let x = 42i32; + let y = 42u16; + assert_eq!(84i32, mock.foo(x, &y)); +} + diff --git a/tests/mock_reference_arguments.rs b/tests/mock_reference_arguments.rs new file mode 100644 index 0000000..53761b5 --- /dev/null +++ b/tests/mock_reference_arguments.rs @@ -0,0 +1,142 @@ +// vim: tw=80 +//! A struct with methods that take arguments by reference +#![deny(warnings)] + +use mockall::*; + +const X: u32 = 99; + +mock!{ + Foo { + fn foo(&self, x: &u32) -> u32; + fn bar(&self, y: &'static u32); + } +} + +mod r#match { + use super::*; + + #[test] + fn with() { + const Y: u32 = 5; + let mut mock = MockFoo::new(); + mock.expect_foo() + .with(predicate::eq(5u32)) + .returning(|x| *x); + mock.expect_bar() + .with(predicate::eq(99u32)) + .returning(|_| ()); + let r = mock.foo(&Y); + assert_eq!(5, r); + mock.bar(&X); + } + + #[test] + fn withf() { + const Y: u32 = 5; + let mut mock = MockFoo::new(); + mock.expect_foo() + .withf(|x| *x == 5) + .returning(|x| *x); + mock.expect_bar() + .withf(|x| *x == 99) + .returning(|_| ()); + let r = mock.foo(&Y); + assert_eq!(5, r); + mock.bar(&X); + } +} + +mod times { + use super::*; + const X: u32 = 42; + + #[test] + fn ok() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .returning(|_| 0) + .times(2); + mock.foo(&X); + mock.foo(&X); + } + + #[test] + #[should_panic( + expected = "MockFoo::foo: Expectation() called 1 time(s) which is fewer than expected 2" + )] + fn too_few() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .returning(|_| 0) + .times(2); + mock.foo(&X); + } + + #[test] + #[should_panic( + expected = "MockFoo::foo: Expectation() called 3 times which is more than the expected 2" + )] + fn too_many() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .returning(|_| 0) + .times(2); + mock.foo(&X); + mock.foo(&X); + mock.foo(&X); + // Verify that we panic quickly and don't reach code below this point. + panic!("Shouldn't get here!"); + } + + #[test] + fn range_ok() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .returning(|_| 0) + .times(2..4); + mock.foo(&X); + mock.foo(&X); + } + + #[test] + #[should_panic( + expected = "MockFoo::foo: Expectation() called 1 time(s) which is fewer than expected 2" + )] + fn range_too_few() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .returning(|_| 0) + .times(2..4); + mock.foo(&X); + } + + #[test] + #[should_panic( + expected = "MockFoo::foo: Expectation() called 4 times which is more than the expected 3" + )] + fn range_too_many() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .returning(|_| 0) + .times(2..4); + mock.foo(&X); + mock.foo(&X); + mock.foo(&X); + mock.foo(&X); + // Verify that we panic quickly and don't reach code below this point. + panic!("Shouldn't get here!"); + } +} + +#[test] +fn times_full() { + const X: u32 = 42; + let mut mock = MockFoo::new(); + mock.expect_foo() + .returning(|_| 0) + .times(1) + .times(..); + mock.foo(&X); + mock.foo(&X); +} diff --git a/tests/mock_refmut_arguments.rs b/tests/mock_refmut_arguments.rs new file mode 100644 index 0000000..66589c4 --- /dev/null +++ b/tests/mock_refmut_arguments.rs @@ -0,0 +1,52 @@ +// vim: tw=80 +//! A struct with methods that take arguments by mutable reference. +#![deny(warnings)] + +use std::mem; + +use mockall::*; + +mock!{ + Foo { + fn foo(&self, x: &mut u32); + // This is almost never safe, but it should still work. + fn bar(&self, y: &'static mut u32); + } +} + +#[test] +fn returning() { + let mut x: u32 = 5; + let mut mock = MockFoo::new(); + mock.expect_foo() + .withf(|x| *x == 5) + .returning(|x| { *x = 42;} ); + mock.foo(&mut x); + assert_eq!(x, 42); +} + +#[test] +fn with() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .with(predicate::eq(0u32)) + .returning(|x| {*x = 6;}); + mock.expect_foo() + .with(predicate::eq(42u32)) + .returning(|x| {*x = 5;}); + let mut x = 42u32; + mock.foo(&mut x); + assert_eq!(5, x); +} + +#[test] +fn static_mut() { + let mut x: u32 = 5; + let mut mock = MockFoo::new(); + mock.expect_bar() + .withf(|x| *x == 5) + .returning(|x| { *x = 42;} ); + // Safe because mock leaves scope before x + unsafe { mock.bar(mem::transmute(&mut x)); } + assert_eq!(x, 42); +} diff --git a/tests/mock_return_anonymous_lifetime.rs b/tests/mock_return_anonymous_lifetime.rs new file mode 100644 index 0000000..1713c57 --- /dev/null +++ b/tests/mock_return_anonymous_lifetime.rs @@ -0,0 +1,54 @@ +// vim: tw=80 +//! A mock method should be able to return an object parameterized on the +//! anonymous lifetime. +//! https://github.com/asomers/mockall/issues/87 +#![deny(warnings)] + +use mockall::*; + +#[derive(Clone)] +struct X<'a>(&'a u32); + +trait T { + fn trait_method(&self) -> X<'_>; +} + +mock! { + Foo { + fn inherent_method(&self) -> X<'_>; + } + impl T for Foo { + fn trait_method(&self) -> X<'_>; + } +} + +// It isn't possible to safely set an expectation for a non-'static return value +// (because the mock object doesn't have any lifetime parameters itself), but +// unsafely setting such an expectation is a common use case. +mod return_nonstatic { + use super::*; + + #[test] + fn inherent_method() { + let d = 42u32; + let x = X(&d); + let xstatic: X<'static> = unsafe{ std::mem::transmute(x) }; + let mut mock = MockFoo::new(); + mock.expect_inherent_method() + .returning(move || xstatic.clone()); + + assert_eq!(42u32, *mock.inherent_method().0); + } + #[test] + fn trait_method() { + let d = 42u32; + let x = X(&d); + let xstatic: X<'static> = unsafe{ std::mem::transmute(x) }; + let mut mock = MockFoo::new(); + mock.expect_trait_method() + .returning(move || xstatic.clone()); + + assert_eq!(42u32, *mock.trait_method().0); + } + +} diff --git a/tests/mock_return_dyn_trait.rs b/tests/mock_return_dyn_trait.rs new file mode 100644 index 0000000..a22ea60 --- /dev/null +++ b/tests/mock_return_dyn_trait.rs @@ -0,0 +1,57 @@ +// vim: tw=80 +//! a method that returns a reference to a trait object +#![deny(warnings)] + +use mockall::*; + +trait Test: Sync { + fn value(&self) -> i32; + fn mutate(&mut self); +} + +impl Test for i32 { + fn value(&self) -> i32 { + *self + } + + fn mutate(&mut self) { + *self = 0; + } +} + +mock! { + Foo { + fn ref_dyn(&self) -> &dyn Test; + fn static_dyn(&self) -> &'static dyn Test; + fn mut_dyn(&mut self) -> &mut dyn Test; + } +} + +#[test] +fn ref_dyn() { + let mut mock = MockFoo::new(); + mock.expect_ref_dyn() + .return_const(Box::new(42) as Box); + + assert_eq!(42, mock.ref_dyn().value()); +} + +#[test] +fn static_dyn() { + let mut mock = MockFoo::new(); + mock.expect_static_dyn() + .return_const(&42 as &'static dyn Test); + + assert_eq!(42, mock.static_dyn().value()); +} + +#[test] +fn mut_dyn() { + let mut mock = MockFoo::new(); + mock.expect_mut_dyn() + .return_var(Box::new(42) as Box); + + assert_eq!(42, mock.mut_dyn().value()); + mock.mut_dyn().mutate(); + assert_eq!(0, mock.mut_dyn().value()); +} diff --git a/tests/mock_return_mutable_reference.rs b/tests/mock_return_mutable_reference.rs new file mode 100644 index 0000000..7bc0ed4 --- /dev/null +++ b/tests/mock_return_mutable_reference.rs @@ -0,0 +1,89 @@ +// vim: tw=80 +//! A struct with a method that returns a mutable reference +#![deny(warnings)] + +use mockall::*; + +mock! { + Foo { + fn foo(&mut self, i: u32) -> &mut u32; + } +} + +#[test] +#[cfg_attr(not(feature = "nightly"), + should_panic(expected = "MockFoo::foo: Expectation() Returning default values requires"))] +#[cfg_attr(not(feature = "nightly"), allow(unused_must_use))] +fn return_default() { + let mut mock = MockFoo::new(); + mock.expect_foo(); + let r = mock.foo(0); + assert_eq!(u32::default(), *r); +} + +#[test] +fn return_var() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .return_var(5u32); + { + let r = mock.foo(0); + assert_eq!(5, *r); + *r = 6; + } + assert_eq!(6, *mock.foo(0)); +} + +#[test] +fn returning() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .returning(|_| 5u32); + let r = mock.foo(0); + assert_eq!(5, *r); +} + +mod sequence { + use super::*; + + #[test] + #[should_panic(expected = "MockFoo::foo(4): Method sequence violation")] + fn fail() { + let mut seq = Sequence::new(); + let mut mock = MockFoo::new(); + mock.expect_foo() + .with(predicate::eq(3)) + .times(1) + .return_var(0) + .in_sequence(&mut seq); + + mock.expect_foo() + .with(predicate::eq(4)) + .times(1) + .return_var(0) + .in_sequence(&mut seq); + + mock.foo(4); + } + + #[test] + fn ok() { + let mut seq = Sequence::new(); + let mut mock = MockFoo::new(); + mock.expect_foo() + .with(predicate::eq(3)) + .times(1) + .return_var(0) + .in_sequence(&mut seq); + + mock.expect_foo() + .with(predicate::eq(4)) + .times(1) + .return_var(0) + .in_sequence(&mut seq); + + mock.foo(3); + mock.foo(4); + } + +} diff --git a/tests/mock_return_reference.rs b/tests/mock_return_reference.rs new file mode 100644 index 0000000..d3fc71d --- /dev/null +++ b/tests/mock_return_reference.rs @@ -0,0 +1,105 @@ +// vim: tw=80 +//! A struct with a method that returns an immutable reference +#![deny(warnings)] + +use mockall::*; + +mock! { + Foo { + fn foo(&self, x: i32) -> &u32; + fn bar(&self) -> &u32; + } +} + +mod never { + use super::*; + + #[test] + #[should_panic(expected = + "MockFoo::bar: Expectation() should not have been called")] + fn fail() { + let mut mock = MockFoo::new(); + mock.expect_bar() + .return_const(0) + .never(); + mock.bar(); + } + + #[test] + fn ok() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .never(); + } +} + +#[test] +fn return_const() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .return_const(5u32); + assert_eq!(5, *mock.foo(4)); +} + +#[test] +#[cfg_attr(not(feature = "nightly"), + should_panic(expected = "MockFoo::foo: Expectation() Returning default values requires"))] +#[cfg_attr(not(feature = "nightly"), allow(unused_must_use))] +fn return_default() { + let mut mock = MockFoo::new(); + mock.expect_foo(); + let r = mock.foo(4); + assert_eq!(u32::default(), *r); +} + +mod sequence { + use super::*; + + #[test] + #[should_panic(expected = "exact call count")] + fn ambiguous() { + let mut seq = Sequence::new(); + let mut mock = MockFoo::new(); + mock.expect_foo() + .times(1..3) + .in_sequence(&mut seq); + mock.foo(4); + } + + #[test] + #[should_panic(expected = "MockFoo::foo(4): Method sequence violation")] + fn fail() { + let mut seq = Sequence::new(); + let mut mock = MockFoo::new(); + mock.expect_bar() + .times(1) + .return_const(0) + .in_sequence(&mut seq); + + mock.expect_foo() + .times(1) + .return_const(0) + .in_sequence(&mut seq); + + mock.foo(4); + mock.bar(); + } + + #[test] + fn ok() { + let mut seq = Sequence::new(); + let mut mock = MockFoo::new(); + mock.expect_foo() + .times(1) + .return_const(0) + .in_sequence(&mut seq); + + mock.expect_bar() + .times(1) + .return_const(0) + .in_sequence(&mut seq); + + mock.foo(4); + mock.bar(); + } +} diff --git a/tests/mock_same_trait_twice_on_generic_struct.rs b/tests/mock_same_trait_twice_on_generic_struct.rs new file mode 100644 index 0000000..51dd88f --- /dev/null +++ b/tests/mock_same_trait_twice_on_generic_struct.rs @@ -0,0 +1,32 @@ +// vim: ts=80 +//! Mock the same generic trait twice on a single struct (with different generic +//! arguments, of course). +//! +#![deny(warnings)] +#![allow(clippy::from_over_into)] + +use mockall::*; + +mock! { + pub Foo {} + impl Into for Foo { + fn into(self) -> u32; + } + impl Into for Foo { + fn into(self) -> i32; + } +} + +/// Ensure we can set expectations for both methods simultaneously +#[test] +fn return_once() { + let mut mocku = MockFoo::::new(); + mocku.expect_into() + .return_once(|| 42); + let mut mocki = MockFoo::::new(); + mocki.expect_into() + .return_once(|| -42); + + assert_eq!( as Into>::into(mocku), 42u32); + assert_eq!( as Into>::into(mocki), -42); +} diff --git a/tests/mock_specializing_methods.rs b/tests/mock_specializing_methods.rs new file mode 100644 index 0000000..f2c0256 --- /dev/null +++ b/tests/mock_specializing_methods.rs @@ -0,0 +1,34 @@ +// vim: tw=80 +//! A specializing method is a non-generic method of a generic struct that +//! places additional bounds on the struct's generic types via a where +//! clause. +#![deny(warnings)] + +use mockall::*; + +struct G(T); + +#[derive(Clone, Copy)] +struct NonDefault{} + +mock!{ + Foo where T: Copy { + fn foo(&self, t: T) -> G where T: Default + 'static; + } +} + +#[test] +fn returning() { + let mut mock = MockFoo::::default(); + mock.expect_foo() + .returning(G); + assert_eq!(42u32, mock.foo(42u32).0); + + // It's possible to instantiate a mock object that doesn't satisfy the + // specializing method's requirements: + let _mock2 = MockFoo::::default(); + // But you can't call the specializing method. This won't work: + // _mock2.expect_foo() + // .returning(|h| G(h)); + // _mock2.foo(NonDefault{}); +} diff --git a/tests/mock_static_method_with_generic_args.rs b/tests/mock_static_method_with_generic_args.rs new file mode 100644 index 0000000..5a8d0fe --- /dev/null +++ b/tests/mock_static_method_with_generic_args.rs @@ -0,0 +1,17 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; + +mock! { + Foo { + fn bar(x: T); + } +} + +#[test] +fn returning() { + let ctx = MockFoo::bar_context(); + ctx.expect::().returning(|_| ()); + MockFoo::bar(0i16) +} diff --git a/tests/mock_static_method_with_generic_return.rs b/tests/mock_static_method_with_generic_return.rs new file mode 100644 index 0000000..98c0cae --- /dev/null +++ b/tests/mock_static_method_with_generic_return.rs @@ -0,0 +1,18 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; + +mock! { + Foo { + fn bar(x: T) -> Vec; + } +} + +#[test] +fn returning() { + let ctx = MockFoo::bar_context(); + ctx.expect::().returning(|x| vec![x]); + let v = MockFoo::bar(42i16); + assert_eq!(v[0], 42i16); +} diff --git a/tests/mock_static_method_with_lifetime_parameters.rs b/tests/mock_static_method_with_lifetime_parameters.rs new file mode 100644 index 0000000..c71ece5 --- /dev/null +++ b/tests/mock_static_method_with_lifetime_parameters.rs @@ -0,0 +1,57 @@ +// vim: tw=80 +//! A static generic method whose only generic parameter is a lifetime parameter +#![deny(warnings)] + +use mockall::*; +use std::sync::Mutex; + +#[derive(Debug, Eq)] +struct X<'a>(&'a u32); + +impl<'a> PartialEq for X<'a> { + fn eq(&self, other: &X<'a>) -> bool { + self.0 == other.0 + } +} + +mock!{ + Foo { + fn foo<'a>(x: &'a X<'a>) -> u32; + } +} + +static FOO_MTX: Mutex<()> = Mutex::new(()); + +#[test] +fn return_const() { + let _m = FOO_MTX.lock().unwrap(); + + let ctx = MockFoo::foo_context(); + ctx.expect() + .return_const(42u32); + let x = X(&5); + assert_eq!(42, MockFoo::foo(&x)); +} + +#[test] +fn returning() { + let _m = FOO_MTX.lock().unwrap(); + + let ctx = MockFoo::foo_context(); + ctx.expect() + .returning(|f| *f.0); + let x = X(&5); + assert_eq!(5, MockFoo::foo(&x)); +} + +#[test] +fn withf() { + let _m = FOO_MTX.lock().unwrap(); + + let ctx = MockFoo::foo_context(); + ctx.expect() + .withf(|f| *f.0 == 5) + .return_const(42u32); + let x = X(&5); + assert_eq!(42, MockFoo::foo(&x)); +} diff --git a/tests/mock_static_method_with_reference_arguments.rs b/tests/mock_static_method_with_reference_arguments.rs new file mode 100644 index 0000000..1785ddc --- /dev/null +++ b/tests/mock_static_method_with_reference_arguments.rs @@ -0,0 +1,19 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; + +mock!{ + Foo { + fn bar(x: &u32) -> u64; + } +} + +#[test] +fn with() { + let ctx = MockFoo::bar_context(); + ctx.expect() + .with(predicate::eq(42)) + .return_const(99u64); + assert_eq!(99, MockFoo::bar(&42)); +} diff --git a/tests/mock_struct.rs b/tests/mock_struct.rs new file mode 100644 index 0000000..71b91e8 --- /dev/null +++ b/tests/mock_struct.rs @@ -0,0 +1,461 @@ +// vim: tw=80 +//! Structs can be mocked with mock! This is useful when the struct's original +//! definition is not accessible. +#![deny(warnings)] + +use mockall::*; + +// A struct with a definition like this: +// struct Foo { +// _x: i16 +// } +// impl Foo { +// fn foo(&self, _x: u32) -> u32 { +// 42 +// } +// } +// Could be mocked like this: +mock!{ + Foo { + fn foo(&self, x: u32) -> u32; + fn bar(&self, x: u32); + fn baz(&self); + } +} + +mod checkpoint { + use std::panic; + use super::*; + + #[test] + fn expect_again() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .returning(|_| 5) + .times(1..3); + mock.foo(0); + mock.checkpoint(); + + mock.expect_foo() + .returning(|_| 25); + assert_eq!(25, mock.foo(0)); + } + + #[test] + #[should_panic(expected = + "MockFoo::foo: Expectation() called 0 time(s) which is fewer than expected 1")] + fn not_yet_satisfied() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .returning(|_| 42) + .times(1); + mock.checkpoint(); + panic!("Shouldn't get here!"); + } + + #[test] + #[should_panic(expected = + "MockFoo::foo: Expectation() 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() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .returning(|_| 5) + .times(1..3); + mock.foo(0); + mock.checkpoint(); + } + + #[test] + #[should_panic(expected = "MockFoo::foo(0): No matching expectation found")] + fn removes_old_expectations() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .returning(|_| 42) + .times(1..3); + mock.foo(0); + mock.checkpoint(); + mock.foo(0); + panic!("Shouldn't get here!"); + } +} + +mod r#match { + use super::*; + + /// Unlike Mockers, Mockall calls should use the oldest matching + /// expectation, if multiple expectations match + #[test] + fn fifo_order() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .with(predicate::eq(5)) + .returning(|_| 99); + mock.expect_foo() + .with(predicate::always()) + .returning(|_| 42); + + assert_eq!(99, mock.foo(5)); + } + + #[test] + fn one_match() { + let mut mock0 = MockFoo::new(); + mock0.expect_foo() + .with(predicate::eq(5)) + .returning(|_| 99); + mock0.expect_foo() + .with(predicate::eq(6)) + .returning(|_| 42); + assert_eq!(42, mock0.foo(6)); + + // And in reverse order + let mut mock1 = MockFoo::new(); + mock1.expect_foo() + .with(predicate::eq(5)) + .returning(|_| 99); + mock1.expect_foo() + .with(predicate::eq(6)) + .returning(|_| 42); + assert_eq!(99, mock0.foo(5)); + } + + #[test] + #[should_panic(expected = "MockFoo::bar(5): No matching expectation found")] + fn with_no_matches() { + let mut mock = MockFoo::new(); + mock.expect_bar() + .with(predicate::eq(4)) + .return_const(()); + mock.bar(5); + } + + #[test] + fn with_ok() { + let mut mock = MockFoo::new(); + mock.expect_bar() + .with(predicate::eq(5)) + .return_const(()); + mock.bar(5); + } + + #[test] + fn withf_ok() { + let mut mock = MockFoo::new(); + mock.expect_bar() + .withf(|x: &u32| *x == 5) + .return_const(()); + mock.bar(5); + } + + #[test] + #[should_panic(expected = "MockFoo::bar(5): No matching expectation found")] + fn withf_no_matches() { + let mut mock = MockFoo::new(); + mock.expect_bar() + .withf(|x: &u32| *x == 6) + .return_const(()); + mock.bar(5); + } + +} + +mod never { + use super::*; + + #[test] + #[should_panic(expected = + "MockFoo::bar: Expectation() should not have been called")] + fn fail() { + let mut mock = MockFoo::new(); + mock.expect_bar() + .returning(|_| ()) + .never(); + mock.bar(0); + } + + #[test] + fn ok() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .never(); + } +} + +#[test] +fn return_const() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .return_const(42u32); + assert_eq!(42, mock.foo(5)); +} + +#[cfg_attr(not(feature = "nightly"), + should_panic(expected = "MockFoo::foo: Expectation() Returning default values requires"))] +#[cfg_attr(not(feature = "nightly"), allow(unused_must_use))] +#[test] +fn return_default() { + let mut mock = MockFoo::new(); + mock.expect_foo(); + let r = mock.foo(5); + assert_eq!(u32::default(), r); +} + +#[test] +fn returning() { + let mut mock = MockFoo::new(); + mock.expect_foo() + .returning(|x| x + 1); + assert_eq!(6, mock.foo(5)); +} + +mod sequence { + use super::*; + + #[test] + #[should_panic(expected = "exact call count")] + fn ambiguous() { + let mut seq = Sequence::new(); + let mut mock = MockFoo::new(); + mock.expect_baz() + .times(1..3) + .in_sequence(&mut seq); + mock.baz(); + } + + #[test] + #[should_panic(expected = "MockFoo::baz(): Method sequence violation")] + fn fail() { + let mut seq = Sequence::new(); + let mut mock = MockFoo::new(); + mock.expect_bar() + .times(1) + .returning(|_| ()) + .in_sequence(&mut seq); + + mock.expect_baz() + .times(1) + .returning(|| ()) + .in_sequence(&mut seq); + + mock.baz(); + mock.bar(0); + } + + #[test] + fn ok() { + let mut seq = Sequence::new(); + let mut mock = MockFoo::new(); + mock.expect_baz() + .times(1) + .returning(|| ()) + .in_sequence(&mut seq); + + mock.expect_bar() + .times(1) + .returning(|_| ()) + .in_sequence(&mut seq); + + mock.baz(); + mock.bar(0); + } + + /// When adding multiple calls of a single method, with the same arguments, + /// to a sequence, expectations should not be called after they are done if + /// there are more expectations to follow. + #[test] + fn single_method() { + let mut seq = Sequence::new(); + let mut mock = MockFoo::new(); + mock.expect_foo() + .times(1) + .in_sequence(&mut seq) + .returning(|_| 1); + mock.expect_foo() + .times(1) + .in_sequence(&mut seq) + .returning(|_| 2); + mock.expect_foo() + .times(1) + .in_sequence(&mut seq) + .returning(|_| 3); + + assert_eq!(1, mock.foo(0)); + assert_eq!(2, mock.foo(0)); + assert_eq!(3, mock.foo(0)); + } + +} + +mod times { + use super::*; + + #[test] + fn ok() { + let mut mock = MockFoo::new(); + mock.expect_baz() + .returning(|| ()) + .times(2); + mock.baz(); + mock.baz(); + } + + #[test] + #[should_panic(expected = + "MockFoo::bar: Expectation(var == 5) called 1 time(s) which is fewer than expected 2")] + fn too_few() { + let mut mock = MockFoo::new(); + mock.expect_bar() + .with(predicate::eq(5)) + .returning(|_| ()) + .times(2); + mock.bar(5); + } + + #[test] + #[should_panic(expected = + "MockFoo::baz: Expectation() called 3 times which is more than the expected 2")] + fn too_many() { + let mut mock = MockFoo::new(); + mock.expect_baz() + .returning(|| ()) + .times(2); + mock.baz(); + mock.baz(); + mock.baz(); + // Verify that we panic quickly and don't reach code below this point. + panic!("Shouldn't get here!"); + } + + #[test] + fn range_ok() { + let mut mock = MockFoo::new(); + mock.expect_baz() + .returning(|| ()) + .times(2..4); + mock.baz(); + mock.baz(); + + mock.expect_bar() + .returning(|_| ()) + .times(2..4); + mock.bar(0); + mock.bar(0); + mock.bar(0); + } + + #[test] + #[should_panic(expected = + "MockFoo::baz: Expectation() called 1 time(s) which is fewer than expected 2")] + fn range_too_few() { + let mut mock = MockFoo::new(); + mock.expect_baz() + .returning(|| ()) + .times(2..4); + mock.baz(); + } + + #[test] + #[should_panic(expected = + "MockFoo::baz: Expectation() called 4 times which is more than the expected 3")] + fn range_too_many() { + let mut mock = MockFoo::new(); + mock.expect_baz() + .returning(|| ()) + .times(2..4); + mock.baz(); + mock.baz(); + mock.baz(); + mock.baz(); + // Verify that we panic quickly and don't reach code below this point. + panic!("Shouldn't get here!"); + } + + #[test] + fn rangeto_ok() { + let mut mock = MockFoo::new(); + mock.expect_bar() + .returning(|_| ()) + .times(..4); + mock.bar(0); + mock.bar(0); + mock.bar(0); + } + + #[test] + #[should_panic(expected = + "MockFoo::baz: Expectation() called 4 times which is more than the expected 3")] + fn rangeto_too_many() { + let mut mock = MockFoo::new(); + mock.expect_baz() + .returning(|| ()) + .times(..4); + mock.baz(); + mock.baz(); + mock.baz(); + mock.baz(); + } + + #[test] + fn rangeinclusive_ok() { + let mut mock = MockFoo::new(); + mock.expect_bar() + .returning(|_| ()) + .times(2..=4); + mock.bar(0); + mock.bar(0); + mock.bar(0); + mock.bar(0); + } + + #[test] + fn rangefrom_ok() { + let mut mock = MockFoo::new(); + mock.expect_baz() + .returning(|| ()) + .times(2..); + mock.baz(); + mock.baz(); + + mock.expect_bar() + .returning(|_| ()) + .times(2..); + mock.bar(0); + mock.bar(0); + mock.bar(0); + } + + #[test] + #[should_panic(expected = + "MockFoo::baz: Expectation() called 1 time(s) which is fewer than expected 2")] + fn rangefrom_too_few() { + let mut mock = MockFoo::new(); + mock.expect_baz() + .returning(|| ()) + .times(2..); + mock.baz(); + } +} + +#[test] +fn times_full() { + let mut mock = MockFoo::new(); + mock.expect_baz() + .returning(|| ()) + .times(1) + .times(..); + mock.baz(); + mock.baz(); +} diff --git a/tests/mock_struct_with_static_method.rs b/tests/mock_struct_with_static_method.rs new file mode 100644 index 0000000..ffaebd0 --- /dev/null +++ b/tests/mock_struct_with_static_method.rs @@ -0,0 +1,103 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; +use std::sync::Mutex; + +mock!{ + Foo { + fn bar(x: u32) -> u64; + } +} + +static BAR_MTX: Mutex<()> = Mutex::new(()); + +// Checkpointing the mock object should not checkpoint static methods +#[test] +fn checkpoint() { + let _m = BAR_MTX.lock(); + + let mut mock = MockFoo::new(); + let ctx = MockFoo::bar_context(); + ctx.expect() + .returning(|_| 32) + .times(1..3); + mock.checkpoint(); + MockFoo::bar(0); +} + +// It should also be possible to checkpoint just the context object +#[test] +#[should_panic(expected = + "MockFoo::bar: Expectation() called 0 time(s) which is fewer than expected 1")] +fn ctx_checkpoint() { + let _m = BAR_MTX.lock(); + + let ctx = MockFoo::bar_context(); + ctx.expect() + .returning(|_| 32) + .times(1..3); + ctx.checkpoint(); + panic!("Shouldn't get here!"); +} + +// Expectations should be cleared when a context object drops +#[test] +#[should_panic(expected = "MockFoo::bar(42): No matching expectation found")] +fn ctx_hygiene() { + let _m = BAR_MTX.lock(); + + { + let ctx0 = MockFoo::bar_context(); + ctx0.expect() + .returning(|x| u64::from(x + 1)); + } + MockFoo::bar(42); +} + +#[test] +fn return_const() { + let _m = BAR_MTX.lock(); + + let ctx = MockFoo::bar_context(); + ctx.expect() + .return_const(42u64); + assert_eq!(42, MockFoo::bar(41)); +} + +#[cfg_attr(not(feature = "nightly"), ignore)] +#[cfg_attr(not(feature = "nightly"), allow(unused_must_use))] +#[test] +fn return_default() { + let _m = BAR_MTX.lock(); + + let ctx = MockFoo::bar_context(); + ctx.expect(); + let r = MockFoo::bar(5); + assert_eq!(u64::default(), r); +} + +#[test] +fn returning() { + let _m = BAR_MTX.lock(); + + let ctx = MockFoo::bar_context(); + ctx.expect() + .returning(|x| u64::from(x + 1)); + assert_eq!(42, MockFoo::bar(41)); +} + +#[test] +fn two_matches() { + let _m = BAR_MTX.lock(); + + let ctx = MockFoo::bar_context(); + ctx.expect() + .with(predicate::eq(42)) + .return_const(99u64); + ctx.expect() + .with(predicate::eq(69)) + .return_const(101u64); + assert_eq!(101, MockFoo::bar(69)); + assert_eq!(99, MockFoo::bar(42)); +} diff --git a/tests/mock_struct_with_trait.rs b/tests/mock_struct_with_trait.rs new file mode 100644 index 0000000..bdf13d4 --- /dev/null +++ b/tests/mock_struct_with_trait.rs @@ -0,0 +1,24 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; + +trait Foo { + fn foo(&self, x: u32) -> i64; +} + +mock!{ + pub Bar {} + impl Foo for Bar { + fn foo(&self, x: u32) -> i64; + } +} + +#[test] +fn return_const() { + let mut mock = MockBar::new(); + mock.expect_foo() + .return_const(6); + assert_eq!(6, mock.foo(5)); +} + diff --git a/tests/mock_struct_with_trait_with_associated_types.rs b/tests/mock_struct_with_trait_with_associated_types.rs new file mode 100644 index 0000000..03a26e5 --- /dev/null +++ b/tests/mock_struct_with_trait_with_associated_types.rs @@ -0,0 +1,21 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; + +mock! { + pub MyIter {} + impl Iterator for MyIter { + type Item=u32; + + fn next(&mut self) -> Option; + } +} + +#[test] +fn return_const() { + let mut mock = MockMyIter::new(); + mock.expect_next() + .return_const(None); + assert!(mock.next().is_none()); +} diff --git a/tests/mock_trait_returning_reference.rs b/tests/mock_trait_returning_reference.rs new file mode 100644 index 0000000..746acbd --- /dev/null +++ b/tests/mock_trait_returning_reference.rs @@ -0,0 +1,24 @@ +// vim: tw=80 +//! A trait with a method that returns an immutable reference +#![deny(warnings)] + +use mockall::*; + +trait Foo { + fn foo(&self) -> &u32; +} + +mock! { + pub Bar {} + impl Foo for Bar { + fn foo(&self) -> &u32; + } +} + +#[test] +fn return_const() { + let mut mock = MockBar::new(); + mock.expect_foo() + .return_const(5u32); + assert_eq!(5, *mock.foo()); +} diff --git a/tests/mock_trait_returning_static_reference.rs b/tests/mock_trait_returning_static_reference.rs new file mode 100644 index 0000000..75900d9 --- /dev/null +++ b/tests/mock_trait_returning_static_reference.rs @@ -0,0 +1,20 @@ +// vim: tw=80 +//! A struct with a method that returns an immutable static reference +#![deny(warnings)] + +use mockall::*; + +mock! { + Foo { + fn foo(&self) -> &'static u32; + } +} + +#[test] +fn return_const() { + const X: u32 = 5; + let mut mock = MockFoo::new(); + mock.expect_foo() + .return_const(&X); + assert_eq!(5, *mock.foo()); +} diff --git a/tests/mock_trait_with_static_method.rs b/tests/mock_trait_with_static_method.rs new file mode 100644 index 0000000..e0d5b1c --- /dev/null +++ b/tests/mock_trait_with_static_method.rs @@ -0,0 +1,23 @@ +// vim: tw=80 +#![deny(warnings)] + +use mockall::*; + +trait Bar { + fn baz(x: u32) -> u64; +} + +mock!{ + pub Foo {} + impl Bar for Foo { + fn baz(x: u32) -> u64; + } +} + +#[test] +fn returning() { + let ctx = MockFoo::baz_context(); + ctx.expect() + .returning(|x| u64::from(x + 1)); + assert_eq!(42, MockFoo::baz(41)); +} diff --git a/tests/mock_unsafe_trait.rs b/tests/mock_unsafe_trait.rs new file mode 100644 index 0000000..8368338 --- /dev/null +++ b/tests/mock_unsafe_trait.rs @@ -0,0 +1,25 @@ +// vim: ts=80 +#![deny(warnings)] + +use mockall::*; + +#[allow(clippy::missing_safety_doc)] +pub unsafe trait Bar { + fn bar(&self) -> i32; +} + +mock! { + pub Foo{} + unsafe impl Bar for Foo { + fn bar(&self) -> i32; + } +} + +#[test] +fn return_const() { + let mut mock = MockFoo::new(); + mock.expect_bar() + .return_const(42); + + assert_eq!(42, mock.bar()); +} diff --git a/tests/mock_unsized.rs b/tests/mock_unsized.rs new file mode 100644 index 0000000..5753510 --- /dev/null +++ b/tests/mock_unsized.rs @@ -0,0 +1,52 @@ +// vim: tw=80 +//! All types of predicate should work for methods with unsized arguments +#![deny(warnings)] + +use mockall::*; + +mock! { + Foo { + fn foo(&self, arg: &str); + } +} + +#[test] +fn with_always() { + let mut foo = MockFoo::new(); + foo.expect_foo() + .with(predicate::always()) + .return_const(()); + + foo.foo("xxx"); +} + +#[test] +fn with_eq() { + let mut foo = MockFoo::new(); + foo.expect_foo() + .with(predicate::eq("xxx")) + .return_const(()); + + foo.foo("xxx"); +} + +#[test] +#[should_panic(expected = "MockFoo::foo(\"xxx\"): No matching expectation found")] +fn with_never() { + let mut foo = MockFoo::new(); + foo.expect_foo() + .with(predicate::never()) + .return_const(()); + + foo.foo("xxx"); +} + +#[test] +fn withf() { + let mut foo = MockFoo::new(); + foo.expect_foo() + .withf(|a| a == "xxx") + .return_const(()); + + foo.foo("xxx"); +} diff --git a/tests/raw_identifier.rs b/tests/raw_identifier.rs new file mode 100644 index 0000000..2d56e37 --- /dev/null +++ b/tests/raw_identifier.rs @@ -0,0 +1,72 @@ +// vim: tw=80 +//! It should be possible to mock things that use raw identifiers +#![deny(warnings)] +#![allow(non_camel_case_types)] + +use mockall::*; + +#[automock] +trait r#while { + fn r#match(&self); + fn r#loop(); +} + +#[automock] +pub mod r#break { + pub fn r#if() {unimplemented!() } +} + +mock! { + r#do {} + impl r#while for r#do { + fn r#match(&self); + fn r#loop(); + } +} + +struct r#else {} +#[automock] +impl r#while for r#else { + fn r#match(&self) {unimplemented!()} + fn r#loop() {unimplemented!()} +} + +#[test] +fn by_ref() { + let mut foo = Mockwhile::new(); + foo.expect_match() + .return_const(()); + foo.r#match(); +} + +#[test] +fn static_method() { + let ctx = Mockwhile::loop_context(); + ctx.expect() + .returning(|| ()); + Mockwhile::r#loop(); +} + +#[test] +fn manual_mock() { + let mut foo = Mockdo::new(); + foo.expect_match() + .return_const(()); + foo.r#match(); +} + +#[test] +fn module() { + let ctx = mock_break::if_context(); + ctx.expect() + .returning(|| ()); + mock_break::r#if(); +} + +#[test] +fn trait_impl() { + let mut mock = Mockelse::new(); + mock.expect_match() + .returning(|| ()); + mock.r#match(); +} diff --git a/tests/specific_impl.rs b/tests/specific_impl.rs new file mode 100644 index 0000000..e16a48e --- /dev/null +++ b/tests/specific_impl.rs @@ -0,0 +1,74 @@ +// vim: ts=80 +#![deny(warnings)] + +use mockall::*; + +trait Bar { + fn bar(&self); +} +//trait Baz { + //fn baz(&self, y: Y); +//} + +mock! { + pub Foo { + } + impl Bar for Foo { + fn bar(&self); + } + impl Bar for Foo { + fn bar(&self); + } + // TODO: support specific impls with generic methods, like this + //impl Baz for Foo { + //fn baz(&self, y: Y); + //} +} + +#[test] +fn specific_impl() { + let mut mocku = MockFoo::::new(); + mocku.expect_bar() + .return_const(()); + let mut mocki = MockFoo::::new(); + mocki.expect_bar() + .return_const(()); + + mocku.bar(); + mocki.bar(); +} + +///// Make sure generic methods work with specific impls, too +//#[test] +//fn withf() { + //let mut mocku = MockFoo::::new(); + //mocku.expect_baz::() + //.withf(|y| y == 3.14159) + //.return_const(()); + //mocku.baz::(3.14159); +//} + +// Here's a partially specific impl: Bar is implemented for Bean where one of +// the generic types is concrete, but the other isn't. +mock! { + pub Bean {} + impl Bar for Bean { + fn bar(&self); + } + impl Bar for Bean { + fn bar(&self); + } +} + +#[test] +fn partially_specific_impl() { + let mut mocku = MockBean::::new(); + mocku.expect_bar() + .return_const(()); + let mut mocki = MockBean::::new(); + mocki.expect_bar() + .return_const(()); + + mocku.bar(); + mocki.bar(); +}