Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Allow mocking methods with complicated receivers like self: Box<Self> #176

Merged
merged 1 commit into from
Aug 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ This project adheres to [Semantic Versioning](http://semver.org/).

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

- Support mocking methods with trait object arguments that use implicit
lifetimes
lifetimes.
([#174](https://github.com/asomers/mockall/pull/174))

### Changed
Expand Down
67 changes: 67 additions & 0 deletions mockall/tests/mock_box_self.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// vim: tw=80
//! Methods that take receivers like Box<Self> instead of &self
#![deny(warnings)]

use mockall::*;
use std::sync::Arc;
use std::pin::Pin;
use std::rc::Rc;

mock! {
Foo {
fn foo(self: &Box<Self>);
fn baz(mut self: Box<Self>);
fn bar(self: Box<Self>);
fn bean(self: Arc<Self>);
fn booz(self: Pin<Box<Self>>);
fn blez(self: Rc<Self>);
}
}

#[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();
}
13 changes: 12 additions & 1 deletion mockall_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,9 @@ fn declosurefy(gen: &Generics, args: &Punctuated<FnArg, Token![,]>) ->
let mut pt2 = pt.clone();
demutify_arg(&mut pt2);
let pat = &pt2.pat;
if hm.contains_key(&pt.ty) {
if pat_is_self(pat) {
None
} else if hm.contains_key(&pt.ty) {
Some(quote!(Box::new(#pat)))
} else {
Some(quote!(#pat))
Expand Down Expand Up @@ -452,6 +454,15 @@ fn format_attrs(attrs: &[syn::Attribute], include_docs: bool) -> TokenStream {
out
}

/// Determine if this Pat is any kind of `self` binding
fn pat_is_self(pat: &Pat) -> bool {
if let Pat::Ident(pi) = pat {
pi.ident == "self"
} else {
false
}
}

fn supersuperfy_path(path: &mut Path, levels: i32) {
if let Some(t) = path.segments.first() {
if t.ident == "super" {
Expand Down
5 changes: 5 additions & 0 deletions mockall_derive/src/mock_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ impl<'a> Builder<'a> {
for fa in declosured_inputs.iter() {
if let FnArg::Typed(pt) = fa {
let argname = (*pt.pat).clone();
if pat_is_self(&argname) {
// A weird receiver like `Box<Self>`
is_static = false;
continue;
}
let aty = supersuperfy(&pt.ty, self.levels);
if let Type::Reference(ref tr) = aty {
predexprs.push(quote!(#argname));
Expand Down