// Copyright 2019 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BASE_TEST_GMOCK_MOVE_SUPPORT_H_ #define BASE_TEST_GMOCK_MOVE_SUPPORT_H_ #include #include #include // Moves the `I`th argument to `out`. Analogous to `testing::SaveArg()`, which // copies instead. // // Example: // // std::unique_ptr result; // EXPECT_CALL(mocked_object, Method).WillOnce(MoveArg<0>(&result)); // mocked_object.Method(std::make_unique(123)); // EXPECT_THAT(result, Pointee(Eq(123))); // // Important: it is not possible to use multiple `MoveArg()` actions in a single // `DoAll()`: per the googlemock documentation, all but the last action receive // a read-only view of the arguments. Allowing an intermediate action to consume // the arguments would leave the original arguments in an unspecified state for // invoking subsequent actions, which is dubious. // // A simple workaround is to use `Invoke()` with a lambda instead, e.g. // // std::unique_ptr int_result; // std::unique_ptr double_result; // EXPECT_CALL(mocked_object, Method).WillOnce(Invoke( // [&] (auto&& arg1, auto&& arg2) { // int_result = std::move(arg1); // double_result = std::move(arg2); // return 42; // })); // EXPECT_EQ(42, mocked_object.Method(std::make_unique(123), // std::make_unique(0.5))); // EXPECT_THAT(int_result, Pointee(Eq(123))); // EXPECT_THAT(double_result, Pointee(DoubleEq(0.5))); template auto MoveArg(T* out) { return [out](auto&&... args) { *out = std::move(std::get(std::tie(args...))); }; } // Moves the `I`th argument to `*out` and returns `return_value`. // // This is a convenience helper for code that wants to write the following: // // DoAll(MoveArg(&saved_arg), Return(value)) // // The above is not actually possible to write because: // - `DoAll()` requires that its final action has a return type that matches the // mocked call's return type. So `Return()` must be last. // - But any actions before the last receive a read-only view of the arguments. // So `MoveArg()` receives a read-only view and cannot move out of it. // // Example: // // std::unique_ptr result; // EXPECT_CALL(mocked_object, Method).WillOnce( // MoveArgAndReturn<0>(&result, true)); // EXPECT_TRUE(mocked_object.Method(std::make_unique(123))); // EXPECT_THAT(int_result, Pointee(Eq(123))); // template auto MoveArgAndReturn(T1* out, T2&& return_value) { return [out, value = std::forward(return_value)](auto&&... args) mutable { *out = std::move(std::get(std::tie(args...))); return std::forward(value); }; } #endif // BASE_TEST_GMOCK_MOVE_SUPPORT_H_