1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/scheduler/browser_task_executor.h"
6
7 #include <map>
8 #include <memory>
9 #include <utility>
10 #include <vector>
11
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/task/task_traits.h"
15 #include "base/task/thread_pool/thread_pool_instance.h"
16 #include "base/test/bind_test_util.h"
17 #include "base/test/mock_callback.h"
18 #include "base/test/task_environment.h"
19 #include "base/threading/thread_task_runner_handle.h"
20 #include "content/browser/scheduler/browser_io_thread_delegate.h"
21 #include "content/browser/scheduler/browser_task_queues.h"
22 #include "content/browser/scheduler/browser_ui_thread_scheduler.h"
23 #include "content/public/browser/browser_task_traits.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/test/browser_task_environment.h"
26 #include "testing/gmock/include/gmock/gmock.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28
29 namespace content {
30
31 using ::base::TaskPriority;
32 using ::testing::ElementsAre;
33 using ::testing::Invoke;
34 using ::testing::Mock;
35 using ::testing::NotNull;
36 using ::testing::SizeIs;
37
38 using QueueType = BrowserTaskQueues::QueueType;
39
40 class BrowserTaskExecutorTest : public testing::Test {
41 private:
42 BrowserTaskEnvironment task_environment_{
43 base::test::TaskEnvironment::MainThreadType::UI,
44 base::test::TaskEnvironment::TimeSource::MOCK_TIME};
45 };
46
47 using StrictMockTask =
48 testing::StrictMock<base::MockCallback<base::RepeatingCallback<void()>>>;
49
TEST_F(BrowserTaskExecutorTest,RegisterExecutorForBothThreads)50 TEST_F(BrowserTaskExecutorTest, RegisterExecutorForBothThreads) {
51 GetUIThreadTaskRunner({})->PostTask(
52 FROM_HERE, base::BindOnce([]() {
53 EXPECT_THAT(base::GetTaskExecutorForCurrentThread(), NotNull());
54 }));
55
56 GetIOThreadTaskRunner({})->PostTask(
57 FROM_HERE, base::BindOnce([]() {
58 EXPECT_THAT(base::GetTaskExecutorForCurrentThread(), NotNull());
59 }));
60
61 BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(BrowserThread::UI);
62 BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(BrowserThread::IO);
63 }
64
TEST_F(BrowserTaskExecutorTest,RunAllPendingTasksForTestingOnUI)65 TEST_F(BrowserTaskExecutorTest, RunAllPendingTasksForTestingOnUI) {
66 StrictMockTask task_1;
67 StrictMockTask task_2;
68 EXPECT_CALL(task_1, Run).WillOnce(testing::Invoke([&]() {
69 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, task_2.Get());
70 }));
71
72 GetUIThreadTaskRunner({})->PostTask(FROM_HERE, task_1.Get());
73
74 BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(BrowserThread::UI);
75
76 // Cleanup pending tasks, as BrowserTaskEnvironment will run them.
77 Mock::VerifyAndClearExpectations(&task_1);
78 EXPECT_CALL(task_2, Run);
79 BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(BrowserThread::IO);
80 }
81
TEST_F(BrowserTaskExecutorTest,RunAllPendingTasksForTestingOnIO)82 TEST_F(BrowserTaskExecutorTest, RunAllPendingTasksForTestingOnIO) {
83 StrictMockTask task_1;
84 StrictMockTask task_2;
85 EXPECT_CALL(task_1, Run).WillOnce(testing::Invoke([&]() {
86 GetIOThreadTaskRunner({})->PostTask(FROM_HERE, task_2.Get());
87 }));
88
89 GetIOThreadTaskRunner({})->PostTask(FROM_HERE, task_1.Get());
90
91 BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(BrowserThread::IO);
92
93 // Cleanup pending tasks, as BrowserTaskEnvironment will run them.
94 Mock::VerifyAndClearExpectations(&task_1);
95 EXPECT_CALL(task_2, Run);
96 BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(BrowserThread::IO);
97 }
98
TEST_F(BrowserTaskExecutorTest,RunAllPendingTasksForTestingOnIOIsReentrant)99 TEST_F(BrowserTaskExecutorTest, RunAllPendingTasksForTestingOnIOIsReentrant) {
100 StrictMockTask task_1;
101 StrictMockTask task_2;
102 StrictMockTask task_3;
103
104 EXPECT_CALL(task_1, Run).WillOnce(Invoke([&]() {
105 GetIOThreadTaskRunner({})->PostTask(FROM_HERE, task_2.Get());
106 BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(
107 BrowserThread::IO);
108 }));
109 EXPECT_CALL(task_2, Run).WillOnce(Invoke([&]() {
110 GetIOThreadTaskRunner({})->PostTask(FROM_HERE, task_3.Get());
111 }));
112
113 GetIOThreadTaskRunner({})->PostTask(FROM_HERE, task_1.Get());
114 BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(BrowserThread::IO);
115
116 // Cleanup pending tasks, as BrowserTaskEnvironment will run them.
117 Mock::VerifyAndClearExpectations(&task_1);
118 Mock::VerifyAndClearExpectations(&task_2);
119 EXPECT_CALL(task_3, Run);
120 BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(BrowserThread::IO);
121 }
122
123 // Helper to perform the same tets for all BrowserThread::ID values.
124 class BrowserTaskTraitsMappingTest : public BrowserTaskExecutorTest {
125 protected:
126 class TestExecutor : public BaseBrowserTaskExecutor {
127 public:
128 ~TestExecutor() override = default;
129
GetCurrentThreadID() const130 BrowserThread::ID GetCurrentThreadID() const override {
131 NOTREACHED();
132 return BrowserThread::UI;
133 }
134 };
135
136 private:
137 TestExecutor test_executor_;
138 };
139
TEST_F(BrowserTaskTraitsMappingTest,BrowserTaskTraitsMapToProperPriorities)140 TEST_F(BrowserTaskTraitsMappingTest, BrowserTaskTraitsMapToProperPriorities) {
141 EXPECT_EQ(BrowserTaskExecutor::GetQueueType({TaskPriority::BEST_EFFORT}),
142 QueueType::kBestEffort);
143 EXPECT_EQ(BrowserTaskExecutor::GetQueueType({TaskPriority::USER_VISIBLE}),
144 QueueType::kUserVisible);
145 EXPECT_EQ(BrowserTaskExecutor::GetQueueType({TaskPriority::USER_BLOCKING}),
146 QueueType::kUserBlocking);
147
148 EXPECT_EQ(BrowserTaskExecutor::GetQueueType({BrowserTaskType::kBootstrap}),
149 QueueType::kBootstrap);
150 EXPECT_EQ(BrowserTaskExecutor::GetQueueType({BrowserTaskType::kDefault}),
151 QueueType::kUserBlocking);
152 EXPECT_EQ(BrowserTaskExecutor::GetQueueType({BrowserTaskType::kPreconnect}),
153 QueueType::kPreconnection);
154
155 EXPECT_EQ(BrowserTaskExecutor::GetQueueType({}), QueueType::kUserBlocking);
156 }
157
TEST_F(BrowserTaskTraitsMappingTest,UIThreadTaskRunnerHasSamePriorityAsUIBlocking)158 TEST_F(BrowserTaskTraitsMappingTest,
159 UIThreadTaskRunnerHasSamePriorityAsUIBlocking) {
160 auto ui_blocking = GetUIThreadTaskRunner({TaskPriority::USER_BLOCKING});
161 auto thread_task_runner = base::ThreadTaskRunnerHandle::Get();
162
163 std::vector<int> order;
164 ui_blocking->PostTask(
165 FROM_HERE, base::BindLambdaForTesting([&]() { order.push_back(1); }));
166 thread_task_runner->PostTask(
167 FROM_HERE, base::BindLambdaForTesting([&]() { order.push_back(10); }));
168 ui_blocking->PostTask(
169 FROM_HERE, base::BindLambdaForTesting([&]() { order.push_back(2); }));
170 thread_task_runner->PostTask(
171 FROM_HERE, base::BindLambdaForTesting([&]() { order.push_back(20); }));
172
173 base::RunLoop().RunUntilIdle();
174
175 EXPECT_THAT(order, ElementsAre(1, 10, 2, 20));
176 }
177
178 class BrowserTaskExecutorWithCustomSchedulerTest : public testing::Test {
179 private:
180 class TaskEnvironmentWithCustomScheduler
181 : public base::test::TaskEnvironment {
182 public:
TaskEnvironmentWithCustomScheduler()183 TaskEnvironmentWithCustomScheduler()
184 : base::test::TaskEnvironment(
185 SubclassCreatesDefaultTaskRunner{},
186 base::test::TaskEnvironment::MainThreadType::UI,
187 base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
188 std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler =
189 BrowserUIThreadScheduler::CreateForTesting(sequence_manager(),
190 GetTimeDomain());
191 DeferredInitFromSubclass(
192 browser_ui_thread_scheduler->GetHandle()->GetBrowserTaskRunner(
193 QueueType::kDefault));
194 BrowserTaskExecutor::CreateForTesting(
195 std::move(browser_ui_thread_scheduler),
196 BrowserIOThreadDelegate::CreateForTesting(sequence_manager()));
197 BrowserTaskExecutor::BindToUIThreadForTesting();
198 }
199 };
200
201 public:
~BrowserTaskExecutorWithCustomSchedulerTest()202 ~BrowserTaskExecutorWithCustomSchedulerTest() override {
203 BrowserTaskExecutor::ResetForTesting();
204 }
205
206 protected:
207 TaskEnvironmentWithCustomScheduler task_environment_;
208 };
209
TEST_F(BrowserTaskExecutorWithCustomSchedulerTest,UserVisibleOrBlockingTasksRunDuringStartup)210 TEST_F(BrowserTaskExecutorWithCustomSchedulerTest,
211 UserVisibleOrBlockingTasksRunDuringStartup) {
212 StrictMockTask best_effort;
213 StrictMockTask user_visible;
214 StrictMockTask user_blocking;
215
216 GetUIThreadTaskRunner({base::TaskPriority::BEST_EFFORT})
217 ->PostTask(FROM_HERE,
218
219 best_effort.Get());
220 GetUIThreadTaskRunner({base::TaskPriority::USER_VISIBLE})
221 ->PostTask(FROM_HERE,
222
223 user_visible.Get());
224 GetUIThreadTaskRunner({base::TaskPriority::USER_BLOCKING})
225 ->PostTask(FROM_HERE,
226
227 user_blocking.Get());
228
229 EXPECT_CALL(user_visible, Run);
230 EXPECT_CALL(user_blocking, Run);
231
232 task_environment_.RunUntilIdle();
233 }
234
TEST_F(BrowserTaskExecutorWithCustomSchedulerTest,BestEffortTasksRunAfterStartup)235 TEST_F(BrowserTaskExecutorWithCustomSchedulerTest,
236 BestEffortTasksRunAfterStartup) {
237 auto ui_best_effort_runner =
238 GetUIThreadTaskRunner({base::TaskPriority::BEST_EFFORT});
239
240 StrictMockTask best_effort;
241
242 ui_best_effort_runner->PostTask(FROM_HERE, best_effort.Get());
243 ui_best_effort_runner->PostDelayedTask(
244 FROM_HERE, best_effort.Get(), base::TimeDelta::FromMilliseconds(100));
245 GetUIThreadTaskRunner({base::TaskPriority::BEST_EFFORT})
246 ->PostDelayedTask(FROM_HERE, best_effort.Get(),
247 base::TimeDelta::FromMilliseconds(100));
248 GetUIThreadTaskRunner({base::TaskPriority::BEST_EFFORT})
249 ->PostTask(FROM_HERE,
250
251 best_effort.Get());
252 task_environment_.RunUntilIdle();
253
254 BrowserTaskExecutor::EnableAllQueues();
255 EXPECT_CALL(best_effort, Run).Times(4);
256 task_environment_.FastForwardBy(base::TimeDelta::FromMilliseconds(100));
257 }
258
259 } // namespace content
260