Document two surprises in DoAllAction::NonFinalType
.
PiperOrigin-RevId: 441589196 Change-Id: Ic3e483ca70d72261046bad464d817f9dfd4bec65
This commit is contained in:
parent
733f875989
commit
80600e56cc
@ -1295,6 +1295,51 @@ struct WithArgsAction {
|
||||
template <typename... Actions>
|
||||
struct DoAllAction {
|
||||
private:
|
||||
// The type of reference that should be provided to an initial action for a
|
||||
// mocked function parameter of type T.
|
||||
//
|
||||
// There are two quirks here:
|
||||
//
|
||||
// * Unlike most forwarding functions, we pass scalars through by value.
|
||||
// This isn't strictly necessary because an lvalue reference would work
|
||||
// fine too and be consistent with other non-reference types, but it's
|
||||
// perhaps less surprising.
|
||||
//
|
||||
// For example if the mocked function has signature void(int), then it
|
||||
// might seem surprising for the user's initial action to need to be
|
||||
// convertible to Action<void(const int&)>. This is perhaps less
|
||||
// surprising for a non-scalar type where there may be a performance
|
||||
// impact, or it might even be impossible, to pass by value.
|
||||
//
|
||||
// * More surprisingly, `const T&` is often not a const reference type.
|
||||
// By the reference collapsing rules in C++17 [dcl.ref]/6, if T refers to
|
||||
// U& or U&& for some non-scalar type U, then NonFinalType<T> is U&. In
|
||||
// other words, we may hand over a non-const reference.
|
||||
//
|
||||
// So for example, given some non-scalar type Obj we have the following
|
||||
// mappings:
|
||||
//
|
||||
// T NonFinalType<T>
|
||||
// ------- ---------------
|
||||
// Obj const Obj&
|
||||
// Obj& Obj&
|
||||
// Obj&& Obj&
|
||||
// const Obj const Obj&
|
||||
// const Obj& const Obj&
|
||||
// const Obj&& const Obj&
|
||||
//
|
||||
// In other words, the initial actions get a mutable view of an non-scalar
|
||||
// argument if and only if the mock function itself accepts a non-const
|
||||
// reference type. They are never given an rvalue reference to an
|
||||
// non-scalar type.
|
||||
//
|
||||
// This situation makes sense if you imagine use with a matcher that is
|
||||
// designed to write through a reference. For example, if the caller wants
|
||||
// to fill in a reference argument and then return a canned value:
|
||||
//
|
||||
// EXPECT_CALL(mock, Call)
|
||||
// .WillOnce(DoAll(SetArgReferee<0>(17), Return(19)));
|
||||
//
|
||||
template <typename T>
|
||||
using NonFinalType =
|
||||
typename std::conditional<std::is_scalar<T>::value, T, const T&>::type;
|
||||
|
@ -1192,6 +1192,89 @@ TEST(AssignTest, CompatibleTypes) {
|
||||
EXPECT_DOUBLE_EQ(5, x);
|
||||
}
|
||||
|
||||
// DoAll should never provide rvalue references to the initial actions. If the
|
||||
// mock action itself accepts an rvalue reference or a non-scalar object by
|
||||
// value then the final action should receive an rvalue reference, but initial
|
||||
// actions should receive only lvalue references.
|
||||
TEST(DoAll, ProvidesLvalueReferencesToInitialActions) {
|
||||
struct Obj {};
|
||||
|
||||
// Mock action accepts by value: the initial action should be fed a const
|
||||
// lvalue reference, and the final action an rvalue reference.
|
||||
{
|
||||
struct InitialAction {
|
||||
void operator()(Obj&) const { FAIL() << "Unexpected call"; }
|
||||
void operator()(const Obj&) const {}
|
||||
void operator()(Obj&&) const { FAIL() << "Unexpected call"; }
|
||||
void operator()(const Obj&&) const { FAIL() << "Unexpected call"; }
|
||||
};
|
||||
|
||||
MockFunction<void(Obj)> mock;
|
||||
EXPECT_CALL(mock, Call)
|
||||
.WillOnce(DoAll(InitialAction{}, InitialAction{}, [](Obj&&) {}))
|
||||
.WillRepeatedly(DoAll(InitialAction{}, InitialAction{}, [](Obj&&) {}));
|
||||
|
||||
mock.AsStdFunction()(Obj{});
|
||||
mock.AsStdFunction()(Obj{});
|
||||
}
|
||||
|
||||
// Mock action accepts by const lvalue reference: both actions should receive
|
||||
// a const lvalue reference.
|
||||
{
|
||||
struct InitialAction {
|
||||
void operator()(Obj&) const { FAIL() << "Unexpected call"; }
|
||||
void operator()(const Obj&) const {}
|
||||
void operator()(Obj&&) const { FAIL() << "Unexpected call"; }
|
||||
void operator()(const Obj&&) const { FAIL() << "Unexpected call"; }
|
||||
};
|
||||
|
||||
MockFunction<void(const Obj&)> mock;
|
||||
EXPECT_CALL(mock, Call)
|
||||
.WillOnce(DoAll(InitialAction{}, InitialAction{}, [](const Obj&) {}))
|
||||
.WillRepeatedly(
|
||||
DoAll(InitialAction{}, InitialAction{}, [](const Obj&) {}));
|
||||
|
||||
mock.AsStdFunction()(Obj{});
|
||||
mock.AsStdFunction()(Obj{});
|
||||
}
|
||||
|
||||
// Mock action accepts by non-const lvalue reference: both actions should get
|
||||
// a non-const lvalue reference if they want them.
|
||||
{
|
||||
struct InitialAction {
|
||||
void operator()(Obj&) const {}
|
||||
void operator()(Obj&&) const { FAIL() << "Unexpected call"; }
|
||||
};
|
||||
|
||||
MockFunction<void(Obj&)> mock;
|
||||
EXPECT_CALL(mock, Call)
|
||||
.WillOnce(DoAll(InitialAction{}, InitialAction{}, [](Obj&) {}))
|
||||
.WillRepeatedly(DoAll(InitialAction{}, InitialAction{}, [](Obj&) {}));
|
||||
|
||||
Obj obj;
|
||||
mock.AsStdFunction()(obj);
|
||||
mock.AsStdFunction()(obj);
|
||||
}
|
||||
|
||||
// Mock action accepts by rvalue reference: the initial actions should receive
|
||||
// a non-const lvalue reference if it wants it, and the final action an rvalue
|
||||
// reference.
|
||||
{
|
||||
struct InitialAction {
|
||||
void operator()(Obj&) const {}
|
||||
void operator()(Obj&&) const { FAIL() << "Unexpected call"; }
|
||||
};
|
||||
|
||||
MockFunction<void(Obj &&)> mock;
|
||||
EXPECT_CALL(mock, Call)
|
||||
.WillOnce(DoAll(InitialAction{}, InitialAction{}, [](Obj&&) {}))
|
||||
.WillRepeatedly(DoAll(InitialAction{}, InitialAction{}, [](Obj&&) {}));
|
||||
|
||||
mock.AsStdFunction()(Obj{});
|
||||
mock.AsStdFunction()(Obj{});
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
Loading…
Reference in New Issue
Block a user