// 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/work_queue.h"

#include <stddef.h>

#include <memory>
#include <optional>

#include "base/functional/bind.h"
#include "base/task/common/lazy_now.h"
#include "base/task/sequence_manager/enqueue_order.h"
#include "base/task/sequence_manager/fence.h"
#include "base/task/sequence_manager/sequence_manager.h"
#include "base/task/sequence_manager/task_order.h"
#include "base/task/sequence_manager/task_queue_impl.h"
#include "base/task/sequence_manager/work_queue_sets.h"
#include "base/time/time.h"
#include "testing/gmock/include/gmock/gmock.h"

namespace base {
namespace sequence_manager {
namespace internal {

namespace {

class MockObserver : public WorkQueueSets::Observer {
  MOCK_METHOD1(WorkQueueSetBecameEmpty, void(size_t set_index));
  MOCK_METHOD1(WorkQueueSetBecameNonEmpty, void(size_t set_index));
};

void NopTask() {}

struct Cancelable {
  Cancelable() {}

  void NopTask() {}

  WeakPtrFactory<Cancelable> weak_ptr_factory{this};
};

}  // namespace

class WorkQueueTest : public testing::Test {
 public:
  WorkQueueTest() : WorkQueueTest(WorkQueue::QueueType::kImmediate) {}

  explicit WorkQueueTest(WorkQueue::QueueType queue_type)
      : queue_type_(queue_type) {}

  void SetUp() override {
    task_queue_ = std::make_unique<TaskQueueImpl>(
        /*sequence_manager=*/nullptr, /*wake_up_queue=*/nullptr,
        TaskQueue::Spec(QueueName::TEST_TQ));

    work_queue_ =
        std::make_unique<WorkQueue>(task_queue_.get(), "test", queue_type_);
    mock_observer_ = std::make_unique<MockObserver>();
    work_queue_sets_ = std::make_unique<WorkQueueSets>(
        "test", mock_observer_.get(), SequenceManager::Settings());
    work_queue_sets_->AddQueue(work_queue_.get(), 0);
  }

  void TearDown() override {
    work_queue_sets_->RemoveQueue(work_queue_.get());
    task_queue_->UnregisterTaskQueue();
  }

 protected:
  Task FakeCancelableTaskWithEnqueueOrder(int enqueue_order,
                                          WeakPtr<Cancelable> weak_ptr) {
    Task fake_task(PostedTask(nullptr, BindOnce(&Cancelable::NopTask, weak_ptr),
                              FROM_HERE),
                   EnqueueOrder(),
                   EnqueueOrder::FromIntForTesting(enqueue_order));
    return fake_task;
  }

  Task FakeTaskWithEnqueueOrder(int enqueue_order) {
    Task fake_task(PostedTask(nullptr, BindOnce(&NopTask), FROM_HERE),
                   EnqueueOrder(),
                   EnqueueOrder::FromIntForTesting(enqueue_order));
    return fake_task;
  }

  Task FakeNonNestableTaskWithEnqueueOrder(int enqueue_order) {
    Task fake_task(PostedTask(nullptr, BindOnce(&NopTask), FROM_HERE),
                   EnqueueOrder(),
                   EnqueueOrder::FromIntForTesting(enqueue_order));
    fake_task.nestable = Nestable::kNonNestable;
    return fake_task;
  }

  Task FakeTaskWithTaskOrder(TaskOrder task_order) {
    Task fake_task(PostedTask(nullptr, BindOnce(&NopTask), FROM_HERE,
                              task_order.delayed_run_time(),
                              subtle::DelayPolicy::kFlexibleNoSooner),
                   EnqueueOrder::FromIntForTesting(task_order.sequence_num()),
                   task_order.enqueue_order(), TimeTicks() + Milliseconds(1));
    return fake_task;
  }

  Fence CreateFenceWithEnqueueOrder(int enqueue_order) {
    return Fence(TaskOrder::CreateForTesting(
        EnqueueOrder::FromIntForTesting(enqueue_order)));
  }

  WorkQueue* GetOldestQueueInSet(int set) {
    if (auto queue_and_task_order =
            work_queue_sets_->GetOldestQueueAndTaskOrderInSet(set)) {
      return queue_and_task_order->queue;
    }
    return nullptr;
  }

  std::unique_ptr<MockObserver> mock_observer_;
  std::unique_ptr<TaskQueueImpl> task_queue_;
  std::unique_ptr<WorkQueue> work_queue_;
  std::unique_ptr<WorkQueueSets> work_queue_sets_;
  std::unique_ptr<TaskQueueImpl::TaskDeque> incoming_queue_;

 private:
  const WorkQueue::QueueType queue_type_;
};

class DelayedWorkQueueTest : public WorkQueueTest {
 public:
  DelayedWorkQueueTest() : WorkQueueTest(WorkQueue::QueueType::kDelayed) {}
};

TEST_F(WorkQueueTest, Empty) {
  EXPECT_TRUE(work_queue_->Empty());
  work_queue_->Push(FakeTaskWithEnqueueOrder(1));
  EXPECT_FALSE(work_queue_->Empty());
}

TEST_F(WorkQueueTest, Empty_IgnoresFences) {
  work_queue_->Push(FakeTaskWithEnqueueOrder(1));
  work_queue_->InsertFence(Fence::BlockingFence());
  EXPECT_FALSE(work_queue_->Empty());
}

TEST_F(WorkQueueTest, GetFrontTaskOrderQueueEmpty) {
  EXPECT_FALSE(work_queue_->GetFrontTaskOrder());
}

TEST_F(WorkQueueTest, GetFrontTaskOrder) {
  work_queue_->Push(FakeTaskWithEnqueueOrder(2));
  work_queue_->Push(FakeTaskWithEnqueueOrder(3));
  work_queue_->Push(FakeTaskWithEnqueueOrder(4));

  std::optional<TaskOrder> task_order = work_queue_->GetFrontTaskOrder();
  EXPECT_TRUE(task_order);
  EXPECT_EQ(2ull, task_order->enqueue_order());
}

TEST_F(WorkQueueTest, GetFrontTaskQueueEmpty) {
  EXPECT_EQ(nullptr, work_queue_->GetFrontTask());
}

TEST_F(WorkQueueTest, GetFrontTask) {
  work_queue_->Push(FakeTaskWithEnqueueOrder(2));
  work_queue_->Push(FakeTaskWithEnqueueOrder(3));
  work_queue_->Push(FakeTaskWithEnqueueOrder(4));

  ASSERT_NE(nullptr, work_queue_->GetFrontTask());
  EXPECT_EQ(2ull, work_queue_->GetFrontTask()->enqueue_order());
}

TEST_F(WorkQueueTest, GetBackTask_Empty) {
  EXPECT_EQ(nullptr, work_queue_->GetBackTask());
}

TEST_F(WorkQueueTest, GetBackTask) {
  work_queue_->Push(FakeTaskWithEnqueueOrder(2));
  work_queue_->Push(FakeTaskWithEnqueueOrder(3));
  work_queue_->Push(FakeTaskWithEnqueueOrder(4));

  ASSERT_NE(nullptr, work_queue_->GetBackTask());
  EXPECT_EQ(4ull, work_queue_->GetBackTask()->enqueue_order());
}

TEST_F(WorkQueueTest, Push) {
  EXPECT_EQ(nullptr, GetOldestQueueInSet(0));

  work_queue_->Push(FakeTaskWithEnqueueOrder(2));
  EXPECT_EQ(work_queue_.get(), GetOldestQueueInSet(0));
}

TEST_F(WorkQueueTest, PushMultiple) {
  EXPECT_EQ(nullptr, GetOldestQueueInSet(0));

  work_queue_->Push(FakeTaskWithEnqueueOrder(2));
  work_queue_->Push(FakeTaskWithEnqueueOrder(3));
  work_queue_->Push(FakeTaskWithEnqueueOrder(4));
  EXPECT_EQ(work_queue_.get(), GetOldestQueueInSet(0));
  EXPECT_EQ(2ull, work_queue_->GetFrontTask()->enqueue_order());
  EXPECT_EQ(4ull, work_queue_->GetBackTask()->enqueue_order());
}

TEST_F(WorkQueueTest, PushAfterFenceHit) {
  work_queue_->InsertFence(Fence::BlockingFence());
  EXPECT_EQ(nullptr, GetOldestQueueInSet(0));

  work_queue_->Push(FakeTaskWithEnqueueOrder(2));
  EXPECT_EQ(nullptr, GetOldestQueueInSet(0));
}

TEST_F(WorkQueueTest, CreateTaskPusherNothingPushed) {
  EXPECT_EQ(nullptr, GetOldestQueueInSet(0));
  { WorkQueue::TaskPusher task_pusher(work_queue_->CreateTaskPusher()); }
  EXPECT_EQ(nullptr, GetOldestQueueInSet(0));
}

TEST_F(WorkQueueTest, CreateTaskPusherOneTask) {
  EXPECT_EQ(nullptr, GetOldestQueueInSet(0));
  {
    WorkQueue::TaskPusher task_pusher(work_queue_->CreateTaskPusher());
    Task task = FakeTaskWithEnqueueOrder(2);
    task_pusher.Push(std::move(task));
  }
  EXPECT_EQ(work_queue_.get(), GetOldestQueueInSet(0));
}

TEST_F(WorkQueueTest, CreateTaskPusherThreeTasks) {
  EXPECT_EQ(nullptr, GetOldestQueueInSet(0));
  {
    WorkQueue::TaskPusher task_pusher(work_queue_->CreateTaskPusher());
    task_pusher.Push(FakeTaskWithEnqueueOrder(2));
    task_pusher.Push(FakeTaskWithEnqueueOrder(3));
    task_pusher.Push(FakeTaskWithEnqueueOrder(4));
  }
  EXPECT_EQ(work_queue_.get(), GetOldestQueueInSet(0));
  EXPECT_EQ(2ull, work_queue_->GetFrontTask()->enqueue_order());
  EXPECT_EQ(4ull, work_queue_->GetBackTask()->enqueue_order());
}

TEST_F(WorkQueueTest, CreateTaskPusherAfterFenceHit) {
  work_queue_->InsertFence(Fence::BlockingFence());
  EXPECT_EQ(nullptr, GetOldestQueueInSet(0));
  {
    WorkQueue::TaskPusher task_pusher(work_queue_->CreateTaskPusher());
    task_pusher.Push(FakeTaskWithEnqueueOrder(2));
    task_pusher.Push(FakeTaskWithEnqueueOrder(3));
    task_pusher.Push(FakeTaskWithEnqueueOrder(4));
  }
  EXPECT_EQ(nullptr, GetOldestQueueInSet(0));
}

TEST_F(WorkQueueTest, PushNonNestableTaskToFront) {
  EXPECT_EQ(nullptr, GetOldestQueueInSet(0));

  work_queue_->PushNonNestableTaskToFront(
      FakeNonNestableTaskWithEnqueueOrder(3));
  EXPECT_EQ(work_queue_.get(), GetOldestQueueInSet(0));

  work_queue_->PushNonNestableTaskToFront(
      FakeNonNestableTaskWithEnqueueOrder(2));
  EXPECT_EQ(2ull, work_queue_->GetFrontTask()->enqueue_order());
  EXPECT_EQ(3ull, work_queue_->GetBackTask()->enqueue_order());
}

TEST_F(WorkQueueTest, PushNonNestableTaskToFrontAfterFenceHit) {
  work_queue_->InsertFence(Fence::BlockingFence());
  EXPECT_EQ(nullptr, GetOldestQueueInSet(0));

  work_queue_->PushNonNestableTaskToFront(
      FakeNonNestableTaskWithEnqueueOrder(2));
  EXPECT_EQ(nullptr, GetOldestQueueInSet(0));
}

TEST_F(WorkQueueTest, PushNonNestableTaskToFrontBeforeFenceHit) {
  work_queue_->InsertFence(CreateFenceWithEnqueueOrder(3));
  EXPECT_EQ(nullptr, GetOldestQueueInSet(0));

  work_queue_->PushNonNestableTaskToFront(
      FakeNonNestableTaskWithEnqueueOrder(2));
  EXPECT_EQ(work_queue_.get(), GetOldestQueueInSet(0));
}

TEST_F(WorkQueueTest, TakeImmediateIncomingQueueTasks) {
  task_queue_->PushImmediateIncomingTaskForTest(FakeTaskWithEnqueueOrder(2));
  task_queue_->PushImmediateIncomingTaskForTest(FakeTaskWithEnqueueOrder(3));
  task_queue_->PushImmediateIncomingTaskForTest(FakeTaskWithEnqueueOrder(4));
  EXPECT_EQ(nullptr, GetOldestQueueInSet(0));
  EXPECT_TRUE(work_queue_->Empty());

  work_queue_->TakeImmediateIncomingQueueTasks();
  EXPECT_EQ(work_queue_.get(), GetOldestQueueInSet(0));
  EXPECT_FALSE(work_queue_->Empty());

  ASSERT_NE(nullptr, work_queue_->GetFrontTask());
  EXPECT_EQ(2ull, work_queue_->GetFrontTask()->enqueue_order());

  ASSERT_NE(nullptr, work_queue_->GetBackTask());
  EXPECT_EQ(4ull, work_queue_->GetBackTask()->enqueue_order());
}

TEST_F(WorkQueueTest, TakeImmediateIncomingQueueTasksAfterFenceHit) {
  work_queue_->InsertFence(Fence::BlockingFence());
  task_queue_->PushImmediateIncomingTaskForTest(FakeTaskWithEnqueueOrder(2));
  task_queue_->PushImmediateIncomingTaskForTest(FakeTaskWithEnqueueOrder(3));
  task_queue_->PushImmediateIncomingTaskForTest(FakeTaskWithEnqueueOrder(4));
  EXPECT_EQ(nullptr, GetOldestQueueInSet(0));
  EXPECT_TRUE(work_queue_->Empty());

  work_queue_->TakeImmediateIncomingQueueTasks();
  EXPECT_EQ(nullptr, GetOldestQueueInSet(0));
  EXPECT_FALSE(work_queue_->Empty());

  ASSERT_NE(nullptr, work_queue_->GetFrontTask());
  EXPECT_EQ(2ull, work_queue_->GetFrontTask()->enqueue_order());

  ASSERT_NE(nullptr, work_queue_->GetBackTask());
  EXPECT_EQ(4ull, work_queue_->GetBackTask()->enqueue_order());
}

TEST_F(WorkQueueTest, TakeTaskFromWorkQueue) {
  work_queue_->Push(FakeTaskWithEnqueueOrder(2));
  work_queue_->Push(FakeTaskWithEnqueueOrder(3));
  work_queue_->Push(FakeTaskWithEnqueueOrder(4));
  EXPECT_EQ(work_queue_.get(), GetOldestQueueInSet(0));
  EXPECT_FALSE(work_queue_->Empty());

  EXPECT_EQ(2ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());
  EXPECT_EQ(3ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());
  EXPECT_EQ(4ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());

  EXPECT_EQ(nullptr, GetOldestQueueInSet(0));
  EXPECT_TRUE(work_queue_->Empty());
}

TEST_F(WorkQueueTest, TakeTaskFromWorkQueue_HitFence) {
  work_queue_->InsertFence(CreateFenceWithEnqueueOrder(3));
  work_queue_->Push(FakeTaskWithEnqueueOrder(2));
  work_queue_->Push(FakeTaskWithEnqueueOrder(4));
  EXPECT_FALSE(work_queue_->BlockedByFence());

  EXPECT_EQ(work_queue_.get(), GetOldestQueueInSet(0));
  EXPECT_FALSE(work_queue_->Empty());
  EXPECT_FALSE(work_queue_->BlockedByFence());

  EXPECT_EQ(2ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());
  EXPECT_EQ(nullptr, GetOldestQueueInSet(0));
  EXPECT_FALSE(work_queue_->Empty());
  EXPECT_TRUE(work_queue_->BlockedByFence());
}

TEST_F(WorkQueueTest, InsertFenceBeforeEnqueueing) {
  EXPECT_FALSE(work_queue_->InsertFence(Fence::BlockingFence()));
  EXPECT_TRUE(work_queue_->BlockedByFence());

  work_queue_->Push(FakeTaskWithEnqueueOrder(2));
  work_queue_->Push(FakeTaskWithEnqueueOrder(3));
  work_queue_->Push(FakeTaskWithEnqueueOrder(4));

  EXPECT_FALSE(work_queue_->GetFrontTaskOrder());
}

TEST_F(WorkQueueTest, InsertFenceAfterEnqueueingNonBlocking) {
  work_queue_->Push(FakeTaskWithEnqueueOrder(2));
  work_queue_->Push(FakeTaskWithEnqueueOrder(3));
  work_queue_->Push(FakeTaskWithEnqueueOrder(4));

  EXPECT_FALSE(work_queue_->InsertFence(CreateFenceWithEnqueueOrder(5)));
  EXPECT_FALSE(work_queue_->BlockedByFence());

  EXPECT_TRUE(work_queue_->GetFrontTaskOrder());
  EXPECT_EQ(2ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());
}

TEST_F(WorkQueueTest, InsertFenceAfterEnqueueing) {
  work_queue_->Push(FakeTaskWithEnqueueOrder(2));
  work_queue_->Push(FakeTaskWithEnqueueOrder(3));
  work_queue_->Push(FakeTaskWithEnqueueOrder(4));

  // NB in reality a fence will always be greater than any currently enqueued
  // tasks.
  EXPECT_FALSE(work_queue_->InsertFence(Fence::BlockingFence()));
  EXPECT_TRUE(work_queue_->BlockedByFence());

  EXPECT_FALSE(work_queue_->GetFrontTaskOrder());
}

TEST_F(WorkQueueTest, InsertNewFence) {
  work_queue_->Push(FakeTaskWithEnqueueOrder(2));
  work_queue_->Push(FakeTaskWithEnqueueOrder(4));
  work_queue_->Push(FakeTaskWithEnqueueOrder(5));

  EXPECT_FALSE(work_queue_->InsertFence(CreateFenceWithEnqueueOrder(3)));
  EXPECT_FALSE(work_queue_->BlockedByFence());

  // Note until TakeTaskFromWorkQueue() is called we don't hit the fence.
  std::optional<TaskOrder> task_order = work_queue_->GetFrontTaskOrder();
  EXPECT_TRUE(task_order);
  EXPECT_EQ(2ull, task_order->enqueue_order());

  EXPECT_EQ(2ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());
  EXPECT_FALSE(work_queue_->GetFrontTaskOrder());
  EXPECT_TRUE(work_queue_->BlockedByFence());

  // Inserting the new fence should temporarily unblock the queue until the new
  // one is hit.
  EXPECT_TRUE(work_queue_->InsertFence(CreateFenceWithEnqueueOrder(6)));
  EXPECT_FALSE(work_queue_->BlockedByFence());

  task_order = work_queue_->GetFrontTaskOrder();
  EXPECT_TRUE(task_order);
  EXPECT_EQ(4ull, task_order->enqueue_order());
  EXPECT_EQ(4ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());
  EXPECT_TRUE(work_queue_->GetFrontTaskOrder());
  EXPECT_FALSE(work_queue_->BlockedByFence());
}

TEST_F(WorkQueueTest, PushWithNonEmptyQueueDoesNotHitFence) {
  work_queue_->Push(FakeTaskWithEnqueueOrder(1));
  EXPECT_FALSE(work_queue_->InsertFence(CreateFenceWithEnqueueOrder(2)));
  work_queue_->Push(FakeTaskWithEnqueueOrder(3));
  EXPECT_FALSE(work_queue_->BlockedByFence());
}

TEST_F(WorkQueueTest, RemoveFence) {
  work_queue_->Push(FakeTaskWithEnqueueOrder(2));
  work_queue_->Push(FakeTaskWithEnqueueOrder(4));
  work_queue_->Push(FakeTaskWithEnqueueOrder(5));
  work_queue_->InsertFence(CreateFenceWithEnqueueOrder(3));
  EXPECT_EQ(work_queue_.get(), GetOldestQueueInSet(0));
  EXPECT_FALSE(work_queue_->Empty());

  EXPECT_EQ(2ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());
  EXPECT_EQ(nullptr, GetOldestQueueInSet(0));
  EXPECT_FALSE(work_queue_->Empty());
  EXPECT_TRUE(work_queue_->BlockedByFence());

  EXPECT_TRUE(work_queue_->RemoveFence());
  EXPECT_EQ(4ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());
  EXPECT_EQ(work_queue_.get(), GetOldestQueueInSet(0));
  EXPECT_FALSE(work_queue_->BlockedByFence());
}

TEST_F(WorkQueueTest, RemoveFenceButNoFence) {
  EXPECT_FALSE(work_queue_->RemoveFence());
}

TEST_F(WorkQueueTest, RemoveFenceNothingUnblocked) {
  EXPECT_FALSE(work_queue_->InsertFence(Fence::BlockingFence()));
  EXPECT_TRUE(work_queue_->BlockedByFence());

  EXPECT_FALSE(work_queue_->RemoveFence());
  EXPECT_FALSE(work_queue_->BlockedByFence());
}

TEST_F(WorkQueueTest, BlockedByFence) {
  EXPECT_FALSE(work_queue_->BlockedByFence());
  EXPECT_FALSE(work_queue_->InsertFence(Fence::BlockingFence()));
  EXPECT_TRUE(work_queue_->BlockedByFence());
}

TEST_F(WorkQueueTest, BlockedByFencePopBecomesEmpty) {
  work_queue_->Push(FakeTaskWithEnqueueOrder(1));
  EXPECT_FALSE(work_queue_->InsertFence(CreateFenceWithEnqueueOrder(2)));
  EXPECT_FALSE(work_queue_->BlockedByFence());

  EXPECT_EQ(1ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());
  EXPECT_TRUE(work_queue_->BlockedByFence());
}

TEST_F(WorkQueueTest, BlockedByFencePop) {
  work_queue_->Push(FakeTaskWithEnqueueOrder(1));
  EXPECT_FALSE(work_queue_->InsertFence(CreateFenceWithEnqueueOrder(2)));
  EXPECT_FALSE(work_queue_->BlockedByFence());

  work_queue_->Push(FakeTaskWithEnqueueOrder(3));
  EXPECT_FALSE(work_queue_->BlockedByFence());

  EXPECT_EQ(1ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());
  EXPECT_TRUE(work_queue_->BlockedByFence());
}

TEST_F(WorkQueueTest, InitiallyEmptyBlockedByFenceNewFenceUnblocks) {
  EXPECT_FALSE(work_queue_->InsertFence(Fence::BlockingFence()));
  EXPECT_TRUE(work_queue_->BlockedByFence());

  work_queue_->Push(FakeTaskWithEnqueueOrder(2));
  EXPECT_TRUE(work_queue_->InsertFence(CreateFenceWithEnqueueOrder(3)));
  EXPECT_FALSE(work_queue_->BlockedByFence());
}

TEST_F(WorkQueueTest, BlockedByFenceNewFenceUnblocks) {
  work_queue_->Push(FakeTaskWithEnqueueOrder(1));
  EXPECT_FALSE(work_queue_->InsertFence(CreateFenceWithEnqueueOrder(2)));
  EXPECT_FALSE(work_queue_->BlockedByFence());

  work_queue_->Push(FakeTaskWithEnqueueOrder(3));
  EXPECT_FALSE(work_queue_->BlockedByFence());

  EXPECT_EQ(1ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order());
  EXPECT_TRUE(work_queue_->BlockedByFence());

  EXPECT_TRUE(work_queue_->InsertFence(CreateFenceWithEnqueueOrder(4)));
  EXPECT_FALSE(work_queue_->BlockedByFence());
}

TEST_F(WorkQueueTest, InsertFenceAfterEnqueuing) {
  work_queue_->Push(FakeTaskWithEnqueueOrder(2));
  work_queue_->Push(FakeTaskWithEnqueueOrder(3));
  work_queue_->Push(FakeTaskWithEnqueueOrder(4));
  EXPECT_FALSE(work_queue_->BlockedByFence());

  EXPECT_FALSE(work_queue_->InsertFence(Fence::BlockingFence()));
  EXPECT_TRUE(work_queue_->BlockedByFence());

  EXPECT_FALSE(work_queue_->GetFrontTaskOrder());
}

TEST_F(WorkQueueTest, RemoveAllCanceledTasksFromFront) {
  {
    Cancelable cancelable;
    work_queue_->Push(FakeCancelableTaskWithEnqueueOrder(
        2, cancelable.weak_ptr_factory.GetWeakPtr()));
    work_queue_->Push(FakeCancelableTaskWithEnqueueOrder(
        3, cancelable.weak_ptr_factory.GetWeakPtr()));
    work_queue_->Push(FakeCancelableTaskWithEnqueueOrder(
        4, cancelable.weak_ptr_factory.GetWeakPtr()));
    work_queue_->Push(FakeTaskWithEnqueueOrder(5));
  }
  EXPECT_TRUE(work_queue_->RemoveAllCanceledTasksFromFront());

  std::optional<TaskOrder> task_order = work_queue_->GetFrontTaskOrder();
  EXPECT_TRUE(task_order);
  EXPECT_EQ(5ull, task_order->enqueue_order());
}

TEST_F(WorkQueueTest, RemoveAllCanceledTasksFromFrontTasksNotCanceled) {
  {
    Cancelable cancelable;
    work_queue_->Push(FakeCancelableTaskWithEnqueueOrder(
        2, cancelable.weak_ptr_factory.GetWeakPtr()));
    work_queue_->Push(FakeCancelableTaskWithEnqueueOrder(
        3, cancelable.weak_ptr_factory.GetWeakPtr()));
    work_queue_->Push(FakeCancelableTaskWithEnqueueOrder(
        4, cancelable.weak_ptr_factory.GetWeakPtr()));
    work_queue_->Push(FakeTaskWithEnqueueOrder(5));
    EXPECT_FALSE(work_queue_->RemoveAllCanceledTasksFromFront());

    std::optional<TaskOrder> task_order = work_queue_->GetFrontTaskOrder();
    EXPECT_TRUE(task_order);
    EXPECT_EQ(2ull, task_order->enqueue_order());
  }
}

TEST_F(WorkQueueTest, RemoveAllCanceledTasksFromFrontQueueBlockedByFence) {
  {
    Cancelable cancelable;
    work_queue_->Push(FakeCancelableTaskWithEnqueueOrder(
        2, cancelable.weak_ptr_factory.GetWeakPtr()));
    work_queue_->Push(FakeCancelableTaskWithEnqueueOrder(
        3, cancelable.weak_ptr_factory.GetWeakPtr()));
    work_queue_->Push(FakeCancelableTaskWithEnqueueOrder(
        4, cancelable.weak_ptr_factory.GetWeakPtr()));
    work_queue_->Push(FakeTaskWithEnqueueOrder(5));
  }

  EXPECT_FALSE(work_queue_->InsertFence(Fence::BlockingFence()));
  EXPECT_TRUE(work_queue_->BlockedByFence());

  EXPECT_TRUE(work_queue_->RemoveAllCanceledTasksFromFront());

  EXPECT_FALSE(work_queue_->GetFrontTaskOrder());
}

TEST_F(WorkQueueTest, CollectTasksOlderThan) {
  work_queue_->Push(FakeTaskWithEnqueueOrder(2));
  work_queue_->Push(FakeTaskWithEnqueueOrder(3));
  work_queue_->Push(FakeTaskWithEnqueueOrder(4));

  std::vector<const Task*> result;
  work_queue_->CollectTasksOlderThan(
      TaskOrder::CreateForTesting(EnqueueOrder::FromIntForTesting(4),
                                  TimeTicks(), 0),
      &result);

  ASSERT_EQ(2u, result.size());
  EXPECT_EQ(2u, result[0]->enqueue_order());
  EXPECT_EQ(3u, result[1]->enqueue_order());
}

TEST_F(DelayedWorkQueueTest, PushMultipleWithSameEnqueueOrder) {
  const EnqueueOrder kEnqueueOrder = EnqueueOrder::FromIntForTesting(5);
  TaskOrder task_orders[3] = {
      TaskOrder::CreateForTesting(kEnqueueOrder, TimeTicks() + Seconds(1),
                                  /*sequence_num=*/4),
      TaskOrder::CreateForTesting(kEnqueueOrder, TimeTicks() + Seconds(2),
                                  /*sequence_num=*/3),
      TaskOrder::CreateForTesting(kEnqueueOrder, TimeTicks() + Seconds(3),
                                  /*sequence_num=*/2),
  };

  EXPECT_EQ(nullptr, GetOldestQueueInSet(0));
  for (auto& task_order : task_orders) {
    work_queue_->Push(FakeTaskWithTaskOrder(task_order));
  }

  EXPECT_TRUE(task_orders[0] == work_queue_->GetFrontTaskOrder());
  EXPECT_TRUE(task_orders[0] == work_queue_->GetFrontTask()->task_order());

  EXPECT_TRUE(task_orders[2] == work_queue_->GetBackTask()->task_order());
}

TEST_F(DelayedWorkQueueTest, DelayedFenceInDelayedTaskGroup) {
  const EnqueueOrder kEnqueueOrder = EnqueueOrder::FromIntForTesting(5);

  TaskOrder task_orders[3] = {
      TaskOrder::CreateForTesting(kEnqueueOrder, TimeTicks() + Seconds(1),
                                  /*sequence_num=*/4),
      TaskOrder::CreateForTesting(kEnqueueOrder, TimeTicks() + Seconds(2),
                                  /*sequence_num=*/3),
      TaskOrder::CreateForTesting(kEnqueueOrder, TimeTicks() + Seconds(3),
                                  /*sequence_num=*/2),
  };

  EXPECT_EQ(nullptr, GetOldestQueueInSet(0));
  for (auto& task_order : task_orders) {
    work_queue_->Push(FakeTaskWithTaskOrder(task_order));
  }

  work_queue_->InsertFence(Fence(task_orders[2]));

  EXPECT_FALSE(work_queue_->BlockedByFence());
  EXPECT_EQ(work_queue_.get(), GetOldestQueueInSet(0));
  EXPECT_FALSE(work_queue_->Empty());
  EXPECT_TRUE(task_orders[0] ==
              work_queue_->TakeTaskFromWorkQueue().task_order());

  EXPECT_FALSE(work_queue_->BlockedByFence());
  EXPECT_EQ(work_queue_.get(), GetOldestQueueInSet(0));
  EXPECT_FALSE(work_queue_->Empty());
  EXPECT_TRUE(task_orders[1] ==
              work_queue_->TakeTaskFromWorkQueue().task_order());

  EXPECT_TRUE(work_queue_->BlockedByFence());
  EXPECT_EQ(nullptr, GetOldestQueueInSet(0));
  EXPECT_FALSE(work_queue_->Empty());
}

}  // namespace internal
}  // namespace sequence_manager
}  // namespace base
