From a47a850a6d81708aacfc5770fa188e5c9ba026ca Mon Sep 17 00:00:00 2001 From: Adam Badura Date: Thu, 11 Jul 2019 10:20:21 +0200 Subject: [PATCH] Add support for std::function in MockFunction (#2277) --- .../include/gmock/gmock-spec-builders.h | 132 ++++++++++++------ googlemock/test/gmock-function-mocker_test.cc | 21 ++- .../gmock-generated-function-mockers_test.cc | 21 ++- 3 files changed, 133 insertions(+), 41 deletions(-) diff --git a/googlemock/include/gmock/gmock-spec-builders.h b/googlemock/include/gmock/gmock-spec-builders.h index 718c9484ab..30a75ce3ff 100644 --- a/googlemock/include/gmock/gmock-spec-builders.h +++ b/googlemock/include/gmock/gmock-spec-builders.h @@ -1786,10 +1786,80 @@ void ReportUninterestingCall(CallReaction reaction, const std::string& msg); } // namespace internal -// A MockFunction 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 +class MockFunction; + +template +class MockFunction { + public: + MockFunction(const MockFunction&) = delete; + MockFunction& operator=(const MockFunction&) = delete; + + std::function AsStdFunction() { + return [this](Args... args) -> R { + return this->Call(std::forward(args)...); + }; + } + + // Implementation detail: the expansion of the MOCK_METHOD macro. + R Call(Args... args) { + mock_.SetOwnerAndName(this, "Call"); + return mock_.Invoke(std::forward(args)...); + } + + MockSpec gmock_Call(Matcher... m) { + mock_.RegisterOwner(this); + return mock_.With(std::move(m)...); + } + + MockSpec gmock_Call(const WithoutMatchers&, + R (*)(Args...)) { + return this->gmock_Call(::testing::A()...); + } + + protected: + MockFunction() = default; + ~MockFunction() = default; + + private: + FunctionMocker mock_; +}; + +/* +The SignatureOf 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 +struct SignatureOf; + +template +struct SignatureOf { + using type = R(Args...); +}; + +template +struct SignatureOf> : SignatureOf {}; + +template +using SignatureOfT = typename SignatureOf::type; + +} // namespace internal + +// A MockFunction type has one mock method whose type is +// internal::SignatureOfT. 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); @@ -1823,49 +1893,33 @@ void ReportUninterestingCall(CallReaction reaction, const std::string& msg); // Bar("a") is called by which call to Foo(). // // MockFunction can also be used to exercise code that accepts -// std::function callbacks. To do so, use AsStdFunction() method -// to create std::function proxy forwarding to original object's Call. -// Example: +// std::function> callbacks. To do so, use +// AsStdFunction() method to create std::function proxy forwarding to +// original object's Call. Example: // // TEST(FooTest, RunsCallbackWithBarArgument) { // MockFunction callback; // EXPECT_CALL(callback, Call("bar")).WillOnce(Return(1)); // Foo(callback.AsStdFunction()); // } +// +// The internal::SignatureOfT 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; +// void MyFilterAlgorithm(FilterPredicate predicate); +// +// TEST(FooTest, FilterPredicateAlwaysAccepts) { +// MockFunction predicateMock; +// EXPECT_CALL(predicateMock, Call(_)).WillRepeatedly(Return(true)); +// MyFilterAlgorithm(predicateMock.AsStdFunction()); +// } template -class MockFunction; - -template -class MockFunction { +class MockFunction : public internal::MockFunction> { + using Base = internal::MockFunction>; public: - MockFunction() {} - MockFunction(const MockFunction&) = delete; - MockFunction& operator=(const MockFunction&) = delete; - - std::function AsStdFunction() { - return [this](Args... args) -> R { - return this->Call(std::forward(args)...); - }; - } - - // Implementation detail: the expansion of the MOCK_METHOD macro. - R Call(Args... args) { - mock_.SetOwnerAndName(this, "Call"); - return mock_.Invoke(std::forward(args)...); - } - - internal::MockSpec gmock_Call(Matcher... m) { - mock_.RegisterOwner(this); - return mock_.With(std::move(m)...); - } - - internal::MockSpec gmock_Call(const internal::WithoutMatchers&, - R (*)(Args...)) { - return this->gmock_Call(::testing::A()...); - } - - private: - internal::FunctionMocker mock_; + using Base::Base; }; // The style guide prohibits "using" statements in a namespace scope diff --git a/googlemock/test/gmock-function-mocker_test.cc b/googlemock/test/gmock-function-mocker_test.cc index e6753c56cf..ecd0f9e648 100644 --- a/googlemock/test/gmock-function-mocker_test.cc +++ b/googlemock/test/gmock-function-mocker_test.cc @@ -40,6 +40,7 @@ # include #endif // GTEST_OS_WINDOWS +#include #include #include #include @@ -665,12 +666,30 @@ using MockMethodMockFunctionSignatureTypes = Types< >; TYPED_TEST_SUITE(MockMethodMockFunctionSignatureTest, MockMethodMockFunctionSignatureTypes); -TYPED_TEST(MockMethodMockFunctionSignatureTest, IsMockFunctionTemplateArgumentDeduced) { +TYPED_TEST(MockMethodMockFunctionSignatureTest, IsMockFunctionTemplateArgumentDeducedForRawSignature) { using Argument = TypeParam; MockFunction foo; EXPECT_TRUE(IsMockFunctionTemplateArgumentDeducedTo(foo)); } +TYPED_TEST(MockMethodMockFunctionSignatureTest, IsMockFunctionTemplateArgumentDeducedForStdFunction) { + using Argument = std::function; + MockFunction foo; + EXPECT_TRUE(IsMockFunctionTemplateArgumentDeducedTo(foo)); +} + +TYPED_TEST(MockMethodMockFunctionSignatureTest, IsMockFunctionCallMethodSignatureTheSameForRawSignatureAndStdFunction) { + using ForRawSignature = decltype(&MockFunction::Call); + using ForStdFunction = decltype(&MockFunction>::Call); + EXPECT_TRUE((std::is_same::value)); +} + +TYPED_TEST(MockMethodMockFunctionSignatureTest, IsMockFunctionAsStdFunctionMethodSignatureTheSameForRawSignatureAndStdFunction) { + using ForRawSignature = decltype(&MockFunction::AsStdFunction); + using ForStdFunction = decltype(&MockFunction>::AsStdFunction); + EXPECT_TRUE((std::is_same::value)); +} + struct MockMethodSizes0 { MOCK_METHOD(void, func, ()); diff --git a/googlemock/test/gmock-generated-function-mockers_test.cc b/googlemock/test/gmock-generated-function-mockers_test.cc index 1c3d5a9b98..b6f9af15f9 100644 --- a/googlemock/test/gmock-generated-function-mockers_test.cc +++ b/googlemock/test/gmock-generated-function-mockers_test.cc @@ -41,6 +41,7 @@ # include #endif // GTEST_OS_WINDOWS +#include #include #include #include @@ -656,12 +657,30 @@ using MockMethodMockFunctionSignatureTypes = Types< >; TYPED_TEST_SUITE(MockMethodMockFunctionSignatureTest, MockMethodMockFunctionSignatureTypes); -TYPED_TEST(MockMethodMockFunctionSignatureTest, IsMockFunctionTemplateArgumentDeduced) { +TYPED_TEST(MockMethodMockFunctionSignatureTest, IsMockFunctionTemplateArgumentDeducedForRawSignature) { using Argument = TypeParam; MockFunction foo; EXPECT_TRUE(IsMockFunctionTemplateArgumentDeducedTo(foo)); } +TYPED_TEST(MockMethodMockFunctionSignatureTest, IsMockFunctionTemplateArgumentDeducedForStdFunction) { + using Argument = std::function; + MockFunction foo; + EXPECT_TRUE(IsMockFunctionTemplateArgumentDeducedTo(foo)); +} + +TYPED_TEST(MockMethodMockFunctionSignatureTest, IsMockFunctionCallMethodSignatureTheSameForRawSignatureAndStdFunction) { + using ForRawSignature = decltype(&MockFunction::Call); + using ForStdFunction = decltype(&MockFunction>::Call); + EXPECT_TRUE((std::is_same::value)); +} + +TYPED_TEST(MockMethodMockFunctionSignatureTest, IsMockFunctionAsStdFunctionMethodSignatureTheSameForRawSignatureAndStdFunction) { + using ForRawSignature = decltype(&MockFunction::AsStdFunction); + using ForStdFunction = decltype(&MockFunction>::AsStdFunction); + EXPECT_TRUE((std::is_same::value)); +} + struct MockMethodSizes0 { MOCK_METHOD0(func, void());