1 // Copyright 2015 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.
5 #include "components/scheduler/child/task_queue_selector.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/pending_task.h"
10 #include "components/scheduler/child/task_queue_impl.h"
11 #include "components/scheduler/child/task_queue_sets.h"
12 #include "testing/gmock/include/gmock/gmock.h"
13 #include "testing/gtest/include/gtest/gtest.h"
20 class MockObserver
: public TaskQueueSelector::Observer
{
23 virtual ~MockObserver() {}
25 MOCK_METHOD1(OnTaskQueueEnabled
, void(internal::TaskQueueImpl
*));
28 DISALLOW_COPY_AND_ASSIGN(MockObserver
);
31 class TaskQueueSelectorTest
: public testing::Test
{
33 TaskQueueSelectorTest()
34 : test_closure_(base::Bind(&TaskQueueSelectorTest::TestFunction
)) {}
35 ~TaskQueueSelectorTest() override
{}
37 std::vector
<TaskQueueImpl::Task
> GetTasks(int count
) {
38 std::vector
<TaskQueueImpl::Task
> tasks
;
39 for (int i
= 0; i
< count
; i
++) {
40 TaskQueueImpl::Task
task(FROM_HERE
, test_closure_
, 0, true);
41 task
.set_enqueue_order(i
);
42 tasks
.push_back(task
);
47 void PushTasks(const std::vector
<TaskQueueImpl::Task
>& tasks
,
48 const size_t queue_indices
[]) {
49 std::set
<size_t> changed_queue_set
;
50 for (size_t i
= 0; i
< tasks
.size(); i
++) {
51 changed_queue_set
.insert(queue_indices
[i
]);
52 task_queues_
[queue_indices
[i
]]->PushTaskOntoWorkQueueForTest(tasks
[i
]);
54 for (size_t queue_index
: changed_queue_set
) {
55 selector_
.GetTaskQueueSets()->OnPushQueue(
56 task_queues_
[queue_index
].get());
60 std::vector
<size_t> PopTasks() {
61 std::vector
<size_t> order
;
62 TaskQueueImpl
* chosen_queue
;
63 while (selector_
.SelectQueueToService(&chosen_queue
)) {
64 size_t chosen_queue_index
=
65 queue_to_index_map_
.find(chosen_queue
)->second
;
66 order
.push_back(chosen_queue_index
);
67 chosen_queue
->PopTaskFromWorkQueueForTest();
68 selector_
.GetTaskQueueSets()->OnPopQueue(chosen_queue
);
73 static void TestFunction() {}
77 for (size_t i
= 0; i
< kTaskQueueCount
; i
++) {
78 scoped_refptr
<TaskQueueImpl
> task_queue
=
79 make_scoped_refptr(new TaskQueueImpl(
80 nullptr, TaskQueue::Spec("test queue"), "test", "test"));
81 selector_
.AddQueue(task_queue
.get());
82 task_queues_
.push_back(task_queue
);
84 for (size_t i
= 0; i
< kTaskQueueCount
; i
++) {
85 EXPECT_TRUE(selector_
.IsQueueEnabled(task_queues_
[i
].get())) << i
;
86 queue_to_index_map_
.insert(std::make_pair(task_queues_
[i
].get(), i
));
90 const size_t kTaskQueueCount
= 5;
91 base::Closure test_closure_
;
92 TaskQueueSelector selector_
;
93 std::vector
<scoped_refptr
<TaskQueueImpl
>> task_queues_
;
94 std::map
<TaskQueueImpl
*, size_t> queue_to_index_map_
;
97 TEST_F(TaskQueueSelectorTest
, TestDefaultPriority
) {
98 std::vector
<TaskQueueImpl::Task
> tasks
= GetTasks(5);
99 size_t queue_order
[] = {4, 3, 2, 1, 0};
100 PushTasks(tasks
, queue_order
);
101 EXPECT_THAT(PopTasks(), testing::ElementsAre(4, 3, 2, 1, 0));
104 TEST_F(TaskQueueSelectorTest
, TestHighPriority
) {
105 std::vector
<TaskQueueImpl::Task
> tasks
= GetTasks(5);
106 size_t queue_order
[] = {0, 1, 2, 3, 4};
107 PushTasks(tasks
, queue_order
);
108 selector_
.SetQueuePriority(task_queues_
[2].get(), TaskQueue::HIGH_PRIORITY
);
109 EXPECT_THAT(PopTasks(), testing::ElementsAre(2, 0, 1, 3, 4));
112 TEST_F(TaskQueueSelectorTest
, TestBestEffortPriority
) {
113 std::vector
<TaskQueueImpl::Task
> tasks
= GetTasks(5);
114 size_t queue_order
[] = {0, 1, 2, 3, 4};
115 PushTasks(tasks
, queue_order
);
116 selector_
.SetQueuePriority(task_queues_
[0].get(),
117 TaskQueue::BEST_EFFORT_PRIORITY
);
118 selector_
.SetQueuePriority(task_queues_
[2].get(), TaskQueue::HIGH_PRIORITY
);
119 EXPECT_THAT(PopTasks(), testing::ElementsAre(2, 1, 3, 4, 0));
122 TEST_F(TaskQueueSelectorTest
, TestControlPriority
) {
123 std::vector
<TaskQueueImpl::Task
> tasks
= GetTasks(5);
124 size_t queue_order
[] = {0, 1, 2, 3, 4};
125 PushTasks(tasks
, queue_order
);
126 selector_
.SetQueuePriority(task_queues_
[4].get(),
127 TaskQueue::CONTROL_PRIORITY
);
128 EXPECT_TRUE(selector_
.IsQueueEnabled(task_queues_
[4].get()));
129 selector_
.SetQueuePriority(task_queues_
[2].get(), TaskQueue::HIGH_PRIORITY
);
130 EXPECT_TRUE(selector_
.IsQueueEnabled(task_queues_
[2].get()));
131 EXPECT_THAT(PopTasks(), testing::ElementsAre(4, 2, 0, 1, 3));
134 TEST_F(TaskQueueSelectorTest
, TestObserverWithSetQueuePriority
) {
135 selector_
.SetQueuePriority(task_queues_
[1].get(),
136 TaskQueue::DISABLED_PRIORITY
);
137 MockObserver mock_observer
;
138 selector_
.SetTaskQueueSelectorObserver(&mock_observer
);
139 EXPECT_CALL(mock_observer
, OnTaskQueueEnabled(_
)).Times(1);
140 selector_
.SetQueuePriority(task_queues_
[1].get(), TaskQueue::NORMAL_PRIORITY
);
143 TEST_F(TaskQueueSelectorTest
,
144 TestObserverWithSetQueuePriorityAndQueueAlreadyEnabed
) {
145 selector_
.SetQueuePriority(task_queues_
[1].get(), TaskQueue::NORMAL_PRIORITY
);
146 MockObserver mock_observer
;
147 selector_
.SetTaskQueueSelectorObserver(&mock_observer
);
148 EXPECT_CALL(mock_observer
, OnTaskQueueEnabled(_
)).Times(0);
149 selector_
.SetQueuePriority(task_queues_
[1].get(), TaskQueue::NORMAL_PRIORITY
);
152 TEST_F(TaskQueueSelectorTest
, TestDisableEnable
) {
153 MockObserver mock_observer
;
154 selector_
.SetTaskQueueSelectorObserver(&mock_observer
);
156 std::vector
<TaskQueueImpl::Task
> tasks
= GetTasks(5);
157 size_t queue_order
[] = {0, 1, 2, 3, 4};
158 PushTasks(tasks
, queue_order
);
159 selector_
.SetQueuePriority(task_queues_
[2].get(),
160 TaskQueue::DISABLED_PRIORITY
);
161 EXPECT_FALSE(selector_
.IsQueueEnabled(task_queues_
[2].get()));
162 selector_
.SetQueuePriority(task_queues_
[4].get(),
163 TaskQueue::DISABLED_PRIORITY
);
164 EXPECT_FALSE(selector_
.IsQueueEnabled(task_queues_
[4].get()));
165 EXPECT_THAT(PopTasks(), testing::ElementsAre(0, 1, 3));
167 EXPECT_CALL(mock_observer
, OnTaskQueueEnabled(_
)).Times(2);
168 selector_
.SetQueuePriority(task_queues_
[2].get(),
169 TaskQueue::BEST_EFFORT_PRIORITY
);
170 EXPECT_THAT(PopTasks(), testing::ElementsAre(2));
171 selector_
.SetQueuePriority(task_queues_
[4].get(), TaskQueue::NORMAL_PRIORITY
);
172 EXPECT_THAT(PopTasks(), testing::ElementsAre(4));
175 TEST_F(TaskQueueSelectorTest
, TestEmptyQueues
) {
176 TaskQueueImpl
* chosen_queue
= nullptr;
177 EXPECT_FALSE(selector_
.SelectQueueToService(&chosen_queue
));
179 // Test only disabled queues.
180 std::vector
<TaskQueueImpl::Task
> tasks
= GetTasks(1);
181 size_t queue_order
[] = {0};
182 PushTasks(tasks
, queue_order
);
183 selector_
.SetQueuePriority(task_queues_
[0].get(),
184 TaskQueue::DISABLED_PRIORITY
);
185 EXPECT_FALSE(selector_
.IsQueueEnabled(task_queues_
[0].get()));
186 EXPECT_FALSE(selector_
.SelectQueueToService(&chosen_queue
));
189 TEST_F(TaskQueueSelectorTest
, TestAge
) {
190 std::vector
<TaskQueueImpl::Task
> tasks
;
191 int enqueue_order
[] = {10, 1, 2, 9, 4};
192 for (int i
= 0; i
< 5; i
++) {
193 TaskQueueImpl::Task
task(FROM_HERE
, test_closure_
, 0, true);
194 task
.set_enqueue_order(enqueue_order
[i
]);
195 tasks
.push_back(task
);
197 size_t queue_order
[] = {0, 1, 2, 3, 4};
198 PushTasks(tasks
, queue_order
);
199 EXPECT_THAT(PopTasks(), testing::ElementsAre(1, 2, 4, 3, 0));
202 TEST_F(TaskQueueSelectorTest
, TestControlStarvesOthers
) {
203 std::vector
<TaskQueueImpl::Task
> tasks
= GetTasks(4);
204 size_t queue_order
[] = {0, 1, 2, 3};
205 PushTasks(tasks
, queue_order
);
206 selector_
.SetQueuePriority(task_queues_
[3].get(),
207 TaskQueue::CONTROL_PRIORITY
);
208 selector_
.SetQueuePriority(task_queues_
[2].get(), TaskQueue::HIGH_PRIORITY
);
209 selector_
.SetQueuePriority(task_queues_
[1].get(),
210 TaskQueue::BEST_EFFORT_PRIORITY
);
211 for (int i
= 0; i
< 100; i
++) {
212 TaskQueueImpl
* chosen_queue
= nullptr;
213 EXPECT_TRUE(selector_
.SelectQueueToService(&chosen_queue
));
214 EXPECT_EQ(task_queues_
[3].get(), chosen_queue
);
215 // Don't remove task from queue to simulate all queues still being full.
219 TEST_F(TaskQueueSelectorTest
, TestHighPriorityDoesNotStarveNormal
) {
220 std::vector
<TaskQueueImpl::Task
> tasks
= GetTasks(3);
221 size_t queue_order
[] = {0, 1, 2};
222 PushTasks(tasks
, queue_order
);
223 selector_
.SetQueuePriority(task_queues_
[2].get(), TaskQueue::HIGH_PRIORITY
);
224 selector_
.SetQueuePriority(task_queues_
[1].get(),
225 TaskQueue::BEST_EFFORT_PRIORITY
);
226 size_t counts
[] = {0, 0, 0};
227 for (int i
= 0; i
< 100; i
++) {
228 TaskQueueImpl
* chosen_queue
= nullptr;
229 EXPECT_TRUE(selector_
.SelectQueueToService(&chosen_queue
));
230 size_t chosen_queue_index
= queue_to_index_map_
.find(chosen_queue
)->second
;
231 counts
[chosen_queue_index
]++;
232 // Don't remove task from queue to simulate all queues still being full.
234 EXPECT_GT(counts
[0], 0ul); // Check high doesn't starve normal.
235 EXPECT_GT(counts
[2], counts
[0]); // Check high gets more chance to run.
236 EXPECT_EQ(0ul, counts
[1]); // Check best effort is starved.
239 TEST_F(TaskQueueSelectorTest
, TestBestEffortGetsStarved
) {
240 std::vector
<TaskQueueImpl::Task
> tasks
= GetTasks(2);
241 size_t queue_order
[] = {0, 1};
242 PushTasks(tasks
, queue_order
);
243 selector_
.SetQueuePriority(task_queues_
[0].get(),
244 TaskQueue::BEST_EFFORT_PRIORITY
);
245 selector_
.SetQueuePriority(task_queues_
[1].get(), TaskQueue::NORMAL_PRIORITY
);
246 TaskQueueImpl
* chosen_queue
= nullptr;
247 for (int i
= 0; i
< 100; i
++) {
248 EXPECT_TRUE(selector_
.SelectQueueToService(&chosen_queue
));
249 EXPECT_EQ(task_queues_
[1].get(), chosen_queue
);
250 // Don't remove task from queue to simulate all queues still being full.
252 selector_
.SetQueuePriority(task_queues_
[1].get(), TaskQueue::HIGH_PRIORITY
);
253 for (int i
= 0; i
< 100; i
++) {
254 EXPECT_TRUE(selector_
.SelectQueueToService(&chosen_queue
));
255 EXPECT_EQ(task_queues_
[1].get(), chosen_queue
);
256 // Don't remove task from queue to simulate all queues still being full.
258 selector_
.SetQueuePriority(task_queues_
[1].get(),
259 TaskQueue::CONTROL_PRIORITY
);
260 for (int i
= 0; i
< 100; i
++) {
261 EXPECT_TRUE(selector_
.SelectQueueToService(&chosen_queue
));
262 EXPECT_EQ(task_queues_
[1].get(), chosen_queue
);
263 // Don't remove task from queue to simulate all queues still being full.
267 TEST_F(TaskQueueSelectorTest
, DisabledPriorityIsPenultimate
) {
268 EXPECT_EQ(TaskQueue::QUEUE_PRIORITY_COUNT
, TaskQueue::DISABLED_PRIORITY
+ 1);
271 TEST_F(TaskQueueSelectorTest
, EnabledWorkQueuesEmpty
) {
272 EXPECT_TRUE(selector_
.EnabledWorkQueuesEmpty());
273 std::vector
<TaskQueueImpl::Task
> tasks
= GetTasks(2);
274 size_t queue_order
[] = {0, 1};
275 PushTasks(tasks
, queue_order
);
277 EXPECT_FALSE(selector_
.EnabledWorkQueuesEmpty());
279 EXPECT_TRUE(selector_
.EnabledWorkQueuesEmpty());
282 } // namespace internal
283 } // namespace scheduler