gmock-actions: add support for move-only values to Return.

`Return(x)` can now be used directly with `WillOnce` (the only place it makes
sense in the type system), without using `ByMove`.

PiperOrigin-RevId: 448380066
Change-Id: Ia71cc60ccbc3b99720662731a2d309735a5ce7c8
This commit is contained in:
Aaron Jacobs 2022-05-12 17:54:39 -07:00 committed by Copybara-Service
parent 8a011b8a38
commit 5126f71661
2 changed files with 83 additions and 29 deletions

View File

@ -878,6 +878,17 @@ class ReturnAction final {
public: public:
explicit ReturnAction(R value) : value_(std::move(value)) {} explicit ReturnAction(R value) : value_(std::move(value)) {}
template <typename U, typename... Args,
typename = typename std::enable_if<conjunction<
// See the requirements documented on Return.
negation<std::is_same<void, U>>, //
negation<std::is_reference<U>>, //
std::is_convertible<R, U>, //
std::is_move_constructible<U>>::value>::type>
operator OnceAction<U(Args...)>() && { // NOLINT
return Impl<U>(std::move(value_));
}
template <typename U, typename... Args, template <typename U, typename... Args,
typename = typename std::enable_if<conjunction< typename = typename std::enable_if<conjunction<
// See the requirements documented on Return. // See the requirements documented on Return.
@ -894,9 +905,17 @@ class ReturnAction final {
template <typename U> template <typename U>
class Impl final { class Impl final {
public: public:
// The constructor used when the return value is allowed to move from the
// input value (i.e. we are converting to OnceAction).
explicit Impl(R&& input_value)
: state_(new State(std::move(input_value))) {}
// The constructor used when the return value is not allowed to move from
// the input value (i.e. we are converting to Action).
explicit Impl(const R& input_value) : state_(new State(input_value)) {} explicit Impl(const R& input_value) : state_(new State(input_value)) {}
U operator()() const { return state_->value; } U operator()() && { return std::move(state_->value); }
U operator()() const& { return state_->value; }
private: private:
// We put our state on the heap so that the compiler-generated copy/move // We put our state on the heap so that the compiler-generated copy/move
@ -923,6 +942,18 @@ class ReturnAction final {
// explicit constructor from R. // explicit constructor from R.
value(ImplicitCast_<U>(internal::as_const(input_value))) {} value(ImplicitCast_<U>(internal::as_const(input_value))) {}
// As above, but for the case where we're moving from the ReturnAction
// object because it's being used as a OnceAction.
explicit State(R&& input_value_in)
: input_value(std::move(input_value_in)),
// For the same reason as above we make an implicit conversion to U
// before initializing the value.
//
// Unlike above we provide the input value as an rvalue to the
// implicit conversion because this is a OnceAction: it's fine if it
// wants to consume the input value.
value(ImplicitCast_<U>(std::move(input_value))) {}
// A copy of the value originally provided by the user. We retain this in // A copy of the value originally provided by the user. We retain this in
// addition to the value of the mock function's result type below in case // addition to the value of the mock function's result type below in case
// the latter is a reference-like type. See the std::string_view example // the latter is a reference-like type. See the std::string_view example
@ -1740,18 +1771,19 @@ internal::WithArgsAction<typename std::decay<InnerAction>::type> WithoutArgs(
// Creates an action that returns a value. // Creates an action that returns a value.
// //
// R must be copy-constructible. The returned type can be used as an // The returned type can be used with a mock function returning a non-void,
// Action<U(Args...)> for any type U where all of the following are true: // non-reference type U as follows:
// //
// * U is not void. // * If R is convertible to U and U is move-constructible, then the action can
// * U is not a reference type. (Use ReturnRef instead.) // be used with WillOnce.
// * U is copy-constructible.
// * const R& is convertible to U.
// //
// The Action<U(Args)...> object contains the R value from which the U return // * If const R& is convertible to U and U is copy-constructible, then the
// value is constructed (a copy of the argument to Return). This means that the // action can be used with both WillOnce and WillRepeatedly.
// R value will survive at least until the mock object's expectations are //
// cleared or the mock object is destroyed, meaning that U can be a // The mock expectation contains the R value from which the U return value is
// constructed (a move/copy of the argument to Return). This means that the R
// value will survive at least until the mock object's expectations are cleared
// or the mock object is destroyed, meaning that U can safely be a
// reference-like type such as std::string_view: // reference-like type such as std::string_view:
// //
// // The mock function returns a view of a copy of the string fed to // // The mock function returns a view of a copy of the string fed to
@ -1794,6 +1826,8 @@ inline internal::ReturnRefOfCopyAction<R> ReturnRefOfCopy(const R& x) {
return internal::ReturnRefOfCopyAction<R>(x); return internal::ReturnRefOfCopyAction<R>(x);
} }
// DEPRECATED: use Return(x) directly with WillOnce.
//
// Modifies the parent action (a Return() action) to perform a move of the // Modifies the parent action (a Return() action) to perform a move of the
// argument instead of a copy. // argument instead of a copy.
// Return(ByMove()) actions can only be executed once and will assert this // Return(ByMove()) actions can only be executed once and will assert this

View File

@ -47,6 +47,7 @@
#include "gmock/gmock-actions.h" #include "gmock/gmock-actions.h"
#include <algorithm> #include <algorithm>
#include <functional>
#include <iterator> #include <iterator>
#include <memory> #include <memory>
#include <string> #include <string>
@ -709,6 +710,24 @@ TEST(ReturnTest, PrefersConversionOperator) {
EXPECT_THAT(mock.AsStdFunction()(), Field(&Out::x, 19)); EXPECT_THAT(mock.AsStdFunction()(), Field(&Out::x, 19));
} }
// It should be possible to use Return(R) with a mock function result type U
// that is convertible from const R& but *not* R (such as
// std::reference_wrapper). This should work for both WillOnce and
// WillRepeatedly.
TEST(ReturnTest, ConversionRequiresConstLvalueReference) {
using R = int;
using U = std::reference_wrapper<const int>;
static_assert(std::is_convertible<const R&, U>::value, "");
static_assert(!std::is_convertible<R, U>::value, "");
MockFunction<U()> mock;
EXPECT_CALL(mock, Call).WillOnce(Return(17)).WillRepeatedly(Return(19));
EXPECT_EQ(17, mock.AsStdFunction()());
EXPECT_EQ(19, mock.AsStdFunction()());
}
// Return(x) should not be usable with a mock function result type that's // Return(x) should not be usable with a mock function result type that's
// implicitly convertible from decltype(x) but requires a non-const lvalue // implicitly convertible from decltype(x) but requires a non-const lvalue
// reference to the input. It doesn't make sense for the conversion operator to // reference to the input. It doesn't make sense for the conversion operator to
@ -731,32 +750,33 @@ TEST(ReturnTest, ConversionRequiresMutableLvalueReference) {
// It shouldn't be possible to use the result of Return(std::string) in a // It shouldn't be possible to use the result of Return(std::string) in a
// context where an S is needed. // context where an S is needed.
//
// Here too we disable the assertion for MSVC, since its incorrect
// implementation of is_convertible causes our SFINAE to be wrong.
using RA = decltype(Return(std::string())); using RA = decltype(Return(std::string()));
static_assert(!std::is_convertible<RA, Action<S()>>::value, ""); static_assert(!std::is_convertible<RA, Action<S()>>::value, "");
#ifndef _MSC_VER
static_assert(!std::is_convertible<RA, OnceAction<S()>>::value, ""); static_assert(!std::is_convertible<RA, OnceAction<S()>>::value, "");
#endif
} }
// Return(x) should not be usable with a mock function result type that's TEST(ReturnTest, MoveOnlyResultType) {
// implicitly convertible from decltype(x) but requires an rvalue reference to // Return should support move-only result types when used with WillOnce.
// the input. We don't yet support handing over the value for consumption. {
TEST(ReturnTest, ConversionRequiresRvalueReference) { MockFunction<std::unique_ptr<int>()> mock;
// Set up a type that is implicitly convertible from std::string&& and EXPECT_CALL(mock, Call)
// `const std::string&&`, but not `const std::string&`. // NOLINTNEXTLINE
struct S { .WillOnce(Return(std::unique_ptr<int>(new int(17))));
S(std::string&&) {} // NOLINT
S(const std::string&&) {} // NOLINT
};
static_assert(std::is_convertible<std::string, S>::value, ""); EXPECT_THAT(mock.AsStdFunction()(), Pointee(17));
static_assert(!std::is_convertible<const std::string&, S>::value, ""); }
// It shouldn't be possible to use the result of Return(std::string) in a // The result of Return should not be convertible to Action (so it can't be
// context where an S is needed. // used with WillRepeatedly).
using RA = decltype(Return(std::string())); static_assert(!std::is_convertible<decltype(Return(std::unique_ptr<int>())),
Action<std::unique_ptr<int>()>>::value,
static_assert(!std::is_convertible<RA, Action<S()>>::value, ""); "");
static_assert(!std::is_convertible<RA, OnceAction<S()>>::value, "");
} }
// Tests that Return(v) is covaraint. // Tests that Return(v) is covaraint.