123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- // Copyright 2021 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #ifndef BASE_TEST_REPEATING_TEST_FUTURE_H_
- #define BASE_TEST_REPEATING_TEST_FUTURE_H_
- #include <utility>
- #include "base/check.h"
- #include "base/containers/queue.h"
- #include "base/memory/weak_ptr.h"
- #include "base/run_loop.h"
- #include "base/sequence_checker.h"
- #include "base/test/test_future_internal.h"
- #include "base/thread_annotations.h"
- #include "third_party/abseil-cpp/absl/types/optional.h"
- namespace base::test {
- // Related class to |base::test::TestFuture|, which allows its callback and
- // AddValue() method to be called multiple times.
- //
- // Each call to Take() will return one element in FIFO fashion.
- // If no element is available, Take() will wait until an element becomes
- // available.
- //
- // Just like |base::test::TestFuture|, |base::test::RepeatingTestFuture| also
- // supports callbacks which take multiple values. If this is the case Take()
- // will return a tuple containing all values passed to the callback.
- //
- // Example usage:
- //
- // TEST_F(MyTestFixture, MyTest) {
- // RepeatingTestFuture<ResultType> future;
- //
- // InvokeCallbackAsyncTwice(future.GetCallback());
- //
- // ResultType first_result = future.Get();
- // ResultType second_result = future.Get();
- //
- // // When you come here, InvokeCallbackAsyncTwice has finished,
- // // |first_result| contains the value passed to the first invocation
- // // of the callback, and |second_result| has the result of the second
- // // invocation.
- // }
- //
- // Example without using a callback but using AddValue() instead:
- //
- // TEST_F(MyTestFixture, MyTest) {
- // RepeatingTestFuture<std::string> future;
- //
- // // AddValue() can be used to add an element to the future.
- // future.AddValue("first-value");
- // future.AddValue("second-value");
- //
- // EXPECT_EQ("first-value", future.Take());
- // EXPECT_EQ("second-value", future.Take());
- // }
- //
- // Or an example using RepeatingTestFuture::Wait():
- //
- // TEST_F(MyTestFixture, MyWaitTest) {
- // RepeatingTestFuture<ResultType> future;
- //
- // object_under_test.DoSomethingAsync(future.GetCallback());
- //
- // // Optional. The Take() 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";
- //
- // ResultType actual_result = future.Take();
- // }
- //
- // All access to this class must be made from the same sequence.
- template <typename... Types>
- class RepeatingTestFuture {
- public:
- using TupleType = std::tuple<std::decay_t<Types>...>;
- using FirstType = typename std::tuple_element<0, TupleType>::type;
- RepeatingTestFuture() = default;
- RepeatingTestFuture(const RepeatingTestFuture&) = delete;
- RepeatingTestFuture& operator=(const RepeatingTestFuture&) = delete;
- RepeatingTestFuture(RepeatingTestFuture&&) = delete;
- RepeatingTestFuture& operator=(RepeatingTestFuture&&) = delete;
- ~RepeatingTestFuture() = default;
- void AddValue(Types... values) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- elements_.push(std::make_tuple(std::forward<Types>(values)...));
- SignalElementIsAvailable();
- }
- // Waits until an element is available.
- // Returns immediately if one or more elements are already available.
- //
- // Returns true if an element arrived, or false if a timeout happens.
- //
- // Directly calling Wait() is not required as 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() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (IsEmpty())
- WaitForANewElement();
- return !IsEmpty();
- }
- // Returns a callback that when invoked will store all the argument values,
- // and unblock any waiters.
- // This method is templated so you can specify how you need the arguments to
- // be passed - be it const, as reference, or anything you can think off.
- // By default the callback accepts the arguments as |Types...|.
- //
- // Example usage:
- //
- // RepeatingTestFuture<int, std::string> future;
- //
- // // returns base::RepeatingCallback<void(int, std::string)>
- // future.GetCallback();
- //
- // // returns base::RepeatingCallback<void(int, const std::string&)>
- // future.GetCallback<int, const std::string&>();
- //
- template <typename... CallbackArgumentsTypes>
- base::RepeatingCallback<void(CallbackArgumentsTypes...)> GetCallback() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return base::BindRepeating(
- [](WeakPtr<RepeatingTestFuture<Types...>> future,
- CallbackArgumentsTypes... values) {
- if (future)
- future->AddValue(std::forward<CallbackArgumentsTypes>(values)...);
- },
- weak_ptr_factory_.GetWeakPtr());
- }
- base::RepeatingCallback<void(Types...)> GetCallback() {
- return GetCallback<Types...>();
- }
- // Returns true if no elements are currently present. Note that consuming all
- // elements through Take() will cause this method to return true after the
- // last available element has been consumed.
- bool IsEmpty() const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return elements_.empty();
- }
- //////////////////////////////////////////////////////////////////////////////
- // Accessor methods only available if each element in the future holds a
- // single value.
- //////////////////////////////////////////////////////////////////////////////
- // Wait for an element to arrive, and move its value out.
- //
- // Will DCHECK if a timeout happens.
- template <typename T = TupleType, internal::EnableIfSingleValue<T> = true>
- FirstType Take() {
- return std::get<0>(TakeTuple());
- }
- //////////////////////////////////////////////////////////////////////////////
- // Accessor methods only available if each element in the future holds
- // multiple values.
- //////////////////////////////////////////////////////////////////////////////
- // Wait for an element to arrive, and move a tuple with its values out.
- //
- // Will DCHECK if a timeout happens.
- template <typename T = TupleType, internal::EnableIfMultiValue<T> = true>
- TupleType Take() {
- return TakeTuple();
- }
- private:
- // Wait until a new element is available.
- void WaitForANewElement() VALID_CONTEXT_REQUIRED(sequence_checker_) {
- DCHECK(!run_loop_.has_value());
- // Create a new run loop.
- run_loop_.emplace();
- // Wait until 'run_loop_->Quit()' is called.
- run_loop_->Run();
- run_loop_.reset();
- }
- void SignalElementIsAvailable() VALID_CONTEXT_REQUIRED(sequence_checker_) {
- if (run_loop_.has_value())
- run_loop_->Quit();
- }
- TupleType TakeTuple() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // Ensure an element is available.
- bool success = Wait();
- DCHECK(success) << "Waiting for an element timed out.";
- auto result = std::move(elements_.front());
- elements_.pop();
- return result;
- }
- base::queue<TupleType> elements_ GUARDED_BY_CONTEXT(sequence_checker_);
- // Used by Wait() to know when AddValue() is called.
- absl::optional<base::RunLoop> run_loop_ GUARDED_BY_CONTEXT(sequence_checker_);
- SEQUENCE_CHECKER(sequence_checker_);
- base::WeakPtrFactory<RepeatingTestFuture<Types...>> weak_ptr_factory_{this};
- };
- } // namespace base::test
- #endif // BASE_TEST_REPEATING_TEST_FUTURE_H_
|