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_TEST_FUTURE_H_ 6 #define BASE_TEST_TEST_FUTURE_H_ 7 8 #include <memory> 9 #include <string> 10 11 #include "base/check.h" 12 #include "base/functional/bind.h" 13 #include "base/functional/callback_forward.h" 14 #include "base/memory/weak_ptr.h" 15 #include "base/run_loop.h" 16 #include "base/sequence_checker.h" 17 #include "base/test/test_future_internal.h" 18 #include "base/thread_annotations.h" 19 #include "third_party/abseil-cpp/absl/types/optional.h" 20 21 namespace base::test { 22 23 // Helper class to test code that returns its result(s) asynchronously through a 24 // callback: 25 // 26 // - Pass the callback provided by TestFuture::GetCallback() to the code 27 // under test. 28 // - Wait for the callback to be invoked by calling TestFuture::Wait(), or 29 // TestFuture::Get() to access the value(s) passed to the callback. 30 // 31 // If the callback takes multiple arguments, use TestFuture::Get<0>() to access 32 // the value of the first argument, TestFuture::Get<1>() to access the value of 33 // the second argument, and so on. 34 // Alternatively you can use the argument type like TestFuture::Get<T>(). 35 // 36 // If for any reason you can't use TestFuture::GetCallback(), you can use 37 // TestFuture::SetValue() to directly set the value. This method must be called 38 // from the main sequence. 39 // 40 // Finally, TestFuture::Take() is similar to TestFuture::Get() but it will 41 // move the result out, which can be helpful when testing a move-only class. 42 // 43 // Example usage: 44 // 45 // TEST_F(MyTestFixture, MyTest) { 46 // TestFuture<ResultType> future; 47 // 48 // object_under_test.DoSomethingAsync(future.GetCallback()); 49 // 50 // const ResultType& actual_result = future.Get(); 51 // 52 // // When you come here, DoSomethingAsync has finished and `actual_result` 53 // // contains the result passed to the callback. 54 // } 55 // 56 // Example if the callback has 2 arguments: 57 // 58 // TEST_F(MyTestFixture, MyTest) { 59 // TestFuture<int, std::string> future; 60 // 61 // object_under_test.DoSomethingAsync(future.GetCallback()); 62 // 63 // // Either select the argument by type... 64 // int first_argument = future.Get<int>(); 65 // const std::string& second_argument = future.Get<std::string>(); 66 // 67 // // ... or by index. 68 // int first_argument = future.Get<0>(); 69 // const std::string& second_argument = future.Get<1>(); 70 // } 71 // 72 // Example if the callback has zero arguments: 73 // 74 // TEST_F(MyTestFixture, MyTest) { 75 // TestFuture<void> signal; 76 // 77 // object_under_test.DoSomethingAsync(signal.GetCallback()); 78 // 79 // EXPECT_TRUE(signal.Wait()); 80 // // When you come here you know the async code is ready. 81 // } 82 // 83 // Or an example using TestFuture::Wait(): 84 // 85 // TEST_F(MyTestFixture, MyWaitTest) { 86 // TestFuture<ResultType> future; 87 // 88 // object_under_test.DoSomethingAsync(future.GetCallback()); 89 // 90 // // Optional. The Get() call below will also wait until the value 91 // // arrives, but this explicit call to Wait() can be useful if you want to 92 // // add extra information. 93 // ASSERT_TRUE(future.Wait()) << "Detailed error message"; 94 // 95 // const ResultType& actual_result = future.Get(); 96 // } 97 // 98 // All access to this class must be made from the same sequence. 99 template <typename... Types> 100 class TestFuture { 101 public: 102 using TupleType = std::tuple<std::decay_t<Types>...>; 103 104 static_assert(std::tuple_size<TupleType>::value > 0, 105 "Don't use TestFuture<> but use TestFuture<void> instead"); 106 107 TestFuture() = default; 108 TestFuture(const TestFuture&) = delete; 109 TestFuture& operator=(const TestFuture&) = delete; 110 ~TestFuture() = default; 111 112 // Waits for the value to arrive. 113 // 114 // Returns true if the value arrived, or false if a timeout happens. 115 // 116 // Directly calling Wait() is not required as Get()/Take() will also wait for 117 // the value to arrive, however you can use a direct call to Wait() to 118 // improve the error reported: 119 // 120 // ASSERT_TRUE(queue.Wait()) << "Detailed error message"; 121 // Wait()122 [[nodiscard]] bool Wait() { 123 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 124 125 if (values_) 126 return true; 127 128 run_loop_.Run(); 129 130 return IsReady(); 131 } 132 133 // Returns true if the value has arrived. IsReady()134 bool IsReady() const { 135 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 136 return values_.has_value(); 137 } 138 139 // Waits for the value to arrive, and returns the I-th value. 140 // 141 // Will DCHECK if a timeout happens. 142 // 143 // Example usage: 144 // 145 // TestFuture<int, std::string> future; 146 // int first = future.Get<0>(); 147 // std::string second = future.Get<1>(); 148 // 149 template <std::size_t I, 150 typename T = TupleType, 151 internal::EnableIfOneOrMoreValues<T> = true> Get()152 const auto& Get() { 153 return std::get<I>(GetTuple()); 154 } 155 156 // Waits for the value to arrive, and returns the value with the given type. 157 // 158 // Will DCHECK if a timeout happens. 159 // 160 // Example usage: 161 // 162 // TestFuture<int, std::string> future; 163 // int first = future.Get<int>(); 164 // std::string second = future.Get<std::string>(); 165 // 166 template <typename Type> Get()167 const auto& Get() { 168 return std::get<Type>(GetTuple()); 169 } 170 171 // Returns a callback that when invoked will store all the argument values, 172 // and unblock any waiters. 173 // Templated so you can specify how you need the arguments to be passed - 174 // const, reference, .... Defaults to simply `Types...`. 175 // 176 // Example usage: 177 // 178 // TestFuture<int, std::string> future; 179 // 180 // // returns base::OnceCallback<void(int, std::string)> 181 // future.GetCallback(); 182 // 183 // // returns base::OnceCallback<void(int, const std::string&)> 184 // future.GetCallback<int, const std::string&>(); 185 // 186 template <typename... CallbackArgumentsTypes> GetCallback()187 base::OnceCallback<void(CallbackArgumentsTypes...)> GetCallback() { 188 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 189 return base::BindOnce( 190 [](WeakPtr<TestFuture<Types...>> future, 191 CallbackArgumentsTypes... values) { 192 if (future) 193 future->SetValue(std::forward<CallbackArgumentsTypes>(values)...); 194 }, 195 weak_ptr_factory_.GetWeakPtr()); 196 } 197 GetCallback()198 base::OnceCallback<void(Types...)> GetCallback() { 199 return GetCallback<Types...>(); 200 } 201 202 // Sets the value of the future. 203 // This will unblock any pending Wait() or Get() call. 204 // This can only be called once. SetValue(Types...values)205 void SetValue(Types... values) { 206 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 207 208 DCHECK(!values_.has_value()) 209 << "The value of a TestFuture can only be set once. If you need to " 210 "handle an ordered stream of result values, use " 211 "`base::test::RepeatingTestFuture`."; 212 213 values_ = std::make_tuple(std::forward<Types>(values)...); 214 run_loop_.Quit(); 215 } 216 217 ////////////////////////////////////////////////////////////////////////////// 218 // Accessor methods only available if the future holds a single value. 219 ////////////////////////////////////////////////////////////////////////////// 220 221 // Waits for the value to arrive, and returns its value. 222 // 223 // Will DCHECK if a timeout happens. 224 template <typename T = TupleType, internal::EnableIfSingleValue<T> = true> Get()225 [[nodiscard]] const auto& Get() { 226 return std::get<0>(GetTuple()); 227 } 228 229 // Waits for the value to arrive, and move it out. 230 // 231 // Will DCHECK if a timeout happens. 232 template <typename T = TupleType, internal::EnableIfSingleValue<T> = true> Take()233 [[nodiscard]] auto Take() { 234 return std::get<0>(TakeTuple()); 235 } 236 237 ////////////////////////////////////////////////////////////////////////////// 238 // Accessor methods only available if the future holds multiple values. 239 ////////////////////////////////////////////////////////////////////////////// 240 241 // Waits for the values to arrive, and returns a tuple with the values. 242 // 243 // Will DCHECK if a timeout happens. 244 template <typename T = TupleType, internal::EnableIfMultiValue<T> = true> Get()245 [[nodiscard]] const TupleType& Get() { 246 return GetTuple(); 247 } 248 249 // Waits for the values to arrive, and move a tuple with the values out. 250 // 251 // Will DCHECK if a timeout happens. 252 template <typename T = TupleType, internal::EnableIfMultiValue<T> = true> Take()253 [[nodiscard]] TupleType Take() { 254 return TakeTuple(); 255 } 256 257 private: GetTuple()258 [[nodiscard]] const TupleType& GetTuple() { 259 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 260 bool success = Wait(); 261 DCHECK(success) << "Waiting for value timed out."; 262 return values_.value(); 263 } 264 TakeTuple()265 [[nodiscard]] TupleType TakeTuple() { 266 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 267 bool success = Wait(); 268 DCHECK(success) << "Waiting for value timed out."; 269 return std::move(values_.value()); 270 } 271 272 SEQUENCE_CHECKER(sequence_checker_); 273 274 base::RunLoop run_loop_ GUARDED_BY_CONTEXT(sequence_checker_); 275 276 absl::optional<TupleType> values_ GUARDED_BY_CONTEXT(sequence_checker_); 277 278 base::WeakPtrFactory<TestFuture<Types...>> weak_ptr_factory_{this}; 279 }; 280 281 // Specialization so you can use `TestFuture` to wait for a no-args callback. 282 // 283 // This specialization offers a subset of the methods provided on the base 284 // `TestFuture`, as there is no value to be returned. 285 template <> 286 class TestFuture<void> { 287 public: 288 // Waits until the callback or `SetValue()` is invoked. 289 // 290 // Fails your test if a timeout happens, but you can check the return value 291 // to improve the error reported: 292 // 293 // ASSERT_TRUE(future.Wait()) << "Detailed error message"; Wait()294 [[nodiscard]] bool Wait() { return implementation_.Wait(); } 295 296 // Waits until the callback or `SetValue()` is invoked. Get()297 void Get() { std::ignore = implementation_.Get(); } 298 299 // Returns true if the callback or `SetValue()` was invoked. IsReady()300 bool IsReady() const { return implementation_.IsReady(); } 301 302 // Returns a callback that when invoked will unblock any waiters. GetCallback()303 base::OnceCallback<void()> GetCallback() { 304 return base::BindOnce(implementation_.GetCallback(), true); 305 } 306 307 // Indicates this `TestFuture` is ready, and unblocks any waiters. SetValue()308 void SetValue() { implementation_.SetValue(true); } 309 310 private: 311 TestFuture<bool> implementation_; 312 }; 313 314 } // namespace base::test 315 316 #endif // BASE_TEST_TEST_FUTURE_H_ 317