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
This commit is contained in:
Aaron Jacobs 2022-04-26 08:05:01 -07:00 committed by Copybara-Service
parent b53547bf01
commit 0498660ea5
3 changed files with 482 additions and 260 deletions

View File

@ -322,6 +322,191 @@ struct is_callable_r_impl<void_t<call_result_t<F, Args...>>, R, F, Args...>
template <typename R, typename F, typename... Args> template <typename R, typename F, typename... Args>
using is_callable_r = is_callable_r_impl<void, R, F, Args...>; using is_callable_r = is_callable_r_impl<void, R, F, Args...>;
template <typename F>
class TypedExpectation;
// Specialized for function types below.
template <typename F>
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> 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> foo;
//
// void operator()() && {
// AcceptUniquePointer(std::move(Foo));
// }
// };
//
// // This action can be used with WillOnce.
// EXPECT_CALL(mock, Call)
// .WillOnce(ProvideFoo{std::make_unique<Foo>(...)});
//
// // 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<Foo>(...)});
//
// 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 <typename Result, typename... Args>
class OnceAction<Result(Args...)> final {
private:
// True iff we can use the given callable type (or lvalue reference) directly
// via StdFunctionAdaptor.
template <typename Callable>
using IsDirectlyCompatible = internal::conjunction<
// It must be possible to capture the callable in StdFunctionAdaptor.
std::is_constructible<typename std::decay<Callable>::type, Callable>,
// The callable must be compatible with our signature.
internal::is_callable_r<Result, typename std::decay<Callable>::type,
Args...>>;
// True iff we can use the given callable type via StdFunctionAdaptor once we
// ignore incoming arguments.
template <typename Callable>
using IsCompatibleAfterIgnoringArguments = internal::conjunction<
// It must be possible to capture the callable in a lambda.
std::is_constructible<typename std::decay<Callable>::type, Callable>,
// The callable must be invocable with zero arguments, returning something
// convertible to Result.
internal::is_callable_r<Result, typename std::decay<Callable>::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 <typename Callable,
typename std::enable_if<
internal::conjunction<
// Teach clang on macOS that we're not talking about a
// copy/move constructor here. Otherwise it gets confused
// when checking the is_constructible requirement of our
// traits above.
internal::negation<std::is_same<
OnceAction, typename std::decay<Callable>::type>>,
IsDirectlyCompatible<Callable>> //
::value,
int>::type = 0>
OnceAction(Callable&& callable) // NOLINT
: function_(StdFunctionAdaptor<typename std::decay<Callable>::type>(
{}, std::forward<Callable>(callable))) {}
// As above, but for a callable that ignores the mocked function's arguments.
template <typename Callable,
typename std::enable_if<
internal::conjunction<
// Teach clang on macOS that we're not talking about a
// copy/move constructor here. Otherwise it gets confused
// when checking the is_constructible requirement of our
// traits above.
internal::negation<std::is_same<
OnceAction, typename std::decay<Callable>::type>>,
// Exclude callables for which the overload above works.
// We'd rather provide the arguments if possible.
internal::negation<IsDirectlyCompatible<Callable>>,
IsCompatibleAfterIgnoringArguments<Callable>>::value,
int>::type = 0>
OnceAction(Callable&& callable) // NOLINT
// Call the constructor above with a callable
// that ignores the input arguments.
: OnceAction(IgnoreIncomingArguments<typename std::decay<Callable>::type>{
std::forward<Callable>(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>(args)...);
}
private:
// Allow TypedExpectation::WillOnce to use our type-unsafe API below.
friend class TypedExpectation<Result(Args...)>;
// 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 <typename Callable>
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 <typename F>
explicit StdFunctionAdaptor(CallableTag, F&& callable)
: callable_(std::make_shared<Callable>(std::forward<F>(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<void()>. 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 <typename... ArgRefs>
internal::call_result_t<Callable, ArgRefs...> operator()(
ArgRefs&&... args) const {
return std::move(*callable_)(std::forward<ArgRefs>(args)...);
}
private:
// We must put the callable on the heap so that we are copyable, which
// std::function needs.
std::shared_ptr<Callable> callable_;
};
// An adaptor that makes a callable that accepts zero arguments callable with
// our mocked arguments.
template <typename Callable>
struct IgnoreIncomingArguments {
internal::call_result_t<Callable> operator()(Args&&...) {
return std::move(callable)();
}
Callable callable;
};
std::function<Result(Args...)> function_;
};
} // namespace internal } // namespace internal
// When an unexpected function call is encountered, Google Mock will // When an unexpected function call is encountered, Google Mock will
@ -484,25 +669,30 @@ class ActionInterface {
ActionInterface& operator=(const ActionInterface&) = delete; ActionInterface& operator=(const ActionInterface&) = delete;
}; };
// An Action<F> 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<T> is just a
// std::shared_ptr to const ActionInterface<T>. Don't inherit from Action!
// You can view an object implementing ActionInterface<F> as a
// concrete action (including its current state), and an Action<F>
// object as a handle to it.
template <typename F> template <typename F>
class Action { class Action;
// An Action<R(Args...)> 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<T> is just a
// std::shared_ptr to const ActionInterface<T>. Don't inherit from Action! You
// can view an object implementing ActionInterface<F> as a concrete action
// (including its current state), and an Action<F> object as a handle to it.
template <typename R, typename... Args>
class Action<R(Args...)> {
private:
using F = R(Args...);
// Adapter class to allow constructing Action from a legacy ActionInterface. // Adapter class to allow constructing Action from a legacy ActionInterface.
// New code should create Actions from functors instead. // New code should create Actions from functors instead.
struct ActionAdapter { struct ActionAdapter {
// Adapter must be copyable to satisfy std::function requirements. // Adapter must be copyable to satisfy std::function requirements.
::std::shared_ptr<ActionInterface<F>> impl_; ::std::shared_ptr<ActionInterface<F>> impl_;
template <typename... Args> template <typename... InArgs>
typename internal::Function<F>::Result operator()(Args&&... args) { typename internal::Function<F>::Result operator()(InArgs&&... args) {
return impl_->Perform( return impl_->Perform(
::std::forward_as_tuple(::std::forward<Args>(args)...)); ::std::forward_as_tuple(::std::forward<InArgs>(args)...));
} }
}; };
@ -537,7 +727,8 @@ class Action {
// Action<F>, as long as F's arguments can be implicitly converted // Action<F>, 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. // to Func's and Func's return type can be implicitly converted to F's.
template <typename Func> template <typename Func>
explicit Action(const Action<Func>& action) : fun_(action.fun_) {} Action(const Action<Func>& action) // NOLINT
: fun_(action.fun_) {}
// Returns true if and only if this is the DoDefault() action. // Returns true if and only if this is the DoDefault() action.
bool IsDoDefault() const { return fun_ == nullptr; } bool IsDoDefault() const { return fun_ == nullptr; }
@ -555,6 +746,24 @@ class Action {
return internal::Apply(fun_, ::std::move(args)); 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<F>() 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<F> action;
R operator()(Args... args) && {
return action.Perform(
std::forward_as_tuple(std::forward<Args>(args)...));
}
};
return OA{*this};
}
private: private:
template <typename G> template <typename G>
friend class Action; friend class Action;
@ -571,8 +780,8 @@ class Action {
template <typename FunctionImpl> template <typename FunctionImpl>
struct IgnoreArgs { struct IgnoreArgs {
template <typename... Args> template <typename... InArgs>
Result operator()(const Args&...) const { Result operator()(const InArgs&...) const {
return function_impl(); return function_impl();
} }
@ -655,213 +864,6 @@ inline PolymorphicAction<Impl> MakePolymorphicAction(const Impl& impl) {
namespace internal { namespace internal {
template <typename F>
class TypedExpectation;
// Specialized for function types below.
template <typename F>
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> 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> foo;
//
// void operator()() && {
// AcceptUniquePointer(std::move(Foo));
// }
// };
//
// // This action can be used with WillOnce.
// EXPECT_CALL(mock, Call)
// .WillOnce(ProvideFoo{std::make_unique<Foo>(...)});
//
// // 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<Foo>(...)});
//
// 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 <typename Result, typename... Args>
class OnceAction<Result(Args...)> final {
private:
// True iff we can use the given callable type (or lvalue reference) directly
// via ActionAdaptor.
template <typename Callable>
using IsDirectlyCompatible = internal::conjunction<
// It must be possible to capture the callable in ActionAdaptor.
std::is_constructible<typename std::decay<Callable>::type, Callable>,
// The callable must be compatible with our signature.
internal::is_callable_r<Result, typename std::decay<Callable>::type,
Args...>>;
// True iff we can use the given callable type via ActionAdaptor once we
// ignore incoming arguments.
template <typename Callable>
using IsCompatibleAfterIgnoringArguments = internal::conjunction<
// It must be possible to capture the callable in a lambda.
std::is_constructible<typename std::decay<Callable>::type, Callable>,
// The callable must be invocable with zero arguments, returning something
// convertible to Result.
internal::is_callable_r<Result, typename std::decay<Callable>::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 <typename Callable,
typename std::enable_if<
internal::conjunction<
// Teach clang on macOS that we're not talking about a
// copy/move constructor here. Otherwise it gets confused
// when checking the is_constructible requirement of our
// traits above.
internal::negation<std::is_same<
OnceAction, typename std::decay<Callable>::type>>,
IsDirectlyCompatible<Callable>> //
::value,
int>::type = 0>
OnceAction(Callable&& callable) // NOLINT
: action_(ActionAdaptor<typename std::decay<Callable>::type>(
{}, std::forward<Callable>(callable))) {}
// As above, but for a callable that ignores the mocked function's arguments.
template <typename Callable,
typename std::enable_if<
internal::conjunction<
// Teach clang on macOS that we're not talking about a
// copy/move constructor here. Otherwise it gets confused
// when checking the is_constructible requirement of our
// traits above.
internal::negation<std::is_same<
OnceAction, typename std::decay<Callable>::type>>,
// Exclude callables for which the overload above works.
// We'd rather provide the arguments if possible.
internal::negation<IsDirectlyCompatible<Callable>>,
IsCompatibleAfterIgnoringArguments<Callable>>::value,
int>::type = 0>
OnceAction(Callable&& callable) // NOLINT
// Call the constructor above with a callable
// that ignores the input arguments.
: OnceAction(IgnoreIncomingArguments<typename std::decay<Callable>::type>{
std::forward<Callable>(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 <typename T,
typename std::enable_if<
internal::conjunction<
// Teach clang on macOS that we're not talking about a
// copy/move constructor here. Otherwise it gets confused
// when checking the is_constructible requirement of our
// traits above.
internal::negation<
std::is_same<OnceAction, typename std::decay<T>::type>>,
// Exclude the overloads above, which we want to take
// precedence.
internal::negation<IsDirectlyCompatible<T>>,
internal::negation<IsCompatibleAfterIgnoringArguments<T>>,
// It must be possible to turn the object into an action of
// the appropriate type.
std::is_convertible<T, Action<Result(Args...)>> //
>::value,
int>::type = 0>
OnceAction(T&& action) : action_(std::forward<T>(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<Result(Args...)>;
// 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 <typename Callable>
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 <typename F>
explicit ActionAdaptor(CallableTag, F&& callable)
: callable_(std::make_shared<Callable>(std::forward<F>(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<void()>. 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 <typename... ArgRefs>
internal::call_result_t<Callable, ArgRefs...> operator()(
ArgRefs&&... args) const {
return std::move(*callable_)(std::forward<ArgRefs>(args)...);
}
private:
// We must put the callable on the heap so that we are copyable, which
// Action needs.
std::shared_ptr<Callable> callable_;
};
// An adaptor that makes a callable that accepts zero arguments callable with
// our mocked arguments.
template <typename Callable>
struct IgnoreIncomingArguments {
internal::call_result_t<Callable> 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<Result(Args...)> ReleaseAction() && { return std::move(action_); }
Action<Result(Args...)> action_;
};
// Helper struct to specialize ReturnAction to execute a move instead of a copy // 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. // on return. Useful for move-only types, but could be used on any type.
template <typename T> template <typename T>
@ -1295,8 +1297,53 @@ struct WithArgsAction {
}; };
template <typename... Actions> template <typename... Actions>
struct DoAllAction { class DoAllAction;
// Base case: only a single action.
template <typename FinalAction>
class DoAllAction<FinalAction> {
public:
struct UserConstructorTag {};
template <typename T>
explicit DoAllAction(UserConstructorTag, T&& action)
: final_action_(std::forward<T>(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 <typename R, typename... Args,
typename std::enable_if<
std::is_convertible<FinalAction, OnceAction<R(Args...)>>::value,
int>::type = 0>
operator OnceAction<R(Args...)>() && { // NOLINT
return std::move(final_action_);
}
template <
typename R, typename... Args,
typename std::enable_if<
std::is_convertible<const FinalAction&, Action<R(Args...)>>::value,
int>::type = 0>
operator Action<R(Args...)>() const { // NOLINT
return final_action_;
}
private: 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 <typename InitialAction, typename... OtherActions>
class DoAllAction<InitialAction, OtherActions...>
: private DoAllAction<OtherActions...> {
private:
using Base = DoAllAction<OtherActions...>;
// The type of reference that should be provided to an initial action for a // The type of reference that should be provided to an initial action for a
// mocked function parameter of type T. // mocked function parameter of type T.
// //
@ -1315,14 +1362,14 @@ struct DoAllAction {
// //
// * More surprisingly, `const T&` is often not a const reference type. // * 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 // 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<T> is U&. In // U& or U&& for some non-scalar type U, then InitialActionArgType<T> is
// other words, we may hand over a non-const reference. // 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 // So for example, given some non-scalar type Obj we have the following
// mappings: // mappings:
// //
// T NonFinalType<T> // T InitialActionArgType<T>
// ------- --------------- // ------- -----------------------
// Obj const Obj& // Obj const Obj&
// Obj& Obj& // Obj& Obj&
// Obj&& Obj& // Obj&& Obj&
@ -1343,34 +1390,85 @@ struct DoAllAction {
// .WillOnce(DoAll(SetArgReferee<0>(17), Return(19))); // .WillOnce(DoAll(SetArgReferee<0>(17), Return(19)));
// //
template <typename T> template <typename T>
using NonFinalType = using InitialActionArgType =
typename std::conditional<std::is_scalar<T>::value, T, const T&>::type; typename std::conditional<std::is_scalar<T>::value, T, const T&>::type;
template <typename ActionT, size_t... I>
std::vector<ActionT> Convert(IndexSequence<I...>) const {
return {ActionT(std::get<I>(actions))...};
}
public: public:
std::tuple<Actions...> actions; struct UserConstructorTag {};
template <typename R, typename... Args> template <typename T, typename... U>
operator Action<R(Args...)>() const { // NOLINT explicit DoAllAction(UserConstructorTag, T&& initial_action,
struct Op { U&&... other_actions)
std::vector<Action<void(NonFinalType<Args>...)>> converted; : Base({}, std::forward<U>(other_actions)...),
Action<R(Args...)> last; initial_action_(std::forward<T>(initial_action)) {}
R operator()(Args... args) const {
auto tuple_args = std::forward_as_tuple(std::forward<Args>(args)...); template <typename R, typename... Args,
for (auto& a : converted) { typename std::enable_if<
a.Perform(tuple_args); conjunction<
} // Both the initial action and the rest must support
return last.Perform(std::move(tuple_args)); // conversion to OnceAction.
std::is_convertible<
InitialAction,
OnceAction<void(InitialActionArgType<Args>...)>>,
std::is_convertible<Base, OnceAction<R(Args...)>>>::value,
int>::type = 0>
operator OnceAction<R(Args...)>() && { // 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<void(InitialActionArgType<Args>...)> initial_action;
OnceAction<R(Args...)> remaining_actions;
R operator()(Args... args) && {
std::move(initial_action)
.Call(static_cast<InitialActionArgType<Args>>(args)...);
return std::move(remaining_actions).Call(std::forward<Args>(args)...);
} }
}; };
return Op{Convert<Action<void(NonFinalType<Args>...)>>(
MakeIndexSequence<sizeof...(Actions) - 1>()), return OA{
std::get<sizeof...(Actions) - 1>(actions)}; std::move(initial_action_),
std::move(static_cast<Base&>(*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<const InitialAction&,
Action<void(InitialActionArgType<Args>...)>>,
std::is_convertible<const Base&, Action<R(Args...)>>>::value,
int>::type = 0>
operator Action<R(Args...)>() const { // 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 {
Action<void(InitialActionArgType<Args>...)> initial_action;
Action<R(Args...)> remaining_actions;
R operator()(Args... args) const {
initial_action.Perform(std::forward_as_tuple(
static_cast<InitialActionArgType<Args>>(args)...));
return remaining_actions.Perform(
std::forward_as_tuple(std::forward<Args>(args)...));
}
};
return OA{
initial_action_,
static_cast<const Base&>(*this),
};
}
private:
InitialAction initial_action_;
}; };
template <typename T, typename... Params> template <typename T, typename... Params>
@ -1513,7 +1611,8 @@ typedef internal::IgnoredValue Unused;
template <typename... Action> template <typename... Action>
internal::DoAllAction<typename std::decay<Action>::type...> DoAll( internal::DoAllAction<typename std::decay<Action>::type...> DoAll(
Action&&... action) { Action&&... action) {
return {std::forward_as_tuple(std::forward<Action>(action)...)}; return internal::DoAllAction<typename std::decay<Action>::type...>(
{}, std::forward<Action>(action)...);
} }
// WithArg<k>(an_action) creates an action that passes the k-th // WithArg<k>(an_action) creates an action that passes the k-th

View File

@ -878,9 +878,15 @@ class GTEST_API_ ExpectationBase {
mutable Mutex mutex_; // Protects action_count_checked_. mutable Mutex mutex_; // Protects action_count_checked_.
}; // class ExpectationBase }; // class ExpectationBase
// Implements an expectation for the given function type.
template <typename F> template <typename F>
class TypedExpectation : public ExpectationBase { class TypedExpectation;
// Implements an expectation for the given function type.
template <typename R, typename... Args>
class TypedExpectation<R(Args...)> : public ExpectationBase {
private:
using F = R(Args...);
public: public:
typedef typename Function<F>::ArgumentTuple ArgumentTuple; typedef typename Function<F>::ArgumentTuple ArgumentTuple;
typedef typename Function<F>::ArgumentMatcherTuple ArgumentMatcherTuple; typedef typename Function<F>::ArgumentMatcherTuple ArgumentMatcherTuple;
@ -993,15 +999,30 @@ class TypedExpectation : public ExpectationBase {
return After(s1, s2, s3, s4).After(s5); 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<F> objects themselves.
TypedExpectation& WillOnce(OnceAction<F> once_action) { TypedExpectation& WillOnce(OnceAction<F> 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<F>(ActionAdaptor{
std::make_shared<OnceAction<F>>(std::move(once_action)),
}));
}
// Fallback overload: accept Action<F> objects and those actions that define
// `operator Action<F>` but not `operator OnceAction<F>`.
//
// This is templated in order to cause the overload above to be preferred
// when the input is convertible to either type.
template <int&... ExplicitArgumentBarrier, typename = void>
TypedExpectation& WillOnce(Action<F> action) {
ExpectSpecProperty(last_clause_ <= kWillOnce, ExpectSpecProperty(last_clause_ <= kWillOnce,
".WillOnce() cannot appear after " ".WillOnce() cannot appear after "
".WillRepeatedly() or .RetiresOnSaturation()."); ".WillRepeatedly() or .RetiresOnSaturation().");
last_clause_ = kWillOnce; last_clause_ = kWillOnce;
untyped_actions_.push_back( untyped_actions_.push_back(new Action<F>(std::move(action)));
new Action<F>(std::move(once_action).ReleaseAction()));
if (!cardinality_specified()) { if (!cardinality_specified()) {
set_cardinality(Exactly(static_cast<int>(untyped_actions_.size()))); set_cardinality(Exactly(static_cast<int>(untyped_actions_.size())));
@ -1074,6 +1095,16 @@ class TypedExpectation : public ExpectationBase {
template <typename Function> template <typename Function>
friend class FunctionMocker; friend class FunctionMocker;
// An adaptor that turns a OneAction<F> into something compatible with
// Action<F>. Must be called at most once.
struct ActionAdaptor {
std::shared_ptr<OnceAction<R(Args...)>> once_action;
R operator()(Args&&... args) const {
return std::move(*once_action).Call(std::forward<Args>(args)...);
}
};
// Returns an Expectation object that references and co-owns this // Returns an Expectation object that references and co-owns this
// expectation. // expectation.
Expectation GetHandle() override { return owner_->GetHandleOf(this); } Expectation GetHandle() override { return owner_->GetHandleOf(this); }

View File

@ -31,10 +31,12 @@
// //
// This file tests the built-in actions. // 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 #ifdef _MSC_VER
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4100) #pragma warning(disable : 4100)
#pragma warning(disable : 4503)
#if _MSC_VER == 1900 #if _MSC_VER == 1900
// and silence C4800 (C4800: 'int *const ': forcing value // and silence C4800 (C4800: 'int *const ': forcing value
// to bool 'true' or 'false') for MSVC 15 // to bool 'true' or 'false') for MSVC 15
@ -1193,6 +1195,21 @@ TEST(AssignTest, CompatibleTypes) {
EXPECT_DOUBLE_EQ(5, x); 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<int(int)> 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 // 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 // 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 // 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{});
mock.AsStdFunction()(Obj{}); mock.AsStdFunction()(Obj{});
} }
// &&-qualified initial actions should also be allowed with WillOnce.
{
struct InitialAction {
void operator()(Obj&) && {}
};
MockFunction<void(Obj&)> mock;
EXPECT_CALL(mock, Call)
.WillOnce(DoAll(InitialAction{}, InitialAction{}, [](Obj&) {}));
Obj obj;
mock.AsStdFunction()(obj);
}
{
struct InitialAction {
void operator()(Obj&) && {}
};
MockFunction<void(Obj &&)> 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<void()> initial_action = [] {};
const Action<int()> final_action = [] { return 17; };
MockFunction<int()> 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. // Tests using WithArgs and with an action that takes 1 argument.
@ -1793,6 +1866,31 @@ TEST(MockMethodTest, ActionSwallowsAllArguments) {
EXPECT_EQ(17, mock.AsStdFunction()(0)); EXPECT_EQ(17, mock.AsStdFunction()(0));
} }
struct ActionWithTemplatedConversionOperators {
template <typename... Args>
operator internal::OnceAction<int(Args...)>() && { // NOLINT
return [] { return 17; };
}
template <typename... Args>
operator Action<int(Args...)>() 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<int()> 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. // Tests for std::function based action.
int Add(int val, int& ref, int* ptr) { // NOLINT int Add(int val, int& ref, int* ptr) { // NOLINT
@ -1919,9 +2017,3 @@ TEST(ActionMacro, LargeArity) {
} // namespace } // namespace
} // namespace testing } // namespace testing
#ifdef _MSC_VER
#if _MSC_VER == 1900
#pragma warning(pop)
#endif
#endif