#include "content/browser/startup_task_runner.h"
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/task_runner.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
using testing::_;
using testing::Assign;
using testing::Invoke;
int observer_calls = 0;
int task_count = 0;
int observer_result;
void Observer(int result) {
observer_calls++;
observer_result = result;
}
class StartupTaskRunnerTest : public testing::Test {
public:
void SetUp() override {
last_task_ = 0;
observer_calls = 0;
task_count = 0;
}
int Task1() {
last_task_ = 1;
task_count++;
return 0;
}
int Task2() {
last_task_ = 2;
task_count++;
return 0;
}
int FailingTask() {
last_task_ = 3;
task_count++;
return 1;
}
int GetLastTask() { return last_task_; }
private:
int last_task_;
};
class MockTaskRunner {
public:
MOCK_METHOD2(PostDelayedTask, bool(const base::Location&, base::TimeDelta));
MOCK_METHOD2(PostNonNestableDelayedTask,
bool(const base::Location&, base::TimeDelta));
};
class TaskRunnerProxy : public base::SingleThreadTaskRunner {
public:
TaskRunnerProxy(MockTaskRunner* mock) : mock_(mock) {}
bool RunsTasksInCurrentSequence() const override { return true; }
bool PostDelayedTask(const base::Location& location,
base::OnceClosure closure,
base::TimeDelta delta) override {
last_task_ = std::move(closure);
return mock_->PostDelayedTask(location, delta);
}
bool PostNonNestableDelayedTask(const base::Location& location,
base::OnceClosure closure,
base::TimeDelta delta) override {
last_task_ = std::move(closure);
return mock_->PostNonNestableDelayedTask(location, delta);
}
base::OnceClosure TakeLastTaskClosure() { return std::move(last_task_); }
private:
~TaskRunnerProxy() override {}
raw_ptr<MockTaskRunner> mock_;
base::OnceClosure last_task_;
};
TEST_F(StartupTaskRunnerTest, SynchronousExecution) {
MockTaskRunner mock_runner;
scoped_refptr<TaskRunnerProxy> proxy = new TaskRunnerProxy(&mock_runner);
EXPECT_CALL(mock_runner, PostDelayedTask(_, _)).Times(0);
EXPECT_CALL(mock_runner, PostNonNestableDelayedTask(_, _)).Times(0);
StartupTaskRunner runner(base::BindOnce(&Observer), proxy);
StartupTask task1 =
base::BindOnce(&StartupTaskRunnerTest::Task1, base::Unretained(this));
runner.AddTask(std::move(task1));
EXPECT_EQ(GetLastTask(), 0);
StartupTask task2 =
base::BindOnce(&StartupTaskRunnerTest::Task2, base::Unretained(this));
runner.AddTask(std::move(task2));
EXPECT_EQ(GetLastTask(), 0);
runner.RunAllTasksNow();
EXPECT_EQ(GetLastTask(), 2);
EXPECT_EQ(task_count, 2);
EXPECT_EQ(observer_calls, 1);
EXPECT_EQ(observer_result, 0);
runner.StartRunningTasksAsync();
EXPECT_EQ(task_count, 2);
EXPECT_EQ(observer_calls, 1);
}
TEST_F(StartupTaskRunnerTest, NullObserver) {
MockTaskRunner mock_runner;
scoped_refptr<TaskRunnerProxy> proxy = new TaskRunnerProxy(&mock_runner);
EXPECT_CALL(mock_runner, PostDelayedTask(_, _)).Times(0);
EXPECT_CALL(mock_runner, PostNonNestableDelayedTask(_, _)).Times(0);
StartupTaskRunner runner(base::OnceCallback<void(int)>(), proxy);
StartupTask task1 =
base::BindOnce(&StartupTaskRunnerTest::Task1, base::Unretained(this));
runner.AddTask(std::move(task1));
EXPECT_EQ(GetLastTask(), 0);
StartupTask task2 =
base::BindOnce(&StartupTaskRunnerTest::Task2, base::Unretained(this));
runner.AddTask(std::move(task2));
EXPECT_EQ(GetLastTask(), 0);
runner.RunAllTasksNow();
EXPECT_EQ(GetLastTask(), 2);
EXPECT_EQ(task_count, 2);
runner.StartRunningTasksAsync();
EXPECT_EQ(task_count, 2);
EXPECT_EQ(observer_calls, 0);
}
TEST_F(StartupTaskRunnerTest, SynchronousExecutionFailedTask) {
MockTaskRunner mock_runner;
scoped_refptr<TaskRunnerProxy> proxy = new TaskRunnerProxy(&mock_runner);
EXPECT_CALL(mock_runner, PostDelayedTask(_, _)).Times(0);
EXPECT_CALL(mock_runner, PostNonNestableDelayedTask(_, _)).Times(0);
StartupTaskRunner runner(base::BindOnce(&Observer), proxy);
StartupTask task3 = base::BindOnce(&StartupTaskRunnerTest::FailingTask,
base::Unretained(this));
runner.AddTask(std::move(task3));
EXPECT_EQ(GetLastTask(), 0);
StartupTask task2 =
base::BindOnce(&StartupTaskRunnerTest::Task2, base::Unretained(this));
runner.AddTask(std::move(task2));
EXPECT_EQ(GetLastTask(), 0);
runner.RunAllTasksNow();
EXPECT_EQ(GetLastTask(), 3);
EXPECT_EQ(task_count, 1);
EXPECT_EQ(observer_calls, 1);
EXPECT_EQ(observer_result, 1);
runner.StartRunningTasksAsync();
EXPECT_EQ(observer_calls, 1);
EXPECT_EQ(task_count, 1);
}
TEST_F(StartupTaskRunnerTest, AsynchronousExecution) {
MockTaskRunner mock_runner;
scoped_refptr<TaskRunnerProxy> proxy = new TaskRunnerProxy(&mock_runner);
EXPECT_CALL(mock_runner, PostDelayedTask(_, _)).Times(0);
EXPECT_CALL(mock_runner, PostNonNestableDelayedTask(_, base::Milliseconds(0)))
.Times(testing::Between(2, 3))
.WillRepeatedly(testing::Return(true));
StartupTaskRunner runner(base::BindOnce(&Observer), proxy);
StartupTask task1 =
base::BindOnce(&StartupTaskRunnerTest::Task1, base::Unretained(this));
runner.AddTask(std::move(task1));
StartupTask task2 =
base::BindOnce(&StartupTaskRunnerTest::Task2, base::Unretained(this));
runner.AddTask(std::move(task2));
EXPECT_EQ(GetLastTask(), 0);
runner.StartRunningTasksAsync();
EXPECT_EQ(GetLastTask(), 0);
for (int i = 0; i < 4 && observer_calls == 0; i++) {
proxy->TakeLastTaskClosure().Run();
EXPECT_EQ(i + 1, GetLastTask());
}
EXPECT_EQ(task_count, 2);
EXPECT_EQ(observer_calls, 1);
EXPECT_EQ(observer_result, 0);
runner.RunAllTasksNow();
EXPECT_EQ(task_count, 2);
EXPECT_EQ(observer_calls, 1);
}
TEST_F(StartupTaskRunnerTest, AsynchronousExecutionFailedTask) {
MockTaskRunner mock_runner;
scoped_refptr<TaskRunnerProxy> proxy = new TaskRunnerProxy(&mock_runner);
EXPECT_CALL(mock_runner, PostDelayedTask(_, _)).Times(0);
EXPECT_CALL(mock_runner, PostNonNestableDelayedTask(_, base::Milliseconds(0)))
.Times(testing::Between(1, 2))
.WillRepeatedly(testing::Return(true));
StartupTaskRunner runner(base::BindOnce(&Observer), proxy);
StartupTask task3 = base::BindOnce(&StartupTaskRunnerTest::FailingTask,
base::Unretained(this));
runner.AddTask(std::move(task3));
StartupTask task2 =
base::BindOnce(&StartupTaskRunnerTest::Task2, base::Unretained(this));
runner.AddTask(std::move(task2));
EXPECT_EQ(GetLastTask(), 0);
runner.StartRunningTasksAsync();
EXPECT_EQ(GetLastTask(), 0);
for (int i = 0; i < 4 && observer_calls == 0; i++)
proxy->TakeLastTaskClosure().Run();
EXPECT_EQ(GetLastTask(), 3);
EXPECT_EQ(task_count, 1);
EXPECT_EQ(observer_calls, 1);
EXPECT_EQ(observer_result, 1);
runner.RunAllTasksNow();
EXPECT_EQ(observer_calls, 1);
EXPECT_EQ(task_count, 1);
}
}
}