Skip to content

Commit

Permalink
Add support for std::function in MockFunction (#2277)
Browse files Browse the repository at this point in the history
  • Loading branch information
adambadura committed Jan 23, 2020
1 parent ad7b742 commit b73a04a
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 41 deletions.
132 changes: 93 additions & 39 deletions googlemock/include/gmock/gmock-spec-builders.h
Original file line number Diff line number Diff line change
Expand Up @@ -1786,10 +1786,80 @@ void ReportUninterestingCall(CallReaction reaction, const std::string& msg);

} // namespace internal

// A MockFunction<F> class has one mock method whose type is F. It is
// useful when you just want your test code to emit some messages and
// have Google Mock verify the right messages are sent (and perhaps at
// the right times). For example, if you are exercising code:
namespace internal {

template <typename F>
class MockFunction;

template <typename R, typename... Args>
class MockFunction<R(Args...)> {
public:
MockFunction(const MockFunction&) = delete;
MockFunction& operator=(const MockFunction&) = delete;

std::function<R(Args...)> AsStdFunction() {
return [this](Args... args) -> R {
return this->Call(std::forward<Args>(args)...);
};
}

// Implementation detail: the expansion of the MOCK_METHOD macro.
R Call(Args... args) {
mock_.SetOwnerAndName(this, "Call");
return mock_.Invoke(std::forward<Args>(args)...);
}

MockSpec<R(Args...)> gmock_Call(Matcher<Args>... m) {
mock_.RegisterOwner(this);
return mock_.With(std::move(m)...);
}

MockSpec<R(Args...)> gmock_Call(const WithoutMatchers&,
R (*)(Args...)) {
return this->gmock_Call(::testing::A<Args>()...);
}

protected:
MockFunction() = default;
~MockFunction() = default;

private:
FunctionMocker<R(Args...)> mock_;
};

/*
The SignatureOf<F> struct is a meta-function returning function signature
corresponding to the provided F argument.
It makes use of MockFunction easier by allowing it to accept more F arguments
than just function signatures.
Specializations provided here cover only a signature type itself and
std::function. However, if need be it can be easily extended to cover also other
types (like for example boost::function).
*/

template <typename F>
struct SignatureOf;

template <typename R, typename... Args>
struct SignatureOf<R(Args...)> {
using type = R(Args...);
};

template <typename F>
struct SignatureOf<std::function<F>> : SignatureOf<F> {};

template <typename F>
using SignatureOfT = typename SignatureOf<F>::type;

} // namespace internal

// A MockFunction<F> type has one mock method whose type is
// internal::SignatureOfT<F>. It is useful when you just want your
// test code to emit some messages and have Google Mock verify the
// right messages are sent (and perhaps at the right times). For
// example, if you are exercising code:
//
// Foo(1);
// Foo(2);
Expand Down Expand Up @@ -1823,49 +1893,33 @@ void ReportUninterestingCall(CallReaction reaction, const std::string& msg);
// Bar("a") is called by which call to Foo().
//
// MockFunction<F> can also be used to exercise code that accepts
// std::function<F> callbacks. To do so, use AsStdFunction() method
// to create std::function proxy forwarding to original object's Call.
// Example:
// std::function<internal::SignatureOfT<F>> callbacks. To do so, use
// AsStdFunction() method to create std::function proxy forwarding to
// original object's Call. Example:
//
// TEST(FooTest, RunsCallbackWithBarArgument) {
// MockFunction<int(string)> callback;
// EXPECT_CALL(callback, Call("bar")).WillOnce(Return(1));
// Foo(callback.AsStdFunction());
// }
//
// The internal::SignatureOfT<F> indirection allows to use other types
// than just function signature type. This is typically useful when
// providing a mock for a predefined std::function type. Example:
//
// using FilterPredicate = std::function<bool(string)>;
// void MyFilterAlgorithm(FilterPredicate predicate);
//
// TEST(FooTest, FilterPredicateAlwaysAccepts) {
// MockFunction<FilterPredicate> predicateMock;
// EXPECT_CALL(predicateMock, Call(_)).WillRepeatedly(Return(true));
// MyFilterAlgorithm(predicateMock.AsStdFunction());
// }
template <typename F>
class MockFunction;

template <typename R, typename... Args>
class MockFunction<R(Args...)> {
class MockFunction : public internal::MockFunction<internal::SignatureOfT<F>> {
using Base = internal::MockFunction<internal::SignatureOfT<F>>;
public:
MockFunction() {}
MockFunction(const MockFunction&) = delete;
MockFunction& operator=(const MockFunction&) = delete;

std::function<R(Args...)> AsStdFunction() {
return [this](Args... args) -> R {
return this->Call(std::forward<Args>(args)...);
};
}

// Implementation detail: the expansion of the MOCK_METHOD macro.
R Call(Args... args) {
mock_.SetOwnerAndName(this, "Call");
return mock_.Invoke(std::forward<Args>(args)...);
}

internal::MockSpec<R(Args...)> gmock_Call(Matcher<Args>... m) {
mock_.RegisterOwner(this);
return mock_.With(std::move(m)...);
}

internal::MockSpec<R(Args...)> gmock_Call(const internal::WithoutMatchers&,
R (*)(Args...)) {
return this->gmock_Call(::testing::A<Args>()...);
}

private:
internal::FunctionMocker<R(Args...)> mock_;
using Base::Base;
};

// The style guide prohibits "using" statements in a namespace scope
Expand Down
21 changes: 20 additions & 1 deletion googlemock/test/gmock-function-mocker_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
# include <objbase.h>
#endif // GTEST_OS_WINDOWS

#include <functional>
#include <map>
#include <string>
#include <type_traits>
Expand Down Expand Up @@ -665,12 +666,30 @@ using MockMethodMockFunctionSignatureTypes = Types<
>;
TYPED_TEST_SUITE(MockMethodMockFunctionSignatureTest, MockMethodMockFunctionSignatureTypes);

TYPED_TEST(MockMethodMockFunctionSignatureTest, IsMockFunctionTemplateArgumentDeduced) {
TYPED_TEST(MockMethodMockFunctionSignatureTest, IsMockFunctionTemplateArgumentDeducedForRawSignature) {
using Argument = TypeParam;
MockFunction<Argument> foo;
EXPECT_TRUE(IsMockFunctionTemplateArgumentDeducedTo<Argument>(foo));
}

TYPED_TEST(MockMethodMockFunctionSignatureTest, IsMockFunctionTemplateArgumentDeducedForStdFunction) {
using Argument = std::function<TypeParam>;
MockFunction<Argument> foo;
EXPECT_TRUE(IsMockFunctionTemplateArgumentDeducedTo<Argument>(foo));
}

TYPED_TEST(MockMethodMockFunctionSignatureTest, IsMockFunctionCallMethodSignatureTheSameForRawSignatureAndStdFunction) {
using ForRawSignature = decltype(&MockFunction<TypeParam>::Call);
using ForStdFunction = decltype(&MockFunction<std::function<TypeParam>>::Call);
EXPECT_TRUE((std::is_same<ForRawSignature, ForStdFunction>::value));
}

TYPED_TEST(MockMethodMockFunctionSignatureTest, IsMockFunctionAsStdFunctionMethodSignatureTheSameForRawSignatureAndStdFunction) {
using ForRawSignature = decltype(&MockFunction<TypeParam>::AsStdFunction);
using ForStdFunction = decltype(&MockFunction<std::function<TypeParam>>::AsStdFunction);
EXPECT_TRUE((std::is_same<ForRawSignature, ForStdFunction>::value));
}


struct MockMethodSizes0 {
MOCK_METHOD(void, func, ());
Expand Down
21 changes: 20 additions & 1 deletion googlemock/test/gmock-generated-function-mockers_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
# include <objbase.h>
#endif // GTEST_OS_WINDOWS

#include <functional>
#include <map>
#include <string>
#include <type_traits>
Expand Down Expand Up @@ -656,12 +657,30 @@ using MockMethodMockFunctionSignatureTypes = Types<
>;
TYPED_TEST_SUITE(MockMethodMockFunctionSignatureTest, MockMethodMockFunctionSignatureTypes);

TYPED_TEST(MockMethodMockFunctionSignatureTest, IsMockFunctionTemplateArgumentDeduced) {
TYPED_TEST(MockMethodMockFunctionSignatureTest, IsMockFunctionTemplateArgumentDeducedForRawSignature) {
using Argument = TypeParam;
MockFunction<Argument> foo;
EXPECT_TRUE(IsMockFunctionTemplateArgumentDeducedTo<Argument>(foo));
}

TYPED_TEST(MockMethodMockFunctionSignatureTest, IsMockFunctionTemplateArgumentDeducedForStdFunction) {
using Argument = std::function<TypeParam>;
MockFunction<Argument> foo;
EXPECT_TRUE(IsMockFunctionTemplateArgumentDeducedTo<Argument>(foo));
}

TYPED_TEST(MockMethodMockFunctionSignatureTest, IsMockFunctionCallMethodSignatureTheSameForRawSignatureAndStdFunction) {
using ForRawSignature = decltype(&MockFunction<TypeParam>::Call);
using ForStdFunction = decltype(&MockFunction<std::function<TypeParam>>::Call);
EXPECT_TRUE((std::is_same<ForRawSignature, ForStdFunction>::value));
}

TYPED_TEST(MockMethodMockFunctionSignatureTest, IsMockFunctionAsStdFunctionMethodSignatureTheSameForRawSignatureAndStdFunction) {
using ForRawSignature = decltype(&MockFunction<TypeParam>::AsStdFunction);
using ForStdFunction = decltype(&MockFunction<std::function<TypeParam>>::AsStdFunction);
EXPECT_TRUE((std::is_same<ForRawSignature, ForStdFunction>::value));
}


struct MockMethodSizes0 {
MOCK_METHOD0(func, void());
Expand Down

0 comments on commit b73a04a

Please sign in to comment.