| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef BASE_TEST_TEST_FUTURE_H_ |
| #define BASE_TEST_TEST_FUTURE_H_ |
| |
| #include <memory> |
| #include <optional> |
| #include <tuple> |
| |
| #include "base/auto_reset.h" |
| #include "base/check.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_forward.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/run_loop.h" |
| #include "base/sequence_checker.h" |
| #include "base/strings/to_string.h" |
| #include "base/task/bind_post_task.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/test/test_future_internal.h" |
| #include "base/thread_annotations.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace base::test { |
| |
| // Helper class to test code that returns its result(s) asynchronously through a |
| // callback: |
| // |
| // - Pass the callback provided by `GetCallback()` to the code under test. |
| // - Wait for the callback to be invoked by calling `Wait(),` or `Get()` to |
| // access the value(s) passed to the callback. |
| // |
| // Example usage: |
| // |
| // TEST_F(MyTestFixture, MyTest) { |
| // TestFuture<ResultType> future; |
| // |
| // object_under_test.DoSomethingAsync(future.GetCallback()); |
| // |
| // const ResultType& actual_result = future.Get(); |
| // |
| // // When you come here, DoSomethingAsync has finished and |
| // // `actual_result` contains the result passed to the callback. |
| // } |
| // |
| // Example using `Wait()`: |
| // |
| // TEST_F(MyTestFixture, MyWaitTest) { |
| // TestFuture<ResultType> future; |
| // |
| // object_under_test.DoSomethingAsync(future.GetCallback()); |
| // |
| // // Optional. The Get() call below will also wait until the value |
| // // arrives, but this explicit call to Wait() can be useful if you want |
| // // to add extra information. |
| // ASSERT_TRUE(future.Wait()) << "Detailed error message"; |
| // |
| // const ResultType& actual_result = future.Get(); |
| // } |
| // |
| // `TestFuture` supports both single- and multiple-argument callbacks. |
| // `TestFuture` provides both index and type based accessors for multi-argument |
| // callbacks. `Get()` and `Take()` return tuples for multi-argument callbacks. |
| // |
| // TestFuture<int, std::string> future; |
| // future.Get<0>(); // Reads the first argument |
| // future.Get<int>(); // Also reads the first argument |
| // future.Get(); // Returns a `const std::tuple<int, std::string>&` |
| // |
| // Example for a multi-argument callback: |
| // |
| // TEST_F(MyTestFixture, MyTest) { |
| // TestFuture<int, std::string> future; |
| // |
| // object_under_test.DoSomethingAsync(future.GetCallback()); |
| // |
| // // You can use type based accessors: |
| // int first_argument = future.Get<int>(); |
| // const std::string& second_argument = future.Get<std::string>(); |
| // |
| // // or index based accessors: |
| // int first_argument = future.Get<0>(); |
| // const std::string& second_argument = future.Get<1>(); |
| // } |
| // |
| // You can also satisfy a `TestFuture` by calling `SetValue()` from the sequence |
| // on which the `TestFuture` was created. This is mostly useful when |
| // implementing an observer: |
| // |
| // class MyTestObserver: public MyObserver { |
| // public: |
| // // `MyObserver` implementation: |
| // void ObserveAnInt(int value) override { |
| // future_.SetValue(value); |
| // } |
| // |
| // int Wait() { return future_.Take(); } |
| // |
| // private: |
| // TestFuture<int> future_; |
| // }; |
| // |
| // TEST_F(MyTestFixture, MyTest) { |
| // MyTestObserver observer; |
| // |
| // object_under_test.DoSomethingAsync(observer); |
| // |
| // int value_passed_to_observer = observer.Wait(); |
| // }; |
| // |
| // `GetRepeatingCallback()` allows you to use a single `TestFuture` in code |
| // that invokes the callback multiple times. |
| // Your test must take care to consume each value before the next value |
| // arrives. You can consume the value by calling either `Take()` or `Clear()`. |
| // |
| // Example for reusing a `TestFuture`: |
| // |
| // TEST_F(MyTestFixture, MyReuseTest) { |
| // TestFuture<std::string> future; |
| // |
| // object_under_test.InstallCallback(future.GetRepeatingCallback()); |
| // |
| // object_under_test.DoSomething(); |
| // EXPECT_EQ(future.Take(), "expected-first-value"); |
| // // Because we used `Take()` the test future is ready for reuse. |
| // |
| // object_under_test.DoSomethingElse(); |
| // EXPECT_EQ(future.Take(), "expected-second-value"); |
| // } |
| // |
| // Example for reusing a `TestFuture` using `Get()` + `Clear()`: |
| // |
| // TEST_F(MyTestFixture, MyReuseTest) { |
| // TestFuture<std::string, int> future; |
| // |
| // object_under_test.InstallCallback(future.GetRepeatingCallback()); |
| // |
| // object_under_test.DoSomething(); |
| // |
| // EXPECT_EQ(future.Get<std::string>(), "expected-first-value"); |
| // EXPECT_EQ(future.Get<int>(), 5); |
| // // Because we used `Get()`, the test future is not ready for reuse, |
| // //so we need an explicit `Clear()` call. |
| // future.Clear(); |
| // |
| // object_under_test.DoSomethingElse(); |
| // EXPECT_EQ(future.Get<std::string>(), "expected-second-value"); |
| // EXPECT_EQ(future.Get<int>(), 2); |
| // } |
| // |
| // Finally, `TestFuture` also supports no-args callbacks: |
| // |
| // Example for no-args callbacks: |
| // |
| // TEST_F(MyTestFixture, MyTest) { |
| // TestFuture<void> signal; |
| // |
| // object_under_test.DoSomethingAsync(signal.GetCallback()); |
| // |
| // EXPECT_TRUE(signal.Wait()); |
| // // When you come here you know the callback was invoked and the async |
| // // code is ready. |
| // } |
| // |
| // All access to this class and its callbacks must be made from the sequence on |
| // which the `TestFuture` was constructed. |
| // |
| template <typename... Types> |
| class TestFuture { |
| public: |
| using TupleType = std::tuple<std::decay_t<Types>...>; |
| |
| static_assert(std::tuple_size_v<TupleType> > 0, |
| "Don't use TestFuture<> but use TestFuture<void> instead"); |
| |
| TestFuture() = default; |
| TestFuture(TestFuture&&) = default; |
| TestFuture(const TestFuture&) = delete; |
| TestFuture& operator=(TestFuture&&) = default; |
| TestFuture& operator=(const TestFuture&) = delete; |
| ~TestFuture() = default; |
| |
| // Waits for the value to arrive. |
| // |
| // Returns true if the value arrived, or false if a timeout happens. |
| // |
| // Directly calling Wait() is not required as Get()/Take() will also wait for |
| // the value to arrive, however you can use a direct call to Wait() to |
| // improve the error reported: |
| // |
| // ASSERT_TRUE(queue.Wait()) << "Detailed error message"; |
| // |
| [[nodiscard]] bool Wait( |
| RunLoop::Type run_loop_type = RunLoop::Type::kDefault) { |
| CheckNotUsedAfterMove(); |
| DCHECK_CALLED_ON_VALID_SEQUENCE(impl_->sequence_checker); |
| |
| if (impl_->values) { |
| return true; |
| } |
| |
| // Wait for the value to arrive. |
| RunLoop loop(run_loop_type); |
| AutoReset<RepeatingClosure> quit_loop(&impl_->ready_signal, |
| loop.QuitClosure()); |
| loop.Run(); |
| |
| return IsReady(); |
| } |
| |
| // Returns true if the value has arrived. |
| bool IsReady() const { |
| CheckNotUsedAfterMove(); |
| DCHECK_CALLED_ON_VALID_SEQUENCE(impl_->sequence_checker); |
| return impl_->values.has_value(); |
| } |
| |
| // Waits for the value to arrive, and returns the I-th value. |
| // |
| // Will CHECK if a timeout happens. |
| // |
| // Example usage: |
| // |
| // TestFuture<int, std::string> future; |
| // int first = future.Get<0>(); |
| // std::string second = future.Get<1>(); |
| // |
| template <std::size_t I, typename T = TupleType> |
| requires(internal::IsNonEmptyTuple<T>) |
| const auto& Get() { |
| return std::get<I>(GetTuple()); |
| } |
| |
| // Waits for the value to arrive, and returns the value with the given type. |
| // |
| // Will CHECK if a timeout happens. |
| // |
| // Example usage: |
| // |
| // TestFuture<int, std::string> future; |
| // int first = future.Get<int>(); |
| // std::string second = future.Get<std::string>(); |
| // |
| template <typename Type> |
| const auto& Get() { |
| return std::get<Type>(GetTuple()); |
| } |
| |
| // Returns a callback that when invoked will store all the argument values, |
| // and unblock any waiters. The callback must be invoked on the sequence the |
| // TestFuture was created on. |
| // |
| // Templated so you can specify how you need the arguments to be passed - |
| // const, reference, .... Defaults to simply `Types...`. |
| // |
| // Example usage: |
| // |
| // TestFuture<int, std::string> future; |
| // |
| // // Without specifying the callback argument types, this returns |
| // // base::OnceCallback<void(int, std::string)>. |
| // future.GetCallback(); |
| // |
| // // By explicitly specifying the callback argument types, this returns |
| // // base::OnceCallback<void(int, const std::string&)>. |
| // future.GetCallback<int, const std::string&>(); |
| // |
| template <typename... CallbackArgumentsTypes> |
| OnceCallback<void(CallbackArgumentsTypes...)> GetCallback() { |
| return GetRepeatingCallback<CallbackArgumentsTypes...>(); |
| } |
| |
| OnceCallback<void(Types...)> GetCallback() { return GetCallback<Types...>(); } |
| |
| // Returns a repeating callback that when invoked will store all the argument |
| // values, and unblock any waiters. The callback must be invoked on the |
| // sequence the TestFuture was created on. |
| // |
| // You must take care that the stored value is consumed before the callback |
| // is invoked a second time. You can consume the value by calling either |
| // `Take()` or `Clear()`. |
| // |
| // Example usage: |
| // |
| // TestFuture<std::string> future; |
| // |
| // object_under_test.InstallCallback(future.GetRepeatingCallback()); |
| // |
| // object_under_test.DoSomething(); |
| // EXPECT_EQ(future.Take(), "expected-first-value"); |
| // // Because we used `Take()` the test future is ready for reuse. |
| // |
| // object_under_test.DoSomethingElse(); |
| // // We can also use `Get()` + `Clear()` to reuse the callback. |
| // EXPECT_EQ(future.Get(), "expected-second-value"); |
| // future.Clear(); |
| // |
| // object_under_test.DoSomethingElse(); |
| // EXPECT_EQ(future.Take(), "expected-third-value"); |
| // |
| template <typename... CallbackArgumentsTypes> |
| RepeatingCallback<void(CallbackArgumentsTypes...)> GetRepeatingCallback() { |
| CheckNotUsedAfterMove(); |
| DCHECK_CALLED_ON_VALID_SEQUENCE(impl_->sequence_checker); |
| return BindRepeating( |
| [](WeakPtr<Impl> impl, CallbackArgumentsTypes... values) { |
| if (impl) { |
| SetValueImpl(*impl, |
| std::forward<CallbackArgumentsTypes>(values)...); |
| } |
| }, |
| impl_->weak_ptr_factory.GetWeakPtr()); |
| } |
| |
| RepeatingCallback<void(Types...)> GetRepeatingCallback() { |
| return GetRepeatingCallback<Types...>(); |
| } |
| |
| // Returns a callback that can be invoked on any sequence. When invoked it |
| // will post a task to the sequence the TestFuture was created on, to store |
| // all the argument values, and unblock any waiters. |
| // |
| // Templated so you can specify how you need the arguments to be passed - |
| // const, reference, .... Defaults to simply `Types...`. |
| // |
| // Example usage: |
| // |
| // TestFuture<int, std::string> future; |
| // |
| // // Without specifying the callback argument types, this returns |
| // // base::OnceCallback<void(int, std::string)>. |
| // auto callback = future.GetSequenceBoundCallback(); |
| // |
| // // By explicitly specifying the callback argument types, this returns |
| // // base::OnceCallback<void(int, const std::string&)>. |
| // auto callback = |
| // future.GetSequenceBoundCallback<int, const std::string&>(); |
| // |
| // // AsyncOperation invokes `callback` with a result. |
| // other_task_runner->PostTask(FROM_HERE, base::BindOnce(&AsyncOperation, |
| // std::move(callback)); |
| // |
| // future.Wait(); |
| // |
| template <typename... CallbackArgumentsTypes> |
| OnceCallback<void(CallbackArgumentsTypes...)> GetSequenceBoundCallback() { |
| return GetSequenceBoundRepeatingCallback<CallbackArgumentsTypes...>(); |
| } |
| |
| OnceCallback<void(Types...)> GetSequenceBoundCallback() { |
| return GetSequenceBoundCallback<Types...>(); |
| } |
| |
| // Returns a repeating callback that can be invoked on any sequence. When |
| // invoked it will post a task to the sequence the TestFuture was created on, |
| // to store all the argument values, and unblock any waiters. |
| // |
| // You must take care that the stored value is consumed before the callback |
| // is invoked a second time. You can consume the value by calling either |
| // `Take()` or `Clear()`. |
| // |
| // Example usage: |
| // |
| // base::SequenceBound<Object> object_under_test(other_task_runner); |
| // TestFuture<std::string> future; |
| // |
| // object_under_test.AsyncCall(&Object::InstallCallback, |
| // future.GetSequenceBoundRepeatingCallback()); |
| // |
| // object_under_test.AsyncCall(&DoSomething); |
| // EXPECT_EQ(future.Take(), "expected-first-value"); |
| // // Because we used `Take()` the test future is ready for reuse. |
| // |
| // object_under_test.AsyncCall(&DoSomethingElse); |
| // // We can also use `Get()` + `Clear()` to reuse the callback. |
| // EXPECT_EQ(future.Get(), "expected-second-value"); |
| // future.Clear(); |
| // |
| // object_under_test.AsyncCall(&DoSomethingElse); |
| // EXPECT_EQ(future.Take(), "expected-third-value"); |
| // |
| template <typename... CallbackArgumentsTypes> |
| RepeatingCallback<void(CallbackArgumentsTypes...)> |
| GetSequenceBoundRepeatingCallback() { |
| CheckNotUsedAfterMove(); |
| DCHECK_CALLED_ON_VALID_SEQUENCE(impl_->sequence_checker); |
| return BindPostTask(base::SequencedTaskRunner::GetCurrentDefault(), |
| GetRepeatingCallback<CallbackArgumentsTypes...>()); |
| } |
| |
| RepeatingCallback<void(Types...)> GetSequenceBoundRepeatingCallback() { |
| return GetSequenceBoundRepeatingCallback<Types...>(); |
| } |
| |
| // Sets the value of the future. |
| // This will unblock any pending Wait() or Get() call. |
| void SetValue(Types... values) { |
| CheckNotUsedAfterMove(); |
| DCHECK_CALLED_ON_VALID_SEQUENCE(impl_->sequence_checker); |
| SetValueImpl(*impl_, std::forward<Types>(values)...); |
| } |
| |
| // Clears the future, allowing it to be reused and accept a new value. |
| // |
| // All outstanding callbacks issued through `GetCallback()` remain valid. |
| void Clear() { |
| if (IsReady()) { |
| std::ignore = Take(); |
| } |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Accessor methods only available if the future holds a single value. |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| // Waits for the value to arrive, and returns a reference to it. |
| // |
| // Will CHECK if a timeout happens. |
| template <typename T = TupleType> |
| requires(internal::IsSingleValuedTuple<T>) |
| [[nodiscard]] const auto& Get() { |
| return std::get<0>(GetTuple()); |
| } |
| |
| // Waits for the value to arrive, and returns it. |
| // |
| // Will CHECK if a timeout happens. |
| template <typename T = TupleType> |
| requires(internal::IsSingleValuedTuple<T>) |
| [[nodiscard]] auto Take() { |
| return std::get<0>(TakeTuple()); |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Accessor methods only available if the future holds multiple values. |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| // Waits for the values to arrive, and returns a tuple with the values. |
| // |
| // Will CHECK if a timeout happens. |
| template <typename T = TupleType> |
| requires(internal::IsMultiValuedTuple<T>) |
| [[nodiscard]] const TupleType& Get() { |
| return GetTuple(); |
| } |
| |
| // Waits for the values to arrive, and moves a tuple with the values out. |
| // |
| // Will CHECK if a timeout happens. |
| template <typename T = TupleType> |
| requires(internal::IsMultiValuedTuple<T>) |
| [[nodiscard]] TupleType Take() { |
| return TakeTuple(); |
| } |
| |
| private: |
| // Nested struct, used together with std::unique_ptr to make TestFuture |
| // movable. |
| struct Impl { |
| Impl() = default; |
| ~Impl() = default; |
| |
| SEQUENCE_CHECKER(sequence_checker); |
| |
| base::RepeatingClosure ready_signal GUARDED_BY_CONTEXT(sequence_checker) = |
| base::DoNothing(); |
| |
| std::optional<TupleType> values GUARDED_BY_CONTEXT(sequence_checker); |
| |
| WeakPtrFactory<Impl> weak_ptr_factory{this}; |
| }; |
| |
| static void SetValueImpl(Impl& impl, Types... values) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(impl.sequence_checker); |
| |
| auto new_values = std::make_tuple(std::forward<Types>(values)...); |
| |
| EXPECT_FALSE(impl.values.has_value()) |
| << "Received new value " << ToString(new_values) << " before old value " |
| << ToString(impl.values.value()) |
| << " was consumed through Take() or Clear()."; |
| |
| impl.values = std::move(new_values); |
| |
| impl.ready_signal.Run(); |
| } |
| |
| void CheckNotUsedAfterMove() const { |
| // `impl_` may only be null of `this` is an instance that has been moved |
| // away, after which `this` becomes unusable. |
| CHECK(impl_); |
| } |
| |
| [[nodiscard]] const TupleType& GetTuple() { |
| CheckNotUsedAfterMove(); |
| DCHECK_CALLED_ON_VALID_SEQUENCE(impl_->sequence_checker); |
| bool success = Wait(); |
| CHECK(success) << "Waiting for value timed out."; |
| return impl_->values.value(); |
| } |
| |
| [[nodiscard]] TupleType TakeTuple() { |
| CheckNotUsedAfterMove(); |
| DCHECK_CALLED_ON_VALID_SEQUENCE(impl_->sequence_checker); |
| bool success = Wait(); |
| CHECK(success) << "Waiting for value timed out."; |
| |
| return std::exchange(impl_->values, {}).value(); |
| } |
| |
| std::unique_ptr<Impl> impl_ = std::make_unique<Impl>(); |
| }; |
| |
| // Specialization so you can use `TestFuture` to wait for a no-args callback. |
| // |
| // This specialization offers a subset of the methods provided on the base |
| // `TestFuture`, as there is no value to be returned. |
| template <> |
| class TestFuture<void> { |
| public: |
| // Waits until the callback or `SetValue()` is invoked. |
| // |
| // Fails your test if a timeout happens, but you can check the return value |
| // to improve the error reported: |
| // |
| // ASSERT_TRUE(future.Wait()) << "Detailed error message"; |
| [[nodiscard]] bool Wait( |
| RunLoop::Type run_loop_type = RunLoop::Type::kDefault) { |
| return implementation_.Wait(run_loop_type); |
| } |
| |
| // Same as above, then clears the future, allowing it to be reused and accept |
| // a new value. |
| [[nodiscard]] bool WaitAndClear( |
| RunLoop::Type run_loop_type = RunLoop::Type::kDefault) { |
| auto result = Wait(run_loop_type); |
| Clear(); |
| return result; |
| } |
| |
| // Waits until the callback or `SetValue()` is invoked. |
| void Get() { std::ignore = implementation_.Get(); } |
| |
| // Returns true if the callback or `SetValue()` was invoked. |
| bool IsReady() const { return implementation_.IsReady(); } |
| |
| // Returns a callback that when invoked will unblock any waiters. |
| OnceClosure GetCallback() { |
| return BindOnce(implementation_.GetCallback(), true); |
| } |
| |
| // Returns a callback that when invoked will unblock any waiters. |
| RepeatingClosure GetRepeatingCallback() { |
| return BindRepeating(implementation_.GetRepeatingCallback(), true); |
| } |
| |
| // Returns a callback that when invoked on any sequence will unblock any |
| // waiters. |
| OnceClosure GetSequenceBoundCallback() { |
| return BindOnce(implementation_.GetSequenceBoundCallback(), true); |
| } |
| |
| // Returns a callback that when invoked on any sequence will unblock any |
| // waiters. |
| RepeatingClosure GetSequenceBoundRepeatingCallback() { |
| return BindRepeating(implementation_.GetSequenceBoundRepeatingCallback(), |
| true); |
| } |
| |
| // Indicates this `TestFuture` is ready, and unblocks any waiters. |
| void SetValue() { implementation_.SetValue(true); } |
| |
| // Clears the future, allowing it to be reused and accept a new value. |
| // |
| // All outstanding callbacks issued through `GetCallback()` remain valid. |
| void Clear() { implementation_.Clear(); } |
| |
| private: |
| TestFuture<bool> implementation_; |
| }; |
| |
| // A gmock action that when invoked will store the argument values and |
| // unblock any waiters. The action must be invoked on the sequence the |
| // TestFuture was created on. |
| // |
| // Usually the action will be used with `WillOnce()` and only invoked once, |
| // but if you consume the value with `Take()` or `Clear()` it is safe to |
| // invoke it again. |
| // |
| // Example usage: |
| // TestFuture<int> future; |
| // |
| // EXPECT_CALL(delegate, OnReadComplete) |
| // .WillOnce(InvokeFuture(future)); |
| // |
| // object_under_test.Read(buffer, 16); |
| // |
| // EXPECT_EQ(future.Take(), 16); |
| // |
| // |
| // |
| // Implementation note: this is not implemented using the MATCHER_P macro as the |
| // C++03-compatible way it implements varargs would make this too verbose. |
| // Instead, it takes advantage of the ability to pass a functor to .WillOnce() |
| // and .WillRepeatedly(). |
| template <typename... Types> |
| class InvokeFuture { |
| public: |
| // The TestFuture must be an lvalue. Passing an rvalue would make no sense as |
| // you wouldn't be able to call Take() on it afterwards. |
| explicit InvokeFuture(TestFuture<Types...>& future) |
| : callback_(future.GetRepeatingCallback()) {} |
| |
| // GMock actions must be copyable. |
| InvokeFuture(const InvokeFuture&) = default; |
| InvokeFuture& operator=(const InvokeFuture&) = default; |
| |
| // WillOnce() can take advantage of move constructors. |
| InvokeFuture(InvokeFuture&&) = default; |
| InvokeFuture& operator=(InvokeFuture&&) = default; |
| |
| void operator()(Types... values) { |
| callback_.Run(std::forward<Types>(values)...); |
| } |
| |
| private: |
| RepeatingCallback<void(Types...)> callback_; |
| }; |
| |
| // Specialization for TestFuture<void>. |
| template <> |
| class InvokeFuture<void> { |
| public: |
| explicit InvokeFuture(TestFuture<void>& future) |
| : closure_(future.GetRepeatingCallback()) {} |
| |
| InvokeFuture(const InvokeFuture&) = default; |
| InvokeFuture& operator=(const InvokeFuture&) = default; |
| InvokeFuture(InvokeFuture&&) = default; |
| InvokeFuture& operator=(InvokeFuture&&) = default; |
| |
| void operator()() { closure_.Run(); } |
| |
| private: |
| RepeatingClosure closure_; |
| }; |
| |
| // Deduction guide so the compiler can choose the correct specialisation of |
| // InvokeFuture. |
| template <typename... Types> |
| InvokeFuture(TestFuture<Types...>&) -> InvokeFuture<Types...>; |
| |
| } // namespace base::test |
| |
| #endif // BASE_TEST_TEST_FUTURE_H_ |