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
|
// 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_REPEATING_TEST_FUTURE_H_
#define BASE_TEST_REPEATING_TEST_FUTURE_H_
#include <utility>
#include "base/check.h"
#include "base/containers/queue.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "base/sequence_checker.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 {
// Related class to |base::test::TestFuture|, which allows its callback and
// AddValue() method to be called multiple times.
//
// Each call to Take() will return one element in FIFO fashion.
// If no element is available, Take() will wait until an element becomes
// available.
//
// Just like |base::test::TestFuture|, |base::test::RepeatingTestFuture| also
// supports callbacks which take multiple values. If this is the case Take()
// will return a tuple containing all values passed to the callback.
//
// Example usage:
//
// TEST_F(MyTestFixture, MyTest) {
// RepeatingTestFuture<ResultType> future;
//
// InvokeCallbackAsyncTwice(future.GetCallback());
//
// ResultType first_result = future.Get();
// ResultType second_result = future.Get();
//
// // When you come here, InvokeCallbackAsyncTwice has finished,
// // |first_result| contains the value passed to the first invocation
// // of the callback, and |second_result| has the result of the second
// // invocation.
// }
//
// Example without using a callback but using AddValue() instead:
//
// TEST_F(MyTestFixture, MyTest) {
// RepeatingTestFuture<std::string> future;
//
// // AddValue() can be used to add an element to the future.
// future.AddValue("first-value");
// future.AddValue("second-value");
//
// EXPECT_EQ("first-value", future.Take());
// EXPECT_EQ("second-value", future.Take());
// }
//
// Or an example using RepeatingTestFuture::Wait():
//
// TEST_F(MyTestFixture, MyWaitTest) {
// RepeatingTestFuture<ResultType> future;
//
// object_under_test.DoSomethingAsync(future.GetCallback());
//
// // Optional. The Take() 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";
//
// ResultType actual_result = future.Take();
// }
//
// All access to this class must be made from the same sequence.
template <typename... Types>
class RepeatingTestFuture {
public:
using TupleType = std::tuple<std::decay_t<Types>...>;
using FirstType = typename std::tuple_element<0, TupleType>::type;
RepeatingTestFuture() = default;
RepeatingTestFuture(const RepeatingTestFuture&) = delete;
RepeatingTestFuture& operator=(const RepeatingTestFuture&) = delete;
RepeatingTestFuture(RepeatingTestFuture&&) = delete;
RepeatingTestFuture& operator=(RepeatingTestFuture&&) = delete;
~RepeatingTestFuture() = default;
void AddValue(Types... values) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
elements_.push(std::make_tuple(std::forward<Types>(values)...));
SignalElementIsAvailable();
}
// Waits until an element is available.
// Returns immediately if one or more elements are already available.
//
// Returns true if an element arrived, or false if a timeout happens.
//
// Directly calling Wait() is not required as 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 (IsEmpty())
WaitForANewElement();
return !IsEmpty();
}
// Returns a callback that when invoked will store all the argument values,
// and unblock any waiters.
// This method is templated so you can specify how you need the arguments to
// be passed - be it const, as reference, or anything you can think off.
// By default the callback accepts the arguments as |Types...|.
//
// Example usage:
//
// RepeatingTestFuture<int, std::string> future;
//
// // returns base::RepeatingCallback<void(int, std::string)>
// future.GetCallback();
//
// // returns base::RepeatingCallback<void(int, const std::string&)>
// future.GetCallback<int, const std::string&>();
//
template <typename... CallbackArgumentsTypes>
base::RepeatingCallback<void(CallbackArgumentsTypes...)> GetCallback() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return base::BindRepeating(
[](WeakPtr<RepeatingTestFuture<Types...>> future,
CallbackArgumentsTypes... values) {
if (future)
future->AddValue(std::forward<CallbackArgumentsTypes>(values)...);
},
weak_ptr_factory_.GetWeakPtr());
}
base::RepeatingCallback<void(Types...)> GetCallback() {
return GetCallback<Types...>();
}
// Returns true if no elements are currently present. Note that consuming all
// elements through Take() will cause this method to return true after the
// last available element has been consumed.
bool IsEmpty() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return elements_.empty();
}
//////////////////////////////////////////////////////////////////////////////
// Accessor methods only available if each element in the future holds a
// single value.
//////////////////////////////////////////////////////////////////////////////
// Wait for an element to arrive, and move its value out.
//
// Will DCHECK if a timeout happens.
template <typename T = TupleType, internal::EnableIfSingleValue<T> = true>
FirstType Take() {
return std::get<0>(TakeTuple());
}
//////////////////////////////////////////////////////////////////////////////
// Accessor methods only available if each element in the future holds
// multiple values.
//////////////////////////////////////////////////////////////////////////////
// Wait for an element to arrive, and move a tuple with its values out.
//
// Will DCHECK if a timeout happens.
template <typename T = TupleType, internal::EnableIfMultiValue<T> = true>
TupleType Take() {
return TakeTuple();
}
private:
// Wait until a new element is available.
void WaitForANewElement() VALID_CONTEXT_REQUIRED(sequence_checker_) {
DCHECK(!run_loop_.has_value());
// Create a new run loop.
run_loop_.emplace();
// Wait until 'run_loop_->Quit()' is called.
run_loop_->Run();
run_loop_.reset();
}
void SignalElementIsAvailable() VALID_CONTEXT_REQUIRED(sequence_checker_) {
if (run_loop_.has_value())
run_loop_->Quit();
}
TupleType TakeTuple() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Ensure an element is available.
bool success = Wait();
DCHECK(success) << "Waiting for an element timed out.";
auto result = std::move(elements_.front());
elements_.pop();
return result;
}
base::queue<TupleType> elements_ GUARDED_BY_CONTEXT(sequence_checker_);
// Used by Wait() to know when AddValue() is called.
absl::optional<base::RunLoop> run_loop_ GUARDED_BY_CONTEXT(sequence_checker_);
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<RepeatingTestFuture<Types...>> weak_ptr_factory_{this};
};
} // namespace base::test
#endif // BASE_TEST_REPEATING_TEST_FUTURE_H_
|