| // Copyright 2019 The Chromium Authors |
| // 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_task_queues.h" |
| |
| #include <array> |
| #include <memory> |
| |
| #include "base/functional/callback_helpers.h" |
| #include "base/message_loop/message_pump.h" |
| #include "base/message_loop/message_pump_type.h" |
| #include "base/run_loop.h" |
| #include "base/task/sequence_manager/sequence_manager.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/test/bind.h" |
| #include "base/test/mock_callback.h" |
| #include "content/browser/scheduler/browser_task_priority.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace content { |
| namespace { |
| |
| using ::base::RunLoop; |
| using ::base::sequence_manager::CreateSequenceManagerOnCurrentThreadWithPump; |
| using ::base::sequence_manager::SequenceManager; |
| using ::testing::Invoke; |
| using ::testing::Mock; |
| |
| using StrictMockTask = |
| testing::StrictMock<base::MockCallback<base::RepeatingCallback<void()>>>; |
| |
| using QueueType = BrowserTaskQueues::QueueType; |
| |
| class BrowserTaskQueuesTest : public testing::Test { |
| protected: |
| BrowserTaskQueuesTest() |
| : sequence_manager_(CreateSequenceManagerOnCurrentThreadWithPump( |
| base::MessagePump::Create(base::MessagePumpType::DEFAULT), |
| base::sequence_manager::SequenceManager::Settings::Builder() |
| .SetPrioritySettings( |
| internal::CreateBrowserTaskPrioritySettings()) |
| .Build())), |
| queues_(std::make_unique<BrowserTaskQueues>(BrowserThread::UI, |
| sequence_manager_.get())), |
| handle_(queues_->GetHandle()) { |
| sequence_manager_->SetDefaultTaskRunner(handle_->GetDefaultTaskRunner()); |
| } |
| |
| std::unique_ptr<SequenceManager> sequence_manager_; |
| std::unique_ptr<BrowserTaskQueues> queues_; |
| scoped_refptr<BrowserTaskQueues::Handle> handle_; |
| }; |
| |
| // Queues are disabled by default and only enabled by the BrowserTaskExecutor |
| // and so no task can be posted until the BrowserTaskExecutor is created. This |
| // allows an embedder to control when to enable the UI task queues. This state |
| // is required for WebView's async startup to work properly. |
| TEST_F(BrowserTaskQueuesTest, NoTaskRunsUntilQueuesAreEnabled) { |
| StrictMockTask task; |
| for (size_t i = 0; i < BrowserTaskQueues::kNumQueueTypes; ++i) { |
| handle_->GetBrowserTaskRunner(static_cast<QueueType>(i)) |
| ->PostTask(FROM_HERE, task.Get()); |
| } |
| |
| { |
| RunLoop run_loop; |
| handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure()); |
| run_loop.Run(); |
| } |
| |
| handle_->OnStartupComplete(); |
| |
| { |
| RunLoop run_loop; |
| handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure()); |
| EXPECT_CALL(task, Run).Times(BrowserTaskQueues::kNumQueueTypes); |
| run_loop.Run(); |
| } |
| } |
| |
| TEST_F(BrowserTaskQueuesTest, SimplePosting) { |
| handle_->OnStartupComplete(); |
| scoped_refptr<base::SingleThreadTaskRunner> tq = |
| handle_->GetBrowserTaskRunner(QueueType::kDefault); |
| |
| StrictMockTask task_1; |
| StrictMockTask task_2; |
| StrictMockTask task_3; |
| |
| { |
| testing::InSequence s; |
| EXPECT_CALL(task_1, Run); |
| EXPECT_CALL(task_2, Run); |
| EXPECT_CALL(task_3, Run); |
| } |
| |
| tq->PostTask(FROM_HERE, task_1.Get()); |
| tq->PostTask(FROM_HERE, task_2.Get()); |
| tq->PostTask(FROM_HERE, task_3.Get()); |
| |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F(BrowserTaskQueuesTest, RunAllPendingTasksForTesting) { |
| handle_->OnStartupComplete(); |
| |
| StrictMockTask task; |
| StrictMockTask followup_task; |
| EXPECT_CALL(task, Run).WillOnce(Invoke([&]() { |
| for (size_t i = 0; i < BrowserTaskQueues::kNumQueueTypes; ++i) { |
| handle_->GetBrowserTaskRunner(static_cast<QueueType>(i)) |
| ->PostTask(FROM_HERE, followup_task.Get()); |
| } |
| })); |
| |
| handle_->GetBrowserTaskRunner(QueueType::kDefault) |
| ->PostTask(FROM_HERE, task.Get()); |
| |
| { |
| RunLoop run_loop; |
| handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure()); |
| run_loop.Run(); |
| } |
| |
| Mock::VerifyAndClearExpectations(&task); |
| EXPECT_CALL(followup_task, Run).Times(BrowserTaskQueues::kNumQueueTypes); |
| |
| RunLoop run_loop; |
| handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure()); |
| run_loop.Run(); |
| } |
| |
| TEST_F(BrowserTaskQueuesTest, RunAllPendingTasksForTestingRunsAllTasks) { |
| constexpr size_t kTasksPerPriority = 100; |
| handle_->OnStartupComplete(); |
| |
| StrictMockTask task; |
| EXPECT_CALL(task, Run).Times(BrowserTaskQueues::kNumQueueTypes * |
| kTasksPerPriority); |
| for (size_t i = 0; i < BrowserTaskQueues::kNumQueueTypes; ++i) { |
| for (size_t j = 0; j < kTasksPerPriority; ++j) { |
| handle_->GetBrowserTaskRunner(static_cast<QueueType>(i)) |
| ->PostTask(FROM_HERE, task.Get()); |
| } |
| } |
| |
| RunLoop run_loop; |
| handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure()); |
| run_loop.Run(); |
| } |
| |
| TEST_F(BrowserTaskQueuesTest, RunAllPendingTasksForTestingIsReentrant) { |
| handle_->OnStartupComplete(); |
| StrictMockTask task_1; |
| StrictMockTask task_2; |
| StrictMockTask task_3; |
| |
| EXPECT_CALL(task_1, Run).WillOnce(Invoke([&]() { |
| handle_->GetBrowserTaskRunner(QueueType::kDefault) |
| ->PostTask(FROM_HERE, task_2.Get()); |
| RunLoop run_loop(RunLoop::Type::kNestableTasksAllowed); |
| handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure()); |
| run_loop.Run(); |
| })); |
| |
| EXPECT_CALL(task_2, Run).WillOnce(Invoke([&]() { |
| handle_->GetBrowserTaskRunner(QueueType::kDefault) |
| ->PostTask(FROM_HERE, task_3.Get()); |
| })); |
| |
| handle_->GetBrowserTaskRunner(QueueType::kDefault) |
| ->PostTask(FROM_HERE, task_1.Get()); |
| |
| RunLoop run_loop; |
| handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure()); |
| run_loop.Run(); |
| } |
| |
| TEST_F(BrowserTaskQueuesTest, |
| RunAllPendingTasksForTestingIgnoresBestEffortIfNotEnabled) { |
| handle_->EnableAllExceptBestEffortQueues(); |
| StrictMockTask best_effort_task; |
| StrictMockTask default_task; |
| |
| handle_->GetBrowserTaskRunner(QueueType::kBestEffort) |
| ->PostTask(FROM_HERE, best_effort_task.Get()); |
| handle_->GetBrowserTaskRunner(QueueType::kDefault) |
| ->PostTask(FROM_HERE, default_task.Get()); |
| |
| EXPECT_CALL(default_task, Run); |
| |
| RunLoop run_loop; |
| handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure()); |
| run_loop.Run(); |
| } |
| |
| TEST_F(BrowserTaskQueuesTest, |
| RunAllPendingTasksForTestingRunsBestEffortTasksWhenEnabled) { |
| handle_->EnableAllExceptBestEffortQueues(); |
| StrictMockTask task_1; |
| StrictMockTask task_2; |
| StrictMockTask task_3; |
| |
| EXPECT_CALL(task_1, Run).WillOnce(Invoke([&]() { |
| // This task should not run as it is posted after the |
| // RunAllPendingTasksForTesting() call |
| handle_->GetBrowserTaskRunner(QueueType::kBestEffort) |
| ->PostTask(FROM_HERE, task_3.Get()); |
| handle_->OnStartupComplete(); |
| })); |
| EXPECT_CALL(task_2, Run); |
| |
| handle_->GetBrowserTaskRunner(QueueType::kDefault) |
| ->PostTask(FROM_HERE, task_1.Get()); |
| handle_->GetBrowserTaskRunner(QueueType::kBestEffort) |
| ->PostTask(FROM_HERE, task_2.Get()); |
| |
| RunLoop run_loop; |
| handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure()); |
| run_loop.Run(); |
| } |
| |
| TEST_F(BrowserTaskQueuesTest, HandleStillWorksWhenQueuesDestroyed) { |
| handle_->OnStartupComplete(); |
| StrictMockTask task; |
| queues_.reset(); |
| |
| for (size_t i = 0; i < BrowserTaskQueues::kNumQueueTypes; ++i) { |
| EXPECT_FALSE(handle_->GetBrowserTaskRunner(static_cast<QueueType>(i)) |
| ->PostTask(FROM_HERE, base::DoNothing())); |
| } |
| |
| RunLoop run_loop; |
| handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure()); |
| run_loop.Run(); |
| } |
| |
| TEST_F(BrowserTaskQueuesTest, TaskIsRunWhenEnableQueueIsCalled) { |
| StrictMockTask task; |
| StrictMockTask user_visible; |
| for (size_t i = 0; i < BrowserTaskQueues::kNumQueueTypes; ++i) { |
| auto queue_type = static_cast<QueueType>(i); |
| if (queue_type != QueueType::kUserVisible) { |
| handle_->GetBrowserTaskRunner(queue_type) |
| ->PostTask(FROM_HERE, task.Get()); |
| } |
| } |
| handle_->GetBrowserTaskRunner(QueueType::kUserVisible) |
| ->PostTask(FROM_HERE, user_visible.Get()); |
| |
| { |
| RunLoop run_loop; |
| handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure()); |
| run_loop.Run(); |
| } |
| |
| handle_->EnableTaskQueue(QueueType::kUserVisible); |
| |
| { |
| RunLoop run_loop; |
| handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure()); |
| EXPECT_CALL(user_visible, Run).Times(1); |
| run_loop.Run(); |
| } |
| } |
| |
| } // namespace |
| } // namespace content |