summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAaron Jacobs <jacobsa@google.com>2024-07-24 02:43:05 (GMT)
committerCopybara-Service <copybara-worker@google.com>2024-07-24 02:43:42 (GMT)
commit352788321faa2f2aa7a098a5a6e53053059b934b (patch)
tree49cf3d4b0f578a067cdfd90dbaa13a997300f4de
parent57e107a10ea4ff5d8d31df9e4833f80b414b0dd2 (diff)
downloadgoogletest-352788321faa2f2aa7a098a5a6e53053059b934b.zip
googletest-352788321faa2f2aa7a098a5a6e53053059b934b.tar.gz
googletest-352788321faa2f2aa7a098a5a6e53053059b934b.tar.bz2
gmock-actions: make DoAll convert to OnceAction via custom conversions.
Currently it will refuse to become a `OnceAction` if its component sub-actions have an `Action` conversion operator but don't know about `OnceAction` in particular because although `Action` is convertible to `OnceAction`, the compiler won't follow the chain of conversions. Instead, teach it explicitly that it can always be a `OnceAction` when it can be an `Action`. PiperOrigin-RevId: 655393035 Change-Id: Ib205b518ceef5f256627f4b02cd93ec9bd98343b
-rw-r--r--googlemock/include/gmock/gmock-actions.h62
-rw-r--r--googlemock/test/gmock-actions_test.cc48
2 files changed, 98 insertions, 12 deletions
diff --git a/googlemock/include/gmock/gmock-actions.h b/googlemock/include/gmock/gmock-actions.h
index 11da899..aa47079 100644
--- a/googlemock/include/gmock/gmock-actions.h
+++ b/googlemock/include/gmock/gmock-actions.h
@@ -1493,6 +1493,7 @@ class DoAllAction<FinalAction> {
// providing a call operator because even with a particular set of arguments
// they don't have a fixed return type.
+ // We support conversion to OnceAction whenever the sub-action does.
template <typename R, typename... Args,
typename std::enable_if<
std::is_convertible<FinalAction, OnceAction<R(Args...)>>::value,
@@ -1501,6 +1502,21 @@ class DoAllAction<FinalAction> {
return std::move(final_action_);
}
+ // We also support conversion to OnceAction whenever the sub-action supports
+ // conversion to Action (since any Action can also be a OnceAction).
+ template <
+ typename R, typename... Args,
+ typename std::enable_if<
+ conjunction<
+ negation<
+ std::is_convertible<FinalAction, OnceAction<R(Args...)>>>,
+ std::is_convertible<FinalAction, Action<R(Args...)>>>::value,
+ int>::type = 0>
+ operator OnceAction<R(Args...)>() && { // NOLINT
+ return Action<R(Args...)>(std::move(final_action_));
+ }
+
+ // We support conversion to Action whenever the sub-action does.
template <
typename R, typename... Args,
typename std::enable_if<
@@ -1580,16 +1596,16 @@ class DoAllAction<InitialAction, OtherActions...>
: Base({}, std::forward<U>(other_actions)...),
initial_action_(std::forward<T>(initial_action)) {}
- template <typename R, typename... Args,
- typename std::enable_if<
- conjunction<
- // Both the initial action and the rest must support
- // conversion to OnceAction.
- std::is_convertible<
- InitialAction,
- OnceAction<void(InitialActionArgType<Args>...)>>,
- std::is_convertible<Base, OnceAction<R(Args...)>>>::value,
- int>::type = 0>
+ // We support conversion to OnceAction whenever both the initial action and
+ // the rest support conversion to OnceAction.
+ template <
+ typename R, typename... Args,
+ typename std::enable_if<
+ conjunction<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
@@ -1612,12 +1628,34 @@ class DoAllAction<InitialAction, OtherActions...>
};
}
+ // We also support conversion to OnceAction whenever the initial action
+ // supports conversion to Action (since any Action can also be a OnceAction).
+ //
+ // The remaining sub-actions must also be compatible, but we don't need to
+ // special case them because the base class deals with them.
+ template <
+ typename R, typename... Args,
+ typename std::enable_if<
+ conjunction<
+ negation<std::is_convertible<
+ InitialAction,
+ OnceAction<void(InitialActionArgType<Args>...)>>>,
+ std::is_convertible<InitialAction,
+ Action<void(InitialActionArgType<Args>...)>>,
+ std::is_convertible<Base, OnceAction<R(Args...)>>>::value,
+ int>::type = 0>
+ operator OnceAction<R(Args...)>() && { // NOLINT
+ return DoAll(
+ Action<void(InitialActionArgType<Args>...)>(std::move(initial_action_)),
+ std::move(static_cast<Base&>(*this)));
+ }
+
+ // We support conversion to Action whenever both the initial action and the
+ // rest support conversion to Action.
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,
diff --git a/googlemock/test/gmock-actions_test.cc b/googlemock/test/gmock-actions_test.cc
index 59b9458..82c22c3 100644
--- a/googlemock/test/gmock-actions_test.cc
+++ b/googlemock/test/gmock-actions_test.cc
@@ -1477,6 +1477,54 @@ TEST(DoAll, SupportsTypeErasedActions) {
}
}
+// A DoAll action should be convertible to a OnceAction, even when its component
+// sub-actions are user-provided types that define only an Action conversion
+// operator. If they supposed being called more than once then they also support
+// being called at most once.
+TEST(DoAll, ConvertibleToOnceActionWithUserProvidedActionConversion) {
+ // Simplest case: only one sub-action.
+ struct CustomFinal final {
+ operator Action<int()>() { // NOLINT
+ return Return(17);
+ }
+
+ operator Action<int(int, char)>() { // NOLINT
+ return Return(19);
+ }
+ };
+
+ {
+ OnceAction<int()> action = DoAll(CustomFinal{});
+ EXPECT_EQ(17, std::move(action).Call());
+ }
+
+ {
+ OnceAction<int(int, char)> action = DoAll(CustomFinal{});
+ EXPECT_EQ(19, std::move(action).Call(0, 0));
+ }
+
+ // It should also work with multiple sub-actions.
+ struct CustomInitial final {
+ operator Action<void()>() { // NOLINT
+ return [] {};
+ }
+
+ operator Action<void(int, char)>() { // NOLINT
+ return [] {};
+ }
+ };
+
+ {
+ OnceAction<int()> action = DoAll(CustomInitial{}, CustomFinal{});
+ EXPECT_EQ(17, std::move(action).Call());
+ }
+
+ {
+ OnceAction<int(int, char)> action = DoAll(CustomInitial{}, CustomFinal{});
+ EXPECT_EQ(19, std::move(action).Call(0, 0));
+ }
+}
+
// Tests using WithArgs and with an action that takes 1 argument.
TEST(WithArgsTest, OneArg) {
Action<bool(double x, int n)> a = WithArgs<1>(Invoke(Unary)); // NOLINT