From 0498660ea575bfeb4b3b0879fa6aa6904d1df373 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 26 Apr 2022 08:05:01 -0700 Subject: Support move-only and &&-qualified actions in DoAll. This is necessary for generic support of these actions, since `DoAll` is a frequently-used action wrapper. PiperOrigin-RevId: 444561964 Change-Id: I02edb55e35ab4207fbd71e371255a319c8253136 --- googlemock/include/gmock/gmock-actions.h | 591 +++++++++++++++---------- googlemock/include/gmock/gmock-spec-builders.h | 41 +- googlemock/test/gmock-actions_test.cc | 106 ++++- 3 files changed, 480 insertions(+), 258 deletions(-) diff --git a/googlemock/include/gmock/gmock-actions.h b/googlemock/include/gmock/gmock-actions.h index a2b25d9..ecc72d5 100644 --- a/googlemock/include/gmock/gmock-actions.h +++ b/googlemock/include/gmock/gmock-actions.h @@ -322,6 +322,191 @@ struct is_callable_r_impl>, R, F, Args...> template using is_callable_r = is_callable_r_impl; +template +class TypedExpectation; + +// Specialized for function types below. +template +class OnceAction; + +// An action that can only be used once. +// +// This is what is accepted by WillOnce, which doesn't require the underlying +// action to be copy-constructible (only move-constructible), and promises to +// invoke it as an rvalue reference. This allows the action to work with +// move-only types like std::move_only_function in a type-safe manner. +// +// For example: +// +// // Assume we have some API that needs to accept a unique pointer to some +// // non-copyable object Foo. +// void AcceptUniquePointer(std::unique_ptr foo); +// +// // We can define an action that provides a Foo to that API. Because It +// // has to give away its unique pointer, it must not be called more than +// // once, so its call operator is &&-qualified. +// struct ProvideFoo { +// std::unique_ptr foo; +// +// void operator()() && { +// AcceptUniquePointer(std::move(Foo)); +// } +// }; +// +// // This action can be used with WillOnce. +// EXPECT_CALL(mock, Call) +// .WillOnce(ProvideFoo{std::make_unique(...)}); +// +// // But a call to WillRepeatedly will fail to compile. This is correct, +// // since the action cannot correctly be used repeatedly. +// EXPECT_CALL(mock, Call) +// .WillRepeatedly(ProvideFoo{std::make_unique(...)}); +// +// A less-contrived example would be an action that returns an arbitrary type, +// whose &&-qualified call operator is capable of dealing with move-only types. +template +class OnceAction final { + private: + // True iff we can use the given callable type (or lvalue reference) directly + // via StdFunctionAdaptor. + template + using IsDirectlyCompatible = internal::conjunction< + // It must be possible to capture the callable in StdFunctionAdaptor. + std::is_constructible::type, Callable>, + // The callable must be compatible with our signature. + internal::is_callable_r::type, + Args...>>; + + // True iff we can use the given callable type via StdFunctionAdaptor once we + // ignore incoming arguments. + template + using IsCompatibleAfterIgnoringArguments = internal::conjunction< + // It must be possible to capture the callable in a lambda. + std::is_constructible::type, Callable>, + // The callable must be invocable with zero arguments, returning something + // convertible to Result. + internal::is_callable_r::type>>; + + public: + // Construct from a callable that is directly compatible with our mocked + // signature: it accepts our function type's arguments and returns something + // convertible to our result type. + template ::type>>, + IsDirectlyCompatible> // + ::value, + int>::type = 0> + OnceAction(Callable&& callable) // NOLINT + : function_(StdFunctionAdaptor::type>( + {}, std::forward(callable))) {} + + // As above, but for a callable that ignores the mocked function's arguments. + template ::type>>, + // Exclude callables for which the overload above works. + // We'd rather provide the arguments if possible. + internal::negation>, + IsCompatibleAfterIgnoringArguments>::value, + int>::type = 0> + OnceAction(Callable&& callable) // NOLINT + // Call the constructor above with a callable + // that ignores the input arguments. + : OnceAction(IgnoreIncomingArguments::type>{ + std::forward(callable)}) {} + + // We are naturally copyable because we store only an std::function, but + // semantically we should not be copyable. + OnceAction(const OnceAction&) = delete; + OnceAction& operator=(const OnceAction&) = delete; + OnceAction(OnceAction&&) = default; + + // Invoke the underlying action callable with which we were constructed, + // handing it the supplied arguments. + Result Call(Args... args) && { + return function_(std::forward(args)...); + } + + private: + // Allow TypedExpectation::WillOnce to use our type-unsafe API below. + friend class TypedExpectation; + + // An adaptor that wraps a callable that is compatible with our signature and + // being invoked as an rvalue reference so that it can be used as an + // StdFunctionAdaptor. This throws away type safety, but that's fine because + // this is only used by WillOnce, which we know calls at most once. + // + // Once we have something like std::move_only_function from C++23, we can do + // away with this. + template + class StdFunctionAdaptor final { + public: + // A tag indicating that the (otherwise universal) constructor is accepting + // the callable itself, instead of e.g. stealing calls for the move + // constructor. + struct CallableTag final {}; + + template + explicit StdFunctionAdaptor(CallableTag, F&& callable) + : callable_(std::make_shared(std::forward(callable))) {} + + // Rather than explicitly returning Result, we return whatever the wrapped + // callable returns. This allows for compatibility with existing uses like + // the following, when the mocked function returns void: + // + // EXPECT_CALL(mock_fn_, Call) + // .WillOnce([&] { + // [...] + // return 0; + // }); + // + // Such a callable can be turned into std::function. If we use an + // explicit return type of Result here then it *doesn't* work with + // std::function, because we'll get a "void function should not return a + // value" error. + // + // We need not worry about incompatible result types because the SFINAE on + // OnceAction already checks this for us. std::is_invocable_r_v itself makes + // the same allowance for void result types. + template + internal::call_result_t operator()( + ArgRefs&&... args) const { + return std::move(*callable_)(std::forward(args)...); + } + + private: + // We must put the callable on the heap so that we are copyable, which + // std::function needs. + std::shared_ptr callable_; + }; + + // An adaptor that makes a callable that accepts zero arguments callable with + // our mocked arguments. + template + struct IgnoreIncomingArguments { + internal::call_result_t operator()(Args&&...) { + return std::move(callable)(); + } + + Callable callable; + }; + + std::function function_; +}; + } // namespace internal // When an unexpected function call is encountered, Google Mock will @@ -484,25 +669,30 @@ class ActionInterface { ActionInterface& operator=(const ActionInterface&) = delete; }; -// An Action is a copyable and IMMUTABLE (except by assignment) -// object that represents an action to be taken when a mock function -// of type F is called. The implementation of Action is just a -// std::shared_ptr to const ActionInterface. Don't inherit from Action! -// You can view an object implementing ActionInterface as a -// concrete action (including its current state), and an Action -// object as a handle to it. template -class Action { +class Action; + +// An Action is a copyable and IMMUTABLE (except by assignment) +// object that represents an action to be taken when a mock function of type +// R(Args...) is called. The implementation of Action is just a +// std::shared_ptr to const ActionInterface. Don't inherit from Action! You +// can view an object implementing ActionInterface as a concrete action +// (including its current state), and an Action object as a handle to it. +template +class Action { + private: + using F = R(Args...); + // Adapter class to allow constructing Action from a legacy ActionInterface. // New code should create Actions from functors instead. struct ActionAdapter { // Adapter must be copyable to satisfy std::function requirements. ::std::shared_ptr> impl_; - template - typename internal::Function::Result operator()(Args&&... args) { + template + typename internal::Function::Result operator()(InArgs&&... args) { return impl_->Perform( - ::std::forward_as_tuple(::std::forward(args)...)); + ::std::forward_as_tuple(::std::forward(args)...)); } }; @@ -537,7 +727,8 @@ class Action { // Action, as long as F's arguments can be implicitly converted // to Func's and Func's return type can be implicitly converted to F's. template - explicit Action(const Action& action) : fun_(action.fun_) {} + Action(const Action& action) // NOLINT + : fun_(action.fun_) {} // Returns true if and only if this is the DoDefault() action. bool IsDoDefault() const { return fun_ == nullptr; } @@ -555,6 +746,24 @@ class Action { return internal::Apply(fun_, ::std::move(args)); } + // An action can be used as a OnceAction, since it's obviously safe to call it + // once. + operator internal::OnceAction() const { // NOLINT + // Return a OnceAction-compatible callable that calls Perform with the + // arguments it is provided. We could instead just return fun_, but then + // we'd need to handle the IsDoDefault() case separately. + struct OA { + Action action; + + R operator()(Args... args) && { + return action.Perform( + std::forward_as_tuple(std::forward(args)...)); + } + }; + + return OA{*this}; + } + private: template friend class Action; @@ -571,8 +780,8 @@ class Action { template struct IgnoreArgs { - template - Result operator()(const Args&...) const { + template + Result operator()(const InArgs&...) const { return function_impl(); } @@ -655,213 +864,6 @@ inline PolymorphicAction MakePolymorphicAction(const Impl& impl) { namespace internal { -template -class TypedExpectation; - -// Specialized for function types below. -template -class OnceAction; - -// An action that can only be used once. -// -// This is what is accepted by WillOnce, which doesn't require the underlying -// action to be copy-constructible (only move-constructible), and promises to -// invoke it as an rvalue reference. This allows the action to work with -// move-only types like std::move_only_function in a type-safe manner. -// -// For example: -// -// // Assume we have some API that needs to accept a unique pointer to some -// // non-copyable object Foo. -// void AcceptUniquePointer(std::unique_ptr foo); -// -// // We can define an action that provides a Foo to that API. Because It -// // has to give away its unique pointer, it must not be called more than -// // once, so its call operator is &&-qualified. -// struct ProvideFoo { -// std::unique_ptr foo; -// -// void operator()() && { -// AcceptUniquePointer(std::move(Foo)); -// } -// }; -// -// // This action can be used with WillOnce. -// EXPECT_CALL(mock, Call) -// .WillOnce(ProvideFoo{std::make_unique(...)}); -// -// // But a call to WillRepeatedly will fail to compile. This is correct, -// // since the action cannot correctly be used repeatedly. -// EXPECT_CALL(mock, Call) -// .WillRepeatedly(ProvideFoo{std::make_unique(...)}); -// -// A less-contrived example would be an action that returns an arbitrary type, -// whose &&-qualified call operator is capable of dealing with move-only types. -template -class OnceAction final { - private: - // True iff we can use the given callable type (or lvalue reference) directly - // via ActionAdaptor. - template - using IsDirectlyCompatible = internal::conjunction< - // It must be possible to capture the callable in ActionAdaptor. - std::is_constructible::type, Callable>, - // The callable must be compatible with our signature. - internal::is_callable_r::type, - Args...>>; - - // True iff we can use the given callable type via ActionAdaptor once we - // ignore incoming arguments. - template - using IsCompatibleAfterIgnoringArguments = internal::conjunction< - // It must be possible to capture the callable in a lambda. - std::is_constructible::type, Callable>, - // The callable must be invocable with zero arguments, returning something - // convertible to Result. - internal::is_callable_r::type>>; - - public: - // Construct from a callable that is directly compatible with our mocked - // signature: it accepts our function type's arguments and returns something - // convertible to our result type. - template ::type>>, - IsDirectlyCompatible> // - ::value, - int>::type = 0> - OnceAction(Callable&& callable) // NOLINT - : action_(ActionAdaptor::type>( - {}, std::forward(callable))) {} - - // As above, but for a callable that ignores the mocked function's arguments. - template ::type>>, - // Exclude callables for which the overload above works. - // We'd rather provide the arguments if possible. - internal::negation>, - IsCompatibleAfterIgnoringArguments>::value, - int>::type = 0> - OnceAction(Callable&& callable) // NOLINT - // Call the constructor above with a callable - // that ignores the input arguments. - : OnceAction(IgnoreIncomingArguments::type>{ - std::forward(callable)}) {} - - // A fallback constructor for anything that is convertible to Action, for use - // with legacy actions that uses older styles like implementing - // ActionInterface or a conversion operator to Action. Modern code should - // implement a call operator with appropriate restrictions. - template ::type>>, - // Exclude the overloads above, which we want to take - // precedence. - internal::negation>, - internal::negation>, - // It must be possible to turn the object into an action of - // the appropriate type. - std::is_convertible> // - >::value, - int>::type = 0> - OnceAction(T&& action) : action_(std::forward(action)) {} // NOLINT - - // We are naturally copyable because we store only an Action, but semantically - // we should not be copyable. - OnceAction(const OnceAction&) = delete; - OnceAction& operator=(const OnceAction&) = delete; - OnceAction(OnceAction&&) = default; - - private: - // Allow TypedExpectation::WillOnce to use our type-unsafe API below. - friend class TypedExpectation; - - // An adaptor that wraps a callable that is compatible with our signature and - // being invoked as an rvalue reference so that it can be used as an - // Action. This throws away type safety, but that's fine because this is only - // used by WillOnce, which we know calls at most once. - template - class ActionAdaptor final { - public: - // A tag indicating that the (otherwise universal) constructor is accepting - // the callable itself, instead of e.g. stealing calls for the move - // constructor. - struct CallableTag final {}; - - template - explicit ActionAdaptor(CallableTag, F&& callable) - : callable_(std::make_shared(std::forward(callable))) {} - - // Rather than explicitly returning Result, we return whatever the wrapped - // callable returns. This allows for compatibility with existing uses like - // the following, when the mocked function returns void: - // - // EXPECT_CALL(mock_fn_, Call) - // .WillOnce([&] { - // [...] - // return 0; - // }); - // - // This works with Action since such a callable can be turned into - // std::function. If we use an explicit return type of Result here - // then it *doesn't* work with OnceAction, because we'll get a "void - // function should not return a value" error. - // - // We need not worry about incompatible result types because the SFINAE on - // OnceAction already checks this for us. std::is_invocable_r_v itself makes - // the same allowance for void result types. - template - internal::call_result_t operator()( - ArgRefs&&... args) const { - return std::move(*callable_)(std::forward(args)...); - } - - private: - // We must put the callable on the heap so that we are copyable, which - // Action needs. - std::shared_ptr callable_; - }; - - // An adaptor that makes a callable that accepts zero arguments callable with - // our mocked arguments. - template - struct IgnoreIncomingArguments { - internal::call_result_t operator()(Args&&...) { - return std::move(callable)(); - } - - Callable callable; - }; - - // Return an Action that calls the underlying callable in a type-safe manner. - // The action's Perform method must be called at most once. - // - // This is the transition from a type-safe API to a type-unsafe one, since - // "must be called at most once" is no longer reflecting in the type system. - Action ReleaseAction() && { return std::move(action_); } - - Action action_; -}; - // Helper struct to specialize ReturnAction to execute a move instead of a copy // on return. Useful for move-only types, but could be used on any type. template @@ -1295,8 +1297,53 @@ struct WithArgsAction { }; template -struct DoAllAction { +class DoAllAction; + +// Base case: only a single action. +template +class DoAllAction { + public: + struct UserConstructorTag {}; + + template + explicit DoAllAction(UserConstructorTag, T&& action) + : final_action_(std::forward(action)) {} + + // 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 >::value, + int>::type = 0> + operator OnceAction() && { // NOLINT + return std::move(final_action_); + } + + template < + typename R, typename... Args, + typename std::enable_if< + std::is_convertible>::value, + int>::type = 0> + operator Action() const { // NOLINT + return final_action_; + } + private: + FinalAction final_action_; +}; + +// Recursive case: support N actions by calling the initial action and then +// calling through to the base class containing N-1 actions. +template +class DoAllAction + : private DoAllAction { + private: + using Base = DoAllAction; + // The type of reference that should be provided to an initial action for a // mocked function parameter of type T. // @@ -1315,14 +1362,14 @@ struct DoAllAction { // // * More surprisingly, `const T&` is often not a const reference type. // By the reference collapsing rules in C++17 [dcl.ref]/6, if T refers to - // U& or U&& for some non-scalar type U, then NonFinalType is U&. In - // other words, we may hand over a non-const reference. + // U& or U&& for some non-scalar type U, then InitialActionArgType is + // U&. In other words, we may hand over a non-const reference. // // So for example, given some non-scalar type Obj we have the following // mappings: // - // T NonFinalType - // ------- --------------- + // T InitialActionArgType + // ------- ----------------------- // Obj const Obj& // Obj& Obj& // Obj&& Obj& @@ -1343,34 +1390,85 @@ struct DoAllAction { // .WillOnce(DoAll(SetArgReferee<0>(17), Return(19))); // template - using NonFinalType = + using InitialActionArgType = typename std::conditional::value, T, const T&>::type; - template - std::vector Convert(IndexSequence) const { - return {ActionT(std::get(actions))...}; - } - public: - std::tuple actions; + struct UserConstructorTag {}; - template + template + explicit DoAllAction(UserConstructorTag, T&& initial_action, + U&&... other_actions) + : Base({}, std::forward(other_actions)...), + initial_action_(std::forward(initial_action)) {} + + template ...)>>, + std::is_convertible>>::value, + int>::type = 0> + operator OnceAction() && { // NOLINT + // Return an action that first calls the initial action with arguments + // filtered through InitialActionArgType, then forwards arguments directly + // to the base class to deal with the remaining actions. + struct OA { + OnceAction...)> initial_action; + OnceAction remaining_actions; + + R operator()(Args... args) && { + std::move(initial_action) + .Call(static_cast>(args)...); + + return std::move(remaining_actions).Call(std::forward(args)...); + } + }; + + return OA{ + std::move(initial_action_), + std::move(static_cast(*this)), + }; + } + + template < + typename R, typename... Args, + typename std::enable_if< + conjunction< + // Both the initial action and the rest must support conversion to + // Action. + std::is_convertible...)>>, + std::is_convertible>>::value, + int>::type = 0> operator Action() const { // NOLINT - struct Op { - std::vector...)>> converted; - Action last; + // Return an action that first calls the initial action with arguments + // filtered through InitialActionArgType, then forwards arguments directly + // to the base class to deal with the remaining actions. + struct OA { + Action...)> initial_action; + Action remaining_actions; + R operator()(Args... args) const { - auto tuple_args = std::forward_as_tuple(std::forward(args)...); - for (auto& a : converted) { - a.Perform(tuple_args); - } - return last.Perform(std::move(tuple_args)); + initial_action.Perform(std::forward_as_tuple( + static_cast>(args)...)); + + return remaining_actions.Perform( + std::forward_as_tuple(std::forward(args)...)); } }; - return Op{Convert...)>>( - MakeIndexSequence()), - std::get(actions)}; + + return OA{ + initial_action_, + static_cast(*this), + }; } + + private: + InitialAction initial_action_; }; template @@ -1513,7 +1611,8 @@ typedef internal::IgnoredValue Unused; template internal::DoAllAction::type...> DoAll( Action&&... action) { - return {std::forward_as_tuple(std::forward(action)...)}; + return internal::DoAllAction::type...>( + {}, std::forward(action)...); } // WithArg(an_action) creates an action that passes the k-th diff --git a/googlemock/include/gmock/gmock-spec-builders.h b/googlemock/include/gmock/gmock-spec-builders.h index a3e32a4..835e518 100644 --- a/googlemock/include/gmock/gmock-spec-builders.h +++ b/googlemock/include/gmock/gmock-spec-builders.h @@ -878,9 +878,15 @@ class GTEST_API_ ExpectationBase { mutable Mutex mutex_; // Protects action_count_checked_. }; // class ExpectationBase -// Implements an expectation for the given function type. template -class TypedExpectation : public ExpectationBase { +class TypedExpectation; + +// Implements an expectation for the given function type. +template +class TypedExpectation : public ExpectationBase { + private: + using F = R(Args...); + public: typedef typename Function::ArgumentTuple ArgumentTuple; typedef typename Function::ArgumentMatcherTuple ArgumentMatcherTuple; @@ -993,15 +999,30 @@ class TypedExpectation : public ExpectationBase { return After(s1, s2, s3, s4).After(s5); } - // Implements the .WillOnce() clause for copyable actions. + // Preferred, type-safe overload: consume anything that can be directly + // converted to a OnceAction, except for Action objects themselves. TypedExpectation& WillOnce(OnceAction once_action) { + // Call the overload below, smuggling the OnceAction as a copyable callable. + // We know this is safe because a WillOnce action will not be called more + // than once. + return WillOnce(Action(ActionAdaptor{ + std::make_shared>(std::move(once_action)), + })); + } + + // Fallback overload: accept Action objects and those actions that define + // `operator Action` but not `operator OnceAction`. + // + // This is templated in order to cause the overload above to be preferred + // when the input is convertible to either type. + template + TypedExpectation& WillOnce(Action action) { ExpectSpecProperty(last_clause_ <= kWillOnce, ".WillOnce() cannot appear after " ".WillRepeatedly() or .RetiresOnSaturation()."); last_clause_ = kWillOnce; - untyped_actions_.push_back( - new Action(std::move(once_action).ReleaseAction())); + untyped_actions_.push_back(new Action(std::move(action))); if (!cardinality_specified()) { set_cardinality(Exactly(static_cast(untyped_actions_.size()))); @@ -1074,6 +1095,16 @@ class TypedExpectation : public ExpectationBase { template friend class FunctionMocker; + // An adaptor that turns a OneAction into something compatible with + // Action. Must be called at most once. + struct ActionAdaptor { + std::shared_ptr> once_action; + + R operator()(Args&&... args) const { + return std::move(*once_action).Call(std::forward(args)...); + } + }; + // Returns an Expectation object that references and co-owns this // expectation. Expectation GetHandle() override { return owner_->GetHandleOf(this); } diff --git a/googlemock/test/gmock-actions_test.cc b/googlemock/test/gmock-actions_test.cc index cce3f4b..e41845e 100644 --- a/googlemock/test/gmock-actions_test.cc +++ b/googlemock/test/gmock-actions_test.cc @@ -31,10 +31,12 @@ // // This file tests the built-in actions. -// Silence C4100 (unreferenced formal parameter) for MSVC +// Silence C4100 (unreferenced formal parameter) and C4503 (decorated name +// length exceeded) for MSVC. #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4100) +#pragma warning(disable : 4503) #if _MSC_VER == 1900 // and silence C4800 (C4800: 'int *const ': forcing value // to bool 'true' or 'false') for MSVC 15 @@ -1193,6 +1195,21 @@ TEST(AssignTest, CompatibleTypes) { EXPECT_DOUBLE_EQ(5, x); } +// DoAll should support &&-qualified actions when used with WillOnce. +TEST(DoAll, SupportsRefQualifiedActions) { + struct InitialAction { + void operator()(const int arg) && { EXPECT_EQ(17, arg); } + }; + + struct FinalAction { + int operator()() && { return 19; } + }; + + MockFunction mock; + EXPECT_CALL(mock, Call).WillOnce(DoAll(InitialAction{}, FinalAction{})); + EXPECT_EQ(19, mock.AsStdFunction()(17)); +} + // DoAll should never provide rvalue references to the initial actions. If the // mock action itself accepts an rvalue reference or a non-scalar object by // value then the final action should receive an rvalue reference, but initial @@ -1274,6 +1291,62 @@ TEST(DoAll, ProvidesLvalueReferencesToInitialActions) { mock.AsStdFunction()(Obj{}); mock.AsStdFunction()(Obj{}); } + + // &&-qualified initial actions should also be allowed with WillOnce. + { + struct InitialAction { + void operator()(Obj&) && {} + }; + + MockFunction mock; + EXPECT_CALL(mock, Call) + .WillOnce(DoAll(InitialAction{}, InitialAction{}, [](Obj&) {})); + + Obj obj; + mock.AsStdFunction()(obj); + } + + { + struct InitialAction { + void operator()(Obj&) && {} + }; + + MockFunction mock; + EXPECT_CALL(mock, Call) + .WillOnce(DoAll(InitialAction{}, InitialAction{}, [](Obj&&) {})); + + mock.AsStdFunction()(Obj{}); + } +} + +// DoAll should support being used with type-erased Action objects, both through +// WillOnce and WillRepeatedly. +TEST(DoAll, SupportsTypeErasedActions) { + // With only type-erased actions. + const Action initial_action = [] {}; + const Action final_action = [] { return 17; }; + + MockFunction mock; + EXPECT_CALL(mock, Call) + .WillOnce(DoAll(initial_action, initial_action, final_action)) + .WillRepeatedly(DoAll(initial_action, initial_action, final_action)); + + EXPECT_EQ(17, mock.AsStdFunction()()); + + // With &&-qualified and move-only final action. + { + struct FinalAction { + FinalAction() = default; + FinalAction(FinalAction&&) = default; + + int operator()() && { return 17; } + }; + + EXPECT_CALL(mock, Call) + .WillOnce(DoAll(initial_action, initial_action, FinalAction{})); + + EXPECT_EQ(17, mock.AsStdFunction()()); + } } // Tests using WithArgs and with an action that takes 1 argument. @@ -1793,6 +1866,31 @@ TEST(MockMethodTest, ActionSwallowsAllArguments) { EXPECT_EQ(17, mock.AsStdFunction()(0)); } +struct ActionWithTemplatedConversionOperators { + template + operator internal::OnceAction() && { // NOLINT + return [] { return 17; }; + } + + template + operator Action() const { // NOLINT + return [] { return 19; }; + } +}; + +// It should be fine to hand both WillOnce and WillRepeatedly a function that +// defines templated conversion operators to OnceAction and Action. WillOnce +// should prefer the OnceAction version. +TEST(MockMethodTest, ActionHasTemplatedConversionOperators) { + MockFunction mock; + EXPECT_CALL(mock, Call) + .WillOnce(ActionWithTemplatedConversionOperators{}) + .WillRepeatedly(ActionWithTemplatedConversionOperators{}); + + EXPECT_EQ(17, mock.AsStdFunction()()); + EXPECT_EQ(19, mock.AsStdFunction()()); +} + // Tests for std::function based action. int Add(int val, int& ref, int* ptr) { // NOLINT @@ -1919,9 +2017,3 @@ TEST(ActionMacro, LargeArity) { } // namespace } // namespace testing - -#ifdef _MSC_VER -#if _MSC_VER == 1900 -#pragma warning(pop) -#endif -#endif -- cgit v0.12