diff options
author | Aaron Jacobs <jacobsa@google.com> | 2022-05-13 00:54:39 (GMT) |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2022-05-13 00:55:11 (GMT) |
commit | 5126f7166109666a9c0534021fb1a3038659494c (patch) | |
tree | d4c0f3887c7f4899832ff4550291a5b5c1042623 | |
parent | 8a011b8a38b7748d1a3eaf1e99c8b859d754d060 (diff) | |
download | googletest-5126f7166109666a9c0534021fb1a3038659494c.zip googletest-5126f7166109666a9c0534021fb1a3038659494c.tar.gz googletest-5126f7166109666a9c0534021fb1a3038659494c.tar.bz2 |
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
-rw-r--r-- | googlemock/include/gmock/gmock-actions.h | 56 | ||||
-rw-r--r-- | googlemock/test/gmock-actions_test.cc | 56 |
2 files changed, 83 insertions, 29 deletions
diff --git a/googlemock/include/gmock/gmock-actions.h b/googlemock/include/gmock/gmock-actions.h index df2effd..e5981f9 100644 --- a/googlemock/include/gmock/gmock-actions.h +++ b/googlemock/include/gmock/gmock-actions.h @@ -881,6 +881,17 @@ class ReturnAction final { 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, + 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<const R&, U>, // @@ -894,9 +905,17 @@ class ReturnAction final { template <typename U> class Impl final { 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)) {} - U operator()() const { return state_->value; } + U operator()() && { return std::move(state_->value); } + U operator()() const& { return state_->value; } private: // 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. 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 // 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 @@ -1740,18 +1771,19 @@ internal::WithArgsAction<typename std::decay<InnerAction>::type> WithoutArgs( // Creates an action that returns a value. // -// R must be copy-constructible. The returned type can be used as an -// Action<U(Args...)> for any type U where all of the following are true: +// The returned type can be used with a mock function returning a non-void, +// non-reference type U as follows: // -// * U is not void. -// * U is not a reference type. (Use ReturnRef instead.) -// * U is copy-constructible. -// * const R& is convertible to U. +// * If R is convertible to U and U is move-constructible, then the action can +// be used with WillOnce. // -// The Action<U(Args)...> object contains the R value from which the U return -// value is constructed (a 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 be a +// * If const R& is convertible to U and U is copy-constructible, then the +// action can be used with both WillOnce and WillRepeatedly. +// +// 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: // // // 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); } +// DEPRECATED: use Return(x) directly with WillOnce. +// // Modifies the parent action (a Return() action) to perform a move of the // argument instead of a copy. // Return(ByMove()) actions can only be executed once and will assert this diff --git a/googlemock/test/gmock-actions_test.cc b/googlemock/test/gmock-actions_test.cc index 414c615..9aa9f81 100644 --- a/googlemock/test/gmock-actions_test.cc +++ b/googlemock/test/gmock-actions_test.cc @@ -47,6 +47,7 @@ #include "gmock/gmock-actions.h" #include <algorithm> +#include <functional> #include <iterator> #include <memory> #include <string> @@ -709,6 +710,24 @@ TEST(ReturnTest, PrefersConversionOperator) { 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 // 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 @@ -731,32 +750,33 @@ TEST(ReturnTest, ConversionRequiresMutableLvalueReference) { // It shouldn't be possible to use the result of Return(std::string) in a // 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())); static_assert(!std::is_convertible<RA, Action<S()>>::value, ""); +#ifndef _MSC_VER static_assert(!std::is_convertible<RA, OnceAction<S()>>::value, ""); +#endif } -// Return(x) should not be usable with a mock function result type that's -// implicitly convertible from decltype(x) but requires an rvalue reference to -// the input. We don't yet support handing over the value for consumption. -TEST(ReturnTest, ConversionRequiresRvalueReference) { - // Set up a type that is implicitly convertible from std::string&& and - // `const std::string&&`, but not `const std::string&`. - struct S { - S(std::string&&) {} // NOLINT - S(const std::string&&) {} // NOLINT - }; - - static_assert(std::is_convertible<std::string, S>::value, ""); - static_assert(!std::is_convertible<const std::string&, S>::value, ""); +TEST(ReturnTest, MoveOnlyResultType) { + // Return should support move-only result types when used with WillOnce. + { + MockFunction<std::unique_ptr<int>()> mock; + EXPECT_CALL(mock, Call) + // NOLINTNEXTLINE + .WillOnce(Return(std::unique_ptr<int>(new int(17)))); - // It shouldn't be possible to use the result of Return(std::string) in a - // context where an S is needed. - using RA = decltype(Return(std::string())); + EXPECT_THAT(mock.AsStdFunction()(), Pointee(17)); + } - static_assert(!std::is_convertible<RA, Action<S()>>::value, ""); - static_assert(!std::is_convertible<RA, OnceAction<S()>>::value, ""); + // The result of Return should not be convertible to Action (so it can't be + // used with WillRepeatedly). + static_assert(!std::is_convertible<decltype(Return(std::unique_ptr<int>())), + Action<std::unique_ptr<int>()>>::value, + ""); } // Tests that Return(v) is covaraint. |