diff --git a/googlemock/docs/cheat_sheet.md b/googlemock/docs/cheat_sheet.md index 239a4c6d5a..b55a5051ba 100644 --- a/googlemock/docs/cheat_sheet.md +++ b/googlemock/docs/cheat_sheet.md @@ -760,6 +760,20 @@ class MockFunction { See this [recipe](cook_book.md#using-check-points) for one application of it. +## Callback Signature ## + +When using `MockFunction` as a mock for a callback the signature often becomes an issue. When the callback type is provided like this: +```cpp +using my_callback = std::function; +``` +there is no easy way to obtain the function signature (`bool(int)`) from the `my_callback` type. + +`MockFunction` avoids this problem by using indirection of `SignatureOf` meta-function. The `MockFunction` retrieves function signature type by providing its `F` argument to the `SignatureOf` meta-function. While the `SignatureOf` recognizes `std::function` and returns the `T` as the proper function signature. (Obviously, the `SignatureOf` is an identity function for the function signature itself!) + +Thanks to this approach, with the example above, `MockFunction` can be used directly instead of having to write `MockFunction`. + +Note that `SignatureOf` is an extension point. It can be specialized for user types (for example `boost::function`) to have them supported by `MockFunction` as well. + ### Flags diff --git a/googlemock/include/gmock/gmock-spec-builders.h b/googlemock/include/gmock/gmock-spec-builders.h index 0d1adda5c6..0173e191a1 100644 --- a/googlemock/include/gmock/gmock-spec-builders.h +++ b/googlemock/include/gmock/gmock-spec-builders.h @@ -1791,10 +1791,72 @@ 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: +// 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. If a user wishes to use +// SignatureOf with other types (like for example boost::function) +// a corresponding specialization must be provided. +template +struct SignatureOf; + +template +struct SignatureOf { + using type = R(Args...); +}; + +template +struct SignatureOf> : SignatureOf {}; + +template +using SignatureOfT = typename SignatureOf::type; + +namespace internal { + +template +class MockFunction; + +template +class 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)...); + } + + 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()...); + } + + private: + FunctionMocker mock_; +}; + +} // namespace internal + +// A MockFunction class has one mock method whose type is +// 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); @@ -1828,50 +1890,30 @@ 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 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 predicate = std::function; +// void MyFilterAlgorithm(predicate pred); +// +// TEST(FooTest, PredicateAlwaysAccepts) { +// MockFunction pred_mock; +// EXPECT_CALL(pred_mock, Call(_)).WillRepeatedly(Return(true)); +// MyFilterAlgorithm(pred_mock.AsStdFunction()); +// } template -class MockFunction; - -template -class 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 MockFunction = internal::MockFunction>; // The style guide prohibits "using" statements in a namespace scope // inside a header file. However, the MockSpec class template is diff --git a/googlemock/test/gmock-function-mocker_test.cc b/googlemock/test/gmock-function-mocker_test.cc index fbc5d5b2bb..a34aca35d1 100644 --- a/googlemock/test/gmock-function-mocker_test.cc +++ b/googlemock/test/gmock-function-mocker_test.cc @@ -40,8 +40,10 @@ # include #endif // GTEST_OS_WINDOWS +#include #include #include +#include #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -558,6 +560,43 @@ TEST(MockMethodOverloadedMockMethodTest, CanOverloadOnConstnessInMacroBody) { EXPECT_EQ(3, const_mock->Overloaded(1)); } +template +class MockMethodMockFunctionSignatureTest : public ::testing::Test { +}; + +using SignatureTypes = ::testing::Types< + void(), + int(), + void(int), + int(bool, int), + int(bool, char, int, int, int, int, int, char, int, bool) +>; +TYPED_TEST_SUITE(MockMethodMockFunctionSignatureTest, SignatureTypes); + +TYPED_TEST(MockMethodMockFunctionSignatureTest, SignatureOfTIsIdentityForSignature) { + using signature = TypeParam; + using expected = signature; + using actual = SignatureOfT; + constexpr auto is_same = std::is_same::value; + EXPECT_TRUE(is_same); +} + +TYPED_TEST(MockMethodMockFunctionSignatureTest, SignatureOfTReturnsSignatureForStdFunction) { + using signature = TypeParam; + using expected = signature; + using actual = SignatureOfT>; + constexpr auto is_same = std::is_same::value; + EXPECT_TRUE(is_same); +} + +TYPED_TEST(MockMethodMockFunctionSignatureTest, MockFunctionOfSignatureIsTheSameTypeAsMockFunctionOfStdFunction) { + using signature = TypeParam; + using expected = MockFunction; + using actual = MockFunction>; + constexpr auto is_same = std::is_same::value; + EXPECT_TRUE(is_same); +} + TEST(MockMethodMockFunctionTest, WorksForVoidNullary) { MockFunction foo; EXPECT_CALL(foo, Call()); diff --git a/googlemock/test/gmock-generated-function-mockers_test.cc b/googlemock/test/gmock-generated-function-mockers_test.cc index dff3a9f0fa..4ab653f646 100644 --- a/googlemock/test/gmock-generated-function-mockers_test.cc +++ b/googlemock/test/gmock-generated-function-mockers_test.cc @@ -41,8 +41,10 @@ # include #endif // GTEST_OS_WINDOWS +#include #include #include +#include #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -557,6 +559,43 @@ TEST(OverloadedMockMethodTest, CanOverloadOnConstnessInMacroBody) { EXPECT_EQ(3, const_mock->Overloaded(1)); } +template +class MockFunctionSignatureTest : public ::testing::Test { +}; + +using SignatureTypes = ::testing::Types< + void(), + int(), + void(int), + int(bool, int), + int(bool, char, int, int, int, int, int, char, int, bool) +>; +TYPED_TEST_SUITE(MockFunctionSignatureTest, SignatureTypes); + +TYPED_TEST(MockFunctionSignatureTest, SignatureOfTIsIdentityForSignature) { + using signature = TypeParam; + using expected = signature; + using actual = SignatureOfT; + constexpr auto is_same = std::is_same::value; + EXPECT_TRUE(is_same); +} + +TYPED_TEST(MockFunctionSignatureTest, SignatureOfTReturnsSignatureForStdFunction) { + using signature = TypeParam; + using expected = signature; + using actual = SignatureOfT>; + constexpr auto is_same = std::is_same::value; + EXPECT_TRUE(is_same); +} + +TYPED_TEST(MockFunctionSignatureTest, MockFunctionOfSignatureIsTheSameTypeAsMockFunctionOfStdFunction) { + using signature = TypeParam; + using expected = MockFunction; + using actual = MockFunction>; + constexpr auto is_same = std::is_same::value; + EXPECT_TRUE(is_same); +} + TEST(MockFunctionTest, WorksForVoidNullary) { MockFunction foo; EXPECT_CALL(foo, Call());