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} ### Using Functions/Methods/Functors/Lambdas as Actions {#FunctionsAsActions}
If the built-in actions don't suit you, you can use an existing callable 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 --> <!-- GOOGLETEST_CM0024 DO NOT DELETE -->
@ -2203,6 +2203,7 @@ class Helper {
.WillRepeatedly(Invoke(NewPermanentCallback(Sum3, 1))); .WillRepeatedly(Invoke(NewPermanentCallback(Sum3, 1)));
EXPECT_CALL(foo, ComplexJob(_)) EXPECT_CALL(foo, ComplexJob(_))
.WillOnce(Invoke(&helper, &Helper::ComplexJob)) .WillOnce(Invoke(&helper, &Helper::ComplexJob))
.WillOnce([] { return true; })
.WillRepeatedly([](int x) { return x > 0; }); .WillRepeatedly([](int x) { return x > 0; });
foo.Sum(5, 6); // Invokes CalculateSum(5, 6). 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* 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 with the signature of the mock function, meaning that the latter's arguments (if
be implicitly converted to the corresponding arguments of the former, and the it takes any) can be implicitly converted to the corresponding arguments of the
former's return type can be implicitly converted to that of the latter. So, you former, and the former's return type can be implicitly converted to that of the
can invoke something whose type is *not* exactly the same as the mock function, latter. So, you can invoke something whose type is *not* exactly the same as the
as long as it's safe to do so - nice, huh? mock function, as long as it's safe to do so - nice, huh?
**`Note:`{.escaped}** **`Note:`{.escaped}**
@ -2267,19 +2268,20 @@ TEST_F(FooTest, Test) {
### Invoking a Function/Method/Functor/Lambda/Callback Without Arguments ### Invoking a Function/Method/Functor/Lambda/Callback Without Arguments
`Invoke()` is very useful for doing actions that are more complex. It passes the `Invoke()` passes the mock function's arguments to the function, etc being
mock function's arguments to the function, etc being invoked such that the invoked such that the callee has the full context of the call to work with. If
callee has the full context of the call to work with. If the invoked function is the invoked function is not interested in some or all of the arguments, it can
not interested in some or all of the arguments, it can simply ignore them. simply ignore them.
Yet, a common pattern is that a test author wants to invoke a function without 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 the arguments of the mock function. She could do that using a wrapper function
wrapper function that throws away the arguments before invoking an underlining that throws away the arguments before invoking an underlining nullary function.
nullary function. Needless to say, this can be tedious and obscures the intent Needless to say, this can be tedious and obscures the intent of the test.
of the test.
`InvokeWithoutArgs()` solves this problem. It's like `Invoke()` except that it There are two solutions to this problem. First, you can pass any callable of
doesn't pass the mock function's arguments to the callee. Here's an example: 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 ```cpp
using ::testing::_; using ::testing::_;
@ -2296,7 +2298,7 @@ bool Job2(int n, char c) { ... }
... ...
MockFoo foo; MockFoo foo;
EXPECT_CALL(foo, ComplexJob(_)) EXPECT_CALL(foo, ComplexJob(_))
.WillOnce(InvokeWithoutArgs(Job1)) .WillOnce([] { Job1(); });
.WillOnce(InvokeWithoutArgs(NewPermanentCallback(Job2, 5, 'a'))); .WillOnce(InvokeWithoutArgs(NewPermanentCallback(Job2, 5, 'a')));
foo.ComplexJob(10); // Invokes Job1(). 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_ #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 } // namespace internal
// When an unexpected function call is encountered, Google Mock will // 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 // This cannot take std::function directly, because then Action would not be
// directly constructible from lambda (it would require two conversions). // directly constructible from lambda (it would require two conversions).
template <typename G, template <typename G,
typename = typename ::std::enable_if< typename IsCompatibleFunctor =
::std::is_constructible<::std::function<F>, G>::value>::type> ::std::is_constructible<::std::function<F>, G>,
Action(G&& fun) : fun_(::std::forward<G>(fun)) {} // NOLINT 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. // Constructs an Action from its implementation.
explicit Action(ActionInterface<F>* impl) explicit Action(ActionInterface<F>* impl)
@ -490,6 +500,26 @@ class Action {
template <typename G> template <typename G>
friend class Action; 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. // fun_ is an empty function if and only if this is the DoDefault() action.
::std::function<F> fun_; ::std::function<F> fun_;
}; };

View File

@ -1470,8 +1470,19 @@ TEST(FunctorActionTest, TypeConversion) {
EXPECT_EQ(1, s2.Perform(std::make_tuple("hello"))); EXPECT_EQ(1, s2.Perform(std::make_tuple("hello")));
// Also between the lambda and the action itself. // Also between the lambda and the action itself.
const Action<bool(std::string)> x = [](Unused) { return 42; }; const Action<bool(std::string)> x1 = [](Unused) { return 42; };
EXPECT_TRUE(x.Perform(std::make_tuple("hello"))); 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) { TEST(FunctorActionTest, UnusedArguments) {