• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 The Chromium Authors
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 "base/task/sequence_manager/task_queue_selector.h"
6 
7 #include <stddef.h>
8 
9 #include <map>
10 #include <memory>
11 #include <set>
12 #include <utility>
13 #include <vector>
14 
15 #include "base/functional/bind.h"
16 #include "base/memory/ptr_util.h"
17 #include "base/pending_task.h"
18 #include "base/task/sequence_manager/enqueue_order_generator.h"
19 #include "base/task/sequence_manager/task_queue_impl.h"
20 #include "base/task/sequence_manager/test/mock_time_domain.h"
21 #include "base/task/sequence_manager/work_queue.h"
22 #include "base/task/sequence_manager/work_queue_sets.h"
23 #include "testing/gmock/include/gmock/gmock.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 
26 using testing::_;
27 using testing::ElementsAre;
28 using testing::NotNull;
29 
30 namespace base {
31 namespace sequence_manager {
32 namespace internal {
33 // To avoid symbol collisions in jumbo builds.
34 namespace task_queue_selector_unittest {
35 
36 namespace {
37 const TaskQueue::QueuePriority kHighestPriority = 0;
38 const TaskQueue::QueuePriority kHighPriority = 4;
39 const TaskQueue::QueuePriority kDefaultPriority = 5;
40 const TaskQueue::QueuePriority kPriorityCount = 10;
41 const size_t kTaskQueueCount = kPriorityCount;
42 
43 // Tests assume high priority is higher than default priority, but nothing about
44 // their values.
45 static_assert(kHighPriority < kDefaultPriority);
46 }  // namespace
47 
48 class MockObserver : public TaskQueueSelector::Observer {
49  public:
50   MockObserver() = default;
51   MockObserver(const MockObserver&) = delete;
52   MockObserver& operator=(const MockObserver&) = delete;
53   ~MockObserver() override = default;
54 
55   MOCK_METHOD1(OnTaskQueueEnabled, void(internal::TaskQueueImpl*));
56 };
57 
58 class TaskQueueSelectorForTest : public TaskQueueSelector {
59  public:
60   using TaskQueueSelector::ActivePriorityTracker;
61   using TaskQueueSelector::ChooseWithPriority;
62   using TaskQueueSelector::delayed_work_queue_sets;
63   using TaskQueueSelector::immediate_work_queue_sets;
64   using TaskQueueSelector::SetImmediateStarvationCountForTest;
65   using TaskQueueSelector::SetOperationOldest;
66 
TaskQueueSelectorForTest(scoped_refptr<AssociatedThreadId> associated_thread)67   explicit TaskQueueSelectorForTest(
68       scoped_refptr<AssociatedThreadId> associated_thread)
69       : TaskQueueSelector(
70             associated_thread,
71             SequenceManager::Settings::Builder()
72                 .SetPrioritySettings(
73                     SequenceManager::PrioritySettings(kPriorityCount,
74                                                       kDefaultPriority))
75                 .Build()) {}
76 };
77 
78 class TaskQueueSelectorTest : public testing::Test {
79  public:
TaskQueueSelectorTest()80   TaskQueueSelectorTest()
81       : test_closure_(BindRepeating(&TaskQueueSelectorTest::TestFunction)),
82         associated_thread_(AssociatedThreadId::CreateBound()),
83         selector_(associated_thread_) {}
84   ~TaskQueueSelectorTest() override = default;
85 
PushTasks(const size_t queue_indices[],size_t num_tasks)86   void PushTasks(const size_t queue_indices[], size_t num_tasks) {
87     EnqueueOrderGenerator enqueue_order_generator;
88     for (size_t i = 0; i < num_tasks; i++) {
89       task_queues_[queue_indices[i]]->immediate_work_queue()->Push(
90           Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
91                enqueue_order_generator.GenerateNext()));
92     }
93   }
94 
PushTasksWithEnqueueOrder(const size_t queue_indices[],const size_t enqueue_orders[],size_t num_tasks)95   void PushTasksWithEnqueueOrder(const size_t queue_indices[],
96                                  const size_t enqueue_orders[],
97                                  size_t num_tasks) {
98     for (size_t i = 0; i < num_tasks; i++) {
99       task_queues_[queue_indices[i]]->immediate_work_queue()->Push(
100           Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
101                EnqueueOrder::FromIntForTesting(enqueue_orders[i])));
102     }
103   }
104 
PushTask(const size_t queue_index,const size_t enqueue_order)105   void PushTask(const size_t queue_index, const size_t enqueue_order) {
106     task_queues_[queue_index]->immediate_work_queue()->Push(
107         Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
108              EnqueueOrder::FromIntForTesting(enqueue_order)));
109   }
110 
PopTasksAndReturnQueueIndices()111   std::vector<size_t> PopTasksAndReturnQueueIndices() {
112     std::vector<size_t> order;
113     while (WorkQueue* chosen_work_queue =
114                selector_.SelectWorkQueueToService()) {
115       size_t chosen_queue_index =
116           queue_to_index_map_.find(chosen_work_queue->task_queue())->second;
117       order.push_back(chosen_queue_index);
118       chosen_work_queue->PopTaskForTesting();
119       chosen_work_queue->work_queue_sets()->OnPopMinQueueInSet(
120           chosen_work_queue);
121     }
122     return order;
123   }
124 
TestFunction()125   static void TestFunction() {}
126 
127  protected:
SetUp()128   void SetUp() final {
129     for (size_t i = 0; i < kTaskQueueCount; i++) {
130       std::unique_ptr<TaskQueueImpl> task_queue =
131           std::make_unique<TaskQueueImpl>(nullptr, nullptr,
132                                           TaskQueue::Spec(QueueName::TEST_TQ));
133       selector_.AddQueue(task_queue.get(), kDefaultPriority);
134       task_queues_.push_back(std::move(task_queue));
135     }
136     for (size_t i = 0; i < kTaskQueueCount; i++) {
137       EXPECT_EQ(kDefaultPriority, task_queues_[i]->GetQueuePriority()) << i;
138       queue_to_index_map_.insert(std::make_pair(task_queues_[i].get(), i));
139     }
140   }
141 
TearDown()142   void TearDown() final {
143     for (std::unique_ptr<TaskQueueImpl>& task_queue : task_queues_) {
144       // Note since this test doesn't have a SequenceManager we need to
145       // manually remove |task_queue| from the |selector_|.  Normally
146       // UnregisterTaskQueue would do that.
147       selector_.RemoveQueue(task_queue.get());
148       task_queue->UnregisterTaskQueue();
149     }
150   }
151 
NewTaskQueueWithBlockReporting()152   std::unique_ptr<TaskQueueImpl> NewTaskQueueWithBlockReporting() {
153     return std::make_unique<TaskQueueImpl>(nullptr, nullptr,
154                                            TaskQueue::Spec(QueueName::TEST_TQ));
155   }
156 
157   RepeatingClosure test_closure_;
158   scoped_refptr<AssociatedThreadId> associated_thread_;
159   TaskQueueSelectorForTest selector_;
160   std::vector<std::unique_ptr<TaskQueueImpl>> task_queues_;
161   std::map<TaskQueueImpl*, size_t> queue_to_index_map_;
162 };
163 
TEST_F(TaskQueueSelectorTest,TestDefaultPriority)164 TEST_F(TaskQueueSelectorTest, TestDefaultPriority) {
165   size_t queue_order[] = {4, 3, 2, 1, 0};
166   PushTasks(queue_order, 5);
167   EXPECT_THAT(PopTasksAndReturnQueueIndices(), ElementsAre(4, 3, 2, 1, 0));
168 }
169 
TEST_F(TaskQueueSelectorTest,TestPriorities)170 TEST_F(TaskQueueSelectorTest, TestPriorities) {
171   for (size_t priority = 0; priority < kDefaultPriority; ++priority) {
172     size_t queue_order[] = {0, 1, 2, 3, 4};
173     PushTasks(queue_order, 5);
174     if (priority != kDefaultPriority) {
175       selector_.SetQueuePriority(task_queues_[2].get(), priority);
176     }
177     EXPECT_THAT(PopTasksAndReturnQueueIndices(), ElementsAre(2, 0, 1, 3, 4));
178   }
179 }
180 
TEST_F(TaskQueueSelectorTest,TestMultiplePriorities)181 TEST_F(TaskQueueSelectorTest, TestMultiplePriorities) {
182   size_t reverse_priority_order[kPriorityCount];
183   for (size_t priority = 0; priority < kPriorityCount; ++priority) {
184     reverse_priority_order[(kPriorityCount - 1) - priority] = priority;
185   }
186 
187   PushTasks(reverse_priority_order, kPriorityCount);
188 
189   for (size_t i = 0; i < kPriorityCount; ++i) {
190     TaskQueue::QueuePriority priority = reverse_priority_order[i];
191     if (priority != kDefaultPriority) {
192       selector_.SetQueuePriority(task_queues_[i].get(), priority);
193     }
194     EXPECT_EQ(task_queues_[i]->GetQueuePriority(), priority);
195   }
196 
197   EXPECT_THAT(PopTasksAndReturnQueueIndices(),
198               ElementsAre(9, 8, 7, 6, 5, 4, 3, 2, 1, 0));
199 }
200 
TEST_F(TaskQueueSelectorTest,TestObserverWithEnabledQueue)201 TEST_F(TaskQueueSelectorTest, TestObserverWithEnabledQueue) {
202   task_queues_[1]->SetQueueEnabled(false);
203   selector_.DisableQueue(task_queues_[1].get());
204   {
205     MockObserver mock_observer;
206     selector_.SetTaskQueueSelectorObserver(&mock_observer);
207     EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(1);
208     task_queues_[1]->SetQueueEnabled(true);
209     selector_.EnableQueue(task_queues_[1].get());
210 
211     // Clear observer before it goes out of scope.
212     selector_.SetTaskQueueSelectorObserver(nullptr);
213   }
214 }
215 
TEST_F(TaskQueueSelectorTest,TestObserverWithSetQueuePriorityAndQueueAlreadyEnabled)216 TEST_F(TaskQueueSelectorTest,
217        TestObserverWithSetQueuePriorityAndQueueAlreadyEnabled) {
218   selector_.SetQueuePriority(task_queues_[1].get(), kHighPriority);
219   {
220     MockObserver mock_observer;
221     selector_.SetTaskQueueSelectorObserver(&mock_observer);
222     EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(0);
223     selector_.SetQueuePriority(task_queues_[1].get(), kDefaultPriority);
224 
225     // Clear observer before it goes out of scope.
226     selector_.SetTaskQueueSelectorObserver(nullptr);
227   }
228 }
229 
TEST_F(TaskQueueSelectorTest,TestDisableEnable)230 TEST_F(TaskQueueSelectorTest, TestDisableEnable) {
231   MockObserver mock_observer;
232   selector_.SetTaskQueueSelectorObserver(&mock_observer);
233 
234   size_t queue_order[] = {0, 1, 2, 3, 4};
235   PushTasks(queue_order, 5);
236   task_queues_[2]->SetQueueEnabled(false);
237   selector_.DisableQueue(task_queues_[2].get());
238   task_queues_[4]->SetQueueEnabled(false);
239   selector_.DisableQueue(task_queues_[4].get());
240   // Disabling a queue should not affect its priority.
241   EXPECT_EQ(kDefaultPriority, task_queues_[2]->GetQueuePriority());
242   EXPECT_EQ(kDefaultPriority, task_queues_[4]->GetQueuePriority());
243   EXPECT_THAT(PopTasksAndReturnQueueIndices(), ElementsAre(0, 1, 3));
244 
245   EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(2);
246   task_queues_[2]->SetQueueEnabled(true);
247   selector_.EnableQueue(task_queues_[2].get());
248   selector_.SetQueuePriority(task_queues_[2].get(), kPriorityCount - 1);
249   EXPECT_THAT(PopTasksAndReturnQueueIndices(), ElementsAre(2));
250   task_queues_[4]->SetQueueEnabled(true);
251   selector_.EnableQueue(task_queues_[4].get());
252   EXPECT_THAT(PopTasksAndReturnQueueIndices(), ElementsAre(4));
253 
254   // Clear observer before it goes out of scope.
255   selector_.SetTaskQueueSelectorObserver(nullptr);
256 }
257 
TEST_F(TaskQueueSelectorTest,TestDisableChangePriorityThenEnable)258 TEST_F(TaskQueueSelectorTest, TestDisableChangePriorityThenEnable) {
259   EXPECT_TRUE(task_queues_[2]->delayed_work_queue()->Empty());
260   EXPECT_TRUE(task_queues_[2]->immediate_work_queue()->Empty());
261 
262   task_queues_[2]->SetQueueEnabled(false);
263   selector_.SetQueuePriority(task_queues_[2].get(), kHighPriority);
264 
265   size_t queue_order[] = {0, 1, 2, 3, 4};
266   PushTasks(queue_order, 5);
267 
268   EXPECT_TRUE(task_queues_[2]->delayed_work_queue()->Empty());
269   EXPECT_FALSE(task_queues_[2]->immediate_work_queue()->Empty());
270   task_queues_[2]->SetQueueEnabled(true);
271 
272   EXPECT_EQ(kHighPriority, task_queues_[2]->GetQueuePriority());
273   EXPECT_THAT(PopTasksAndReturnQueueIndices(), ElementsAre(2, 0, 1, 3, 4));
274 }
275 
TEST_F(TaskQueueSelectorTest,TestEmptyQueues)276 TEST_F(TaskQueueSelectorTest, TestEmptyQueues) {
277   EXPECT_EQ(nullptr, selector_.SelectWorkQueueToService());
278 
279   // Test only disabled queues.
280   size_t queue_order[] = {0};
281   PushTasks(queue_order, 1);
282   task_queues_[0]->SetQueueEnabled(false);
283   selector_.DisableQueue(task_queues_[0].get());
284   EXPECT_EQ(nullptr, selector_.SelectWorkQueueToService());
285 
286   // These tests are unusual since there's no TQM. To avoid a later DCHECK when
287   // deleting the task queue, we re-enable the queue here so the selector
288   // doesn't get out of sync.
289   task_queues_[0]->SetQueueEnabled(true);
290   selector_.EnableQueue(task_queues_[0].get());
291 }
292 
TEST_F(TaskQueueSelectorTest,TestAge)293 TEST_F(TaskQueueSelectorTest, TestAge) {
294   size_t enqueue_order[] = {10, 1, 2, 9, 4};
295   size_t queue_order[] = {0, 1, 2, 3, 4};
296   PushTasksWithEnqueueOrder(queue_order, enqueue_order, 5);
297   EXPECT_THAT(PopTasksAndReturnQueueIndices(), ElementsAre(1, 2, 4, 3, 0));
298 }
299 
300 class TaskQueueSelectorStarvationTest : public TaskQueueSelectorTest {
301  public:
302   TaskQueueSelectorStarvationTest() = default;
303 
304  protected:
TestPriorityOrder(const size_t queue_order[],size_t num_tasks)305   void TestPriorityOrder(const size_t queue_order[], size_t num_tasks) {
306     for (size_t i = 0; i < kTaskQueueCount; i++) {
307       // Setting the queue priority to its current value causes a check to fail.
308       if (task_queues_[i]->GetQueuePriority() !=
309           static_cast<TaskQueue::QueuePriority>(i)) {
310         selector_.SetQueuePriority(task_queues_[i].get(),
311                                    static_cast<TaskQueue::QueuePriority>(i));
312       }
313     }
314 
315     ASSERT_EQ(num_tasks, kTaskQueueCount);
316     PushTasks(queue_order, kTaskQueueCount);
317 
318     for (size_t priority = 0; priority < kTaskQueueCount; priority++) {
319       for (int i = 0; i < 100; i++) {
320         WorkQueue* chosen_work_queue = selector_.SelectWorkQueueToService();
321         ASSERT_THAT(chosen_work_queue, NotNull());
322         EXPECT_EQ(task_queues_[priority].get(),
323                   chosen_work_queue->task_queue());
324         // Don't remove task from queue to simulate all queues still being full.
325       }
326 
327       // Simulate the highest priority queue becoming empty.
328       WorkQueue* chosen_work_queue = selector_.SelectWorkQueueToService();
329       chosen_work_queue->PopTaskForTesting();
330       chosen_work_queue->work_queue_sets()->OnPopMinQueueInSet(
331           chosen_work_queue);
332     }
333   }
334 };
335 
TEST_F(TaskQueueSelectorStarvationTest,HigherPriorityWorkStarvesLowerPriorityWork)336 TEST_F(TaskQueueSelectorStarvationTest,
337        HigherPriorityWorkStarvesLowerPriorityWork) {
338   size_t queue_order[kTaskQueueCount];
339   for (size_t i = 0; i < kTaskQueueCount; i++)
340     queue_order[i] = i;
341   TestPriorityOrder(queue_order, kTaskQueueCount);
342 }
343 
TEST_F(TaskQueueSelectorStarvationTest,NewHigherPriorityTasksStarveOldLowerPriorityTasks)344 TEST_F(TaskQueueSelectorStarvationTest,
345        NewHigherPriorityTasksStarveOldLowerPriorityTasks) {
346   // Enqueue tasks in order from lowest to highest priority, and check that they
347   // still run in order from highest to lowest priority.
348   size_t queue_order[kTaskQueueCount];
349   for (size_t i = 0; i < kTaskQueueCount; i++)
350     queue_order[i] = (kTaskQueueCount - i) - 1;
351   TestPriorityOrder(queue_order, kTaskQueueCount);
352 }
353 
TEST_F(TaskQueueSelectorTest,GetHighestPendingPriority)354 TEST_F(TaskQueueSelectorTest, GetHighestPendingPriority) {
355   EXPECT_FALSE(selector_.GetHighestPendingPriority().has_value());
356   size_t queue_order[] = {0, 1};
357   PushTasks(queue_order, 2);
358 
359   selector_.SetQueuePriority(task_queues_[1].get(), kHighPriority);
360 
361   EXPECT_EQ(kHighPriority, *selector_.GetHighestPendingPriority());
362   PopTasksAndReturnQueueIndices();
363   EXPECT_FALSE(selector_.GetHighestPendingPriority().has_value());
364 
365   PushTasks(queue_order, 1);
366   EXPECT_EQ(kDefaultPriority, *selector_.GetHighestPendingPriority());
367   PopTasksAndReturnQueueIndices();
368   EXPECT_FALSE(selector_.GetHighestPendingPriority().has_value());
369 }
370 
TEST_F(TaskQueueSelectorTest,ChooseWithPriority_Empty)371 TEST_F(TaskQueueSelectorTest, ChooseWithPriority_Empty) {
372   EXPECT_EQ(
373       nullptr,
374       selector_
375           .ChooseWithPriority<TaskQueueSelectorForTest::SetOperationOldest>(
376               kDefaultPriority));
377 }
378 
TEST_F(TaskQueueSelectorTest,ChooseWithPriority_OnlyDelayed)379 TEST_F(TaskQueueSelectorTest, ChooseWithPriority_OnlyDelayed) {
380   task_queues_[0]->delayed_work_queue()->Push(
381       Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
382            EnqueueOrder::FromIntForTesting(2)));
383 
384   EXPECT_EQ(
385       task_queues_[0]->delayed_work_queue(),
386       selector_
387           .ChooseWithPriority<TaskQueueSelectorForTest::SetOperationOldest>(
388               kDefaultPriority));
389 }
390 
TEST_F(TaskQueueSelectorTest,ChooseWithPriority_OnlyImmediate)391 TEST_F(TaskQueueSelectorTest, ChooseWithPriority_OnlyImmediate) {
392   task_queues_[0]->immediate_work_queue()->Push(
393       Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
394            EnqueueOrder::FromIntForTesting(2)));
395 
396   EXPECT_EQ(
397       task_queues_[0]->immediate_work_queue(),
398       selector_
399           .ChooseWithPriority<TaskQueueSelectorForTest::SetOperationOldest>(
400               kDefaultPriority));
401 }
402 
TEST_F(TaskQueueSelectorTest,SelectWorkQueueToServiceImmediateOnlyWithoutImmediateTask)403 TEST_F(TaskQueueSelectorTest,
404        SelectWorkQueueToServiceImmediateOnlyWithoutImmediateTask) {
405   task_queues_[0]->delayed_work_queue()->Push(
406       Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
407            EnqueueOrder::FromIntForTesting(2)));
408 
409   EXPECT_EQ(nullptr,
410             selector_.SelectWorkQueueToService(
411                 TaskQueueSelector::SelectTaskOption::kSkipDelayedTask));
412   EXPECT_EQ(task_queues_[0]->delayed_work_queue(),
413             selector_.SelectWorkQueueToService());
414 }
415 
TEST_F(TaskQueueSelectorTest,SelectWorkQueueToServiceImmediateOnlyWithDelayedTasks)416 TEST_F(TaskQueueSelectorTest,
417        SelectWorkQueueToServiceImmediateOnlyWithDelayedTasks) {
418   task_queues_[0]->delayed_work_queue()->Push(
419       Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
420            EnqueueOrder::FromIntForTesting(1)));
421   task_queues_[0]->immediate_work_queue()->Push(
422       Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
423            EnqueueOrder::FromIntForTesting(2)));
424 
425   EXPECT_EQ(task_queues_[0]->immediate_work_queue(),
426             selector_.SelectWorkQueueToService(
427                 TaskQueueSelector::SelectTaskOption::kSkipDelayedTask));
428   EXPECT_EQ(task_queues_[0]->delayed_work_queue(),
429             selector_.SelectWorkQueueToService());
430 }
431 
TEST_F(TaskQueueSelectorTest,SelectWorkQueueToServiceImmediateOnlyWithDisabledQueues)432 TEST_F(TaskQueueSelectorTest,
433        SelectWorkQueueToServiceImmediateOnlyWithDisabledQueues) {
434   task_queues_[0]->delayed_work_queue()->Push(
435       Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
436            EnqueueOrder::FromIntForTesting(1)));
437   task_queues_[0]->immediate_work_queue()->Push(
438       Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
439            EnqueueOrder::FromIntForTesting(2)));
440   task_queues_[1]->delayed_work_queue()->Push(
441       Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
442            EnqueueOrder::FromIntForTesting(3)));
443   task_queues_[2]->immediate_work_queue()->Push(
444       Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
445            EnqueueOrder::FromIntForTesting(4)));
446 
447   EXPECT_EQ(task_queues_[0]->delayed_work_queue(),
448             selector_.SelectWorkQueueToService());
449   EXPECT_EQ(task_queues_[0]->immediate_work_queue(),
450             selector_.SelectWorkQueueToService(
451                 TaskQueueSelector::SelectTaskOption::kSkipDelayedTask));
452 
453   task_queues_[0]->SetQueueEnabled(false);
454   selector_.DisableQueue(task_queues_[0].get());
455 
456   EXPECT_EQ(task_queues_[1]->delayed_work_queue(),
457             selector_.SelectWorkQueueToService());
458   EXPECT_EQ(task_queues_[2]->immediate_work_queue(),
459             selector_.SelectWorkQueueToService(
460                 TaskQueueSelector::SelectTaskOption::kSkipDelayedTask));
461 
462   task_queues_[1]->SetQueueEnabled(false);
463   selector_.DisableQueue(task_queues_[1].get());
464 
465   EXPECT_EQ(task_queues_[2]->immediate_work_queue(),
466             selector_.SelectWorkQueueToService(
467                 TaskQueueSelector::SelectTaskOption::kSkipDelayedTask));
468   EXPECT_EQ(task_queues_[2]->immediate_work_queue(),
469             selector_.SelectWorkQueueToService());
470 }
471 
TEST_F(TaskQueueSelectorTest,TestObserverWithOneBlockedQueue)472 TEST_F(TaskQueueSelectorTest, TestObserverWithOneBlockedQueue) {
473   MockObserver mock_observer;  // Must outlive `selector`
474   TaskQueueSelectorForTest selector(associated_thread_);
475   selector.SetTaskQueueSelectorObserver(&mock_observer);
476 
477   EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(1);
478 
479   std::unique_ptr<TaskQueueImpl> task_queue(NewTaskQueueWithBlockReporting());
480   selector.AddQueue(task_queue.get(), kDefaultPriority);
481 
482   task_queue->SetQueueEnabled(false);
483   selector.DisableQueue(task_queue.get());
484 
485   Task task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
486             EnqueueOrder::FromIntForTesting(2));
487   task_queue->immediate_work_queue()->Push(std::move(task));
488 
489   EXPECT_EQ(nullptr, selector.SelectWorkQueueToService());
490 
491   task_queue->SetQueueEnabled(true);
492   selector.EnableQueue(task_queue.get());
493   selector.RemoveQueue(task_queue.get());
494   task_queue->UnregisterTaskQueue();
495 }
496 
TEST_F(TaskQueueSelectorTest,TestObserverWithTwoBlockedQueues)497 TEST_F(TaskQueueSelectorTest, TestObserverWithTwoBlockedQueues) {
498   MockObserver mock_observer;  // Must outlive `selector`
499   TaskQueueSelectorForTest selector(associated_thread_);
500   selector.SetTaskQueueSelectorObserver(&mock_observer);
501 
502   std::unique_ptr<TaskQueueImpl> task_queue(NewTaskQueueWithBlockReporting());
503   std::unique_ptr<TaskQueueImpl> task_queue2(NewTaskQueueWithBlockReporting());
504   selector.AddQueue(task_queue.get(), kDefaultPriority);
505   selector.AddQueue(task_queue2.get(), kDefaultPriority);
506 
507   task_queue->SetQueueEnabled(false);
508   task_queue2->SetQueueEnabled(false);
509   selector.DisableQueue(task_queue.get());
510   selector.DisableQueue(task_queue2.get());
511 
512   selector.SetQueuePriority(task_queue2.get(), kHighestPriority);
513 
514   Task task1(PostedTask(nullptr, test_closure_, FROM_HERE),
515              EnqueueOrder::FromIntForTesting(2),
516              EnqueueOrder::FromIntForTesting(2));
517   Task task2(PostedTask(nullptr, test_closure_, FROM_HERE),
518              EnqueueOrder::FromIntForTesting(3),
519              EnqueueOrder::FromIntForTesting(3));
520   task_queue->immediate_work_queue()->Push(std::move(task1));
521   task_queue2->immediate_work_queue()->Push(std::move(task2));
522   EXPECT_EQ(nullptr, selector.SelectWorkQueueToService());
523   testing::Mock::VerifyAndClearExpectations(&mock_observer);
524 
525   EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(2);
526 
527   task_queue->SetQueueEnabled(true);
528   selector.EnableQueue(task_queue.get());
529 
530   selector.RemoveQueue(task_queue.get());
531   task_queue->UnregisterTaskQueue();
532   EXPECT_EQ(nullptr, selector.SelectWorkQueueToService());
533 
534   task_queue2->SetQueueEnabled(true);
535   selector.EnableQueue(task_queue2.get());
536   selector.RemoveQueue(task_queue2.get());
537   task_queue2->UnregisterTaskQueue();
538 }
539 
TEST_F(TaskQueueSelectorTest,CollectSkippedOverLowerPriorityTasks)540 TEST_F(TaskQueueSelectorTest, CollectSkippedOverLowerPriorityTasks) {
541   size_t queue_order[] = {0, 1, 2, 3, 2, 1, 0};
542   PushTasks(queue_order, 7);
543   selector_.SetQueuePriority(task_queues_[3].get(), kHighPriority);
544 
545   std::vector<const Task*> result;
546   selector_.CollectSkippedOverLowerPriorityTasks(
547       task_queues_[3]->immediate_work_queue(), &result);
548 
549   ASSERT_EQ(3u, result.size());
550   EXPECT_EQ(2u, result[0]->enqueue_order());  // The order here isn't important.
551   EXPECT_EQ(3u, result[1]->enqueue_order());
552   EXPECT_EQ(4u, result[2]->enqueue_order());
553 }
554 
555 struct ChooseWithPriorityTestParam {
556   int delayed_task_enqueue_order;
557   int immediate_task_enqueue_order;
558   int immediate_starvation_count;
559   const char* expected_work_queue_name;
560 };
561 
562 static const ChooseWithPriorityTestParam kChooseWithPriorityTestCases[] = {
563     {1, 2, 0, "delayed"},   {1, 2, 1, "delayed"},   {1, 2, 2, "delayed"},
564     {1, 2, 3, "immediate"}, {1, 2, 4, "immediate"}, {2, 1, 4, "immediate"},
565     {2, 1, 4, "immediate"},
566 };
567 
568 class ChooseWithPriorityTest
569     : public TaskQueueSelectorTest,
570       public testing::WithParamInterface<ChooseWithPriorityTestParam> {};
571 
TEST_P(ChooseWithPriorityTest,RoundRobinTest)572 TEST_P(ChooseWithPriorityTest, RoundRobinTest) {
573   task_queues_[0]->immediate_work_queue()->Push(Task(
574       PostedTask(nullptr, test_closure_, FROM_HERE),
575       EnqueueOrder::FromIntForTesting(GetParam().immediate_task_enqueue_order),
576       EnqueueOrder::FromIntForTesting(
577           GetParam().immediate_task_enqueue_order)));
578 
579   task_queues_[0]->delayed_work_queue()->Push(Task(
580       PostedTask(nullptr, test_closure_, FROM_HERE),
581       EnqueueOrder::FromIntForTesting(GetParam().delayed_task_enqueue_order),
582       EnqueueOrder::FromIntForTesting(GetParam().delayed_task_enqueue_order)));
583 
584   selector_.SetImmediateStarvationCountForTest(
585       GetParam().immediate_starvation_count);
586 
587   WorkQueue* chosen_work_queue =
588       selector_
589           .ChooseWithPriority<TaskQueueSelectorForTest::SetOperationOldest>(
590               kDefaultPriority);
591   EXPECT_EQ(chosen_work_queue->task_queue(), task_queues_[0].get());
592   EXPECT_STREQ(chosen_work_queue->name(), GetParam().expected_work_queue_name);
593 }
594 
595 INSTANTIATE_TEST_SUITE_P(ChooseWithPriorityTest,
596                          ChooseWithPriorityTest,
597                          testing::ValuesIn(kChooseWithPriorityTestCases));
598 
599 class ActivePriorityTrackerTest : public testing::Test {
600  protected:
601   TaskQueueSelectorForTest::ActivePriorityTracker active_priority_tracker_;
602 };
603 
TEST_F(ActivePriorityTrackerTest,SetPriorityActiveAndInactive)604 TEST_F(ActivePriorityTrackerTest, SetPriorityActiveAndInactive) {
605   EXPECT_FALSE(active_priority_tracker_.HasActivePriority());
606   EXPECT_FALSE(active_priority_tracker_.IsActive(kDefaultPriority));
607 
608   active_priority_tracker_.SetActive(kDefaultPriority, true);
609 
610   EXPECT_TRUE(active_priority_tracker_.HasActivePriority());
611   EXPECT_TRUE(active_priority_tracker_.IsActive(kDefaultPriority));
612 
613   active_priority_tracker_.SetActive(kDefaultPriority, false);
614 
615   EXPECT_FALSE(active_priority_tracker_.HasActivePriority());
616   EXPECT_FALSE(active_priority_tracker_.IsActive(kDefaultPriority));
617 }
618 
TEST_F(ActivePriorityTrackerTest,HighestActivePriority)619 TEST_F(ActivePriorityTrackerTest, HighestActivePriority) {
620   EXPECT_FALSE(active_priority_tracker_.HasActivePriority());
621 
622   for (size_t i = 0; i < SequenceManager::PrioritySettings::kMaxPriorities;
623        i++) {
624     TaskQueue::QueuePriority priority =
625         static_cast<TaskQueue::QueuePriority>(i);
626     EXPECT_FALSE(active_priority_tracker_.IsActive(priority));
627     active_priority_tracker_.SetActive(priority, true);
628     EXPECT_TRUE(active_priority_tracker_.IsActive(priority));
629   }
630 
631   for (size_t i = 0; i < SequenceManager::PrioritySettings::kMaxPriorities;
632        i++) {
633     EXPECT_TRUE(active_priority_tracker_.HasActivePriority());
634     TaskQueue::QueuePriority priority =
635         static_cast<TaskQueue::QueuePriority>(i);
636     EXPECT_EQ(active_priority_tracker_.HighestActivePriority(), priority);
637     active_priority_tracker_.SetActive(priority, false);
638   }
639 
640   EXPECT_FALSE(active_priority_tracker_.HasActivePriority());
641 }
642 
643 }  // namespace task_queue_selector_unittest
644 }  // namespace internal
645 }  // namespace sequence_manager
646 }  // namespace base
647