blob: 4300988980c4a67736b9850f15dfd2a7afb5590a [file] [log] [blame]
// Copyright 2016 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/thread_pool/delayed_priority_queue.h"
#include <memory>
#include <utility>
#include "base/functional/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool/sequence.h"
#include "base/task/thread_pool/task.h"
#include "base/test/gtest_util.h"
#include "base/test/task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base::internal {
namespace {
class DelayedPriorityQueueWithSequencesTest : public testing::Test {
protected:
scoped_refptr<Sequence> MakeSequenceWithDelayedTask(
TimeDelta delayed_run_time) {
// FastForward time to ensure that queue order between task sources is well
// defined.
task_environment.FastForwardBy(Microseconds(1));
scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>(
TaskTraits(), nullptr, TaskSourceExecutionMode::kParallel);
sequence->BeginTransaction().PushDelayedTask(
Task(FROM_HERE, DoNothing(), TimeTicks::Now(), delayed_run_time));
return sequence;
}
void Push(scoped_refptr<Sequence> task_source) {
auto delayed_sort_key = task_source->GetDelayedSortKey();
pq.Push(std::move(task_source), delayed_sort_key);
}
test::TaskEnvironment task_environment{
test::TaskEnvironment::TimeSource::MOCK_TIME};
scoped_refptr<Sequence> sequence_a =
MakeSequenceWithDelayedTask(Milliseconds(90));
TimeTicks sort_key_a = sequence_a->GetDelayedSortKey();
scoped_refptr<Sequence> sequence_b =
MakeSequenceWithDelayedTask(Milliseconds(70));
TimeTicks sort_key_b = sequence_b->GetDelayedSortKey();
scoped_refptr<Sequence> sequence_c =
MakeSequenceWithDelayedTask(Milliseconds(80));
TimeTicks sort_key_c = sequence_c->GetDelayedSortKey();
scoped_refptr<Sequence> sequence_d =
MakeSequenceWithDelayedTask(Milliseconds(100));
TimeTicks sort_key_d = sequence_d->GetDelayedSortKey();
DelayedPriorityQueue pq;
};
} // namespace
TEST_F(DelayedPriorityQueueWithSequencesTest, PushPopPeek) {
EXPECT_TRUE(pq.IsEmpty());
// Push |sequence_a| in the DelayedPriorityQueue. It becomes the sequence with
// the earliest delayed runtime.
Push(sequence_a);
EXPECT_EQ(sort_key_a, pq.PeekDelayedSortKey());
// Push |sequence_b| in the DelayedPriorityQueue. It becomes the sequence with
// the earliest delayed runtime.
Push(sequence_b);
EXPECT_EQ(sort_key_b, pq.PeekDelayedSortKey());
// Push |sequence_c| in the DelayedPriorityQueue. |sequence_b| is still the
// sequence with the earliest delayed runtime.
Push(sequence_c);
EXPECT_EQ(sort_key_b, pq.PeekDelayedSortKey());
// Push |sequence_d| in the DelayedPriorityQueue. |sequence_b| is still the
// sequence with the earliest delayed runtime.
Push(sequence_d);
EXPECT_EQ(sort_key_b, pq.PeekDelayedSortKey());
// Pop |sequence_b| from the DelayedPriorityQueue. |sequence_c| becomes the
// sequence with the earliest delayed runtime.
EXPECT_EQ(sequence_b, pq.PopTaskSource());
EXPECT_EQ(sort_key_c, pq.PeekDelayedSortKey());
// Pop |sequence_c| from the DelayedPriorityQueue. |sequence_a| becomes the
// sequence with the earliest delayed runtime.
EXPECT_EQ(sequence_c, pq.PopTaskSource());
EXPECT_EQ(sort_key_a, pq.PeekDelayedSortKey());
// Pop |sequence_a| from the DelayedPriorityQueue. |sequence_d| becomes the
// sequence with the earliest delayed runtime.
EXPECT_EQ(sequence_a, pq.PopTaskSource());
EXPECT_EQ(sort_key_d, pq.PeekDelayedSortKey());
// Pop |sequence_d| from the DelayedPriorityQueue. It is now empty.
EXPECT_EQ(sequence_d, pq.PopTaskSource());
EXPECT_TRUE(pq.IsEmpty());
}
TEST_F(DelayedPriorityQueueWithSequencesTest, RemoveSequence) {
EXPECT_TRUE(pq.IsEmpty());
// Push all test Sequences into the PriorityQueue. |sequence_b|
// will be the sequence with the highest priority.
Push(sequence_a);
Push(sequence_b);
Push(sequence_c);
Push(sequence_d);
EXPECT_EQ(sort_key_b, pq.PeekDelayedSortKey());
// Remove |sequence_a| from the PriorityQueue. |sequence_b| is still the
// sequence with the highest priority.
EXPECT_TRUE(pq.RemoveTaskSource(sequence_a));
EXPECT_EQ(sort_key_b, pq.PeekDelayedSortKey());
// RemoveTaskSource() should return false if called on a sequence not in the
// PriorityQueue.
EXPECT_FALSE(pq.RemoveTaskSource(sequence_a));
// Remove |sequence_b| from the PriorityQueue. |sequence_c| becomes the
// sequence with the highest priority.
EXPECT_TRUE(pq.RemoveTaskSource(sequence_b));
EXPECT_EQ(sort_key_c, pq.PeekDelayedSortKey());
// Remove |sequence_d| from the PriorityQueue. |sequence_c| is still the
// sequence with the highest priority.
EXPECT_TRUE(pq.RemoveTaskSource(sequence_d));
EXPECT_EQ(sort_key_c, pq.PeekDelayedSortKey());
// Remove |sequence_c| from the PriorityQueue, making it empty.
EXPECT_TRUE(pq.RemoveTaskSource(sequence_c));
EXPECT_TRUE(pq.IsEmpty());
// Return false if RemoveTaskSource() is called on an empty PriorityQueue.
EXPECT_FALSE(pq.RemoveTaskSource(sequence_c));
}
// Test that when the top of a task source changes, the delayed queue is
// appropriately rearranged
TEST_F(DelayedPriorityQueueWithSequencesTest, UpdateDelayedSortKey) {
EXPECT_TRUE(pq.IsEmpty());
// Push all test Sequences into the PriorityQueue. |sequence_b| becomes the
// sequence with the highest priority.
Push(sequence_a);
Push(sequence_b);
Push(sequence_c);
Push(sequence_d);
EXPECT_EQ(sort_key_b, pq.PeekDelayedSortKey());
{
// Push a new delayed task earlier than pq's top into sequence_c.
// |sequence_c| becomes the sequence on top of the delayed priority queue.
sequence_c->BeginTransaction().PushDelayedTask(
Task(FROM_HERE, DoNothing(), TimeTicks::Now(), Milliseconds(60)));
sort_key_c = sequence_c->GetDelayedSortKey();
pq.UpdateDelayedSortKey(sequence_c);
EXPECT_EQ(sort_key_c, pq.PeekDelayedSortKey());
}
{
// Push a new delayed task earlier than pq's top into sequence_d.
// |sequence_d| becomes the sequence on top of the delayed priority queue.
sequence_d->BeginTransaction().PushDelayedTask(
Task(FROM_HERE, DoNothing(), TimeTicks::Now(), Milliseconds(50)));
sort_key_d = sequence_d->GetDelayedSortKey();
pq.UpdateDelayedSortKey(sequence_d);
EXPECT_EQ(sort_key_d, pq.PeekDelayedSortKey());
// Pop top task source and verify that it is |sequence_d|
EXPECT_EQ(sequence_d, pq.PopTaskSource());
// New top should be |sequence_c|
EXPECT_EQ(sort_key_c, pq.PeekDelayedSortKey());
}
{
EXPECT_EQ(sequence_c, pq.PopTaskSource());
EXPECT_EQ(sequence_b, pq.PopTaskSource());
EXPECT_EQ(sequence_a, pq.PopTaskSource());
}
{
// No-op if UpdateSortKey() is called on an empty PriorityQueue.
pq.UpdateDelayedSortKey(sequence_b);
EXPECT_TRUE(pq.IsEmpty());
}
}
} // namespace base::internal