blob: 9b47b06732cc5da3e25b3eddd2571964735326e0 [file] [log] [blame]
// Copyright 2019 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.
#include "content/browser/scheduler/browser_ui_thread_scheduler.h"
#include <utility>
#include <vector>
#include "base/callback_helpers.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool/thread_pool.h"
#include "base/test/mock_callback.h"
#include "content/public/browser/browser_task_traits.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
using ::testing::ElementsAre;
using ::testing::Invoke;
using ::testing::Mock;
void RecordRunOrder(std::vector<int>* run_order, int order) {
run_order->push_back(order);
}
base::OnceClosure RunOnDestruction(base::OnceClosure task) {
return base::BindOnce(
[](std::unique_ptr<base::ScopedClosureRunner>) {},
base::Passed(
std::make_unique<base::ScopedClosureRunner>(std::move(task))));
}
base::OnceClosure PostOnDestruction(
scoped_refptr<base::SingleThreadTaskRunner> task_queue,
base::OnceClosure task) {
return RunOnDestruction(base::BindOnce(
[](base::OnceClosure task,
scoped_refptr<base::SingleThreadTaskRunner> task_queue) {
task_queue->PostTask(FROM_HERE, std::move(task));
},
base::Passed(std::move(task)), task_queue));
}
} // namespace
class BrowserUIThreadSchedulerTest : public testing::Test {
public:
BrowserUIThreadSchedulerTest() {
browser_ui_thread_scheduler_ = std::make_unique<BrowserUIThreadScheduler>();
for (int i = 0;
i < static_cast<int>(BrowserUIThreadTaskQueue::QueueType::kCount);
i++) {
auto queue_type = static_cast<BrowserUIThreadTaskQueue::QueueType>(i);
task_runners_.emplace(
queue_type,
browser_ui_thread_scheduler_->GetTaskRunnerForTesting(queue_type));
}
}
protected:
using QueueType = BrowserUIThreadScheduler::QueueType;
using MockTask =
testing::StrictMock<base::MockCallback<base::RepeatingCallback<void()>>>;
std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler_;
base::flat_map<BrowserUIThreadScheduler::QueueType,
scoped_refptr<base::SingleThreadTaskRunner>>
task_runners_;
};
TEST_F(BrowserUIThreadSchedulerTest, RunAllPendingTasksForTesting) {
browser_ui_thread_scheduler_->EnableBestEffortQueues();
MockTask task_1;
MockTask task_2;
EXPECT_CALL(task_1, Run).WillOnce(Invoke([&]() {
task_runners_[QueueType::kDefault]->PostTask(FROM_HERE, task_2.Get());
task_runners_[QueueType::kBestEffort]->PostTask(FROM_HERE, task_2.Get());
}));
task_runners_[QueueType::kDefault]->PostTask(FROM_HERE, task_1.Get());
browser_ui_thread_scheduler_->RunAllPendingTasksForTesting();
Mock::VerifyAndClearExpectations(&task_1);
EXPECT_CALL(task_2, Run).Times(2);
browser_ui_thread_scheduler_->RunAllPendingTasksForTesting();
}
TEST_F(BrowserUIThreadSchedulerTest,
RunAllPendingTasksForTestingIgnoresBestEffortIfNotEnabled) {
MockTask best_effort_task;
MockTask default_task;
task_runners_[QueueType::kBestEffort]->PostTask(FROM_HERE,
best_effort_task.Get());
task_runners_[QueueType::kDefault]->PostTask(FROM_HERE, default_task.Get());
EXPECT_CALL(default_task, Run);
browser_ui_thread_scheduler_->RunAllPendingTasksForTesting();
}
TEST_F(BrowserUIThreadSchedulerTest,
RunAllPendingTasksForTestingRunsBestEffortTasksWhenEnabled) {
MockTask task_1;
MockTask task_2;
MockTask task_3;
EXPECT_CALL(task_1, Run).WillOnce(Invoke([&]() {
// This task should not run as it is posted after the
// RunAllPendingTasksForTesting() call
task_runners_[QueueType::kBestEffort]->PostTask(FROM_HERE, task_3.Get());
browser_ui_thread_scheduler_->EnableBestEffortQueues();
}));
EXPECT_CALL(task_2, Run);
task_runners_[QueueType::kDefault]->PostTask(FROM_HERE, task_1.Get());
task_runners_[QueueType::kBestEffort]->PostTask(FROM_HERE, task_2.Get());
browser_ui_thread_scheduler_->RunAllPendingTasksForTesting();
}
TEST_F(BrowserUIThreadSchedulerTest, RunAllPendingTasksForTestingIsReentrant) {
MockTask task_1;
MockTask task_2;
MockTask task_3;
EXPECT_CALL(task_1, Run).WillOnce(Invoke([&]() {
task_runners_[QueueType::kDefault]->PostTask(FROM_HERE, task_2.Get());
browser_ui_thread_scheduler_->RunAllPendingTasksForTesting();
}));
EXPECT_CALL(task_2, Run).WillOnce(Invoke([&]() {
task_runners_[QueueType::kDefault]->PostTask(FROM_HERE, task_3.Get());
}));
task_runners_[QueueType::kDefault]->PostTask(FROM_HERE, task_1.Get());
browser_ui_thread_scheduler_->RunAllPendingTasksForTesting();
}
TEST_F(BrowserUIThreadSchedulerTest, SimplePosting) {
scoped_refptr<base::SingleThreadTaskRunner> tq =
task_runners_[QueueType::kDefault];
std::vector<int> order;
tq->PostTask(FROM_HERE, base::BindOnce(RecordRunOrder, &order, 1));
tq->PostTask(FROM_HERE, base::BindOnce(RecordRunOrder, &order, 2));
tq->PostTask(FROM_HERE, base::BindOnce(RecordRunOrder, &order, 3));
base::RunLoop().RunUntilIdle();
EXPECT_THAT(order, ElementsAre(1, 2, 3));
}
TEST_F(BrowserUIThreadSchedulerTest, DestructorPostChainDuringShutdown) {
scoped_refptr<base::SingleThreadTaskRunner> task_queue =
task_runners_[QueueType::kDefault];
bool run = false;
task_queue->PostTask(
FROM_HERE,
PostOnDestruction(
task_queue,
PostOnDestruction(task_queue,
RunOnDestruction(base::BindOnce(
[](bool* run) { *run = true; }, &run)))));
EXPECT_FALSE(run);
browser_ui_thread_scheduler_.reset();
EXPECT_TRUE(run);
}
} // namespace content