#include "base/task/thread_pool/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 "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base::internal {
namespace {
class PriorityQueueWithSequencesTest : public testing::Test {
protected:
void ExpectNumSequences(size_t num_best_effort,
size_t num_user_visible,
size_t num_user_blocking) {
EXPECT_EQ(pq.GetNumTaskSourcesWithPriority(TaskPriority::BEST_EFFORT),
num_best_effort);
EXPECT_EQ(pq.GetNumTaskSourcesWithPriority(TaskPriority::USER_VISIBLE),
num_user_visible);
EXPECT_EQ(pq.GetNumTaskSourcesWithPriority(TaskPriority::USER_BLOCKING),
num_user_blocking);
}
scoped_refptr<TaskSource> MakeSequenceWithTraitsAndTask(
const TaskTraits& traits) {
task_environment.FastForwardBy(Microseconds(1));
scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>(
traits, nullptr, TaskSourceExecutionMode::kParallel);
auto transaction = sequence->BeginTransaction();
transaction.WillPushImmediateTask();
transaction.PushImmediateTask(
Task(FROM_HERE, DoNothing(), TimeTicks::Now(), TimeDelta()));
return sequence;
}
void Push(scoped_refptr<TaskSource> task_source) {
auto sort_key = task_source->GetSortKey();
pq.Push(RegisteredTaskSource::CreateForTesting(std::move(task_source)),
sort_key);
}
test::TaskEnvironment task_environment{
test::TaskEnvironment::TimeSource::MOCK_TIME};
scoped_refptr<TaskSource> sequence_a =
MakeSequenceWithTraitsAndTask(TaskTraits(TaskPriority::USER_VISIBLE));
TaskSourceSortKey sort_key_a = sequence_a->GetSortKey();
scoped_refptr<TaskSource> sequence_b =
MakeSequenceWithTraitsAndTask(TaskTraits(TaskPriority::USER_BLOCKING));
TaskSourceSortKey sort_key_b = sequence_b->GetSortKey();
scoped_refptr<TaskSource> sequence_c =
MakeSequenceWithTraitsAndTask(TaskTraits(TaskPriority::USER_BLOCKING));
TaskSourceSortKey sort_key_c = sequence_c->GetSortKey();
scoped_refptr<TaskSource> sequence_d =
MakeSequenceWithTraitsAndTask(TaskTraits(TaskPriority::BEST_EFFORT));
TaskSourceSortKey sort_key_d = sequence_d->GetSortKey();
PriorityQueue pq;
};
}
TEST_F(PriorityQueueWithSequencesTest, PushPopPeek) {
EXPECT_TRUE(pq.IsEmpty());
ExpectNumSequences(0U, 0U, 0U);
Push(sequence_a);
EXPECT_EQ(sort_key_a, pq.PeekSortKey());
ExpectNumSequences(0U, 1U, 0U);
Push(sequence_b);
EXPECT_EQ(sort_key_b, pq.PeekSortKey());
ExpectNumSequences(0U, 1U, 1U);
Push(sequence_c);
EXPECT_EQ(sort_key_b, pq.PeekSortKey());
ExpectNumSequences(0U, 1U, 2U);
Push(sequence_d);
EXPECT_EQ(sort_key_b, pq.PeekSortKey());
ExpectNumSequences(1U, 1U, 2U);
EXPECT_EQ(sequence_b, pq.PopTaskSource().Unregister());
EXPECT_EQ(sort_key_c, pq.PeekSortKey());
ExpectNumSequences(1U, 1U, 1U);
EXPECT_EQ(sequence_c, pq.PopTaskSource().Unregister());
EXPECT_EQ(sort_key_a, pq.PeekSortKey());
ExpectNumSequences(1U, 1U, 0U);
EXPECT_EQ(sequence_a, pq.PopTaskSource().Unregister());
EXPECT_EQ(sort_key_d, pq.PeekSortKey());
ExpectNumSequences(1U, 0U, 0U);
EXPECT_EQ(sequence_d, pq.PopTaskSource().Unregister());
EXPECT_TRUE(pq.IsEmpty());
ExpectNumSequences(0U, 0U, 0U);
}
TEST_F(PriorityQueueWithSequencesTest, RemoveSequence) {
EXPECT_TRUE(pq.IsEmpty());
Push(sequence_a);
Push(sequence_b);
Push(sequence_c);
Push(sequence_d);
EXPECT_EQ(sort_key_b, pq.PeekSortKey());
ExpectNumSequences(1U, 1U, 2U);
EXPECT_TRUE(pq.RemoveTaskSource(*sequence_a).Unregister());
EXPECT_EQ(sort_key_b, pq.PeekSortKey());
ExpectNumSequences(1U, 0U, 2U);
EXPECT_FALSE(pq.RemoveTaskSource(*sequence_a).Unregister());
ExpectNumSequences(1U, 0U, 2U);
EXPECT_TRUE(pq.RemoveTaskSource(*sequence_b).Unregister());
EXPECT_EQ(sort_key_c, pq.PeekSortKey());
ExpectNumSequences(1U, 0U, 1U);
EXPECT_TRUE(pq.RemoveTaskSource(*sequence_d).Unregister());
EXPECT_EQ(sort_key_c, pq.PeekSortKey());
ExpectNumSequences(0U, 0U, 1U);
EXPECT_TRUE(pq.RemoveTaskSource(*sequence_c).Unregister());
EXPECT_TRUE(pq.IsEmpty());
ExpectNumSequences(0U, 0U, 0U);
EXPECT_FALSE(pq.RemoveTaskSource(*sequence_c).Unregister());
ExpectNumSequences(0U, 0U, 0U);
}
TEST_F(PriorityQueueWithSequencesTest, UpdateSortKey) {
EXPECT_TRUE(pq.IsEmpty());
Push(sequence_a);
Push(sequence_b);
Push(sequence_c);
Push(sequence_d);
EXPECT_EQ(sort_key_b, pq.PeekSortKey());
ExpectNumSequences(1U, 1U, 2U);
{
auto sequence_b_transaction = sequence_b->BeginTransaction();
sequence_b_transaction.UpdatePriority(TaskPriority::BEST_EFFORT);
pq.UpdateSortKey(*sequence_b, sequence_b->GetSortKey());
EXPECT_EQ(sort_key_c, pq.PeekSortKey());
ExpectNumSequences(2U, 1U, 1U);
}
{
auto sequence_c_transaction = sequence_c->BeginTransaction();
sequence_c_transaction.UpdatePriority(TaskPriority::USER_BLOCKING);
pq.UpdateSortKey(*sequence_c, sequence_c->GetSortKey());
ExpectNumSequences(2U, 1U, 1U);
EXPECT_EQ(sequence_c, pq.PopTaskSource().Unregister());
EXPECT_EQ(sort_key_a, pq.PeekSortKey());
ExpectNumSequences(2U, 1U, 0U);
}
{
auto sequence_d_and_transaction = sequence_d->BeginTransaction();
sequence_d_and_transaction.UpdatePriority(TaskPriority::USER_BLOCKING);
pq.UpdateSortKey(*sequence_d, sequence_d->GetSortKey());
ExpectNumSequences(1U, 1U, 1U);
EXPECT_EQ(sequence_d, pq.PopTaskSource().Unregister());
EXPECT_EQ(sort_key_a, pq.PeekSortKey());
ExpectNumSequences(1U, 1U, 0U);
}
{
pq.UpdateSortKey(*sequence_d, sequence_d->GetSortKey());
ExpectNumSequences(1U, 1U, 0U);
EXPECT_EQ(sequence_a, pq.PopTaskSource().Unregister());
ExpectNumSequences(1U, 0U, 0U);
EXPECT_EQ(sequence_b, pq.PopTaskSource().Unregister());
ExpectNumSequences(0U, 0U, 0U);
}
{
pq.UpdateSortKey(*sequence_b, sequence_b->GetSortKey());
EXPECT_TRUE(pq.IsEmpty());
ExpectNumSequences(0U, 0U, 0U);
}
}
}