xref: /aosp_15_r20/external/cronet/base/test/repeating_test_future.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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_REPEATING_TEST_FUTURE_H_
6 #define BASE_TEST_REPEATING_TEST_FUTURE_H_
7 
8 #include <optional>
9 #include <utility>
10 
11 #include "base/check.h"
12 #include "base/containers/queue.h"
13 #include "base/memory/weak_ptr.h"
14 #include "base/run_loop.h"
15 #include "base/sequence_checker.h"
16 #include "base/test/test_future_internal.h"
17 #include "base/thread_annotations.h"
18 
19 namespace base::test {
20 
21 // DEPRECATED!
22 //
23 // Please use `TestFuture` with `TestFuture::GetRepeatingCallback()` instead.
24 template <typename... Types>
25 class RepeatingTestFuture {
26  public:
27   using TupleType = std::tuple<std::decay_t<Types>...>;
28 
29   RepeatingTestFuture() = default;
30   RepeatingTestFuture(const RepeatingTestFuture&) = delete;
31   RepeatingTestFuture& operator=(const RepeatingTestFuture&) = delete;
32   RepeatingTestFuture(RepeatingTestFuture&&) = delete;
33   RepeatingTestFuture& operator=(RepeatingTestFuture&&) = delete;
34   ~RepeatingTestFuture() = default;
35 
AddValue(Types...values)36   void AddValue(Types... values) {
37     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
38 
39     elements_.push(std::make_tuple(std::forward<Types>(values)...));
40     SignalElementIsAvailable();
41   }
42 
43   // Waits until an element is available.
44   // Returns immediately if one or more elements are already available.
45   //
46   // Returns true if an element arrived, or false if a timeout happens.
47   //
48   // Directly calling Wait() is not required as Take() will also wait for
49   // the value to arrive, however you can use a direct call to Wait() to
50   // improve the error reported:
51   //
52   //   ASSERT_TRUE(queue.Wait()) << "Detailed error message";
53   //
Wait()54   [[nodiscard]] bool Wait() {
55     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
56 
57     if (IsEmpty()) {
58       WaitForANewElement();
59     }
60 
61     return !IsEmpty();
62   }
63 
64   // Returns a callback that when invoked will store all the argument values,
65   // and unblock any waiters.
66   // This method is templated so you can specify how you need the arguments to
67   // be passed - be it const, as reference, or anything you can think off.
68   // By default the callback accepts the arguments as `Types...`.
69   //
70   // Example usage:
71   //
72   //   RepeatingTestFuture<int, std::string> future;
73   //
74   //   // returns base::RepeatingCallback<void(int, std::string)>
75   //   future.GetCallback();
76   //
77   //   // returns base::RepeatingCallback<void(int, const std::string&)>
78   //   future.GetCallback<int, const std::string&>();
79   //
80   template <typename... CallbackArgumentsTypes>
GetCallback()81   base::RepeatingCallback<void(CallbackArgumentsTypes...)> GetCallback() {
82     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
83     return base::BindRepeating(
84         [](WeakPtr<RepeatingTestFuture<Types...>> future,
85            CallbackArgumentsTypes... values) {
86           if (future) {
87             future->AddValue(std::forward<CallbackArgumentsTypes>(values)...);
88           }
89         },
90         weak_ptr_factory_.GetWeakPtr());
91   }
92 
GetCallback()93   base::RepeatingCallback<void(Types...)> GetCallback() {
94     return GetCallback<Types...>();
95   }
96 
97   // Returns true if no elements are currently present. Note that consuming all
98   // elements through Take() will cause this method to return true after the
99   // last available element has been consumed.
IsEmpty()100   bool IsEmpty() const {
101     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
102 
103     return elements_.empty();
104   }
105 
106   //////////////////////////////////////////////////////////////////////////////
107   //  Accessor methods only available if each element in the future holds a
108   //  single value.
109   //////////////////////////////////////////////////////////////////////////////
110 
111   // Wait for an element to arrive, and move its value out.
112   //
113   // Will DCHECK if a timeout happens.
114   template <typename T = TupleType, internal::EnableIfSingleValue<T> = true>
Take()115   auto Take() {
116     return std::get<0>(TakeTuple());
117   }
118 
119   //////////////////////////////////////////////////////////////////////////////
120   //  Accessor methods only available if each element in the future holds
121   //  multiple values.
122   //////////////////////////////////////////////////////////////////////////////
123 
124   // Wait for an element to arrive, and move a tuple with its values out.
125   //
126   // Will DCHECK if a timeout happens.
127   template <typename T = TupleType, internal::EnableIfMultiValue<T> = true>
Take()128   TupleType Take() {
129     return TakeTuple();
130   }
131 
132  private:
133   // Wait until a new element is available.
WaitForANewElement()134   void WaitForANewElement() VALID_CONTEXT_REQUIRED(sequence_checker_) {
135     DCHECK(!run_loop_.has_value());
136 
137     // Create a new run loop.
138     run_loop_.emplace();
139     // Wait until 'run_loop_->Quit()' is called.
140     run_loop_->Run();
141     run_loop_.reset();
142   }
143 
SignalElementIsAvailable()144   void SignalElementIsAvailable() VALID_CONTEXT_REQUIRED(sequence_checker_) {
145     if (run_loop_.has_value()) {
146       run_loop_->Quit();
147     }
148   }
149 
TakeTuple()150   TupleType TakeTuple() {
151     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
152 
153     // Ensure an element is available.
154     bool success = Wait();
155     DCHECK(success) << "Waiting for an element timed out.";
156 
157     auto result = std::move(elements_.front());
158     elements_.pop();
159     return result;
160   }
161 
162   base::queue<TupleType> elements_ GUARDED_BY_CONTEXT(sequence_checker_);
163 
164   // Used by Wait() to know when AddValue() is called.
165   std::optional<base::RunLoop> run_loop_ GUARDED_BY_CONTEXT(sequence_checker_);
166 
167   SEQUENCE_CHECKER(sequence_checker_);
168 
169   base::WeakPtrFactory<RepeatingTestFuture<Types...>> weak_ptr_factory_{this};
170 };
171 
172 // Specialization so you can use `RepeatingTestFuture` to wait for a no-args
173 // callback.
174 template <>
175 class RepeatingTestFuture<void> {
176  public:
AddValue()177   void AddValue() { implementation_.AddValue(true); }
178 
179   // Waits until the callback or `AddValue()` is invoked.
180   // Returns immediately if one or more invocations have already happened.
181   //
182   // Returns true if an invocation arrived, or false if a timeout happens.
183   //
184   // Directly calling Wait() is not required as Take() will also wait for
185   // the invocation to arrive, however you can use a direct call to Wait() to
186   // improve the error reported:
187   //
188   //   ASSERT_TRUE(queue.Wait()) << "Detailed error message";
189   //
Wait()190   [[nodiscard]] bool Wait() { return implementation_.Wait(); }
191 
192   // Returns a callback that when invoked will unblock any waiters.
GetCallback()193   base::RepeatingClosure GetCallback() {
194     return base::BindRepeating(implementation_.GetCallback(), true);
195   }
196 
197   // Returns true if no elements are currently present. Note that consuming all
198   // elements through Take() will cause this method to return true after the
199   // last available element has been consumed.
IsEmpty()200   bool IsEmpty() const { return implementation_.IsEmpty(); }
201 
202   // Waits until the callback or `AddValue()` is invoked.
203   //
204   // Will DCHECK if a timeout happens.
Take()205   void Take() { implementation_.Take(); }
206 
207  private:
208   RepeatingTestFuture<bool> implementation_;
209 };
210 
211 }  // namespace base::test
212 
213 #endif  // BASE_TEST_REPEATING_TEST_FUTURE_H_
214