// 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. #include "base/test/test_future.h" #include #include "base/functional/callback.h" #include "base/run_loop.h" #include "base/task/sequenced_task_runner.h" #include "base/task/thread_pool.h" #include "base/test/bind.h" #include "base/test/gtest_util.h" #include "base/test/scoped_run_loop_timeout.h" #include "base/test/task_environment.h" #include "testing/gtest/include/gtest/gtest-spi.h" #include "testing/gtest/include/gtest/gtest.h" namespace base::test { namespace { using AnyType = int; constexpr int kAnyValue = 5; constexpr int kOtherValue = 10; struct MoveOnlyValue { public: MoveOnlyValue() = default; explicit MoveOnlyValue(int data) : data(data) {} MoveOnlyValue(MoveOnlyValue&&) = default; MoveOnlyValue& operator=(MoveOnlyValue&&) = default; ~MoveOnlyValue() = default; int data; }; } // namespace class TestFutureTest : public ::testing::Test { public: TestFutureTest() = default; TestFutureTest(const TestFutureTest&) = delete; TestFutureTest& operator=(const TestFutureTest&) = delete; ~TestFutureTest() override = default; template void RunLater(Lambda lambda, scoped_refptr task_runner = SequencedTaskRunner::GetCurrentDefault()) { task_runner->PostTask(FROM_HERE, BindLambdaForTesting(lambda)); } void RunLater(OnceClosure callable, scoped_refptr task_runner = SequencedTaskRunner::GetCurrentDefault()) { task_runner->PostTask(FROM_HERE, std::move(callable)); } void RunLater(RepeatingClosure callable, scoped_refptr task_runner = SequencedTaskRunner::GetCurrentDefault()) { task_runner->PostTask(FROM_HERE, std::move(callable)); } void PostDelayedTask(OnceClosure callable, base::TimeDelta delay, scoped_refptr task_runner = SequencedTaskRunner::GetCurrentDefault()) { task_runner->PostDelayedTask(FROM_HERE, std::move(callable), delay); } private: TaskEnvironment environment_{TaskEnvironment::TimeSource::MOCK_TIME}; }; using TestFutureDeathTest = TestFutureTest; TEST_F(TestFutureTest, WaitShouldBlockUntilValueArrives) { const int expected_value = 42; TestFuture future; PostDelayedTask(BindOnce(future.GetCallback(), expected_value), Milliseconds(1)); std::ignore = future.Wait(); EXPECT_EQ(expected_value, future.Get()); } TEST_F(TestFutureTest, WaitShouldBlockUntilValueArrivesOnOtherSequence) { const int expected_value = 42; TestFuture future; PostDelayedTask(BindOnce(future.GetSequenceBoundCallback(), expected_value), Milliseconds(1), ThreadPool::CreateSequencedTaskRunner({})); std::ignore = future.Wait(); EXPECT_EQ(expected_value, future.Get()); } TEST_F(TestFutureTest, WaitShouldReturnTrueWhenValueArrives) { TestFuture future; PostDelayedTask(BindOnce(future.GetCallback(), kAnyValue), Milliseconds(1)); bool success = future.Wait(); EXPECT_TRUE(success); } TEST_F(TestFutureTest, WaitShouldReturnTrueWhenValueArrivesOnOtherSequence) { TestFuture future; PostDelayedTask(BindOnce(future.GetSequenceBoundCallback(), kAnyValue), Milliseconds(1), ThreadPool::CreateSequencedTaskRunner({})); bool success = future.Wait(); EXPECT_TRUE(success); } TEST_F(TestFutureTest, WaitShouldReturnFalseIfTimeoutHappens) { ScopedRunLoopTimeout timeout(FROM_HERE, Milliseconds(1)); // `ScopedRunLoopTimeout` will automatically fail the test when a timeout // happens, so we use EXPECT_FATAL_FAILURE to handle this failure. // EXPECT_FATAL_FAILURE only works on static objects. static bool success; static TestFuture future; EXPECT_NONFATAL_FAILURE({ success = future.Wait(); }, "timed out"); EXPECT_FALSE(success); } TEST_F(TestFutureTest, GetShouldBlockUntilValueArrives) { const int expected_value = 42; TestFuture future; PostDelayedTask(BindOnce(future.GetCallback(), expected_value), Milliseconds(1)); int actual_value = future.Get(); EXPECT_EQ(expected_value, actual_value); } TEST_F(TestFutureTest, GetShouldBlockUntilValueArrivesOnOtherSequence) { const int expected_value = 42; TestFuture future; PostDelayedTask(BindOnce(future.GetSequenceBoundCallback(), expected_value), Milliseconds(1), ThreadPool::CreateSequencedTaskRunner({})); int actual_value = future.Get(); EXPECT_EQ(expected_value, actual_value); } TEST_F(TestFutureDeathTest, GetShouldCheckIfTimeoutHappens) { ScopedRunLoopTimeout timeout(FROM_HERE, Milliseconds(1)); TestFuture future; EXPECT_CHECK_DEATH_WITH(std::ignore = future.Get(), "timed out"); } TEST_F(TestFutureTest, TakeShouldWorkWithMoveOnlyValue) { const int expected_data = 99; TestFuture future; RunLater(BindOnce(future.GetCallback(), MoveOnlyValue(expected_data))); MoveOnlyValue actual_value = future.Take(); EXPECT_EQ(expected_data, actual_value.data); } TEST_F(TestFutureTest, TakeShouldWorkWithMoveOnlyValueOnOtherSequence) { const int expected_data = 99; TestFuture future; RunLater( BindOnce(future.GetSequenceBoundCallback(), MoveOnlyValue(expected_data)), ThreadPool::CreateSequencedTaskRunner({})); MoveOnlyValue actual_value = future.Take(); EXPECT_EQ(expected_data, actual_value.data); } TEST_F(TestFutureDeathTest, TakeShouldCheckIfTimeoutHappens) { ScopedRunLoopTimeout timeout(FROM_HERE, Milliseconds(1)); TestFuture future; EXPECT_CHECK_DEATH_WITH(std::ignore = future.Take(), "timed out"); } TEST_F(TestFutureTest, IsReadyShouldBeTrueWhenValueIsSet) { TestFuture future; EXPECT_FALSE(future.IsReady()); future.SetValue(kAnyValue); EXPECT_TRUE(future.IsReady()); } TEST_F(TestFutureTest, ClearShouldRemoveStoredValue) { TestFuture future; future.SetValue(kAnyValue); future.Clear(); EXPECT_FALSE(future.IsReady()); } TEST_F(TestFutureTest, ShouldNotAllowOverwritingStoredValue) { TestFuture future; future.SetValue(kAnyValue); EXPECT_NONFATAL_FAILURE(future.SetValue(kOtherValue), "Received new value"); } TEST_F(TestFutureTest, ShouldAllowReuseIfPreviousValueIsFirstConsumed) { TestFuture future; RunLater([&]() { future.SetValue("first value"); }); EXPECT_EQ(future.Take(), "first value"); ASSERT_FALSE(future.IsReady()); RunLater([&]() { future.SetValue("second value"); }); EXPECT_EQ(future.Take(), "second value"); } TEST_F(TestFutureTest, ShouldAllowReusingCallback) { TestFuture future; RepeatingCallback callback = future.GetRepeatingCallback(); RunLater(BindOnce(callback, "first value")); EXPECT_EQ(future.Take(), "first value"); RunLater(BindOnce(callback, "second value")); EXPECT_EQ(future.Take(), "second value"); RepeatingCallback sequence_bound_callback = future.GetSequenceBoundRepeatingCallback(); auto other_task_runner = ThreadPool::CreateSequencedTaskRunner({}); RunLater(BindOnce(sequence_bound_callback, "third value"), other_task_runner); EXPECT_EQ(future.Take(), "third value"); RunLater(BindOnce(sequence_bound_callback, "fourth value"), other_task_runner); EXPECT_EQ(future.Take(), "fourth value"); } TEST_F(TestFutureTest, WaitShouldWorkAfterTake) { TestFuture future; future.SetValue("first value"); std::ignore = future.Take(); RunLater([&]() { future.SetValue("second value"); }); EXPECT_TRUE(future.Wait()); EXPECT_EQ(future.Get(), "second value"); } TEST_F(TestFutureTest, ShouldSignalWhenSetValueIsInvoked) { const int expected_value = 111; TestFuture future; RunLater([&future]() { future.SetValue(expected_value); }); int actual_value = future.Get(); EXPECT_EQ(expected_value, actual_value); } TEST_F(TestFutureTest, ShouldAllowReferenceArgumentsForCallback) { const int expected_value = 222; TestFuture future; OnceCallback callback = future.GetCallback(); RunLater(BindOnce(std::move(callback), expected_value)); int actual_value = future.Get(); EXPECT_EQ(expected_value, actual_value); } TEST_F(TestFutureTest, ShouldAllowReferenceArgumentsForCallbackOnOtherSequence) { const int expected_value = 222; TestFuture future; OnceCallback callback = future.GetSequenceBoundCallback(); RunLater(BindOnce(std::move(callback), expected_value), ThreadPool::CreateSequencedTaskRunner({})); int actual_value = future.Get(); EXPECT_EQ(expected_value, actual_value); } TEST_F(TestFutureTest, ShouldAllowInvokingCallbackAfterFutureIsDestroyed) { OnceCallback callback; { TestFuture future; callback = future.GetCallback(); } std::move(callback).Run(1); } TEST_F(TestFutureTest, ShouldAllowInvokingCallbackOnOtherSequenceAfterFutureIsDestroyed) { OnceCallback callback; { TestFuture future; callback = future.GetSequenceBoundCallback(); } base::RunLoop run_loop; ThreadPool::PostTask( FROM_HERE, BindOnce(std::move(callback), 1).Then(run_loop.QuitClosure())); run_loop.Run(); } TEST_F(TestFutureTest, ShouldReturnTupleValue) { const int expected_int_value = 5; const std::string expected_string_value = "value"; TestFuture future; RunLater(BindOnce(future.GetCallback(), expected_int_value, expected_string_value)); const std::tuple& actual = future.Get(); EXPECT_EQ(expected_int_value, std::get<0>(actual)); EXPECT_EQ(expected_string_value, std::get<1>(actual)); } TEST_F(TestFutureTest, ShouldReturnTupleValueOnOtherSequence) { const int expected_int_value = 5; const std::string expected_string_value = "value"; TestFuture future; RunLater(BindOnce(future.GetSequenceBoundCallback(), expected_int_value, expected_string_value), ThreadPool::CreateSequencedTaskRunner({})); const std::tuple& actual = future.Get(); EXPECT_EQ(expected_int_value, std::get<0>(actual)); EXPECT_EQ(expected_string_value, std::get<1>(actual)); } TEST_F(TestFutureTest, ShouldAllowAccessingTupleValueUsingGetWithIndex) { const int expected_int_value = 5; const std::string expected_string_value = "value"; TestFuture future; RunLater(BindOnce(future.GetCallback(), expected_int_value, expected_string_value)); std::ignore = future.Get(); EXPECT_EQ(expected_int_value, future.Get<0>()); EXPECT_EQ(expected_string_value, future.Get<1>()); } TEST_F(TestFutureTest, ShouldAllowAccessingTupleValueUsingGetWithType) { const int expected_int_value = 5; const std::string expected_string_value = "value"; TestFuture future; RunLater(BindOnce(future.GetCallback(), expected_int_value, expected_string_value)); std::ignore = future.Get(); EXPECT_EQ(expected_int_value, future.Get()); EXPECT_EQ(expected_string_value, future.Get()); } TEST_F(TestFutureTest, ShouldAllowReferenceArgumentsForMultiArgumentCallback) { const int expected_int_value = 5; const std::string expected_string_value = "value"; TestFuture future; OnceCallback callback = future.GetCallback(); RunLater( BindOnce(std::move(callback), expected_int_value, expected_string_value)); std::tuple actual = future.Get(); EXPECT_EQ(expected_int_value, std::get<0>(actual)); EXPECT_EQ(expected_string_value, std::get<1>(actual)); } TEST_F(TestFutureTest, ShouldAllowReferenceArgumentsForMultiArgumentCallbackOnOtherSequence) { const int expected_int_value = 5; const std::string expected_string_value = "value"; TestFuture future; OnceCallback callback = future.GetSequenceBoundCallback(); RunLater( BindOnce(std::move(callback), expected_int_value, expected_string_value), ThreadPool::CreateSequencedTaskRunner({})); std::tuple actual = future.Get(); EXPECT_EQ(expected_int_value, std::get<0>(actual)); EXPECT_EQ(expected_string_value, std::get<1>(actual)); } TEST_F(TestFutureTest, SetValueShouldAllowMultipleArguments) { const int expected_int_value = 5; const std::string expected_string_value = "value"; TestFuture future; RunLater([&future, expected_string_value]() { future.SetValue(expected_int_value, expected_string_value); }); const std::tuple& actual = future.Get(); EXPECT_EQ(expected_int_value, std::get<0>(actual)); EXPECT_EQ(expected_string_value, std::get<1>(actual)); } TEST_F(TestFutureTest, ShouldSupportCvRefType) { std::string expected_value = "value"; TestFuture future; OnceCallback callback = future.GetCallback(); std::move(callback).Run(expected_value); // both get and take should compile, and take should return the decayed value. const std::string& get_result = future.Get(); EXPECT_EQ(expected_value, get_result); std::string take_result = future.Take(); EXPECT_EQ(expected_value, take_result); } TEST_F(TestFutureTest, ShouldSupportMultipleCvRefTypes) { const int expected_first_value = 5; std::string expected_second_value = "value"; const long expected_third_value = 10; TestFuture future; OnceCallback callback = future.GetCallback(); std::move(callback).Run(expected_first_value, expected_second_value, expected_third_value); // both get and take should compile, and return the decayed value. const std::tuple& get_result = future.Get(); EXPECT_EQ(expected_first_value, std::get<0>(get_result)); EXPECT_EQ(expected_second_value, std::get<1>(get_result)); EXPECT_EQ(expected_third_value, std::get<2>(get_result)); // Get should also work EXPECT_EQ(expected_first_value, future.Get<0>()); EXPECT_EQ(expected_second_value, future.Get<1>()); EXPECT_EQ(expected_third_value, future.Get<2>()); std::tuple take_result = future.Take(); EXPECT_EQ(expected_first_value, std::get<0>(take_result)); EXPECT_EQ(expected_second_value, std::get<1>(take_result)); EXPECT_EQ(expected_third_value, std::get<2>(take_result)); } TEST_F(TestFutureTest, ShouldAllowReuseIfPreviousTupleValueIsFirstConsumed) { TestFuture future; future.SetValue("first value", 1); std::ignore = future.Take(); ASSERT_FALSE(future.IsReady()); future.SetValue("second value", 2); EXPECT_EQ(future.Take(), std::make_tuple("second value", 2)); } TEST_F(TestFutureTest, ShouldPrintCurrentValueIfItIsOverwritten) { using UnprintableValue = MoveOnlyValue; TestFuture future; future.SetValue("first-value", 1111, UnprintableValue()); EXPECT_NONFATAL_FAILURE( future.SetValue("second-value", 2222, UnprintableValue()), "old value future; future.SetValue("first-value", 1111, UnprintableValue()); EXPECT_NONFATAL_FAILURE( future.SetValue("second-value", 2222, UnprintableValue()), "new value future; // Sequence-bound callback may run any time between RunLater() and Wait(), // should succeed. auto other_task_runner = ThreadPool::CreateSequencedTaskRunner({}); RunLater(BindOnce(future.GetSequenceBoundCallback(), 1), other_task_runner); EXPECT_TRUE(future.Wait()); future.Clear(); // Callback may run any time between RunLater() and Wait(), should DCHECK. EXPECT_DCHECK_DEATH_WITH( { RunLater(BindOnce(future.GetCallback(), 2), other_task_runner); EXPECT_TRUE(future.Wait()); }, "CalledOnValidSequence"); } using TestFutureWithoutValuesTest = TestFutureTest; TEST_F(TestFutureWithoutValuesTest, IsReadyShouldBeTrueWhenSetValueIsInvoked) { TestFuture future; EXPECT_FALSE(future.IsReady()); future.SetValue(); EXPECT_TRUE(future.IsReady()); } TEST_F(TestFutureWithoutValuesTest, WaitShouldUnblockWhenSetValueIsInvoked) { TestFuture future; RunLater([&future]() { future.SetValue(); }); ASSERT_FALSE(future.IsReady()); std::ignore = future.Wait(); EXPECT_TRUE(future.IsReady()); } TEST_F(TestFutureWithoutValuesTest, WaitShouldUnblockWhenCallbackIsInvoked) { TestFuture future; RunLater(future.GetCallback()); ASSERT_FALSE(future.IsReady()); std::ignore = future.Wait(); EXPECT_TRUE(future.IsReady()); } TEST_F(TestFutureWithoutValuesTest, WaitShouldUnblockWhenCallbackIsInvokedOnOtherSequence) { TestFuture future; RunLater(future.GetSequenceBoundCallback(), ThreadPool::CreateSequencedTaskRunner({})); ASSERT_FALSE(future.IsReady()); std::ignore = future.Wait(); EXPECT_TRUE(future.IsReady()); } TEST_F(TestFutureWithoutValuesTest, WaitAndClearShouldAllowFutureReusing) { TestFuture future; RunLater(future.GetCallback()); EXPECT_TRUE(future.WaitAndClear()); ASSERT_FALSE(future.IsReady()); RunLater(future.GetCallback()); EXPECT_TRUE(future.Wait()); auto other_task_runner = ThreadPool::CreateSequencedTaskRunner({}); RunLater(future.GetSequenceBoundCallback(), other_task_runner); EXPECT_TRUE(future.WaitAndClear()); ASSERT_FALSE(future.IsReady()); RunLater(future.GetSequenceBoundCallback(), other_task_runner); EXPECT_TRUE(future.Wait()); } TEST_F(TestFutureWithoutValuesTest, GetShouldUnblockWhenCallbackIsInvoked) { TestFuture future; RunLater(future.GetCallback()); ASSERT_FALSE(future.IsReady()); future.Get(); EXPECT_TRUE(future.IsReady()); } TEST_F(TestFutureWithoutValuesTest, GetShouldUnblockWhenCallbackIsInvokedOnOtherSequence) { TestFuture future; RunLater(future.GetSequenceBoundCallback(), ThreadPool::CreateSequencedTaskRunner({})); ASSERT_FALSE(future.IsReady()); future.Get(); EXPECT_TRUE(future.IsReady()); } TEST(TestFutureWithoutTaskEnvironmentTest, CanCreateTestFutureBeforeTaskEnvironment) { TestFuture future; // If we come here the test passes, since it means we can create a // `TestFuture` without having a `TaskEnvironment`. } TEST(TestFutureWithoutTaskEnvironmentDeathTest, WaitShouldDcheckWithoutTaskEnvironment) { TestFuture future; EXPECT_CHECK_DEATH_WITH((void)future.Wait(), "requires a single-threaded context"); } } // namespace base::test