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