• 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_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>
requires(internal::IsSingleValuedTuple<T>)115     requires(internal::IsSingleValuedTuple<T>)
116   auto Take() {
117     return std::get<0>(TakeTuple());
118   }
119 
120   //////////////////////////////////////////////////////////////////////////////
121   //  Accessor methods only available if each element in the future holds
122   //  multiple values.
123   //////////////////////////////////////////////////////////////////////////////
124 
125   // Wait for an element to arrive, and move a tuple with its values out.
126   //
127   // Will DCHECK if a timeout happens.
128   template <typename T = TupleType>
requires(internal::IsMultiValuedTuple<T>)129     requires(internal::IsMultiValuedTuple<T>)
130   TupleType Take() {
131     return TakeTuple();
132   }
133 
134  private:
135   // Wait until a new element is available.
WaitForANewElement()136   void WaitForANewElement() VALID_CONTEXT_REQUIRED(sequence_checker_) {
137     DCHECK(!run_loop_.has_value());
138 
139     // Create a new run loop.
140     run_loop_.emplace();
141     // Wait until 'run_loop_->Quit()' is called.
142     run_loop_->Run();
143     run_loop_.reset();
144   }
145 
SignalElementIsAvailable()146   void SignalElementIsAvailable() VALID_CONTEXT_REQUIRED(sequence_checker_) {
147     if (run_loop_.has_value()) {
148       run_loop_->Quit();
149     }
150   }
151 
TakeTuple()152   TupleType TakeTuple() {
153     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
154 
155     // Ensure an element is available.
156     bool success = Wait();
157     DCHECK(success) << "Waiting for an element timed out.";
158 
159     auto result = std::move(elements_.front());
160     elements_.pop();
161     return result;
162   }
163 
164   base::queue<TupleType> elements_ GUARDED_BY_CONTEXT(sequence_checker_);
165 
166   // Used by Wait() to know when AddValue() is called.
167   std::optional<base::RunLoop> run_loop_ GUARDED_BY_CONTEXT(sequence_checker_);
168 
169   SEQUENCE_CHECKER(sequence_checker_);
170 
171   base::WeakPtrFactory<RepeatingTestFuture<Types...>> weak_ptr_factory_{this};
172 };
173 
174 // Specialization so you can use `RepeatingTestFuture` to wait for a no-args
175 // callback.
176 template <>
177 class RepeatingTestFuture<void> {
178  public:
AddValue()179   void AddValue() { implementation_.AddValue(true); }
180 
181   // Waits until the callback or `AddValue()` is invoked.
182   // Returns immediately if one or more invocations have already happened.
183   //
184   // Returns true if an invocation arrived, or false if a timeout happens.
185   //
186   // Directly calling Wait() is not required as Take() will also wait for
187   // the invocation to arrive, however you can use a direct call to Wait() to
188   // improve the error reported:
189   //
190   //   ASSERT_TRUE(queue.Wait()) << "Detailed error message";
191   //
Wait()192   [[nodiscard]] bool Wait() { return implementation_.Wait(); }
193 
194   // Returns a callback that when invoked will unblock any waiters.
GetCallback()195   base::RepeatingClosure GetCallback() {
196     return base::BindRepeating(implementation_.GetCallback(), true);
197   }
198 
199   // Returns true if no elements are currently present. Note that consuming all
200   // elements through Take() will cause this method to return true after the
201   // last available element has been consumed.
IsEmpty()202   bool IsEmpty() const { return implementation_.IsEmpty(); }
203 
204   // Waits until the callback or `AddValue()` is invoked.
205   //
206   // Will DCHECK if a timeout happens.
Take()207   void Take() { implementation_.Take(); }
208 
209  private:
210   RepeatingTestFuture<bool> implementation_;
211 };
212 
213 }  // namespace base::test
214 
215 #endif  // BASE_TEST_REPEATING_TEST_FUTURE_H_
216