blob: fb8fd7a1e08087eb77aae8bac391d6ff68c472d2 [file] [log] [blame]
// Copyright 2017 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_mock_time_task_runner.h"
#include "base/cancelable_callback.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/gtest_util.h"
#include "base/test/test_timeouts.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
// Basic usage should work the same from default and bound
// TestMockTimeTaskRunners.
TEST(TestMockTimeTaskRunnerTest, Basic) {
static constexpr TestMockTimeTaskRunner::Type kTestCases[] = {
TestMockTimeTaskRunner::Type::kStandalone,
TestMockTimeTaskRunner::Type::kBoundToThread};
for (auto type : kTestCases) {
SCOPED_TRACE(static_cast<int>(type));
auto mock_time_task_runner = MakeRefCounted<TestMockTimeTaskRunner>(type);
int counter = 0;
mock_time_task_runner->PostTask(
FROM_HERE, base::BindOnce([](int* counter) { *counter += 1; },
Unretained(&counter)));
mock_time_task_runner->PostTask(
FROM_HERE, base::BindOnce([](int* counter) { *counter += 32; },
Unretained(&counter)));
mock_time_task_runner->PostDelayedTask(
FROM_HERE,
base::BindOnce([](int* counter) { *counter += 256; },
Unretained(&counter)),
Seconds(3));
mock_time_task_runner->PostDelayedTask(
FROM_HERE,
base::BindOnce([](int* counter) { *counter += 64; },
Unretained(&counter)),
Seconds(1));
mock_time_task_runner->PostDelayedTask(
FROM_HERE,
base::BindOnce([](int* counter) { *counter += 1024; },
Unretained(&counter)),
Minutes(20));
mock_time_task_runner->PostDelayedTask(
FROM_HERE,
base::BindOnce([](int* counter) { *counter += 4096; },
Unretained(&counter)),
Days(20));
int expected_value = 0;
EXPECT_EQ(expected_value, counter);
mock_time_task_runner->RunUntilIdle();
expected_value += 1;
expected_value += 32;
EXPECT_EQ(expected_value, counter);
mock_time_task_runner->RunUntilIdle();
EXPECT_EQ(expected_value, counter);
mock_time_task_runner->FastForwardBy(Seconds(1));
expected_value += 64;
EXPECT_EQ(expected_value, counter);
mock_time_task_runner->FastForwardBy(Seconds(5));
expected_value += 256;
EXPECT_EQ(expected_value, counter);
mock_time_task_runner->FastForwardUntilNoTasksRemain();
expected_value += 1024;
expected_value += 4096;
EXPECT_EQ(expected_value, counter);
}
}
// A default TestMockTimeTaskRunner shouldn't result in a thread association.
TEST(TestMockTimeTaskRunnerTest, DefaultUnbound) {
auto unbound_mock_time_task_runner = MakeRefCounted<TestMockTimeTaskRunner>();
EXPECT_FALSE(SingleThreadTaskRunner::HasCurrentDefault());
EXPECT_FALSE(SequencedTaskRunner::HasCurrentDefault());
EXPECT_DEATH_IF_SUPPORTED({ RunLoop().RunUntilIdle(); }, "");
}
TEST(TestMockTimeTaskRunnerTest, RunLoopDriveableWhenBound) {
auto bound_mock_time_task_runner = MakeRefCounted<TestMockTimeTaskRunner>(
TestMockTimeTaskRunner::Type::kBoundToThread);
int counter = 0;
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce([](int* counter) { *counter += 1; },
Unretained(&counter)));
SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce([](int* counter) { *counter += 32; },
Unretained(&counter)));
SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce([](int* counter) { *counter += 256; },
Unretained(&counter)),
Seconds(3));
SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce([](int* counter) { *counter += 64; },
Unretained(&counter)),
Seconds(1));
SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce([](int* counter) { *counter += 1024; },
Unretained(&counter)),
Minutes(20));
SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce([](int* counter) { *counter += 4096; },
Unretained(&counter)),
Days(20));
int expected_value = 0;
EXPECT_EQ(expected_value, counter);
RunLoop().RunUntilIdle();
expected_value += 1;
expected_value += 32;
EXPECT_EQ(expected_value, counter);
RunLoop().RunUntilIdle();
EXPECT_EQ(expected_value, counter);
{
RunLoop run_loop;
SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), Seconds(1));
SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce([](int* counter) { *counter += 8192; },
Unretained(&counter)),
Seconds(1));
// The QuitClosure() should be ordered between the 64 and the 8192
// increments and should preempt the latter.
run_loop.Run();
expected_value += 64;
EXPECT_EQ(expected_value, counter);
// Running until idle should process the 8192 increment whose delay has
// expired in the previous Run().
RunLoop().RunUntilIdle();
expected_value += 8192;
EXPECT_EQ(expected_value, counter);
}
{
RunLoop run_loop;
SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, run_loop.QuitWhenIdleClosure(), Seconds(5));
SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce([](int* counter) { *counter += 16384; },
Unretained(&counter)),
Seconds(5));
// The QuitWhenIdleClosure() shouldn't preempt equally delayed tasks and as
// such the 16384 increment should be processed before quitting.
run_loop.Run();
expected_value += 256;
expected_value += 16384;
EXPECT_EQ(expected_value, counter);
}
// Process the remaining tasks (note: do not mimic this elsewhere,
// TestMockTimeTaskRunner::FastForwardUntilNoTasksRemain() is a better API to
// do this, this is just done here for the purpose of extensively testing the
// RunLoop approach).
RunLoop run_loop;
SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, run_loop.QuitWhenIdleClosure(), Days(50));
run_loop.Run();
expected_value += 1024;
expected_value += 4096;
EXPECT_EQ(expected_value, counter);
}
TEST(TestMockTimeTaskRunnerTest, AvoidCaptureWhenBound) {
// Make sure that capturing the active task runner --- which sometimes happens
// unknowingly due to ThreadsafeObserverList deep within some singleton ---
// does not keep the entire TestMockTimeTaskRunner alive, as in bound mode
// that's a RunLoop::Delegate, and leaking that renders any further tests that
// need RunLoop support unrunnable.
//
// (This used to happen when code run from ProcessAllTasksNoLaterThan grabbed
// the runner.).
scoped_refptr<SingleThreadTaskRunner> captured;
{
auto task_runner = MakeRefCounted<TestMockTimeTaskRunner>(
TestMockTimeTaskRunner::Type::kBoundToThread);
task_runner->PostTask(FROM_HERE, base::BindLambdaForTesting([&]() {
captured =
SingleThreadTaskRunner::GetCurrentDefault();
}));
task_runner->RunUntilIdle();
}
{
// This should not complain about RunLoop::Delegate already existing.
auto task_runner2 = MakeRefCounted<TestMockTimeTaskRunner>(
TestMockTimeTaskRunner::Type::kBoundToThread);
}
}
// Regression test that receiving the quit-when-idle signal when already empty
// works as intended (i.e. that |TestMockTimeTaskRunner::tasks_lock_cv| is
// properly signaled).
TEST(TestMockTimeTaskRunnerTest, RunLoopQuitFromIdle) {
auto bound_mock_time_task_runner = MakeRefCounted<TestMockTimeTaskRunner>(
TestMockTimeTaskRunner::Type::kBoundToThread);
Thread quitting_thread("quitting thread");
quitting_thread.Start();
RunLoop run_loop;
quitting_thread.task_runner()->PostDelayedTask(
FROM_HERE, run_loop.QuitWhenIdleClosure(), TestTimeouts::tiny_timeout());
run_loop.Run();
}
TEST(TestMockTimeTaskRunnerTest, TakePendingTasks) {
auto task_runner = MakeRefCounted<TestMockTimeTaskRunner>();
task_runner->PostTask(FROM_HERE, DoNothing());
EXPECT_TRUE(task_runner->HasPendingTask());
EXPECT_EQ(1u, task_runner->TakePendingTasks().size());
EXPECT_FALSE(task_runner->HasPendingTask());
}
TEST(TestMockTimeTaskRunnerTest, CancelPendingTask) {
auto task_runner = MakeRefCounted<TestMockTimeTaskRunner>();
CancelableOnceClosure task1(DoNothing());
task_runner->PostDelayedTask(FROM_HERE, task1.callback(), Seconds(1));
EXPECT_TRUE(task_runner->HasPendingTask());
EXPECT_EQ(1u, task_runner->GetPendingTaskCount());
EXPECT_EQ(Seconds(1), task_runner->NextPendingTaskDelay());
task1.Cancel();
EXPECT_FALSE(task_runner->HasPendingTask());
CancelableOnceClosure task2(DoNothing());
task_runner->PostDelayedTask(FROM_HERE, task2.callback(), Seconds(1));
task2.Cancel();
EXPECT_EQ(0u, task_runner->GetPendingTaskCount());
CancelableOnceClosure task3(DoNothing());
task_runner->PostDelayedTask(FROM_HERE, task3.callback(), Seconds(1));
task3.Cancel();
EXPECT_EQ(TimeDelta::Max(), task_runner->NextPendingTaskDelay());
CancelableOnceClosure task4(DoNothing());
task_runner->PostDelayedTask(FROM_HERE, task4.callback(), Seconds(1));
task4.Cancel();
EXPECT_TRUE(task_runner->TakePendingTasks().empty());
}
TEST(TestMockTimeTaskRunnerTest, NoFastForwardToCancelledTask) {
auto task_runner = MakeRefCounted<TestMockTimeTaskRunner>();
TimeTicks start_time = task_runner->NowTicks();
CancelableOnceClosure task(DoNothing());
task_runner->PostDelayedTask(FROM_HERE, task.callback(), Seconds(1));
EXPECT_EQ(Seconds(1), task_runner->NextPendingTaskDelay());
task.Cancel();
task_runner->FastForwardUntilNoTasksRemain();
EXPECT_EQ(start_time, task_runner->NowTicks());
}
TEST(TestMockTimeTaskRunnerTest, AdvanceMockTickClockDoesNotRunTasks) {
auto task_runner = MakeRefCounted<TestMockTimeTaskRunner>();
TimeTicks start_time = task_runner->NowTicks();
task_runner->PostTask(FROM_HERE, BindOnce([]() { ADD_FAILURE(); }));
task_runner->PostDelayedTask(FROM_HERE, BindOnce([]() { ADD_FAILURE(); }),
Seconds(1));
task_runner->AdvanceMockTickClock(Seconds(3));
EXPECT_EQ(start_time + Seconds(3), task_runner->NowTicks());
EXPECT_EQ(2u, task_runner->GetPendingTaskCount());
}
TEST(TestMockTimeTaskRunnerTest, ProcessNextNTasks) {
auto mock_time_task_runner = MakeRefCounted<TestMockTimeTaskRunner>(
TestMockTimeTaskRunner::Type::kStandalone);
int counter = 0;
mock_time_task_runner->PostTask(
FROM_HERE, base::BindOnce([](int* counter) { *counter += 1; },
Unretained(&counter)));
mock_time_task_runner->PostTask(
FROM_HERE, base::BindOnce([](int* counter) { *counter += 32; },
Unretained(&counter)));
CancelableOnceClosure task1(base::BindOnce(
[](int* counter) { *counter += 16; }, Unretained(&counter)));
mock_time_task_runner->PostTask(FROM_HERE, task1.callback());
mock_time_task_runner->PostDelayedTask(
FROM_HERE,
base::BindOnce([](int* counter) { *counter += 256; },
Unretained(&counter)),
Seconds(3));
mock_time_task_runner->PostDelayedTask(
FROM_HERE,
base::BindOnce([](int* counter) { *counter += 64; },
Unretained(&counter)),
Seconds(1));
mock_time_task_runner->PostDelayedTask(
FROM_HERE,
base::BindOnce([](int* counter) { *counter += 1024; },
Unretained(&counter)),
Minutes(20));
mock_time_task_runner->PostDelayedTask(
FROM_HERE,
base::BindOnce([](int* counter) { *counter += 4096; },
Unretained(&counter)),
Days(20));
task1.Cancel();
int expected_value = 0;
EXPECT_EQ(expected_value, counter);
mock_time_task_runner->ProcessNextNTasks(0);
EXPECT_EQ(expected_value, counter);
mock_time_task_runner->ProcessNextNTasks(2);
expected_value += 1;
expected_value += 32;
EXPECT_EQ(expected_value, counter);
// The next task was canceled, so |counter| shouldn't change.
mock_time_task_runner->ProcessNextNTasks(1);
EXPECT_EQ(expected_value, counter);
mock_time_task_runner->ProcessNextNTasks(1);
expected_value += 64;
EXPECT_EQ(expected_value, counter);
mock_time_task_runner->ProcessNextNTasks(-1);
expected_value += 256;
expected_value += 1024;
expected_value += 4096;
EXPECT_EQ(expected_value, counter);
}
} // namespace base