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