mirror of
https://github.com/google/googletest.git
synced 2025-01-14 00:20:57 +08:00
Add support for move-only and &&-qualified actions in WillOnce.
This provides a type-safe way for an action to express that it wants to be called only once, or to capture move-only objects. It is a generalization of the type system-evading hack in ByMove, with the improvement that it works for _any_ action (including user-defined ones), and correctly expresses that the action can only be used with WillOnce. I'll make existing actions benefit in a future commit. PiperOrigin-RevId: 440496139 Change-Id: I4145d191cca5655995ef41360bb126c123cb41d3
This commit is contained in:
parent
5f467ec04d
commit
a1cc8c5519
@ -248,7 +248,9 @@ EXPECT_CALL(my_mock, GetNumber())
|
||||
.WillOnce(Return(3));
|
||||
```
|
||||
|
||||
The `WillOnce` clause can be used any number of times on an expectation.
|
||||
The `WillOnce` clause can be used any number of times on an expectation. Unlike
|
||||
`WillRepeatedly`, the action fed to each `WillOnce` call will be called at most
|
||||
once, so may be a move-only type and/or have an `&&`-qualified call operator.
|
||||
|
||||
#### WillRepeatedly {#EXPECT_CALL.WillRepeatedly}
|
||||
|
||||
|
@ -262,9 +262,65 @@ GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(double, 0);
|
||||
|
||||
#undef GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_
|
||||
|
||||
// Simple two-arg form of std::disjunction.
|
||||
template <typename P, typename Q>
|
||||
using disjunction = typename ::std::conditional<P::value, P, Q>::type;
|
||||
// Partial implementations of metaprogramming types from the standard library
|
||||
// not available in C++11.
|
||||
|
||||
template <typename P>
|
||||
struct negation
|
||||
// NOLINTNEXTLINE
|
||||
: std::integral_constant<bool, bool(!P::value)> {};
|
||||
|
||||
// Base case: with zero predicates the answer is always true.
|
||||
template <typename...>
|
||||
struct conjunction : std::true_type {};
|
||||
|
||||
// With a single predicate, the answer is that predicate.
|
||||
template <typename P1>
|
||||
struct conjunction<P1> : P1 {};
|
||||
|
||||
// With multiple predicates the answer is the first predicate if that is false,
|
||||
// and we recurse otherwise.
|
||||
template <typename P1, typename... Ps>
|
||||
struct conjunction<P1, Ps...>
|
||||
: std::conditional<bool(P1::value), conjunction<Ps...>, P1>::type {};
|
||||
|
||||
template <typename...>
|
||||
struct disjunction : std::false_type {};
|
||||
|
||||
template <typename P1>
|
||||
struct disjunction<P1> : P1 {};
|
||||
|
||||
template <typename P1, typename... Ps>
|
||||
struct disjunction<P1, Ps...>
|
||||
// NOLINTNEXTLINE
|
||||
: std::conditional<!bool(P1::value), disjunction<Ps...>, P1>::type {};
|
||||
|
||||
template <typename...>
|
||||
using void_t = void;
|
||||
|
||||
// Like std::invoke_result_t from C++17, but works only for objects with call
|
||||
// operators (not e.g. member function pointers, which we don't need specific
|
||||
// support for in OnceAction because std::function deals with them).
|
||||
template <typename F, typename... Args>
|
||||
using call_result_t = decltype(std::declval<F>()(std::declval<Args>()...));
|
||||
|
||||
template <typename Void, typename R, typename F, typename... Args>
|
||||
struct is_callable_r_impl : std::false_type {};
|
||||
|
||||
// Specialize the struct for those template arguments where call_result_t is
|
||||
// well-formed. When it's not, the generic template above is chosen, resulting
|
||||
// in std::false_type.
|
||||
template <typename R, typename F, typename... Args>
|
||||
struct is_callable_r_impl<void_t<call_result_t<F, Args...>>, R, F, Args...>
|
||||
: std::conditional<
|
||||
std::is_same<R, void>::value, //
|
||||
std::true_type, //
|
||||
std::is_convertible<call_result_t<F, Args...>, R>>::type {};
|
||||
|
||||
// Like std::is_invocable_r from C++17, but works only for objects with call
|
||||
// operators. See the note on call_result_t.
|
||||
template <typename R, typename F, typename... Args>
|
||||
using is_callable_r = is_callable_r_impl<void, R, F, Args...>;
|
||||
|
||||
} // namespace internal
|
||||
|
||||
@ -596,6 +652,213 @@ inline PolymorphicAction<Impl> MakePolymorphicAction(const Impl& impl) {
|
||||
|
||||
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
|
||||
// on return. Useful for move-only types, but could be used on any type.
|
||||
template <typename T>
|
||||
|
@ -992,14 +992,16 @@ class TypedExpectation : public ExpectationBase {
|
||||
return After(s1, s2, s3, s4).After(s5);
|
||||
}
|
||||
|
||||
// Implements the .WillOnce() clause.
|
||||
TypedExpectation& WillOnce(const Action<F>& action) {
|
||||
// Implements the .WillOnce() clause for copyable actions.
|
||||
TypedExpectation& WillOnce(OnceAction<F> once_action) {
|
||||
ExpectSpecProperty(last_clause_ <= kWillOnce,
|
||||
".WillOnce() cannot appear after "
|
||||
".WillRepeatedly() or .RetiresOnSaturation().");
|
||||
last_clause_ = kWillOnce;
|
||||
|
||||
untyped_actions_.push_back(new Action<F>(action));
|
||||
untyped_actions_.push_back(
|
||||
new Action<F>(std::move(once_action).ReleaseAction()));
|
||||
|
||||
if (!cardinality_specified()) {
|
||||
set_cardinality(Exactly(static_cast<int>(untyped_actions_.size())));
|
||||
}
|
||||
|
@ -55,37 +55,170 @@
|
||||
#include "gtest/gtest-spi.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace testing {
|
||||
namespace {
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Action;
|
||||
using ::testing::ActionInterface;
|
||||
using ::testing::Assign;
|
||||
using ::testing::ByMove;
|
||||
using ::testing::ByRef;
|
||||
using ::testing::DefaultValue;
|
||||
using ::testing::DoAll;
|
||||
using ::testing::DoDefault;
|
||||
using ::testing::IgnoreResult;
|
||||
using ::testing::Invoke;
|
||||
using ::testing::InvokeWithoutArgs;
|
||||
using ::testing::MakePolymorphicAction;
|
||||
using ::testing::PolymorphicAction;
|
||||
using ::testing::Return;
|
||||
using ::testing::ReturnNew;
|
||||
using ::testing::ReturnNull;
|
||||
using ::testing::ReturnRef;
|
||||
using ::testing::ReturnRefOfCopy;
|
||||
using ::testing::ReturnRoundRobin;
|
||||
using ::testing::SetArgPointee;
|
||||
using ::testing::SetArgumentPointee;
|
||||
using ::testing::Unused;
|
||||
using ::testing::WithArgs;
|
||||
using ::testing::internal::BuiltInDefaultValue;
|
||||
|
||||
#if !GTEST_OS_WINDOWS_MOBILE
|
||||
using ::testing::SetErrnoAndReturn;
|
||||
#endif
|
||||
TEST(TypeTraits, Negation) {
|
||||
// Direct use with std types.
|
||||
static_assert(std::is_base_of<std::false_type,
|
||||
internal::negation<std::true_type>>::value,
|
||||
"");
|
||||
|
||||
static_assert(std::is_base_of<std::true_type,
|
||||
internal::negation<std::false_type>>::value,
|
||||
"");
|
||||
|
||||
// With other types that fit the requirement of a value member that is
|
||||
// convertible to bool.
|
||||
static_assert(std::is_base_of<
|
||||
std::true_type,
|
||||
internal::negation<std::integral_constant<int, 0>>>::value,
|
||||
"");
|
||||
|
||||
static_assert(std::is_base_of<
|
||||
std::false_type,
|
||||
internal::negation<std::integral_constant<int, 1>>>::value,
|
||||
"");
|
||||
|
||||
static_assert(std::is_base_of<
|
||||
std::false_type,
|
||||
internal::negation<std::integral_constant<int, -1>>>::value,
|
||||
"");
|
||||
}
|
||||
|
||||
// Weird false/true types that aren't actually bool constants (but should still
|
||||
// be legal according to [meta.logical] because `bool(T::value)` is valid), are
|
||||
// distinct from std::false_type and std::true_type, and are distinct from other
|
||||
// instantiations of the same template.
|
||||
//
|
||||
// These let us check finicky details mandated by the standard like
|
||||
// "std::conjunction should evaluate to a type that inherits from the first
|
||||
// false-y input".
|
||||
template <int>
|
||||
struct MyFalse : std::integral_constant<int, 0> {};
|
||||
|
||||
template <int>
|
||||
struct MyTrue : std::integral_constant<int, -1> {};
|
||||
|
||||
TEST(TypeTraits, Conjunction) {
|
||||
// Base case: always true.
|
||||
static_assert(std::is_base_of<std::true_type, internal::conjunction<>>::value,
|
||||
"");
|
||||
|
||||
// One predicate: inherits from that predicate, regardless of value.
|
||||
static_assert(
|
||||
std::is_base_of<MyFalse<0>, internal::conjunction<MyFalse<0>>>::value,
|
||||
"");
|
||||
|
||||
static_assert(
|
||||
std::is_base_of<MyTrue<0>, internal::conjunction<MyTrue<0>>>::value, "");
|
||||
|
||||
// Multiple predicates, with at least one false: inherits from that one.
|
||||
static_assert(
|
||||
std::is_base_of<MyFalse<1>, internal::conjunction<MyTrue<0>, MyFalse<1>,
|
||||
MyTrue<2>>>::value,
|
||||
"");
|
||||
|
||||
static_assert(
|
||||
std::is_base_of<MyFalse<1>, internal::conjunction<MyTrue<0>, MyFalse<1>,
|
||||
MyFalse<2>>>::value,
|
||||
"");
|
||||
|
||||
// Short circuiting: in the case above, additional predicates need not even
|
||||
// define a value member.
|
||||
struct Empty {};
|
||||
static_assert(
|
||||
std::is_base_of<MyFalse<1>, internal::conjunction<MyTrue<0>, MyFalse<1>,
|
||||
Empty>>::value,
|
||||
"");
|
||||
|
||||
// All predicates true: inherits from the last.
|
||||
static_assert(
|
||||
std::is_base_of<MyTrue<2>, internal::conjunction<MyTrue<0>, MyTrue<1>,
|
||||
MyTrue<2>>>::value,
|
||||
"");
|
||||
}
|
||||
|
||||
TEST(TypeTraits, Disjunction) {
|
||||
// Base case: always false.
|
||||
static_assert(
|
||||
std::is_base_of<std::false_type, internal::disjunction<>>::value, "");
|
||||
|
||||
// One predicate: inherits from that predicate, regardless of value.
|
||||
static_assert(
|
||||
std::is_base_of<MyFalse<0>, internal::disjunction<MyFalse<0>>>::value,
|
||||
"");
|
||||
|
||||
static_assert(
|
||||
std::is_base_of<MyTrue<0>, internal::disjunction<MyTrue<0>>>::value, "");
|
||||
|
||||
// Multiple predicates, with at least one true: inherits from that one.
|
||||
static_assert(
|
||||
std::is_base_of<MyTrue<1>, internal::disjunction<MyFalse<0>, MyTrue<1>,
|
||||
MyFalse<2>>>::value,
|
||||
"");
|
||||
|
||||
static_assert(
|
||||
std::is_base_of<MyTrue<1>, internal::disjunction<MyFalse<0>, MyTrue<1>,
|
||||
MyTrue<2>>>::value,
|
||||
"");
|
||||
|
||||
// Short circuiting: in the case above, additional predicates need not even
|
||||
// define a value member.
|
||||
struct Empty {};
|
||||
static_assert(
|
||||
std::is_base_of<MyTrue<1>, internal::disjunction<MyFalse<0>, MyTrue<1>,
|
||||
Empty>>::value,
|
||||
"");
|
||||
|
||||
// All predicates false: inherits from the last.
|
||||
static_assert(
|
||||
std::is_base_of<MyFalse<2>, internal::disjunction<MyFalse<0>, MyFalse<1>,
|
||||
MyFalse<2>>>::value,
|
||||
"");
|
||||
}
|
||||
|
||||
TEST(TypeTraits, IsInvocableRV) {
|
||||
struct C {
|
||||
int operator()() const { return 0; }
|
||||
void operator()(int) & {}
|
||||
std::string operator()(int) && { return ""; };
|
||||
};
|
||||
|
||||
// The first overload is callable for const and non-const rvalues and lvalues.
|
||||
// It can be used to obtain an int, void, or anything int is convertible too.
|
||||
static_assert(internal::is_callable_r<int, C>::value, "");
|
||||
static_assert(internal::is_callable_r<int, C&>::value, "");
|
||||
static_assert(internal::is_callable_r<int, const C>::value, "");
|
||||
static_assert(internal::is_callable_r<int, const C&>::value, "");
|
||||
|
||||
static_assert(internal::is_callable_r<void, C>::value, "");
|
||||
static_assert(internal::is_callable_r<char, C>::value, "");
|
||||
|
||||
// It's possible to provide an int. If it's given to an lvalue, the result is
|
||||
// void. Otherwise it is std::string (which is also treated as allowed for a
|
||||
// void result type).
|
||||
static_assert(internal::is_callable_r<void, C&, int>::value, "");
|
||||
static_assert(!internal::is_callable_r<int, C&, int>::value, "");
|
||||
static_assert(!internal::is_callable_r<std::string, C&, int>::value, "");
|
||||
static_assert(!internal::is_callable_r<void, const C&, int>::value, "");
|
||||
|
||||
static_assert(internal::is_callable_r<std::string, C, int>::value, "");
|
||||
static_assert(internal::is_callable_r<void, C, int>::value, "");
|
||||
static_assert(!internal::is_callable_r<int, C, int>::value, "");
|
||||
|
||||
// It's not possible to provide other arguments.
|
||||
static_assert(!internal::is_callable_r<void, C, std::string>::value, "");
|
||||
static_assert(!internal::is_callable_r<void, C, int, int>::value, "");
|
||||
|
||||
// Nothing should choke when we try to call other arguments besides directly
|
||||
// callable objects, but they should not show up as callable.
|
||||
static_assert(!internal::is_callable_r<void, int>::value, "");
|
||||
static_assert(!internal::is_callable_r<void, void (C::*)()>::value, "");
|
||||
static_assert(!internal::is_callable_r<void, void (C::*)(), C*>::value, "");
|
||||
}
|
||||
|
||||
// Tests that BuiltInDefaultValue<T*>::Get() returns NULL.
|
||||
TEST(BuiltInDefaultValueTest, IsNullForPointerTypes) {
|
||||
@ -1428,6 +1561,154 @@ TEST(MockMethodTest, CanTakeMoveOnlyValue) {
|
||||
EXPECT_EQ(42, *saved);
|
||||
}
|
||||
|
||||
// It should be possible to use callables with an &&-qualified call operator
|
||||
// with WillOnce, since they will be called only once. This allows actions to
|
||||
// contain and manipulate move-only types.
|
||||
TEST(MockMethodTest, ActionHasRvalueRefQualifiedCallOperator) {
|
||||
struct Return17 {
|
||||
int operator()() && { return 17; }
|
||||
};
|
||||
|
||||
// Action is directly compatible with mocked function type.
|
||||
{
|
||||
MockFunction<int()> mock;
|
||||
EXPECT_CALL(mock, Call).WillOnce(Return17());
|
||||
|
||||
EXPECT_EQ(17, mock.AsStdFunction()());
|
||||
}
|
||||
|
||||
// Action doesn't want mocked function arguments.
|
||||
{
|
||||
MockFunction<int(int)> mock;
|
||||
EXPECT_CALL(mock, Call).WillOnce(Return17());
|
||||
|
||||
EXPECT_EQ(17, mock.AsStdFunction()(0));
|
||||
}
|
||||
}
|
||||
|
||||
// Edge case: if an action has both a const-qualified and an &&-qualified call
|
||||
// operator, there should be no "ambiguous call" errors. The &&-qualified
|
||||
// operator should be used by WillOnce (since it doesn't need to retain the
|
||||
// action beyond one call), and the const-qualified one by WillRepeatedly.
|
||||
TEST(MockMethodTest, ActionHasMultipleCallOperators) {
|
||||
struct ReturnInt {
|
||||
int operator()() && { return 17; }
|
||||
int operator()() const& { return 19; }
|
||||
};
|
||||
|
||||
// Directly compatible with mocked function type.
|
||||
{
|
||||
MockFunction<int()> mock;
|
||||
EXPECT_CALL(mock, Call).WillOnce(ReturnInt()).WillRepeatedly(ReturnInt());
|
||||
|
||||
EXPECT_EQ(17, mock.AsStdFunction()());
|
||||
EXPECT_EQ(19, mock.AsStdFunction()());
|
||||
EXPECT_EQ(19, mock.AsStdFunction()());
|
||||
}
|
||||
|
||||
// Ignores function arguments.
|
||||
{
|
||||
MockFunction<int(int)> mock;
|
||||
EXPECT_CALL(mock, Call).WillOnce(ReturnInt()).WillRepeatedly(ReturnInt());
|
||||
|
||||
EXPECT_EQ(17, mock.AsStdFunction()(0));
|
||||
EXPECT_EQ(19, mock.AsStdFunction()(0));
|
||||
EXPECT_EQ(19, mock.AsStdFunction()(0));
|
||||
}
|
||||
}
|
||||
|
||||
// WillOnce should have no problem coping with a move-only action, whether it is
|
||||
// &&-qualified or not.
|
||||
TEST(MockMethodTest, MoveOnlyAction) {
|
||||
// &&-qualified
|
||||
{
|
||||
struct Return17 {
|
||||
Return17() = default;
|
||||
Return17(Return17&&) = default;
|
||||
|
||||
Return17(const Return17&) = delete;
|
||||
Return17 operator=(const Return17&) = delete;
|
||||
|
||||
int operator()() && { return 17; }
|
||||
};
|
||||
|
||||
MockFunction<int()> mock;
|
||||
EXPECT_CALL(mock, Call).WillOnce(Return17());
|
||||
EXPECT_EQ(17, mock.AsStdFunction()());
|
||||
}
|
||||
|
||||
// Not &&-qualified
|
||||
{
|
||||
struct Return17 {
|
||||
Return17() = default;
|
||||
Return17(Return17&&) = default;
|
||||
|
||||
Return17(const Return17&) = delete;
|
||||
Return17 operator=(const Return17&) = delete;
|
||||
|
||||
int operator()() const { return 17; }
|
||||
};
|
||||
|
||||
MockFunction<int()> mock;
|
||||
EXPECT_CALL(mock, Call).WillOnce(Return17());
|
||||
EXPECT_EQ(17, mock.AsStdFunction()());
|
||||
}
|
||||
}
|
||||
|
||||
// It should be possible to use an action that returns a value with a mock
|
||||
// function that doesn't, both through WillOnce and WillRepeatedly.
|
||||
TEST(MockMethodTest, ActionReturnsIgnoredValue) {
|
||||
struct ReturnInt {
|
||||
int operator()() const { return 0; }
|
||||
};
|
||||
|
||||
MockFunction<void()> mock;
|
||||
EXPECT_CALL(mock, Call).WillOnce(ReturnInt()).WillRepeatedly(ReturnInt());
|
||||
|
||||
mock.AsStdFunction()();
|
||||
mock.AsStdFunction()();
|
||||
}
|
||||
|
||||
// Despite the fanciness around move-only actions and so on, it should still be
|
||||
// possible to hand an lvalue reference to a copyable action to WillOnce.
|
||||
TEST(MockMethodTest, WillOnceCanAcceptLvalueReference) {
|
||||
MockFunction<int()> mock;
|
||||
|
||||
const auto action = [] { return 17; };
|
||||
EXPECT_CALL(mock, Call).WillOnce(action);
|
||||
|
||||
EXPECT_EQ(17, mock.AsStdFunction()());
|
||||
}
|
||||
|
||||
// A callable that doesn't use SFINAE to restrict its call operator's overload
|
||||
// set, but is still picky about which arguments it will accept.
|
||||
struct StaticAssertSingleArgument {
|
||||
template <typename... Args>
|
||||
static constexpr bool CheckArgs() {
|
||||
static_assert(sizeof...(Args) == 1, "");
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename... Args, bool = CheckArgs<Args...>()>
|
||||
int operator()(Args...) const {
|
||||
return 17;
|
||||
}
|
||||
};
|
||||
|
||||
// WillOnce and WillRepeatedly should both work fine with naïve implementations
|
||||
// of actions that don't use SFINAE to limit the overload set for their call
|
||||
// operator. If they are compatible with the actual mocked signature, we
|
||||
// shouldn't probe them with no arguments and trip a static_assert.
|
||||
TEST(MockMethodTest, ActionSwallowsAllArguments) {
|
||||
MockFunction<int(int)> mock;
|
||||
EXPECT_CALL(mock, Call)
|
||||
.WillOnce(StaticAssertSingleArgument{})
|
||||
.WillRepeatedly(StaticAssertSingleArgument{});
|
||||
|
||||
EXPECT_EQ(17, mock.AsStdFunction()(0));
|
||||
EXPECT_EQ(17, mock.AsStdFunction()(0));
|
||||
}
|
||||
|
||||
// Tests for std::function based action.
|
||||
|
||||
int Add(int val, int& ref, int* ptr) { // NOLINT
|
||||
@ -1552,7 +1833,8 @@ TEST(ActionMacro, LargeArity) {
|
||||
14, 15, 16, 17, 18, 19)));
|
||||
}
|
||||
|
||||
} // Unnamed namespace
|
||||
} // namespace
|
||||
} // namespace testing
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#if _MSC_VER == 1900
|
||||
|
Loading…
x
Reference in New Issue
Block a user