Skip to content

Commit 3527883

Browse files
jacobsacopybara-github
authored andcommitted
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
1 parent 57e107a commit 3527883

File tree

2 files changed

+98
-12
lines changed

2 files changed

+98
-12
lines changed

googlemock/include/gmock/gmock-actions.h

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1493,6 +1493,7 @@ class DoAllAction<FinalAction> {
14931493
// providing a call operator because even with a particular set of arguments
14941494
// they don't have a fixed return type.
14951495

1496+
// We support conversion to OnceAction whenever the sub-action does.
14961497
template <typename R, typename... Args,
14971498
typename std::enable_if<
14981499
std::is_convertible<FinalAction, OnceAction<R(Args...)>>::value,
@@ -1501,6 +1502,21 @@ class DoAllAction<FinalAction> {
15011502
return std::move(final_action_);
15021503
}
15031504

1505+
// We also support conversion to OnceAction whenever the sub-action supports
1506+
// conversion to Action (since any Action can also be a OnceAction).
1507+
template <
1508+
typename R, typename... Args,
1509+
typename std::enable_if<
1510+
conjunction<
1511+
negation<
1512+
std::is_convertible<FinalAction, OnceAction<R(Args...)>>>,
1513+
std::is_convertible<FinalAction, Action<R(Args...)>>>::value,
1514+
int>::type = 0>
1515+
operator OnceAction<R(Args...)>() && { // NOLINT
1516+
return Action<R(Args...)>(std::move(final_action_));
1517+
}
1518+
1519+
// We support conversion to Action whenever the sub-action does.
15041520
template <
15051521
typename R, typename... Args,
15061522
typename std::enable_if<
@@ -1580,16 +1596,16 @@ class DoAllAction<InitialAction, OtherActions...>
15801596
: Base({}, std::forward<U>(other_actions)...),
15811597
initial_action_(std::forward<T>(initial_action)) {}
15821598

1583-
template <typename R, typename... Args,
1584-
typename std::enable_if<
1585-
conjunction<
1586-
// Both the initial action and the rest must support
1587-
// conversion to OnceAction.
1588-
std::is_convertible<
1589-
InitialAction,
1590-
OnceAction<void(InitialActionArgType<Args>...)>>,
1591-
std::is_convertible<Base, OnceAction<R(Args...)>>>::value,
1592-
int>::type = 0>
1599+
// We support conversion to OnceAction whenever both the initial action and
1600+
// the rest support conversion to OnceAction.
1601+
template <
1602+
typename R, typename... Args,
1603+
typename std::enable_if<
1604+
conjunction<std::is_convertible<
1605+
InitialAction,
1606+
OnceAction<void(InitialActionArgType<Args>...)>>,
1607+
std::is_convertible<Base, OnceAction<R(Args...)>>>::value,
1608+
int>::type = 0>
15931609
operator OnceAction<R(Args...)>() && { // NOLINT
15941610
// Return an action that first calls the initial action with arguments
15951611
// filtered through InitialActionArgType, then forwards arguments directly
@@ -1612,12 +1628,34 @@ class DoAllAction<InitialAction, OtherActions...>
16121628
};
16131629
}
16141630

1631+
// We also support conversion to OnceAction whenever the initial action
1632+
// supports conversion to Action (since any Action can also be a OnceAction).
1633+
//
1634+
// The remaining sub-actions must also be compatible, but we don't need to
1635+
// special case them because the base class deals with them.
1636+
template <
1637+
typename R, typename... Args,
1638+
typename std::enable_if<
1639+
conjunction<
1640+
negation<std::is_convertible<
1641+
InitialAction,
1642+
OnceAction<void(InitialActionArgType<Args>...)>>>,
1643+
std::is_convertible<InitialAction,
1644+
Action<void(InitialActionArgType<Args>...)>>,
1645+
std::is_convertible<Base, OnceAction<R(Args...)>>>::value,
1646+
int>::type = 0>
1647+
operator OnceAction<R(Args...)>() && { // NOLINT
1648+
return DoAll(
1649+
Action<void(InitialActionArgType<Args>...)>(std::move(initial_action_)),
1650+
std::move(static_cast<Base&>(*this)));
1651+
}
1652+
1653+
// We support conversion to Action whenever both the initial action and the
1654+
// rest support conversion to Action.
16151655
template <
16161656
typename R, typename... Args,
16171657
typename std::enable_if<
16181658
conjunction<
1619-
// Both the initial action and the rest must support conversion to
1620-
// Action.
16211659
std::is_convertible<const InitialAction&,
16221660
Action<void(InitialActionArgType<Args>...)>>,
16231661
std::is_convertible<const Base&, Action<R(Args...)>>>::value,

googlemock/test/gmock-actions_test.cc

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1477,6 +1477,54 @@ TEST(DoAll, SupportsTypeErasedActions) {
14771477
}
14781478
}
14791479

1480+
// A DoAll action should be convertible to a OnceAction, even when its component
1481+
// sub-actions are user-provided types that define only an Action conversion
1482+
// operator. If they supposed being called more than once then they also support
1483+
// being called at most once.
1484+
TEST(DoAll, ConvertibleToOnceActionWithUserProvidedActionConversion) {
1485+
// Simplest case: only one sub-action.
1486+
struct CustomFinal final {
1487+
operator Action<int()>() { // NOLINT
1488+
return Return(17);
1489+
}
1490+
1491+
operator Action<int(int, char)>() { // NOLINT
1492+
return Return(19);
1493+
}
1494+
};
1495+
1496+
{
1497+
OnceAction<int()> action = DoAll(CustomFinal{});
1498+
EXPECT_EQ(17, std::move(action).Call());
1499+
}
1500+
1501+
{
1502+
OnceAction<int(int, char)> action = DoAll(CustomFinal{});
1503+
EXPECT_EQ(19, std::move(action).Call(0, 0));
1504+
}
1505+
1506+
// It should also work with multiple sub-actions.
1507+
struct CustomInitial final {
1508+
operator Action<void()>() { // NOLINT
1509+
return [] {};
1510+
}
1511+
1512+
operator Action<void(int, char)>() { // NOLINT
1513+
return [] {};
1514+
}
1515+
};
1516+
1517+
{
1518+
OnceAction<int()> action = DoAll(CustomInitial{}, CustomFinal{});
1519+
EXPECT_EQ(17, std::move(action).Call());
1520+
}
1521+
1522+
{
1523+
OnceAction<int(int, char)> action = DoAll(CustomInitial{}, CustomFinal{});
1524+
EXPECT_EQ(19, std::move(action).Call(0, 0));
1525+
}
1526+
}
1527+
14801528
// Tests using WithArgs and with an action that takes 1 argument.
14811529
TEST(WithArgsTest, OneArg) {
14821530
Action<bool(double x, int n)> a = WithArgs<1>(Invoke(Unary)); // NOLINT

0 commit comments

Comments
 (0)