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