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