Commit 0498660e authored by Aaron Jacobs's avatar Aaron Jacobs Committed by Copybara-Service
Browse files

Support move-only and &&-qualified actions in DoAll.

This is necessary for generic support of these actions, since `DoAll` is a
frequently-used action wrapper.

PiperOrigin-RevId: 444561964
Change-Id: I02edb55e35ab4207fbd71e371255a319c8253136
parent b53547bf
This diff is collapsed.
...@@ -878,9 +878,15 @@ class GTEST_API_ ExpectationBase { ...@@ -878,9 +878,15 @@ class GTEST_API_ ExpectationBase {
mutable Mutex mutex_; // Protects action_count_checked_. mutable Mutex mutex_; // Protects action_count_checked_.
}; // class ExpectationBase }; // class ExpectationBase
// Implements an expectation for the given function type.
template <typename F> template <typename F>
class TypedExpectation : public ExpectationBase { class TypedExpectation;
// Implements an expectation for the given function type.
template <typename R, typename... Args>
class TypedExpectation<R(Args...)> : public ExpectationBase {
private:
using F = R(Args...);
public: public:
typedef typename Function<F>::ArgumentTuple ArgumentTuple; typedef typename Function<F>::ArgumentTuple ArgumentTuple;
typedef typename Function<F>::ArgumentMatcherTuple ArgumentMatcherTuple; typedef typename Function<F>::ArgumentMatcherTuple ArgumentMatcherTuple;
...@@ -993,15 +999,30 @@ class TypedExpectation : public ExpectationBase { ...@@ -993,15 +999,30 @@ class TypedExpectation : public ExpectationBase {
return After(s1, s2, s3, s4).After(s5); return After(s1, s2, s3, s4).After(s5);
} }
// Implements the .WillOnce() clause for copyable actions. // Preferred, type-safe overload: consume anything that can be directly
// converted to a OnceAction, except for Action<F> objects themselves.
TypedExpectation& WillOnce(OnceAction<F> once_action) { TypedExpectation& WillOnce(OnceAction<F> once_action) {
// Call the overload below, smuggling the OnceAction as a copyable callable.
// We know this is safe because a WillOnce action will not be called more
// than once.
return WillOnce(Action<F>(ActionAdaptor{
std::make_shared<OnceAction<F>>(std::move(once_action)),
}));
}
// Fallback overload: accept Action<F> objects and those actions that define
// `operator Action<F>` but not `operator OnceAction<F>`.
//
// This is templated in order to cause the overload above to be preferred
// when the input is convertible to either type.
template <int&... ExplicitArgumentBarrier, typename = void>
TypedExpectation& WillOnce(Action<F> action) {
ExpectSpecProperty(last_clause_ <= kWillOnce, ExpectSpecProperty(last_clause_ <= kWillOnce,
".WillOnce() cannot appear after " ".WillOnce() cannot appear after "
".WillRepeatedly() or .RetiresOnSaturation()."); ".WillRepeatedly() or .RetiresOnSaturation().");
last_clause_ = kWillOnce; last_clause_ = kWillOnce;
untyped_actions_.push_back( untyped_actions_.push_back(new Action<F>(std::move(action)));
new Action<F>(std::move(once_action).ReleaseAction()));
if (!cardinality_specified()) { if (!cardinality_specified()) {
set_cardinality(Exactly(static_cast<int>(untyped_actions_.size()))); set_cardinality(Exactly(static_cast<int>(untyped_actions_.size())));
...@@ -1074,6 +1095,16 @@ class TypedExpectation : public ExpectationBase { ...@@ -1074,6 +1095,16 @@ class TypedExpectation : public ExpectationBase {
template <typename Function> template <typename Function>
friend class FunctionMocker; friend class FunctionMocker;
// An adaptor that turns a OneAction<F> into something compatible with
// Action<F>. Must be called at most once.
struct ActionAdaptor {
std::shared_ptr<OnceAction<R(Args...)>> once_action;
R operator()(Args&&... args) const {
return std::move(*once_action).Call(std::forward<Args>(args)...);
}
};
// Returns an Expectation object that references and co-owns this // Returns an Expectation object that references and co-owns this
// expectation. // expectation.
Expectation GetHandle() override { return owner_->GetHandleOf(this); } Expectation GetHandle() override { return owner_->GetHandleOf(this); }
......
...@@ -31,10 +31,12 @@ ...@@ -31,10 +31,12 @@
// //
// This file tests the built-in actions. // This file tests the built-in actions.
// Silence C4100 (unreferenced formal parameter) for MSVC // Silence C4100 (unreferenced formal parameter) and C4503 (decorated name
// length exceeded) for MSVC.
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(push) #pragma warning(push)
#pragma warning(disable : 4100) #pragma warning(disable : 4100)
#pragma warning(disable : 4503)
#if _MSC_VER == 1900 #if _MSC_VER == 1900
// and silence C4800 (C4800: 'int *const ': forcing value // and silence C4800 (C4800: 'int *const ': forcing value
// to bool 'true' or 'false') for MSVC 15 // to bool 'true' or 'false') for MSVC 15
...@@ -1193,6 +1195,21 @@ TEST(AssignTest, CompatibleTypes) { ...@@ -1193,6 +1195,21 @@ TEST(AssignTest, CompatibleTypes) {
EXPECT_DOUBLE_EQ(5, x); EXPECT_DOUBLE_EQ(5, x);
} }
// DoAll should support &&-qualified actions when used with WillOnce.
TEST(DoAll, SupportsRefQualifiedActions) {
struct InitialAction {
void operator()(const int arg) && { EXPECT_EQ(17, arg); }
};
struct FinalAction {
int operator()() && { return 19; }
};
MockFunction<int(int)> mock;
EXPECT_CALL(mock, Call).WillOnce(DoAll(InitialAction{}, FinalAction{}));
EXPECT_EQ(19, mock.AsStdFunction()(17));
}
// DoAll should never provide rvalue references to the initial actions. If the // 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 // 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 // value then the final action should receive an rvalue reference, but initial
...@@ -1274,6 +1291,62 @@ TEST(DoAll, ProvidesLvalueReferencesToInitialActions) { ...@@ -1274,6 +1291,62 @@ TEST(DoAll, ProvidesLvalueReferencesToInitialActions) {
mock.AsStdFunction()(Obj{}); mock.AsStdFunction()(Obj{});
mock.AsStdFunction()(Obj{}); mock.AsStdFunction()(Obj{});
} }
// &&-qualified initial actions should also be allowed with WillOnce.
{
struct InitialAction {
void operator()(Obj&) && {}
};
MockFunction<void(Obj&)> mock;
EXPECT_CALL(mock, Call)
.WillOnce(DoAll(InitialAction{}, InitialAction{}, [](Obj&) {}));
Obj obj;
mock.AsStdFunction()(obj);
}
{
struct InitialAction {
void operator()(Obj&) && {}
};
MockFunction<void(Obj &&)> mock;
EXPECT_CALL(mock, Call)
.WillOnce(DoAll(InitialAction{}, InitialAction{}, [](Obj&&) {}));
mock.AsStdFunction()(Obj{});
}
}
// DoAll should support being used with type-erased Action objects, both through
// WillOnce and WillRepeatedly.
TEST(DoAll, SupportsTypeErasedActions) {
// With only type-erased actions.
const Action<void()> initial_action = [] {};
const Action<int()> final_action = [] { return 17; };
MockFunction<int()> mock;
EXPECT_CALL(mock, Call)
.WillOnce(DoAll(initial_action, initial_action, final_action))
.WillRepeatedly(DoAll(initial_action, initial_action, final_action));
EXPECT_EQ(17, mock.AsStdFunction()());
// With &&-qualified and move-only final action.
{
struct FinalAction {
FinalAction() = default;
FinalAction(FinalAction&&) = default;
int operator()() && { return 17; }
};
EXPECT_CALL(mock, Call)
.WillOnce(DoAll(initial_action, initial_action, FinalAction{}));
EXPECT_EQ(17, mock.AsStdFunction()());
}
} }
// Tests using WithArgs and with an action that takes 1 argument. // Tests using WithArgs and with an action that takes 1 argument.
...@@ -1793,6 +1866,31 @@ TEST(MockMethodTest, ActionSwallowsAllArguments) { ...@@ -1793,6 +1866,31 @@ TEST(MockMethodTest, ActionSwallowsAllArguments) {
EXPECT_EQ(17, mock.AsStdFunction()(0)); EXPECT_EQ(17, mock.AsStdFunction()(0));
} }
struct ActionWithTemplatedConversionOperators {
template <typename... Args>
operator internal::OnceAction<int(Args...)>() && { // NOLINT
return [] { return 17; };
}
template <typename... Args>
operator Action<int(Args...)>() const { // NOLINT
return [] { return 19; };
}
};
// It should be fine to hand both WillOnce and WillRepeatedly a function that
// defines templated conversion operators to OnceAction and Action. WillOnce
// should prefer the OnceAction version.
TEST(MockMethodTest, ActionHasTemplatedConversionOperators) {
MockFunction<int()> mock;
EXPECT_CALL(mock, Call)
.WillOnce(ActionWithTemplatedConversionOperators{})
.WillRepeatedly(ActionWithTemplatedConversionOperators{});
EXPECT_EQ(17, mock.AsStdFunction()());
EXPECT_EQ(19, mock.AsStdFunction()());
}
// Tests for std::function based action. // Tests for std::function based action.
int Add(int val, int& ref, int* ptr) { // NOLINT int Add(int val, int& ref, int* ptr) { // NOLINT
...@@ -1919,9 +2017,3 @@ TEST(ActionMacro, LargeArity) { ...@@ -1919,9 +2017,3 @@ TEST(ActionMacro, LargeArity) {
} // namespace } // namespace
} // namespace testing } // namespace testing
#ifdef _MSC_VER
#if _MSC_VER == 1900
#pragma warning(pop)
#endif
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment