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