// Copyright 2015 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/task/sequence_manager/task_queue_selector.h" #include #include #include #include #include #include #include "base/functional/bind.h" #include "base/memory/ptr_util.h" #include "base/pending_task.h" #include "base/task/sequence_manager/enqueue_order_generator.h" #include "base/task/sequence_manager/task_queue_impl.h" #include "base/task/sequence_manager/test/mock_time_domain.h" #include "base/task/sequence_manager/work_queue.h" #include "base/task/sequence_manager/work_queue_sets.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using testing::_; using testing::ElementsAre; using testing::NotNull; namespace base { namespace sequence_manager { namespace internal { // To avoid symbol collisions in jumbo builds. namespace task_queue_selector_unittest { namespace { const TaskQueue::QueuePriority kHighestPriority = 0; const TaskQueue::QueuePriority kHighPriority = 4; const TaskQueue::QueuePriority kDefaultPriority = 5; const TaskQueue::QueuePriority kPriorityCount = 10; const size_t kTaskQueueCount = kPriorityCount; // Tests assume high priority is higher than default priority, but nothing about // their values. static_assert(kHighPriority < kDefaultPriority); } // namespace class MockObserver : public TaskQueueSelector::Observer { public: MockObserver() = default; MockObserver(const MockObserver&) = delete; MockObserver& operator=(const MockObserver&) = delete; ~MockObserver() override = default; MOCK_METHOD1(OnTaskQueueEnabled, void(internal::TaskQueueImpl*)); }; class TaskQueueSelectorForTest : public TaskQueueSelector { public: using TaskQueueSelector::ActivePriorityTracker; using TaskQueueSelector::ChooseWithPriority; using TaskQueueSelector::delayed_work_queue_sets; using TaskQueueSelector::immediate_work_queue_sets; using TaskQueueSelector::SetImmediateStarvationCountForTest; using TaskQueueSelector::SetOperationOldest; explicit TaskQueueSelectorForTest( scoped_refptr associated_thread) : TaskQueueSelector( associated_thread, SequenceManager::Settings::Builder() .SetPrioritySettings( SequenceManager::PrioritySettings(kPriorityCount, kDefaultPriority)) .Build()) {} }; class TaskQueueSelectorTest : public testing::Test { public: TaskQueueSelectorTest() : test_closure_(BindRepeating(&TaskQueueSelectorTest::TestFunction)), associated_thread_(AssociatedThreadId::CreateBound()), selector_(associated_thread_) {} ~TaskQueueSelectorTest() override = default; void PushTasks(const size_t queue_indices[], size_t num_tasks) { EnqueueOrderGenerator enqueue_order_generator; for (size_t i = 0; i < num_tasks; i++) { task_queues_[queue_indices[i]]->immediate_work_queue()->Push( Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(), enqueue_order_generator.GenerateNext())); } } void PushTasksWithEnqueueOrder(const size_t queue_indices[], const size_t enqueue_orders[], size_t num_tasks) { for (size_t i = 0; i < num_tasks; i++) { task_queues_[queue_indices[i]]->immediate_work_queue()->Push( Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(), EnqueueOrder::FromIntForTesting(enqueue_orders[i]))); } } void PushTask(const size_t queue_index, const size_t enqueue_order) { task_queues_[queue_index]->immediate_work_queue()->Push( Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(), EnqueueOrder::FromIntForTesting(enqueue_order))); } std::vector PopTasksAndReturnQueueIndices() { std::vector order; while (WorkQueue* chosen_work_queue = selector_.SelectWorkQueueToService()) { size_t chosen_queue_index = queue_to_index_map_.find(chosen_work_queue->task_queue())->second; order.push_back(chosen_queue_index); chosen_work_queue->PopTaskForTesting(); chosen_work_queue->work_queue_sets()->OnPopMinQueueInSet( chosen_work_queue); } return order; } static void TestFunction() {} protected: void SetUp() final { for (size_t i = 0; i < kTaskQueueCount; i++) { std::unique_ptr task_queue = std::make_unique(nullptr, nullptr, TaskQueue::Spec(QueueName::TEST_TQ)); selector_.AddQueue(task_queue.get(), kDefaultPriority); task_queues_.push_back(std::move(task_queue)); } for (size_t i = 0; i < kTaskQueueCount; i++) { EXPECT_EQ(kDefaultPriority, task_queues_[i]->GetQueuePriority()) << i; queue_to_index_map_.insert(std::make_pair(task_queues_[i].get(), i)); } } void TearDown() final { for (std::unique_ptr& task_queue : task_queues_) { // Note since this test doesn't have a SequenceManager we need to // manually remove |task_queue| from the |selector_|. Normally // UnregisterTaskQueue would do that. selector_.RemoveQueue(task_queue.get()); task_queue->UnregisterTaskQueue(); } } std::unique_ptr NewTaskQueueWithBlockReporting() { return std::make_unique(nullptr, nullptr, TaskQueue::Spec(QueueName::TEST_TQ)); } RepeatingClosure test_closure_; scoped_refptr associated_thread_; TaskQueueSelectorForTest selector_; std::vector> task_queues_; std::map queue_to_index_map_; }; TEST_F(TaskQueueSelectorTest, TestDefaultPriority) { size_t queue_order[] = {4, 3, 2, 1, 0}; PushTasks(queue_order, 5); EXPECT_THAT(PopTasksAndReturnQueueIndices(), ElementsAre(4, 3, 2, 1, 0)); } TEST_F(TaskQueueSelectorTest, TestPriorities) { for (size_t priority = 0; priority < kDefaultPriority; ++priority) { size_t queue_order[] = {0, 1, 2, 3, 4}; PushTasks(queue_order, 5); if (priority != kDefaultPriority) { selector_.SetQueuePriority(task_queues_[2].get(), priority); } EXPECT_THAT(PopTasksAndReturnQueueIndices(), ElementsAre(2, 0, 1, 3, 4)); } } TEST_F(TaskQueueSelectorTest, TestMultiplePriorities) { size_t reverse_priority_order[kPriorityCount]; for (size_t priority = 0; priority < kPriorityCount; ++priority) { reverse_priority_order[(kPriorityCount - 1) - priority] = priority; } PushTasks(reverse_priority_order, kPriorityCount); for (size_t i = 0; i < kPriorityCount; ++i) { TaskQueue::QueuePriority priority = reverse_priority_order[i]; if (priority != kDefaultPriority) { selector_.SetQueuePriority(task_queues_[i].get(), priority); } EXPECT_EQ(task_queues_[i]->GetQueuePriority(), priority); } EXPECT_THAT(PopTasksAndReturnQueueIndices(), ElementsAre(9, 8, 7, 6, 5, 4, 3, 2, 1, 0)); } TEST_F(TaskQueueSelectorTest, TestObserverWithEnabledQueue) { task_queues_[1]->SetQueueEnabled(false); selector_.DisableQueue(task_queues_[1].get()); { MockObserver mock_observer; selector_.SetTaskQueueSelectorObserver(&mock_observer); EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(1); task_queues_[1]->SetQueueEnabled(true); selector_.EnableQueue(task_queues_[1].get()); // Clear observer before it goes out of scope. selector_.SetTaskQueueSelectorObserver(nullptr); } } TEST_F(TaskQueueSelectorTest, TestObserverWithSetQueuePriorityAndQueueAlreadyEnabled) { selector_.SetQueuePriority(task_queues_[1].get(), kHighPriority); { MockObserver mock_observer; selector_.SetTaskQueueSelectorObserver(&mock_observer); EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(0); selector_.SetQueuePriority(task_queues_[1].get(), kDefaultPriority); // Clear observer before it goes out of scope. selector_.SetTaskQueueSelectorObserver(nullptr); } } TEST_F(TaskQueueSelectorTest, TestDisableEnable) { MockObserver mock_observer; selector_.SetTaskQueueSelectorObserver(&mock_observer); size_t queue_order[] = {0, 1, 2, 3, 4}; PushTasks(queue_order, 5); task_queues_[2]->SetQueueEnabled(false); selector_.DisableQueue(task_queues_[2].get()); task_queues_[4]->SetQueueEnabled(false); selector_.DisableQueue(task_queues_[4].get()); // Disabling a queue should not affect its priority. EXPECT_EQ(kDefaultPriority, task_queues_[2]->GetQueuePriority()); EXPECT_EQ(kDefaultPriority, task_queues_[4]->GetQueuePriority()); EXPECT_THAT(PopTasksAndReturnQueueIndices(), ElementsAre(0, 1, 3)); EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(2); task_queues_[2]->SetQueueEnabled(true); selector_.EnableQueue(task_queues_[2].get()); selector_.SetQueuePriority(task_queues_[2].get(), kPriorityCount - 1); EXPECT_THAT(PopTasksAndReturnQueueIndices(), ElementsAre(2)); task_queues_[4]->SetQueueEnabled(true); selector_.EnableQueue(task_queues_[4].get()); EXPECT_THAT(PopTasksAndReturnQueueIndices(), ElementsAre(4)); // Clear observer before it goes out of scope. selector_.SetTaskQueueSelectorObserver(nullptr); } TEST_F(TaskQueueSelectorTest, TestDisableChangePriorityThenEnable) { EXPECT_TRUE(task_queues_[2]->delayed_work_queue()->Empty()); EXPECT_TRUE(task_queues_[2]->immediate_work_queue()->Empty()); task_queues_[2]->SetQueueEnabled(false); selector_.SetQueuePriority(task_queues_[2].get(), kHighPriority); size_t queue_order[] = {0, 1, 2, 3, 4}; PushTasks(queue_order, 5); EXPECT_TRUE(task_queues_[2]->delayed_work_queue()->Empty()); EXPECT_FALSE(task_queues_[2]->immediate_work_queue()->Empty()); task_queues_[2]->SetQueueEnabled(true); EXPECT_EQ(kHighPriority, task_queues_[2]->GetQueuePriority()); EXPECT_THAT(PopTasksAndReturnQueueIndices(), ElementsAre(2, 0, 1, 3, 4)); } TEST_F(TaskQueueSelectorTest, TestEmptyQueues) { EXPECT_EQ(nullptr, selector_.SelectWorkQueueToService()); // Test only disabled queues. size_t queue_order[] = {0}; PushTasks(queue_order, 1); task_queues_[0]->SetQueueEnabled(false); selector_.DisableQueue(task_queues_[0].get()); EXPECT_EQ(nullptr, selector_.SelectWorkQueueToService()); // These tests are unusual since there's no TQM. To avoid a later DCHECK when // deleting the task queue, we re-enable the queue here so the selector // doesn't get out of sync. task_queues_[0]->SetQueueEnabled(true); selector_.EnableQueue(task_queues_[0].get()); } TEST_F(TaskQueueSelectorTest, TestAge) { size_t enqueue_order[] = {10, 1, 2, 9, 4}; size_t queue_order[] = {0, 1, 2, 3, 4}; PushTasksWithEnqueueOrder(queue_order, enqueue_order, 5); EXPECT_THAT(PopTasksAndReturnQueueIndices(), ElementsAre(1, 2, 4, 3, 0)); } class TaskQueueSelectorStarvationTest : public TaskQueueSelectorTest { public: TaskQueueSelectorStarvationTest() = default; protected: void TestPriorityOrder(const size_t queue_order[], size_t num_tasks) { for (size_t i = 0; i < kTaskQueueCount; i++) { // Setting the queue priority to its current value causes a check to fail. if (task_queues_[i]->GetQueuePriority() != static_cast(i)) { selector_.SetQueuePriority(task_queues_[i].get(), static_cast(i)); } } ASSERT_EQ(num_tasks, kTaskQueueCount); PushTasks(queue_order, kTaskQueueCount); for (size_t priority = 0; priority < kTaskQueueCount; priority++) { for (int i = 0; i < 100; i++) { WorkQueue* chosen_work_queue = selector_.SelectWorkQueueToService(); ASSERT_THAT(chosen_work_queue, NotNull()); EXPECT_EQ(task_queues_[priority].get(), chosen_work_queue->task_queue()); // Don't remove task from queue to simulate all queues still being full. } // Simulate the highest priority queue becoming empty. WorkQueue* chosen_work_queue = selector_.SelectWorkQueueToService(); chosen_work_queue->PopTaskForTesting(); chosen_work_queue->work_queue_sets()->OnPopMinQueueInSet( chosen_work_queue); } } }; TEST_F(TaskQueueSelectorStarvationTest, HigherPriorityWorkStarvesLowerPriorityWork) { size_t queue_order[kTaskQueueCount]; for (size_t i = 0; i < kTaskQueueCount; i++) queue_order[i] = i; TestPriorityOrder(queue_order, kTaskQueueCount); } TEST_F(TaskQueueSelectorStarvationTest, NewHigherPriorityTasksStarveOldLowerPriorityTasks) { // Enqueue tasks in order from lowest to highest priority, and check that they // still run in order from highest to lowest priority. size_t queue_order[kTaskQueueCount]; for (size_t i = 0; i < kTaskQueueCount; i++) queue_order[i] = (kTaskQueueCount - i) - 1; TestPriorityOrder(queue_order, kTaskQueueCount); } TEST_F(TaskQueueSelectorTest, GetHighestPendingPriority) { EXPECT_FALSE(selector_.GetHighestPendingPriority().has_value()); size_t queue_order[] = {0, 1}; PushTasks(queue_order, 2); selector_.SetQueuePriority(task_queues_[1].get(), kHighPriority); EXPECT_EQ(kHighPriority, *selector_.GetHighestPendingPriority()); PopTasksAndReturnQueueIndices(); EXPECT_FALSE(selector_.GetHighestPendingPriority().has_value()); PushTasks(queue_order, 1); EXPECT_EQ(kDefaultPriority, *selector_.GetHighestPendingPriority()); PopTasksAndReturnQueueIndices(); EXPECT_FALSE(selector_.GetHighestPendingPriority().has_value()); } TEST_F(TaskQueueSelectorTest, ChooseWithPriority_Empty) { EXPECT_EQ( nullptr, selector_ .ChooseWithPriority( kDefaultPriority)); } TEST_F(TaskQueueSelectorTest, ChooseWithPriority_OnlyDelayed) { task_queues_[0]->delayed_work_queue()->Push( Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(), EnqueueOrder::FromIntForTesting(2))); EXPECT_EQ( task_queues_[0]->delayed_work_queue(), selector_ .ChooseWithPriority( kDefaultPriority)); } TEST_F(TaskQueueSelectorTest, ChooseWithPriority_OnlyImmediate) { task_queues_[0]->immediate_work_queue()->Push( Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(), EnqueueOrder::FromIntForTesting(2))); EXPECT_EQ( task_queues_[0]->immediate_work_queue(), selector_ .ChooseWithPriority( kDefaultPriority)); } TEST_F(TaskQueueSelectorTest, SelectWorkQueueToServiceImmediateOnlyWithoutImmediateTask) { task_queues_[0]->delayed_work_queue()->Push( Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(), EnqueueOrder::FromIntForTesting(2))); EXPECT_EQ(nullptr, selector_.SelectWorkQueueToService( TaskQueueSelector::SelectTaskOption::kSkipDelayedTask)); EXPECT_EQ(task_queues_[0]->delayed_work_queue(), selector_.SelectWorkQueueToService()); } TEST_F(TaskQueueSelectorTest, SelectWorkQueueToServiceImmediateOnlyWithDelayedTasks) { task_queues_[0]->delayed_work_queue()->Push( Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(), EnqueueOrder::FromIntForTesting(1))); task_queues_[0]->immediate_work_queue()->Push( Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(), EnqueueOrder::FromIntForTesting(2))); EXPECT_EQ(task_queues_[0]->immediate_work_queue(), selector_.SelectWorkQueueToService( TaskQueueSelector::SelectTaskOption::kSkipDelayedTask)); EXPECT_EQ(task_queues_[0]->delayed_work_queue(), selector_.SelectWorkQueueToService()); } TEST_F(TaskQueueSelectorTest, SelectWorkQueueToServiceImmediateOnlyWithDisabledQueues) { task_queues_[0]->delayed_work_queue()->Push( Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(), EnqueueOrder::FromIntForTesting(1))); task_queues_[0]->immediate_work_queue()->Push( Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(), EnqueueOrder::FromIntForTesting(2))); task_queues_[1]->delayed_work_queue()->Push( Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(), EnqueueOrder::FromIntForTesting(3))); task_queues_[2]->immediate_work_queue()->Push( Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(), EnqueueOrder::FromIntForTesting(4))); EXPECT_EQ(task_queues_[0]->delayed_work_queue(), selector_.SelectWorkQueueToService()); EXPECT_EQ(task_queues_[0]->immediate_work_queue(), selector_.SelectWorkQueueToService( TaskQueueSelector::SelectTaskOption::kSkipDelayedTask)); task_queues_[0]->SetQueueEnabled(false); selector_.DisableQueue(task_queues_[0].get()); EXPECT_EQ(task_queues_[1]->delayed_work_queue(), selector_.SelectWorkQueueToService()); EXPECT_EQ(task_queues_[2]->immediate_work_queue(), selector_.SelectWorkQueueToService( TaskQueueSelector::SelectTaskOption::kSkipDelayedTask)); task_queues_[1]->SetQueueEnabled(false); selector_.DisableQueue(task_queues_[1].get()); EXPECT_EQ(task_queues_[2]->immediate_work_queue(), selector_.SelectWorkQueueToService( TaskQueueSelector::SelectTaskOption::kSkipDelayedTask)); EXPECT_EQ(task_queues_[2]->immediate_work_queue(), selector_.SelectWorkQueueToService()); } TEST_F(TaskQueueSelectorTest, TestObserverWithOneBlockedQueue) { MockObserver mock_observer; // Must outlive `selector` TaskQueueSelectorForTest selector(associated_thread_); selector.SetTaskQueueSelectorObserver(&mock_observer); EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(1); std::unique_ptr task_queue(NewTaskQueueWithBlockReporting()); selector.AddQueue(task_queue.get(), kDefaultPriority); task_queue->SetQueueEnabled(false); selector.DisableQueue(task_queue.get()); Task task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(), EnqueueOrder::FromIntForTesting(2)); task_queue->immediate_work_queue()->Push(std::move(task)); EXPECT_EQ(nullptr, selector.SelectWorkQueueToService()); task_queue->SetQueueEnabled(true); selector.EnableQueue(task_queue.get()); selector.RemoveQueue(task_queue.get()); task_queue->UnregisterTaskQueue(); } TEST_F(TaskQueueSelectorTest, TestObserverWithTwoBlockedQueues) { MockObserver mock_observer; // Must outlive `selector` TaskQueueSelectorForTest selector(associated_thread_); selector.SetTaskQueueSelectorObserver(&mock_observer); std::unique_ptr task_queue(NewTaskQueueWithBlockReporting()); std::unique_ptr task_queue2(NewTaskQueueWithBlockReporting()); selector.AddQueue(task_queue.get(), kDefaultPriority); selector.AddQueue(task_queue2.get(), kDefaultPriority); task_queue->SetQueueEnabled(false); task_queue2->SetQueueEnabled(false); selector.DisableQueue(task_queue.get()); selector.DisableQueue(task_queue2.get()); selector.SetQueuePriority(task_queue2.get(), kHighestPriority); Task task1(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder::FromIntForTesting(2), EnqueueOrder::FromIntForTesting(2)); Task task2(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder::FromIntForTesting(3), EnqueueOrder::FromIntForTesting(3)); task_queue->immediate_work_queue()->Push(std::move(task1)); task_queue2->immediate_work_queue()->Push(std::move(task2)); EXPECT_EQ(nullptr, selector.SelectWorkQueueToService()); testing::Mock::VerifyAndClearExpectations(&mock_observer); EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(2); task_queue->SetQueueEnabled(true); selector.EnableQueue(task_queue.get()); selector.RemoveQueue(task_queue.get()); task_queue->UnregisterTaskQueue(); EXPECT_EQ(nullptr, selector.SelectWorkQueueToService()); task_queue2->SetQueueEnabled(true); selector.EnableQueue(task_queue2.get()); selector.RemoveQueue(task_queue2.get()); task_queue2->UnregisterTaskQueue(); } TEST_F(TaskQueueSelectorTest, CollectSkippedOverLowerPriorityTasks) { size_t queue_order[] = {0, 1, 2, 3, 2, 1, 0}; PushTasks(queue_order, 7); selector_.SetQueuePriority(task_queues_[3].get(), kHighPriority); std::vector result; selector_.CollectSkippedOverLowerPriorityTasks( task_queues_[3]->immediate_work_queue(), &result); ASSERT_EQ(3u, result.size()); EXPECT_EQ(2u, result[0]->enqueue_order()); // The order here isn't important. EXPECT_EQ(3u, result[1]->enqueue_order()); EXPECT_EQ(4u, result[2]->enqueue_order()); } struct ChooseWithPriorityTestParam { int delayed_task_enqueue_order; int immediate_task_enqueue_order; int immediate_starvation_count; const char* expected_work_queue_name; }; static const ChooseWithPriorityTestParam kChooseWithPriorityTestCases[] = { {1, 2, 0, "delayed"}, {1, 2, 1, "delayed"}, {1, 2, 2, "delayed"}, {1, 2, 3, "immediate"}, {1, 2, 4, "immediate"}, {2, 1, 4, "immediate"}, {2, 1, 4, "immediate"}, }; class ChooseWithPriorityTest : public TaskQueueSelectorTest, public testing::WithParamInterface {}; TEST_P(ChooseWithPriorityTest, RoundRobinTest) { task_queues_[0]->immediate_work_queue()->Push(Task( PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder::FromIntForTesting(GetParam().immediate_task_enqueue_order), EnqueueOrder::FromIntForTesting( GetParam().immediate_task_enqueue_order))); task_queues_[0]->delayed_work_queue()->Push(Task( PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder::FromIntForTesting(GetParam().delayed_task_enqueue_order), EnqueueOrder::FromIntForTesting(GetParam().delayed_task_enqueue_order))); selector_.SetImmediateStarvationCountForTest( GetParam().immediate_starvation_count); WorkQueue* chosen_work_queue = selector_ .ChooseWithPriority( kDefaultPriority); EXPECT_EQ(chosen_work_queue->task_queue(), task_queues_[0].get()); EXPECT_STREQ(chosen_work_queue->name(), GetParam().expected_work_queue_name); } INSTANTIATE_TEST_SUITE_P(ChooseWithPriorityTest, ChooseWithPriorityTest, testing::ValuesIn(kChooseWithPriorityTestCases)); class ActivePriorityTrackerTest : public testing::Test { protected: TaskQueueSelectorForTest::ActivePriorityTracker active_priority_tracker_; }; TEST_F(ActivePriorityTrackerTest, SetPriorityActiveAndInactive) { EXPECT_FALSE(active_priority_tracker_.HasActivePriority()); EXPECT_FALSE(active_priority_tracker_.IsActive(kDefaultPriority)); active_priority_tracker_.SetActive(kDefaultPriority, true); EXPECT_TRUE(active_priority_tracker_.HasActivePriority()); EXPECT_TRUE(active_priority_tracker_.IsActive(kDefaultPriority)); active_priority_tracker_.SetActive(kDefaultPriority, false); EXPECT_FALSE(active_priority_tracker_.HasActivePriority()); EXPECT_FALSE(active_priority_tracker_.IsActive(kDefaultPriority)); } TEST_F(ActivePriorityTrackerTest, HighestActivePriority) { EXPECT_FALSE(active_priority_tracker_.HasActivePriority()); for (size_t i = 0; i < SequenceManager::PrioritySettings::kMaxPriorities; i++) { TaskQueue::QueuePriority priority = static_cast(i); EXPECT_FALSE(active_priority_tracker_.IsActive(priority)); active_priority_tracker_.SetActive(priority, true); EXPECT_TRUE(active_priority_tracker_.IsActive(priority)); } for (size_t i = 0; i < SequenceManager::PrioritySettings::kMaxPriorities; i++) { EXPECT_TRUE(active_priority_tracker_.HasActivePriority()); TaskQueue::QueuePriority priority = static_cast(i); EXPECT_EQ(active_priority_tracker_.HighestActivePriority(), priority); active_priority_tracker_.SetActive(priority, false); } EXPECT_FALSE(active_priority_tracker_.HasActivePriority()); } } // namespace task_queue_selector_unittest } // namespace internal } // namespace sequence_manager } // namespace base