1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
|
// Copyright 2021 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_TEST_FUTURE_H_
#define BASE_TEST_TEST_FUTURE_H_
#include <memory>
#include <string>
#include "base/callback_forward.h"
#include "base/check.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "base/sequence_checker.h"
#include "base/test/bind.h"
#include "base/test/test_future_internal.h"
#include "base/thread_annotations.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace base::test {
// Helper class to test code that returns its result(s) asynchronously through a
// callback:
//
// - Pass the callback provided by TestFuture::GetCallback() to the code
// under test.
// - Wait for the callback to be invoked by calling TestFuture::Wait(), or
// TestFuture::Get() to access the value(s) passed to the callback.
//
// If the callback takes multiple arguments, use TestFuture::Get<0>() to access
// the value of the first argument, TestFuture::Get<1>() to access the value of
// the second argument, and so on.
//
// If for any reason you can't use TestFuture::GetCallback(), you can use
// TestFuture::SetValue() to directly set the value. This method must be called
// from the main sequence.
//
// Finally, TestFuture::Take() is similar to TestFuture::Get() but it will
// move the result out, which can be helpful when testing a move-only class.
//
// Example usage:
//
// TEST_F(MyTestFixture, MyTest) {
// TestFuture<ResultType> future;
//
// object_under_test.DoSomethingAsync(future.GetCallback());
//
// const ResultType& actual_result = future.Get();
//
// // When you come here, DoSomethingAsync has finished and |actual_result|
// // contains the result passed to the callback.
// }
//
// Example if the callback has 2 arguments:
//
// TEST_F(MyTestFixture, MyTest) {
// TestFuture<int, std::string> future;
//
// object_under_test.DoSomethingAsync(future.GetCallback());
//
// int first_argument = future.Get<0>();
// const std::string & second_argument = future.Get<1>();
// }
//
// Or an example using TestFuture::Wait():
//
// TEST_F(MyTestFixture, MyWaitTest) {
// TestFuture<ResultType> future;
//
// object_under_test.DoSomethingAsync(future.GetCallback());
//
// // Optional. The Get() call below will also wait until the value
// // arrives, but this explicit call to Wait() can be useful if you want to
// // add extra information.
// ASSERT_TRUE(future.Wait()) << "Detailed error message";
//
// const ResultType& actual_result = future.Get();
// }
//
// All access to this class must be made from the same sequence.
template <typename... Types>
class TestFuture {
public:
using TupleType = std::tuple<std::decay_t<Types>...>;
using FirstType = typename std::tuple_element<0, TupleType>::type;
TestFuture() = default;
TestFuture(const TestFuture&) = delete;
TestFuture& operator=(const TestFuture&) = delete;
~TestFuture() = default;
// Wait for the value to arrive.
//
// Returns true if the value arrived, or false if a timeout happens.
//
// Directly calling Wait() is not required as Get()/Take() will also wait for
// the value to arrive, however you can use a direct call to Wait() to
// improve the error reported:
//
// ASSERT_TRUE(queue.Wait()) << "Detailed error message";
//
[[nodiscard]] bool Wait() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (values_)
return true;
run_loop_.Run();
return IsReady();
}
// Returns true if the value has arrived.
bool IsReady() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return values_.has_value();
}
// Wait for the value to arrive, and return the I-th value.
//
// Will DCHECK if a timeout happens.
//
// Example usage:
//
// TestFuture<int, std::string> future;
// int first = future.Get<0>();
// std::string second = future.Get<1>();
//
template <std::size_t I>
const typename std::tuple_element<I, TupleType>::type& Get() {
return std::get<I>(GetTuple());
}
// Returns a callback that when invoked will store all the argument values,
// and unblock any waiters.
// Templated so you can specify how you need the arguments to be passed -
// const, reference, .... Defaults to simply |Types...|.
//
// Example usage:
//
// TestFuture<int, std::string> future;
//
// // returns base::OnceCallback<void(int, std::string)>
// future.GetCallback();
//
// // returns base::OnceCallback<void(int, const std::string&)>
// future.GetCallback<int, const std::string&>();
//
template <typename... CallbackArgumentsTypes>
base::OnceCallback<void(CallbackArgumentsTypes...)> GetCallback() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return base::BindOnce(
[](WeakPtr<TestFuture<Types...>> future,
CallbackArgumentsTypes... values) {
if (future)
future->SetValue(std::forward<CallbackArgumentsTypes>(values)...);
},
weak_ptr_factory_.GetWeakPtr());
}
base::OnceCallback<void(Types...)> GetCallback() {
return GetCallback<Types...>();
}
// Set the value of the future.
// This will unblock any pending Wait() or Get() call.
// This can only be called once.
void SetValue(Types... values) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!values_.has_value())
<< "The value of a TestFuture can only be set once. If you need to "
"handle an ordered stream of result values, use "
"|base::test::RepeatingTestFuture|.";
values_ = std::make_tuple(std::forward<Types>(values)...);
run_loop_.Quit();
}
//////////////////////////////////////////////////////////////////////////////
// Accessor methods only available if the future holds a single value.
//////////////////////////////////////////////////////////////////////////////
// Wait for the value to arrive, and returns its value.
//
// Will DCHECK if a timeout happens.
template <typename T = TupleType, internal::EnableIfSingleValue<T> = true>
[[nodiscard]] const FirstType& Get() {
return std::get<0>(GetTuple());
}
// Wait for the value to arrive, and move it out.
//
// Will DCHECK if a timeout happens.
template <typename T = TupleType, internal::EnableIfSingleValue<T> = true>
[[nodiscard]] FirstType Take() {
return std::get<0>(TakeTuple());
}
//////////////////////////////////////////////////////////////////////////////
// Accessor methods only available if the future holds multiple values.
//////////////////////////////////////////////////////////////////////////////
// Wait for the values to arrive, and returns a tuple with the values.
//
// Will DCHECK if a timeout happens.
template <typename T = TupleType, internal::EnableIfMultiValue<T> = true>
[[nodiscard]] const TupleType& Get() {
return GetTuple();
}
// Wait for the values to arrive, and move a tuple with the values out.
//
// Will DCHECK if a timeout happens.
template <typename T = TupleType, internal::EnableIfMultiValue<T> = true>
[[nodiscard]] TupleType Take() {
return TakeTuple();
}
private:
[[nodiscard]] const TupleType& GetTuple() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
bool success = Wait();
DCHECK(success) << "Waiting for value timed out.";
return values_.value();
}
[[nodiscard]] TupleType TakeTuple() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
bool success = Wait();
DCHECK(success) << "Waiting for value timed out.";
return std::move(values_.value());
}
SEQUENCE_CHECKER(sequence_checker_);
base::RunLoop run_loop_ GUARDED_BY_CONTEXT(sequence_checker_);
absl::optional<TupleType> values_ GUARDED_BY_CONTEXT(sequence_checker_);
base::WeakPtrFactory<TestFuture<Types...>> weak_ptr_factory_{this};
};
} // namespace base::test
#endif // BASE_TEST_TEST_FUTURE_H_
|