blob: 1b5dcc0cb8366b680b4ace7fefb12951c2c6a075 [file] [log] [blame] [edit]
// 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_