1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
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_CALLBACK_SUPPORT_H_
6 #define BASE_TEST_GMOCK_CALLBACK_SUPPORT_H_
7
8 #include <functional>
9 #include <tuple>
10 #include <type_traits>
11 #include <utility>
12
13 #include "base/callback.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/memory/scoped_refptr.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17
18 namespace base {
19
20 namespace internal {
21
22 // Small helper to get the `I`th argument.
23 template <size_t I, typename... Args>
decltype(auto)24 decltype(auto) get(Args&&... args) {
25 return std::get<I>(std::forward_as_tuple(std::forward<Args>(args)...));
26 }
27
28 // Invokes `cb` with the arguments stored in `tuple`. Both `cb` and `tuple` are
29 // perfectly forwarded, allowing callers to specify whether they should be
30 // passed by move or copy.
31 template <typename Callback, typename Tuple, size_t... Is>
decltype(auto)32 decltype(auto) RunImpl(Callback&& cb,
33 Tuple&& tuple,
34 std::index_sequence<Is...>) {
35 return std::forward<Callback>(cb).Run(
36 std::get<Is>(std::forward<Tuple>(tuple))...);
37 }
38
39 // Invokes `cb` with the arguments stored in `tuple`. Both `cb` and `tuple` are
40 // perfectly forwarded, allowing callers to specify whether they should be
41 // passed by move or copy. Needs to dispatch to the three arguments version to
42 // be able to construct a `std::index_sequence` of the corresponding size.
43 template <typename Callback, typename Tuple>
decltype(auto)44 decltype(auto) RunImpl(Callback&& cb, Tuple&& tuple) {
45 return RunImpl(std::forward<Callback>(cb), std::forward<Tuple>(tuple),
46 std::make_index_sequence<
47 std::tuple_size<std::remove_reference_t<Tuple>>::value>());
48 }
49
50 // Invoked when the arguments to a OnceCallback are copy constructible. In this
51 // case the returned lambda will pass the arguments to the provided callback by
52 // copy, allowing it to be used multiple times.
53 template <size_t I,
54 typename Tuple,
55 std::enable_if_t<std::is_copy_constructible<Tuple>::value, int> = 0>
RunOnceCallbackImpl(Tuple && tuple)56 auto RunOnceCallbackImpl(Tuple&& tuple) {
57 return
58 [tuple = std::forward<Tuple>(tuple)](auto&&... args) -> decltype(auto) {
59 return RunImpl(std::move(internal::get<I>(args...)), tuple);
60 };
61 }
62
63 // Invoked when the arguments to a OnceCallback are not copy constructible. In
64 // this case the returned lambda will pass the arguments to the provided
65 // callback by move, allowing it to be only used once.
66 template <size_t I,
67 typename Tuple,
68 std::enable_if_t<!std::is_copy_constructible<Tuple>::value, int> = 0>
RunOnceCallbackImpl(Tuple && tuple)69 auto RunOnceCallbackImpl(Tuple&& tuple) {
70 // Mock actions need to be copyable, but `tuple` is not. Wrap it in in a
71 // `scoped_refptr` to allow it to be copied.
72 auto tuple_ptr = base::MakeRefCounted<base::RefCountedData<Tuple>>(
73 std::forward<Tuple>(tuple));
74 return [tuple_ptr =
75 std::move(tuple_ptr)](auto&&... args) mutable -> decltype(auto) {
76 // Since running the action will move out of the arguments, `tuple_ptr` is
77 // nulled out, so that attempting to run it twice will result in a run-time
78 // crash.
79 return RunImpl(std::move(internal::get<I>(args...)),
80 std::move(std::exchange(tuple_ptr, nullptr)->data));
81 };
82 }
83
84 // Invoked for RepeatingCallbacks. In this case the returned lambda will pass
85 // the arguments to the provided callback by copy, allowing it to be used
86 // multiple times. Move-only arguments are not supported.
87 template <size_t I, typename Tuple>
RunRepeatingCallbackImpl(Tuple && tuple)88 auto RunRepeatingCallbackImpl(Tuple&& tuple) {
89 return
90 [tuple = std::forward<Tuple>(tuple)](auto&&... args) -> decltype(auto) {
91 return RunImpl(internal::get<I>(args...), tuple);
92 };
93 }
94
95 } // namespace internal
96
97 namespace test {
98
99 // Matchers for base::{Once,Repeating}Callback and
100 // base::{Once,Repeating}Closure.
101 MATCHER(IsNullCallback, "a null callback") {
102 return (arg.is_null());
103 }
104
105 MATCHER(IsNotNullCallback, "a non-null callback") {
106 return (!arg.is_null());
107 }
108
109 // The Run[Once]Closure() action invokes the Run() method on the closure
110 // provided when the action is constructed. Function arguments passed when the
111 // action is run will be ignored.
ACTION_P(RunClosure,closure)112 ACTION_P(RunClosure, closure) {
113 closure.Run();
114 }
115
116 // This action can be invoked at most once. Any further invocation will trigger
117 // a CHECK failure.
RunOnceClosure(base::OnceClosure cb)118 inline auto RunOnceClosure(base::OnceClosure cb) {
119 // Mock actions need to be copyable, but OnceClosure is not. Wrap the closure
120 // in a base::RefCountedData<> to allow it to be copied. An alternative would
121 // be to use AdaptCallbackForRepeating(), but that allows the closure to be
122 // run more than once and silently ignores any invocation after the first.
123 // Since this is for use by tests, it's better to crash or CHECK-fail and
124 // surface the incorrect usage, rather than have a silent unexpected success.
125 using RefCountedOnceClosure = base::RefCountedData<base::OnceClosure>;
126 scoped_refptr<RefCountedOnceClosure> copyable_cb =
127 base::MakeRefCounted<RefCountedOnceClosure>(std::move(cb));
128 return [copyable_cb](auto&&...) {
129 CHECK(copyable_cb->data);
130 std::move(copyable_cb->data).Run();
131 };
132 }
133
134 // The Run[Once]Closure<N>() action invokes the Run() method on the N-th
135 // (0-based) argument of the mock function.
136 template <size_t I>
RunClosure()137 auto RunClosure() {
138 return [](auto&&... args) -> decltype(auto) {
139 return internal::get<I>(args...).Run();
140 };
141 }
142
143 template <size_t I>
RunOnceClosure()144 auto RunOnceClosure() {
145 return [](auto&&... args) -> decltype(auto) {
146 return std::move(internal::get<I>(args...)).Run();
147 };
148 }
149
150 // The Run[Once]Callback<N>(p1, p2, ..., p_k) action invokes the Run() method on
151 // the N-th (0-based) argument of the mock function, with arguments p1, p2, ...,
152 // p_k.
153 //
154 // Notes:
155 //
156 // 1. The arguments are passed by value by default. If you need to
157 // pass an argument by reference, wrap it inside ByRef(). For example,
158 //
159 // RunCallback<1>(5, string("Hello"), ByRef(foo))
160 //
161 // passes 5 and string("Hello") by value, and passes foo by reference.
162 //
163 // 2. If the callback takes an argument by reference but ByRef() is
164 // not used, it will receive the reference to a copy of the value,
165 // instead of the original value. For example, when the 0-th
166 // argument of the callback takes a const string&, the action
167 //
168 // RunCallback<0>(string("Hello"))
169 //
170 // makes a copy of the temporary string("Hello") object and passes a
171 // reference of the copy, instead of the original temporary object,
172 // to the callback. This makes it easy for a user to define an
173 // RunCallback action from temporary values and have it performed later.
174 //
175 // 3. In order to facilitate re-use of the `RunOnceCallback()` action,
176 // the arguments are copied during each run if possible. If this can't
177 // be done (e.g. one of the arguments is move-only), the arguments will
178 // be passed by move. However, since moving potentially invalidates the
179 // arguments, the resulting action is only allowed to run once in this
180 // case. Attempting to run it twice will result in a runtime crash.
181 // Using move-only arguments with `RunCallback()` is not supported.
182 template <size_t I, typename... RunArgs>
RunOnceCallback(RunArgs &&...run_args)183 auto RunOnceCallback(RunArgs&&... run_args) {
184 return internal::RunOnceCallbackImpl<I>(
185 std::make_tuple(std::forward<RunArgs>(run_args)...));
186 }
187
188 template <size_t I, typename... RunArgs>
RunCallback(RunArgs &&...run_args)189 auto RunCallback(RunArgs&&... run_args) {
190 return internal::RunRepeatingCallbackImpl<I>(
191 std::make_tuple(std::forward<RunArgs>(run_args)...));
192 }
193
194 } // namespace test
195 } // namespace base
196
197 #endif // BASE_TEST_GMOCK_CALLBACK_SUPPORT_H_
198