| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/task/task_scheduler/sequence.h" |
| |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/test/gtest_util.h" |
| #include "base/time/time.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace base { |
| namespace internal { |
| |
| namespace { |
| |
| class MockTask { |
| public: |
| MOCK_METHOD0(Run, void()); |
| }; |
| |
| Task CreateTask(MockTask* mock_task) { |
| return Task(FROM_HERE, BindOnce(&MockTask::Run, Unretained(mock_task)), |
| TimeDelta()); |
| } |
| |
| void ExpectMockTask(MockTask* mock_task, Task* task) { |
| EXPECT_CALL(*mock_task, Run()); |
| std::move(task->task).Run(); |
| testing::Mock::VerifyAndClear(mock_task); |
| } |
| |
| } // namespace |
| |
| TEST(TaskSchedulerSequenceTest, PushTakeRemove) { |
| testing::StrictMock<MockTask> mock_task_a; |
| testing::StrictMock<MockTask> mock_task_b; |
| testing::StrictMock<MockTask> mock_task_c; |
| testing::StrictMock<MockTask> mock_task_d; |
| testing::StrictMock<MockTask> mock_task_e; |
| |
| scoped_refptr<Sequence> sequence = |
| MakeRefCounted<Sequence>(TaskTraits(TaskPriority::BEST_EFFORT)); |
| |
| // Push task A in the sequence. PushTask() should return true since it's the |
| // first task-> |
| EXPECT_TRUE(sequence->PushTask(CreateTask(&mock_task_a))); |
| |
| // Push task B, C and D in the sequence. PushTask() should return false since |
| // there is already a task in a sequence. |
| EXPECT_FALSE(sequence->PushTask(CreateTask(&mock_task_b))); |
| EXPECT_FALSE(sequence->PushTask(CreateTask(&mock_task_c))); |
| EXPECT_FALSE(sequence->PushTask(CreateTask(&mock_task_d))); |
| |
| // Take the task in front of the sequence. It should be task A. |
| Optional<Task> task = sequence->TakeTask(); |
| ExpectMockTask(&mock_task_a, &task.value()); |
| EXPECT_FALSE(task->sequenced_time.is_null()); |
| |
| // Remove the empty slot. Task B should now be in front. |
| EXPECT_FALSE(sequence->Pop()); |
| task = sequence->TakeTask(); |
| ExpectMockTask(&mock_task_b, &task.value()); |
| EXPECT_FALSE(task->sequenced_time.is_null()); |
| |
| // Remove the empty slot. Task C should now be in front. |
| EXPECT_FALSE(sequence->Pop()); |
| task = sequence->TakeTask(); |
| ExpectMockTask(&mock_task_c, &task.value()); |
| EXPECT_FALSE(task->sequenced_time.is_null()); |
| |
| // Remove the empty slot. |
| EXPECT_FALSE(sequence->Pop()); |
| |
| // Push task E in the sequence. |
| EXPECT_FALSE(sequence->PushTask(CreateTask(&mock_task_e))); |
| |
| // Task D should be in front. |
| task = sequence->TakeTask(); |
| ExpectMockTask(&mock_task_d, &task.value()); |
| EXPECT_FALSE(task->sequenced_time.is_null()); |
| |
| // Remove the empty slot. Task E should now be in front. |
| EXPECT_FALSE(sequence->Pop()); |
| task = sequence->TakeTask(); |
| ExpectMockTask(&mock_task_e, &task.value()); |
| EXPECT_FALSE(task->sequenced_time.is_null()); |
| |
| // Remove the empty slot. The sequence should now be empty. |
| EXPECT_TRUE(sequence->Pop()); |
| } |
| |
| // Verifies the sort key of a BEST_EFFORT sequence that contains one task. |
| TEST(TaskSchedulerSequenceTest, GetSortKeyBestEffort) { |
| // Create a BEST_EFFORT sequence with a task. |
| Task best_effort_task(FROM_HERE, DoNothing(), TimeDelta()); |
| scoped_refptr<Sequence> best_effort_sequence = |
| MakeRefCounted<Sequence>(TaskTraits(TaskPriority::BEST_EFFORT)); |
| best_effort_sequence->PushTask(std::move(best_effort_task)); |
| |
| // Get the sort key. |
| const SequenceSortKey best_effort_sort_key = |
| best_effort_sequence->GetSortKey(); |
| |
| // Take the task from the sequence, so that its sequenced time is available |
| // for the check below. |
| auto take_best_effort_task = best_effort_sequence->TakeTask(); |
| |
| // Verify the sort key. |
| EXPECT_EQ(TaskPriority::BEST_EFFORT, best_effort_sort_key.priority()); |
| EXPECT_EQ(take_best_effort_task->sequenced_time, |
| best_effort_sort_key.next_task_sequenced_time()); |
| |
| // Pop for correctness. |
| best_effort_sequence->Pop(); |
| } |
| |
| // Same as TaskSchedulerSequenceTest.GetSortKeyBestEffort, but with a |
| // USER_VISIBLE sequence. |
| TEST(TaskSchedulerSequenceTest, GetSortKeyForeground) { |
| // Create a USER_VISIBLE sequence with a task. |
| Task foreground_task(FROM_HERE, DoNothing(), TimeDelta()); |
| scoped_refptr<Sequence> foreground_sequence = |
| MakeRefCounted<Sequence>(TaskTraits(TaskPriority::USER_VISIBLE)); |
| foreground_sequence->PushTask(std::move(foreground_task)); |
| |
| // Get the sort key. |
| const SequenceSortKey foreground_sort_key = foreground_sequence->GetSortKey(); |
| |
| // Take the task from the sequence, so that its sequenced time is available |
| // for the check below. |
| auto take_foreground_task = foreground_sequence->TakeTask(); |
| |
| // Verify the sort key. |
| EXPECT_EQ(TaskPriority::USER_VISIBLE, foreground_sort_key.priority()); |
| EXPECT_EQ(take_foreground_task->sequenced_time, |
| foreground_sort_key.next_task_sequenced_time()); |
| |
| // Pop for correctness. |
| foreground_sequence->Pop(); |
| } |
| |
| // Verify that a DCHECK fires if Pop() is called on a sequence whose front slot |
| // isn't empty. |
| TEST(TaskSchedulerSequenceTest, PopNonEmptyFrontSlot) { |
| scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>(TaskTraits()); |
| sequence->PushTask(Task(FROM_HERE, DoNothing(), TimeDelta())); |
| |
| EXPECT_DCHECK_DEATH({ sequence->Pop(); }); |
| } |
| |
| // Verify that a DCHECK fires if TakeTask() is called on a sequence whose front |
| // slot is empty. |
| TEST(TaskSchedulerSequenceTest, TakeEmptyFrontSlot) { |
| scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>(TaskTraits()); |
| sequence->PushTask(Task(FROM_HERE, DoNothing(), TimeDelta())); |
| |
| EXPECT_TRUE(sequence->TakeTask()); |
| EXPECT_DCHECK_DEATH({ sequence->TakeTask(); }); |
| } |
| |
| // Verify that a DCHECK fires if TakeTask() is called on an empty sequence. |
| TEST(TaskSchedulerSequenceTest, TakeEmptySequence) { |
| scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>(TaskTraits()); |
| EXPECT_DCHECK_DEATH({ sequence->TakeTask(); }); |
| } |
| |
| } // namespace internal |
| } // namespace base |