Skip to content
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).

- Raised MSRV to 1.60.0 because predicates-tree did.
([#430](https://github.com/asomers/mockall/pull/430))
- Better "No matching expectation found" messages on stable.
([#425](https://github.com/asomers/mockall/pull/425))

### Fixed

Expand Down
54 changes: 32 additions & 22 deletions mockall/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1146,7 +1146,7 @@
use downcast::*;
use std::{
any,
fmt::{self, Debug, Formatter},
fmt::Debug,
marker::PhantomData,
ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo,
RangeToInclusive},
Expand Down Expand Up @@ -1462,28 +1462,38 @@ pub struct DefaultReturner<O>(PhantomData<O>);
}
}

// Wrapper type to allow for better expectation messages for any type.
// Will first try Debug, otherwise will print '?'
#[doc(hidden)]
pub struct MaybeDebugger<'a, T>(pub &'a T);
::cfg_if::cfg_if! {
if #[cfg(feature = "nightly")] {
impl<'a, T> Debug for MaybeDebugger<'a, T> {
default fn fmt(&self, f: &mut Formatter<'_>)
-> Result<(), fmt::Error>
{
write!(f, "?")
}
}
impl<'a, T: Debug> Debug for MaybeDebugger<'a, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
self.0.fmt(f)
}
}
} else {
impl<'a, T> Debug for MaybeDebugger<'a, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "?")
}
}
pub struct ArgPrinter<'a, T>(pub &'a T);

#[doc(hidden)]
pub struct DebugPrint<'a, T: Debug>(pub &'a T);
impl<'a, T> Debug for DebugPrint<'a, T> where T: Debug {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Debug::fmt(self.0, f)
}
}
#[doc(hidden)]
pub trait ViaDebug<T> where T: Debug { fn debug_string(&self) -> DebugPrint<'_, T>; }
impl<'a, T: Debug> ViaDebug<T> for &ArgPrinter<'a, T> {
fn debug_string(&self) -> DebugPrint<'a, T> {
DebugPrint(self.0)
}
}

#[doc(hidden)]
pub struct NothingPrint;
impl Debug for NothingPrint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "?")
}
}
#[doc(hidden)]
pub trait ViaNothing { fn debug_string(&self) -> NothingPrint; }
impl<'a, T> ViaNothing for ArgPrinter<'a, T> {
fn debug_string(&self) -> NothingPrint {
NothingPrint
}
}

Expand Down
7 changes: 1 addition & 6 deletions mockall/tests/automock_foreign_c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,7 @@ pub fn normal_usage() {
}

#[test]
#[cfg_attr(feature = "nightly", should_panic(
expected = "mock_ffi::foo1(5): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "mock_ffi::foo1(?): No matching expectation found"
))]
#[should_panic(expected = "mock_ffi::foo1(5): No matching expectation found")]
fn with_no_matches() {
let ctx = mock_ffi::foo1_context();
ctx.expect()
Expand Down
7 changes: 1 addition & 6 deletions mockall/tests/automock_foreign_extern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,7 @@ extern "C" {
}

#[test]
#[cfg_attr(feature = "nightly", should_panic(
expected = "mock_ffi::foo1(5): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "mock_ffi::foo1(?): No matching expectation found"
))]
#[should_panic(expected = "mock_ffi::foo1(5): No matching expectation found")]
fn with_no_matches() {
let ctx = mock_ffi::foo1_context();
ctx.expect()
Expand Down
7 changes: 1 addition & 6 deletions mockall/tests/automock_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,7 @@ pub mod m {
}

#[test]
#[cfg_attr(feature = "nightly", should_panic(
expected = "mock_foo::bar1(5): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "mock_foo::bar1(?): No matching expectation found"
))]
#[should_panic(expected = "mock_foo::bar1(5): No matching expectation found")]
fn with_no_matches() {
let ctx = mock_foo::bar1_context();
ctx.expect()
Expand Down
7 changes: 1 addition & 6 deletions mockall/tests/automock_nondebug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,7 @@ pub trait Foo {
}

#[test]
#[cfg_attr(feature = "nightly", should_panic(
expected = "MockFoo::foo(?): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "MockFoo::foo(?): No matching expectation found"
))]
#[should_panic(expected = "MockFoo::foo(?): No matching expectation found")]
fn with_no_matches() {
let mock = MockFoo::new();
mock.foo(NonDebug(5));
Expand Down
7 changes: 1 addition & 6 deletions mockall/tests/automock_slice_arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,7 @@ mod withf {
use super::*;

#[test]
#[cfg_attr(feature = "nightly", should_panic(
expected = "MockFoo::foo([1, 2, 3, 4]): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "MockFoo::foo(?): No matching expectation found"
))]
#[should_panic(expected = "MockFoo::foo([1, 2, 3, 4]): No matching expectation found")]
fn fail() {
let mut mock = MockFoo::new();
mock.expect_foo()
Expand Down
19 changes: 12 additions & 7 deletions mockall/tests/mock_generic_arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use mockall::*;

mock! {
Foo {
fn foo<T: 'static>(&self, t: T) -> i32;
fn foo<T: 'static + std::fmt::Debug>(&self, t: T) -> i32;
fn bar<T: 'static>(&self, t: T) -> i32;
}
}
Expand Down Expand Up @@ -47,17 +47,22 @@ mod with {
}

#[test]
#[cfg_attr(feature = "nightly", should_panic(
expected = "MockFoo::foo(4): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "MockFoo::foo(?): No matching expectation found"
))]
#[should_panic(expected = "MockFoo::foo(4): No matching expectation found")]
fn wrong_generic_type() {
let mut mock = MockFoo::new();
mock.expect_foo::<i16>()
.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::<i16>()
.with(predicate::eq(4))
.return_const(0);
mock.bar(4i32);
}
}
15 changes: 5 additions & 10 deletions mockall/tests/mock_generic_struct_with_generic_static_method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ use mockall::*;
use std::sync::Mutex;

mock! {
Foo<T: 'static> {
fn foo<Q: 'static>(t: T, q: Q) -> u64;
Foo<T: 'static + std::fmt::Debug> {
fn foo<Q: 'static + std::fmt::Debug>(t: T, q: Q) -> u64;
// We must use a different method for every should_panic test, so the
// shared mutex doesn't get poisoned.
fn foo2<Q: 'static>(t: T, q: Q) -> u64;
fn foo3<Q: 'static>(t: T, q: Q) -> u64;
fn foo2<Q: 'static + std::fmt::Debug>(t: T, q: Q) -> u64;
fn foo3<Q: 'static + std::fmt::Debug>(t: T, q: Q) -> u64;
}
}

Expand Down Expand Up @@ -48,12 +48,7 @@ fn ctx_checkpoint() {

// Expectations should be cleared when a context object drops
#[test]
#[cfg_attr(feature = "nightly", should_panic(
expected = "MockFoo::foo3(42, 69): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "MockFoo::foo3(?, ?): No matching expectation found"
))]
#[should_panic(expected = "MockFoo::foo3(42, 69): No matching expectation found")]
fn ctx_hygiene() {
{
let ctx0 = MockFoo::<u32>::foo3_context();
Expand Down
5 changes: 1 addition & 4 deletions mockall/tests/mock_return_mutable_reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,7 @@ mod sequence {
use super::*;

#[test]
#[cfg_attr(feature = "nightly",
should_panic(expected = "MockFoo::foo(4): Method sequence violation"))]
#[cfg_attr(not(feature = "nightly"),
should_panic(expected = "MockFoo::foo(?): Method sequence violation"))]
#[should_panic(expected = "MockFoo::foo(4): Method sequence violation")]
fn fail() {
let mut seq = Sequence::new();
let mut mock = MockFoo::new();
Expand Down
5 changes: 1 addition & 4 deletions mockall/tests/mock_return_reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,7 @@ mod sequence {
}

#[test]
#[cfg_attr(feature = "nightly",
should_panic(expected = "MockFoo::foo(4): Method sequence violation"))]
#[cfg_attr(not(feature = "nightly"),
should_panic(expected = "MockFoo::foo(?): Method sequence violation"))]
#[should_panic(expected = "MockFoo::foo(4): Method sequence violation")]
fn fail() {
let mut seq = Sequence::new();
let mut mock = MockFoo::new();
Expand Down
21 changes: 3 additions & 18 deletions mockall/tests/mock_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,7 @@ mod checkpoint {
}

#[test]
#[cfg_attr(feature = "nightly", should_panic(
expected = "MockFoo::foo(0): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "MockFoo::foo(?): No matching expectation found"
))]
#[should_panic(expected = "MockFoo::foo(0): No matching expectation found")]
fn removes_old_expectations() {
let mut mock = MockFoo::new();
mock.expect_foo()
Expand Down Expand Up @@ -123,12 +118,7 @@ mod r#match {
}

#[test]
#[cfg_attr(feature = "nightly", should_panic(
expected = "MockFoo::bar(5): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "MockFoo::bar(?): No matching expectation found"
))]
#[should_panic(expected = "MockFoo::bar(5): No matching expectation found")]
fn with_no_matches() {
let mut mock = MockFoo::new();
mock.expect_bar()
Expand Down Expand Up @@ -156,12 +146,7 @@ mod r#match {
}

#[test]
#[cfg_attr(feature = "nightly", should_panic(
expected = "MockFoo::bar(5): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "MockFoo::bar(?): No matching expectation found"
))]
#[should_panic(expected = "MockFoo::bar(5): No matching expectation found")]
fn withf_no_matches() {
let mut mock = MockFoo::new();
mock.expect_bar()
Expand Down
7 changes: 1 addition & 6 deletions mockall/tests/mock_struct_with_static_method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,7 @@ fn ctx_checkpoint() {

// Expectations should be cleared when a context object drops
#[test]
#[cfg_attr(feature = "nightly", should_panic(
expected = "MockFoo::bar3(42): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "MockFoo::bar3(?): No matching expectation found"
))]
#[should_panic(expected = "MockFoo::bar3(42): No matching expectation found")]
fn ctx_hygiene() {
{
let ctx0 = MockFoo::bar3_context();
Expand Down
7 changes: 1 addition & 6 deletions mockall/tests/mock_unsized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,7 @@ fn with_eq() {
}

#[test]
#[cfg_attr(feature = "nightly", should_panic(
expected = "MockFoo::foo(\"xxx\"): No matching expectation found"
))]
#[cfg_attr(not(feature = "nightly"), should_panic(
expected = "MockFoo::foo(?): No matching expectation found"
))]
#[should_panic(expected = "MockFoo::foo(\"xxx\"): No matching expectation found")]
fn with_never() {
let mut foo = MockFoo::new();
foo.expect_foo()
Expand Down
7 changes: 6 additions & 1 deletion mockall_derive/src/mock_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,7 @@ impl MockFunction {
#(#attrs)*
#dead_code
#vis #sig {
use ::mockall::{ViaDebug, ViaNothing};
let no_match_msg = #no_match_msg;
#deref {
let __mockall_guard = #outer_mod_path::EXPECTATIONS
Expand All @@ -532,6 +533,7 @@ impl MockFunction {
#(#attrs)*
#dead_code
#vis #sig {
use ::mockall::{ViaDebug, ViaNothing};
let no_match_msg = #no_match_msg;
#deref self.#substruct_obj #name.#call #tbf(#(#call_exprs,)*)
.expect(&no_match_msg)
Expand Down Expand Up @@ -602,7 +604,7 @@ impl MockFunction {
};
let fields = vec!["{:?}"; argnames.len()].join(", ");
let fstr = format!("{name}({fields})");
quote!(std::format!(#fstr, #(::mockall::MaybeDebugger(&#argnames)),*))
quote!(std::format!(#fstr, #((&&::mockall::ArgPrinter(&#argnames)).debug_string()),*))
}

/// Generate code for the expect_ method
Expand Down Expand Up @@ -1952,6 +1954,7 @@ impl<'a> ToTokens for RefExpectation<'a> {
/// Call this [`Expectation`] as if it were the real method.
#v fn call #lg (&self, #(#argnames: #argty, )*) -> #output
{
use ::mockall::{ViaDebug, ViaNothing};
self.common.call(&#desc);
self.rfunc.call().unwrap_or_else(|m| {
let desc = std::format!(
Expand Down Expand Up @@ -2016,6 +2019,7 @@ impl<'a> ToTokens for RefMutExpectation<'a> {
#v fn call_mut #lg (&mut self, #(#argnames: #argty, )*)
-> &mut #owned_output
{
use ::mockall::{ViaDebug, ViaNothing};
self.common.call(&#desc);
let desc = std::format!(
"{}", self.common.matcher.lock().unwrap());
Expand Down Expand Up @@ -2104,6 +2108,7 @@ impl<'a> ToTokens for StaticExpectation<'a> {
#[doc(hidden)]
#v fn call #lg (&self, #(#argnames: #argty, )* ) -> #output
{
use ::mockall::{ViaDebug, ViaNothing};
self.common.call(&#desc);
self.rfunc.lock().unwrap().call_mut(#(#argnames, )*)
.unwrap_or_else(|message| {
Expand Down