mirror of
https://github.com/google/googletest.git
synced 2024-12-27 10:11:03 +08:00
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:
parent
b53547bf01
commit
0498660ea5
@ -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
|
||||||
|
@ -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); }
|
||||||
|
@ -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
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user