Googletest export

Allow construction of an Action from a callable of zero args

Action already allows construction from a callable with the same args as the mocked function, without needing to wrap the callable in Invoke. However, if you don't care about the arguments to the mocked function you need to either accept all of them or wrap your callable in InvokeWithoutArgs. This change makes both of those unnecessary, since it allows you to pass a no-args callable to Action directly.

PiperOrigin-RevId: 296117034
This commit is contained in:
Abseil Team 2020-02-19 23:05:01 -05:00 committed by CJ Johnson
parent 23b2a3b1cf
commit fd538161f4
3 changed files with 65 additions and 22 deletions

View File

@ -2174,7 +2174,7 @@ own precedence order distinct from the `ON_CALL` precedence order.
### Using Functions/Methods/Functors/Lambdas as Actions {#FunctionsAsActions}
If the built-in actions don't suit you, you can use an existing callable
(function, `std::function`, method, functor, lambda as an action.
(function, `std::function`, method, functor, lambda) as an action.
<!-- GOOGLETEST_CM0024 DO NOT DELETE -->
@ -2203,6 +2203,7 @@ class Helper {
.WillRepeatedly(Invoke(NewPermanentCallback(Sum3, 1)));
EXPECT_CALL(foo, ComplexJob(_))
.WillOnce(Invoke(&helper, &Helper::ComplexJob))
.WillOnce([] { return true; })
.WillRepeatedly([](int x) { return x > 0; });
foo.Sum(5, 6); // Invokes CalculateSum(5, 6).
@ -2212,11 +2213,11 @@ class Helper {
```
The only requirement is that the type of the function, etc must be *compatible*
with the signature of the mock function, meaning that the latter's arguments can
be implicitly converted to the corresponding arguments of the former, and the
former's return type can be implicitly converted to that of the latter. So, you
can invoke something whose type is *not* exactly the same as the mock function,
as long as it's safe to do so - nice, huh?
with the signature of the mock function, meaning that the latter's arguments (if
it takes any) can be implicitly converted to the corresponding arguments of the
former, and the former's return type can be implicitly converted to that of the
latter. So, you can invoke something whose type is *not* exactly the same as the
mock function, as long as it's safe to do so - nice, huh?
**`Note:`{.escaped}**
@ -2267,19 +2268,20 @@ TEST_F(FooTest, Test) {
### Invoking a Function/Method/Functor/Lambda/Callback Without Arguments
`Invoke()` is very useful for doing actions that are more complex. It passes the
mock function's arguments to the function, etc being invoked such that the
callee has the full context of the call to work with. If the invoked function is
not interested in some or all of the arguments, it can simply ignore them.
`Invoke()` passes the mock function's arguments to the function, etc being
invoked such that the callee has the full context of the call to work with. If
the invoked function is not interested in some or all of the arguments, it can
simply ignore them.
Yet, a common pattern is that a test author wants to invoke a function without
the arguments of the mock function. `Invoke()` allows her to do that using a
wrapper function that throws away the arguments before invoking an underlining
nullary function. Needless to say, this can be tedious and obscures the intent
of the test.
the arguments of the mock function. She could do that using a wrapper function
that throws away the arguments before invoking an underlining nullary function.
Needless to say, this can be tedious and obscures the intent of the test.
`InvokeWithoutArgs()` solves this problem. It's like `Invoke()` except that it
doesn't pass the mock function's arguments to the callee. Here's an example:
There are two solutions to this problem. First, you can pass any callable of
zero args as an action. Alternatively, use `InvokeWithoutArgs()`, which is like
`Invoke()` except that it doesn't pass the mock function's arguments to the
callee. Here's an example of each:
```cpp
using ::testing::_;
@ -2296,7 +2298,7 @@ bool Job2(int n, char c) { ... }
...
MockFoo foo;
EXPECT_CALL(foo, ComplexJob(_))
.WillOnce(InvokeWithoutArgs(Job1))
.WillOnce([] { Job1(); });
.WillOnce(InvokeWithoutArgs(NewPermanentCallback(Job2, 5, 'a')));
foo.ComplexJob(10); // Invokes Job1().

View File

@ -263,6 +263,10 @@ 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;
} // namespace internal
// When an unexpected function call is encountered, Google Mock will
@ -456,9 +460,15 @@ class Action {
// This cannot take std::function directly, because then Action would not be
// directly constructible from lambda (it would require two conversions).
template <typename G,
typename = typename ::std::enable_if<
::std::is_constructible<::std::function<F>, G>::value>::type>
Action(G&& fun) : fun_(::std::forward<G>(fun)) {} // NOLINT
typename IsCompatibleFunctor =
::std::is_constructible<::std::function<F>, G>,
typename IsNoArgsFunctor =
::std::is_constructible<::std::function<Result()>, G>,
typename = typename ::std::enable_if<internal::disjunction<
IsCompatibleFunctor, IsNoArgsFunctor>::value>::type>
Action(G&& fun) { // NOLINT
Init(::std::forward<G>(fun), IsCompatibleFunctor());
}
// Constructs an Action from its implementation.
explicit Action(ActionInterface<F>* impl)
@ -490,6 +500,26 @@ class Action {
template <typename G>
friend class Action;
template <typename G>
void Init(G&& g, ::std::true_type) {
fun_ = ::std::forward<G>(g);
}
template <typename G>
void Init(G&& g, ::std::false_type) {
fun_ = IgnoreArgs<typename ::std::decay<G>::type>{::std::forward<G>(g)};
}
template <typename FunctionImpl>
struct IgnoreArgs {
template <typename... Args>
Result operator()(const Args&...) const {
return function_impl();
}
FunctionImpl function_impl;
};
// fun_ is an empty function if and only if this is the DoDefault() action.
::std::function<F> fun_;
};

View File

@ -1470,8 +1470,19 @@ TEST(FunctorActionTest, TypeConversion) {
EXPECT_EQ(1, s2.Perform(std::make_tuple("hello")));
// Also between the lambda and the action itself.
const Action<bool(std::string)> x = [](Unused) { return 42; };
EXPECT_TRUE(x.Perform(std::make_tuple("hello")));
const Action<bool(std::string)> x1 = [](Unused) { return 42; };
const Action<bool(std::string)> x2 = [] { return 42; };
EXPECT_TRUE(x1.Perform(std::make_tuple("hello")));
EXPECT_TRUE(x2.Perform(std::make_tuple("hello")));
// Ensure decay occurs where required.
std::function<int()> f = [] { return 7; };
Action<int(int)> d = f;
f = nullptr;
EXPECT_EQ(7, d.Perform(std::make_tuple(1)));
// Ensure creation of an empty action succeeds.
Action<void(int)>(nullptr);
}
TEST(FunctorActionTest, UnusedArguments) {