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