From c144d78f8295da3dbae3ad2d5fe66a9a42f8ce74 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 26 Apr 2022 14:39:03 -0700 Subject: [PATCH] Support move-only and &&-qualified actions in WithArgs. PiperOrigin-RevId: 444671005 Change-Id: I7df5f038caf17afb60d4fb35434ff0b656d4c954 --- googlemock/include/gmock/gmock-actions.h | 57 +++++++++++++++++++++--- googlemock/test/gmock-actions_test.cc | 25 ++++++++++- 2 files changed, 73 insertions(+), 9 deletions(-) diff --git a/googlemock/include/gmock/gmock-actions.h b/googlemock/include/gmock/gmock-actions.h index ecc72d5348..5836f17938 100644 --- a/googlemock/include/gmock/gmock-actions.h +++ b/googlemock/include/gmock/gmock-actions.h @@ -1279,17 +1279,60 @@ class IgnoreResultAction { template struct WithArgsAction { - InnerAction action; + InnerAction inner_action; - // The inner action could be anything convertible to Action. - // We use the conversion operator to detect the signature of the inner Action. + // The signature of the function as seen by the inner action, given an out + // action with the given result and argument types. template + using InnerSignature = + R(typename std::tuple_element>::type...); + + // Rather than a call operator, we must define conversion operators to + // particular action types. This is necessary for embedded actions like + // DoDefault(), which rely on an action conversion operators rather than + // providing a call operator because even with a particular set of arguments + // they don't have a fixed return type. + + template >::type...)>>::value, + int>::type = 0> + operator OnceAction() && { // NOLINT + struct OA { + OnceAction> inner_action; + + R operator()(Args&&... args) && { + return std::move(inner_action) + .Call(std::get( + std::forward_as_tuple(std::forward(args)...))...); + } + }; + + return OA{std::move(inner_action)}; + } + + template >::type...)>>::value, + int>::type = 0> operator Action() const { // NOLINT - using TupleType = std::tuple; - Action::type...)> converted( - action); + Action> converted(inner_action); - return [converted](Args... args) -> R { + return [converted](Args&&... args) -> R { return converted.Perform(std::forward_as_tuple( std::get(std::forward_as_tuple(std::forward(args)...))...)); }; diff --git a/googlemock/test/gmock-actions_test.cc b/googlemock/test/gmock-actions_test.cc index e41845e70f..a33358a76b 100644 --- a/googlemock/test/gmock-actions_test.cc +++ b/googlemock/test/gmock-actions_test.cc @@ -1444,8 +1444,29 @@ TEST(WithArgsTest, ReturnReference) { TEST(WithArgsTest, InnerActionWithConversion) { Action inner = [] { return nullptr; }; - Action a = testing::WithoutArgs(inner); - EXPECT_EQ(nullptr, a.Perform(std::make_tuple(1.1))); + + MockFunction mock; + EXPECT_CALL(mock, Call) + .WillOnce(WithoutArgs(inner)) + .WillRepeatedly(WithoutArgs(inner)); + + EXPECT_EQ(nullptr, mock.AsStdFunction()(1.1)); + EXPECT_EQ(nullptr, mock.AsStdFunction()(1.1)); +} + +// It should be possible to use an &&-qualified inner action as long as the +// whole shebang is used as an rvalue with WillOnce. +TEST(WithArgsTest, RefQualifiedInnerAction) { + struct SomeAction { + int operator()(const int arg) && { + EXPECT_EQ(17, arg); + return 19; + } + }; + + MockFunction mock; + EXPECT_CALL(mock, Call).WillOnce(WithArg<1>(SomeAction{})); + EXPECT_EQ(19, mock.AsStdFunction()(0, 17)); } #if !GTEST_OS_WINDOWS_MOBILE