xref: /aosp_15_r20/external/cronet/base/test/gmock_move_support.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef BASE_TEST_GMOCK_MOVE_SUPPORT_H_
6 #define BASE_TEST_GMOCK_MOVE_SUPPORT_H_
7 
8 #include <cstddef>
9 #include <tuple>
10 #include <utility>
11 
12 // Moves the `I`th argument to `out`. Analogous to `testing::SaveArg()`, which
13 // copies instead.
14 //
15 // Example:
16 //
17 //   std::unique_ptr<int> result;
18 //   EXPECT_CALL(mocked_object, Method).WillOnce(MoveArg<0>(&result));
19 //   mocked_object.Method(std::make_unique<int>(123));
20 //   EXPECT_THAT(result, Pointee(Eq(123)));
21 //
22 // Important: it is not possible to use multiple `MoveArg()` actions in a single
23 // `DoAll()`: per the googlemock documentation, all but the last action receive
24 // a read-only view of the arguments. Allowing an intermediate action to consume
25 // the arguments would leave the original arguments in an unspecified state for
26 // invoking subsequent actions, which is dubious.
27 //
28 // A simple workaround is to use `Invoke()` with a lambda instead, e.g.
29 //
30 //   std::unique_ptr<int> int_result;
31 //   std::unique_ptr<double> double_result;
32 //   EXPECT_CALL(mocked_object, Method).WillOnce(Invoke(
33 //       [&] (auto&& arg1, auto&& arg2) {
34 //         int_result = std::move(arg1);
35 //         double_result = std::move(arg2);
36 //         return 42;
37 //       }));
38 //   EXPECT_EQ(42, mocked_object.Method(std::make_unique<int>(123),
39 //                                      std::make_unique<double>(0.5)));
40 //   EXPECT_THAT(int_result, Pointee(Eq(123)));
41 //   EXPECT_THAT(double_result, Pointee(DoubleEq(0.5)));
42 
43 template <size_t I = 0, typename T>
MoveArg(T * out)44 auto MoveArg(T* out) {
45   return [out](auto&&... args) {
46     *out = std::move(std::get<I>(std::tie(args...)));
47   };
48 }
49 
50 // Moves the `I`th argument to `*out` and returns `return_value`.
51 //
52 // This is a convenience helper for code that wants to write the following:
53 //
54 //   DoAll(MoveArg<N>(&saved_arg), Return(value))
55 //
56 // The above is not actually possible to write because:
57 // - `DoAll()` requires that its final action has a return type that matches the
58 //   mocked call's return type. So `Return()` must be last.
59 // - But any actions before the last receive a read-only view of the arguments.
60 //   So `MoveArg()` receives a read-only view and cannot move out of it.
61 //
62 // Example:
63 //
64 //   std::unique_ptr<int> result;
65 //   EXPECT_CALL(mocked_object, Method).WillOnce(
66 //       MoveArgAndReturn<0>(&result, true));
67 //   EXPECT_TRUE(mocked_object.Method(std::make_unique<int>(123)));
68 //   EXPECT_THAT(int_result, Pointee(Eq(123)));
69 //
70 template <size_t I = 0, typename T1, typename T2>
MoveArgAndReturn(T1 * out,T2 && return_value)71 auto MoveArgAndReturn(T1* out, T2&& return_value) {
72   return [out, value = std::forward<T2>(return_value)](auto&&... args) mutable {
73     *out = std::move(std::get<I>(std::tie(args...)));
74     return std::forward<T2>(value);
75   };
76 }
77 
78 #endif  // BASE_TEST_GMOCK_MOVE_SUPPORT_H_
79