// 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 "third_party/blink/renderer/platform/timer.h"

#include <memory>
#include <queue>

#include "base/memory/raw_ptr.h"
#include "base/task/common/lazy_now.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/heap/thread_state.h"
#include "third_party/blink/renderer/platform/heap/thread_state_scopes.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
#include "third_party/blink/renderer/platform/scheduler/test/task_environment.h"
#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
#include "third_party/blink/renderer/platform/wtf/ref_counted.h"

using base::sequence_manager::TaskQueue;
using blink::scheduler::MainThreadTaskQueue;
using testing::ElementsAre;

namespace blink {
namespace {

class TimerTest : public testing::Test {
 public:
  TimerTest()
      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
    scoped_refptr<MainThreadTaskQueue> task_queue =
        task_environment_.GetMainThreadScheduler()->NewTaskQueue(
            MainThreadTaskQueue::QueueCreationParams(
                MainThreadTaskQueue::QueueType::kTest));
    task_runner_ = task_queue->CreateTaskRunner(TaskType::kInternalTest);
  }

  void SetUp() override {
    run_times_.clear();
    task_environment_.AdvanceClock(base::Seconds(10));
    start_time_ = Now();
  }

  base::TimeTicks Now() { return task_environment_.NowTicks(); }

  void CountingTask(TimerBase*) { run_times_.push_back(Now()); }

  void RecordNextFireTimeTask(TimerBase* timer) {
    next_fire_times_.push_back(Now() + timer->NextFireInterval());
  }

  void RunUntilDeadline(base::TimeTicks deadline) {
    base::TimeDelta period = deadline - Now();
    EXPECT_GE(period, base::TimeDelta());
    task_environment_.FastForwardBy(period);
  }

  // Returns false if there are no pending delayed tasks, otherwise sets |time|
  // to the delay in seconds till the next pending delayed task is scheduled to
  // fire.
  bool TimeTillNextDelayedTask(base::TimeDelta* time) const {
    base::LazyNow lazy_now(task_environment_.NowTicks());
    auto* scheduler_helper = task_environment_.GetMainThreadScheduler()
                                 ->GetSchedulerHelperForTesting();
    scheduler_helper->ReclaimMemory();
    auto wake_up = scheduler_helper->GetNextWakeUp();
    if (!wake_up)
      return false;
    *time = wake_up->time - lazy_now.Now();
    return true;
  }

  scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() {
    return task_runner_;
  }

 protected:
  test::TaskEnvironmentWithMainThreadScheduler task_environment_;
  ScopedTestingPlatformSupport<TestingPlatformSupport> platform_;
  base::TimeTicks start_time_;
  Vector<base::TimeTicks> run_times_;
  Vector<base::TimeTicks> next_fire_times_;
  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
};

class OnHeapTimerOwner final : public GarbageCollected<OnHeapTimerOwner> {
 public:
  class Record final : public RefCounted<Record> {
   public:
    static scoped_refptr<Record> Create() { return base::AdoptRef(new Record); }

    bool TimerHasFired() const { return timer_has_fired_; }
    bool IsDisposed() const { return is_disposed_; }
    bool OwnerIsDestructed() const { return owner_is_destructed_; }
    void SetTimerHasFired() { timer_has_fired_ = true; }
    void Dispose() { is_disposed_ = true; }
    void SetOwnerIsDestructed() { owner_is_destructed_ = true; }

   private:
    Record() = default;

    bool timer_has_fired_ = false;
    bool is_disposed_ = false;
    bool owner_is_destructed_ = false;
  };

  explicit OnHeapTimerOwner(
      scoped_refptr<Record> record,
      scoped_refptr<base::SingleThreadTaskRunner> task_runner)
      : timer_(std::move(task_runner), this, &OnHeapTimerOwner::Fired),
        record_(std::move(record)) {}
  ~OnHeapTimerOwner() { record_->SetOwnerIsDestructed(); }

  void StartOneShot(base::TimeDelta interval, const base::Location& caller) {
    timer_.StartOneShot(interval, caller);
  }

  void Trace(Visitor* visitor) const { visitor->Trace(timer_); }

 private:
  void Fired(TimerBase*) {
    EXPECT_FALSE(record_->IsDisposed());
    record_->SetTimerHasFired();
  }

  HeapTaskRunnerTimer<OnHeapTimerOwner> timer_;
  scoped_refptr<Record> record_;
};

TEST_F(TimerTest, StartOneShot_Zero) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartOneShot(base::TimeDelta(), FROM_HERE);

  base::TimeDelta run_time;
  EXPECT_FALSE(TimeTillNextDelayedTask(&run_time));

  task_environment_.RunUntilIdle();
  EXPECT_THAT(run_times_, ElementsAre(start_time_));
}

TEST_F(TimerTest, StartOneShot_ZeroAndCancel) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartOneShot(base::TimeDelta(), FROM_HERE);

  base::TimeDelta run_time;
  EXPECT_FALSE(TimeTillNextDelayedTask(&run_time));

  timer.Stop();

  task_environment_.RunUntilIdle();
  EXPECT_FALSE(run_times_.size());
}

TEST_F(TimerTest, StartOneShot_ZeroAndCancelThenRepost) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartOneShot(base::TimeDelta(), FROM_HERE);

  base::TimeDelta run_time;
  EXPECT_FALSE(TimeTillNextDelayedTask(&run_time));

  timer.Stop();

  task_environment_.RunUntilIdle();
  EXPECT_FALSE(run_times_.size());

  timer.StartOneShot(base::TimeDelta(), FROM_HERE);

  EXPECT_FALSE(TimeTillNextDelayedTask(&run_time));

  task_environment_.RunUntilIdle();
  EXPECT_THAT(run_times_, ElementsAre(start_time_));
}

TEST_F(TimerTest, StartOneShot_Zero_RepostingAfterRunning) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartOneShot(base::TimeDelta(), FROM_HERE);

  base::TimeDelta run_time;
  EXPECT_FALSE(TimeTillNextDelayedTask(&run_time));

  task_environment_.RunUntilIdle();
  EXPECT_THAT(run_times_, ElementsAre(start_time_));

  timer.StartOneShot(base::TimeDelta(), FROM_HERE);

  EXPECT_FALSE(TimeTillNextDelayedTask(&run_time));

  task_environment_.RunUntilIdle();
  EXPECT_THAT(run_times_, ElementsAre(start_time_, start_time_));
}

TEST_F(TimerTest, StartOneShot_NonZero) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartOneShot(base::Seconds(10), FROM_HERE);

  base::TimeDelta run_time;
  EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
  EXPECT_EQ(base::Seconds(10), run_time);

  task_environment_.FastForwardUntilNoTasksRemain();
  EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(10)));
}

TEST_F(TimerTest, StartOneShot_NonZeroAndCancel) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartOneShot(base::Seconds(10), FROM_HERE);

  base::TimeDelta run_time;
  EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
  EXPECT_EQ(base::Seconds(10), run_time);

  timer.Stop();
  EXPECT_FALSE(TimeTillNextDelayedTask(&run_time));

  task_environment_.RunUntilIdle();
  EXPECT_FALSE(run_times_.size());
}

TEST_F(TimerTest, StartOneShot_NonZeroAndCancelThenRepost) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartOneShot(base::Seconds(10), FROM_HERE);

  base::TimeDelta run_time;
  EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
  EXPECT_EQ(base::Seconds(10), run_time);

  timer.Stop();
  EXPECT_FALSE(TimeTillNextDelayedTask(&run_time));

  task_environment_.RunUntilIdle();
  EXPECT_FALSE(run_times_.size());

  base::TimeTicks second_post_time = Now();
  timer.StartOneShot(base::Seconds(10), FROM_HERE);

  EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
  EXPECT_EQ(base::Seconds(10), run_time);

  task_environment_.FastForwardUntilNoTasksRemain();
  EXPECT_THAT(run_times_, ElementsAre(second_post_time + base::Seconds(10)));
}

TEST_F(TimerTest, StartOneShot_NonZero_RepostingAfterRunning) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartOneShot(base::Seconds(10), FROM_HERE);

  base::TimeDelta run_time;
  EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
  EXPECT_EQ(base::Seconds(10), run_time);

  task_environment_.FastForwardUntilNoTasksRemain();
  EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(10)));

  timer.StartOneShot(base::Seconds(20), FROM_HERE);

  EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
  EXPECT_EQ(base::Seconds(20), run_time);

  task_environment_.FastForwardBy(base::Seconds(20));
  EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(10),
                                      start_time_ + base::Seconds(30)));
}

TEST_F(TimerTest, PostingTimerTwiceWithSameRunTimeDoesNothing) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartOneShot(base::Seconds(10), FROM_HERE);
  timer.StartOneShot(base::Seconds(10), FROM_HERE);

  base::TimeDelta run_time;
  EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
  EXPECT_EQ(base::Seconds(10), run_time);

  task_environment_.FastForwardUntilNoTasksRemain();
  EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(10)));
}

TEST_F(TimerTest, PostingTimerTwiceWithNewerRunTimeCancelsOriginalTask) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartOneShot(base::Seconds(10), FROM_HERE);
  timer.StartOneShot(base::TimeDelta(), FROM_HERE);

  task_environment_.RunUntilIdle();
  EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(0)));
}

TEST_F(TimerTest, PostingTimerTwiceWithLaterRunTimeCancelsOriginalTask) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartOneShot(base::TimeDelta(), FROM_HERE);
  timer.StartOneShot(base::Seconds(10), FROM_HERE);

  task_environment_.FastForwardBy(base::Seconds(10));
  EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(10)));
}

TEST_F(TimerTest, StartRepeatingTask) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartRepeating(base::Seconds(1), FROM_HERE);

  base::TimeDelta run_time;
  EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
  EXPECT_EQ(base::Seconds(1), run_time);

  RunUntilDeadline(start_time_ + base::Milliseconds(5500));
  EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(1),
                                      start_time_ + base::Seconds(2),
                                      start_time_ + base::Seconds(3),
                                      start_time_ + base::Seconds(4),
                                      start_time_ + base::Seconds(5)));
}

TEST_F(TimerTest, StartRepeatingTask_ThenCancel) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartRepeating(base::Seconds(1), FROM_HERE);

  base::TimeDelta run_time;
  EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
  EXPECT_EQ(base::Seconds(1), run_time);

  RunUntilDeadline(start_time_ + base::Milliseconds(2500));
  EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(1),
                                      start_time_ + base::Seconds(2)));

  timer.Stop();
  task_environment_.RunUntilIdle();

  EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(1),
                                      start_time_ + base::Seconds(2)));
}

TEST_F(TimerTest, StartRepeatingTask_ThenPostOneShot) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartRepeating(base::Seconds(1), FROM_HERE);

  base::TimeDelta run_time;
  EXPECT_TRUE(TimeTillNextDelayedTask(&run_time));
  EXPECT_EQ(base::Seconds(1), run_time);

  RunUntilDeadline(start_time_ + base::Milliseconds(2500));
  EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(1),
                                      start_time_ + base::Seconds(2)));

  timer.StartOneShot(base::TimeDelta(), FROM_HERE);
  task_environment_.RunUntilIdle();

  EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(1),
                                      start_time_ + base::Seconds(2),
                                      start_time_ + base::Milliseconds(2500)));
}

TEST_F(TimerTest, IsActive_NeverPosted) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);

  EXPECT_FALSE(timer.IsActive());
}

TEST_F(TimerTest, IsActive_AfterPosting_OneShotZero) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartOneShot(base::TimeDelta(), FROM_HERE);

  EXPECT_TRUE(timer.IsActive());
}

TEST_F(TimerTest, IsActive_AfterPosting_OneShotNonZero) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartOneShot(base::Seconds(10), FROM_HERE);

  EXPECT_TRUE(timer.IsActive());
}

TEST_F(TimerTest, IsActive_AfterPosting_Repeating) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartRepeating(base::Seconds(1), FROM_HERE);

  EXPECT_TRUE(timer.IsActive());
}

TEST_F(TimerTest, IsActive_AfterRunning_OneShotZero) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartOneShot(base::TimeDelta(), FROM_HERE);

  task_environment_.RunUntilIdle();
  EXPECT_FALSE(timer.IsActive());
}

TEST_F(TimerTest, IsActive_AfterRunning_OneShotNonZero) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartOneShot(base::Seconds(10), FROM_HERE);

  task_environment_.FastForwardBy(base::Seconds(10));
  EXPECT_FALSE(timer.IsActive());
}

TEST_F(TimerTest, IsActive_AfterRunning_Repeating) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartRepeating(base::Seconds(1), FROM_HERE);

  RunUntilDeadline(start_time_ + base::Seconds(10));
  EXPECT_TRUE(timer.IsActive());  // It should run until cancelled.
}

TEST_F(TimerTest, NextFireInterval_OneShotZero) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartOneShot(base::TimeDelta(), FROM_HERE);

  EXPECT_TRUE(timer.NextFireInterval().is_zero());
}

TEST_F(TimerTest, NextFireInterval_OneShotNonZero) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartOneShot(base::Seconds(10), FROM_HERE);

  EXPECT_EQ(base::Seconds(10), timer.NextFireInterval());
}

TEST_F(TimerTest, NextFireInterval_OneShotNonZero_AfterAFewSeconds) {
  // Use task_environment_ directly if needed, but here we just need to advance
  // time.
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartOneShot(base::Seconds(10), FROM_HERE);

  task_environment_.AdvanceClock(base::Seconds(2));
  EXPECT_EQ(base::Seconds(8), timer.NextFireInterval());
}

TEST_F(TimerTest, NextFireInterval_Repeating) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartRepeating(base::Seconds(20), FROM_HERE);

  EXPECT_EQ(base::Seconds(20), timer.NextFireInterval());
}

TEST_F(TimerTest, RepeatInterval_NeverStarted) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);

  EXPECT_FALSE(timer.RepeatInterval());
}

TEST_F(TimerTest, RepeatInterval_OneShotZero) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartOneShot(base::TimeDelta(), FROM_HERE);

  EXPECT_FALSE(timer.RepeatInterval());
}

TEST_F(TimerTest, RepeatInterval_OneShotNonZero) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartOneShot(base::Seconds(10), FROM_HERE);

  EXPECT_FALSE(timer.RepeatInterval());
}

TEST_F(TimerTest, RepeatInterval_Repeating) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartRepeating(base::Seconds(20), FROM_HERE);

  EXPECT_EQ(base::Seconds(20), *timer.RepeatInterval());
}

TEST_F(TimerTest, AugmentRepeatInterval) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartRepeating(base::Seconds(10), FROM_HERE);
  EXPECT_EQ(base::Seconds(10), *timer.RepeatInterval());
  EXPECT_EQ(base::Seconds(10), timer.NextFireInterval());

  task_environment_.AdvanceClock(base::Seconds(2));
  timer.AugmentRepeatInterval(base::Seconds(10));

  EXPECT_EQ(base::Seconds(20), *timer.RepeatInterval());
  EXPECT_EQ(base::Seconds(18), timer.NextFireInterval());

  RunUntilDeadline(start_time_ + base::Seconds(50));
  EXPECT_THAT(run_times_, ElementsAre(start_time_ + base::Seconds(20),
                                      start_time_ + base::Seconds(40)));
}

TEST_F(TimerTest, AugmentRepeatInterval_TimerFireDelayed) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::CountingTask);
  timer.StartRepeating(base::Seconds(10), FROM_HERE);
  EXPECT_EQ(base::Seconds(10), *timer.RepeatInterval());
  EXPECT_EQ(base::Seconds(10), timer.NextFireInterval());

  task_environment_.AdvanceClock(
      base::Seconds(123));  // Make the timer long overdue.
  timer.AugmentRepeatInterval(base::Seconds(10));

  EXPECT_EQ(base::Seconds(20), *timer.RepeatInterval());
  // The timer is overdue so it should be scheduled to fire immediatly.
  EXPECT_TRUE(timer.NextFireInterval().is_zero());
}

TEST_F(TimerTest, RepeatingTimerDoesNotDrift) {
  TaskRunnerTimer<TimerTest> timer(GetTaskRunner(), this,
                                   &TimerTest::RecordNextFireTimeTask);
  timer.StartRepeating(base::Seconds(2), FROM_HERE);

  RecordNextFireTimeTask(
      &timer);  // Next scheduled task to run at |start_time_| + 2s

  // Simulate timer firing early. Next scheduled task to run at
  // |start_time_| + 4s
  task_environment_.AdvanceClock(base::Milliseconds(1900));
  RunUntilDeadline(Now() + base::Milliseconds(200));

  // Next scheduled task to run at |start_time_| + 6s
  task_environment_.FastForwardBy(base::Seconds(2));
  // Next scheduled task to run at |start_time_| + 8s
  task_environment_.FastForwardBy(base::Milliseconds(2100));
  // Next scheduled task to run at |start_time_| + 10s
  task_environment_.FastForwardBy(base::Milliseconds(2900));
  // Next scheduled task to run at |start_time_| + 12s
  task_environment_.AdvanceClock(base::Milliseconds(1800));
  task_environment_.RunUntilIdle();
  // Next scheduled task to run at |start_time_| + 14s
  task_environment_.AdvanceClock(base::Milliseconds(1900));
  task_environment_.RunUntilIdle();
  // Next scheduled task to run at |start_time_| + 18s (skips a beat)
  task_environment_.AdvanceClock(base::Milliseconds(50));
  task_environment_.RunUntilIdle();
  // Next scheduled task to run at |start_time_| + 28s (skips 5 beats)
  task_environment_.AdvanceClock(base::Seconds(10));
  task_environment_.RunUntilIdle();

  EXPECT_THAT(
      next_fire_times_,
      ElementsAre(
          start_time_ + base::Seconds(2), start_time_ + base::Seconds(4),
          start_time_ + base::Seconds(6), start_time_ + base::Seconds(8),
          start_time_ + base::Seconds(10), start_time_ + base::Seconds(12),
          start_time_ + base::Seconds(14), start_time_ + base::Seconds(24)));
}

template <typename TimerFiredClass>
class TimerForTest : public TaskRunnerTimer<TimerFiredClass> {
 public:
  using TimerFiredFunction =
      typename TaskRunnerTimer<TimerFiredClass>::TimerFiredFunction;

  ~TimerForTest() override = default;

  TimerForTest(scoped_refptr<base::SingleThreadTaskRunner> task_runner,
               TimerFiredClass* timer_fired_class,
               TimerFiredFunction timer_fired_function)
      : TaskRunnerTimer<TimerFiredClass>(std::move(task_runner),
                                         timer_fired_class,
                                         timer_fired_function) {}
};

TEST_F(TimerTest, UserSuppliedTaskRunner) {
  scoped_refptr<MainThreadTaskQueue> task_queue(
      task_environment_.GetMainThreadScheduler()
          ->NewThrottleableTaskQueueForTest(nullptr));
  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
      task_queue->CreateTaskRunner(TaskType::kInternalTest);
  TimerForTest<TimerTest> timer(task_runner, this, &TimerTest::CountingTask);
  timer.StartOneShot(base::TimeDelta(), FROM_HERE);

  // Make sure the task was posted on taskRunner.
  EXPECT_FALSE(task_queue->IsEmpty());
}

TEST_F(TimerTest, RunOnHeapTimer) {
  scoped_refptr<OnHeapTimerOwner::Record> record =
      OnHeapTimerOwner::Record::Create();
  Persistent<OnHeapTimerOwner> owner =
      MakeGarbageCollected<OnHeapTimerOwner>(record, GetTaskRunner());

  owner->StartOneShot(base::TimeDelta(), FROM_HERE);

  EXPECT_FALSE(record->TimerHasFired());
  task_environment_.RunUntilIdle();
  EXPECT_TRUE(record->TimerHasFired());
}

TEST_F(TimerTest, DestructOnHeapTimer) {
  scoped_refptr<OnHeapTimerOwner::Record> record =
      OnHeapTimerOwner::Record::Create();
  Persistent<OnHeapTimerOwner> owner =
      MakeGarbageCollected<OnHeapTimerOwner>(record, GetTaskRunner());

  record->Dispose();
  owner->StartOneShot(base::TimeDelta(), FROM_HERE);

  owner = nullptr;
  ThreadState::Current()->CollectAllGarbageForTesting(
      ThreadState::StackState::kNoHeapPointers);
  EXPECT_TRUE(record->OwnerIsDestructed());

  EXPECT_FALSE(record->TimerHasFired());
  task_environment_.RunUntilIdle();
  EXPECT_FALSE(record->TimerHasFired());
}

// TODO(1056170): Re-enable test.
TEST_F(TimerTest, DISABLED_MarkOnHeapTimerAsUnreachable) {
  scoped_refptr<OnHeapTimerOwner::Record> record =
      OnHeapTimerOwner::Record::Create();
  Persistent<OnHeapTimerOwner> owner =
      MakeGarbageCollected<OnHeapTimerOwner>(record, GetTaskRunner());

  record->Dispose();
  owner->StartOneShot(base::TimeDelta(), FROM_HERE);

  owner = nullptr;
  // Explicit regular GC call to allow lazy sweeping.
  // TODO(1056170): Needs a specific forced GC call to be able to test the
  // scenario below.
  // ThreadState::Current()->CollectGarbageForTesting(
  //     BlinkGC::CollectionType::kMajor, BlinkGC::kNoHeapPointersOnStack,
  //     BlinkGC::kAtomicMarking, BlinkGC::kConcurrentAndLazySweeping,
  //     BlinkGC::GCReason::kForcedGCForTesting);
  // Since the heap is laziy swept, owner is not yet destructed.
  EXPECT_FALSE(record->OwnerIsDestructed());

  {
    ThreadState::GCForbiddenScope gc_forbidden(ThreadState::Current());
    EXPECT_FALSE(record->TimerHasFired());
    task_environment_.RunUntilIdle();
    EXPECT_FALSE(record->TimerHasFired());
    EXPECT_FALSE(record->OwnerIsDestructed());
    // ThreadState::Current()->CompleteSweep();
  }
}

namespace {

class TaskObserver : public base::TaskObserver {
 public:
  TaskObserver(scoped_refptr<base::SingleThreadTaskRunner> task_runner,
               Vector<scoped_refptr<base::SingleThreadTaskRunner>>* run_order)
      : task_runner_(std::move(task_runner)), run_order_(run_order) {}

  void WillProcessTask(const base::PendingTask&, bool) override {}

  void DidProcessTask(const base::PendingTask&) override {
    run_order_->push_back(task_runner_);
  }

 private:
  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
  raw_ptr<Vector<scoped_refptr<base::SingleThreadTaskRunner>>> run_order_;
};

}  // namespace

TEST_F(TimerTest, MoveToNewTaskRunnerOneShot) {
  Vector<scoped_refptr<base::SingleThreadTaskRunner>> run_order;

  scoped_refptr<MainThreadTaskQueue> task_queue1(
      task_environment_.GetMainThreadScheduler()
          ->NewThrottleableTaskQueueForTest(nullptr));
  scoped_refptr<base::SingleThreadTaskRunner> task_runner1 =
      task_queue1->CreateTaskRunner(TaskType::kInternalTest);
  TaskObserver task_observer1(task_runner1, &run_order);
  task_queue1->AddTaskObserver(&task_observer1);

  scoped_refptr<MainThreadTaskQueue> task_queue2(
      task_environment_.GetMainThreadScheduler()
          ->NewThrottleableTaskQueueForTest(nullptr));
  scoped_refptr<base::SingleThreadTaskRunner> task_runner2 =
      task_queue2->CreateTaskRunner(TaskType::kInternalTest);
  TaskObserver task_observer2(task_runner2, &run_order);
  task_queue2->AddTaskObserver(&task_observer2);

  TimerForTest<TimerTest> timer(task_runner1, this, &TimerTest::CountingTask);

  base::TimeTicks start_time = Now();

  timer.StartOneShot(base::Seconds(1), FROM_HERE);

  task_environment_.FastForwardBy(base::Milliseconds(500));

  timer.MoveToNewTaskRunner(task_runner2);

  task_environment_.FastForwardUntilNoTasksRemain();

  EXPECT_THAT(run_times_, ElementsAre(start_time + base::Seconds(1)));

  EXPECT_THAT(run_order, ElementsAre(task_runner2));

  EXPECT_TRUE(task_queue1->IsEmpty());
  EXPECT_TRUE(task_queue2->IsEmpty());
}

TEST_F(TimerTest, MoveToNewTaskRunnerRepeating) {
  Vector<scoped_refptr<base::SingleThreadTaskRunner>> run_order;

  scoped_refptr<MainThreadTaskQueue> task_queue1(
      task_environment_.GetMainThreadScheduler()
          ->NewThrottleableTaskQueueForTest(nullptr));
  scoped_refptr<base::SingleThreadTaskRunner> task_runner1 =
      task_queue1->CreateTaskRunner(TaskType::kInternalTest);
  TaskObserver task_observer1(task_runner1, &run_order);
  task_queue1->AddTaskObserver(&task_observer1);

  scoped_refptr<MainThreadTaskQueue> task_queue2(
      task_environment_.GetMainThreadScheduler()
          ->NewThrottleableTaskQueueForTest(nullptr));
  scoped_refptr<base::SingleThreadTaskRunner> task_runner2 =
      task_queue2->CreateTaskRunner(TaskType::kInternalTest);
  TaskObserver task_observer2(task_runner2, &run_order);
  task_queue2->AddTaskObserver(&task_observer2);

  TimerForTest<TimerTest> timer(task_runner1, this, &TimerTest::CountingTask);

  base::TimeTicks start_time = Now();

  timer.StartRepeating(base::Seconds(1), FROM_HERE);

  task_environment_.FastForwardBy(base::Milliseconds(2500));

  timer.MoveToNewTaskRunner(task_runner2);

  task_environment_.FastForwardBy(base::Seconds(2));

  EXPECT_THAT(run_times_, ElementsAre(start_time + base::Seconds(1),
                                      start_time + base::Seconds(2),
                                      start_time + base::Seconds(3),
                                      start_time + base::Seconds(4)));

  EXPECT_THAT(run_order, ElementsAre(task_runner1, task_runner1, task_runner2,
                                     task_runner2));

  EXPECT_TRUE(task_queue1->IsEmpty());
  EXPECT_FALSE(task_queue2->IsEmpty());
}

// This test checks that when inactive timer is moved to a different task
// runner it isn't activated.
TEST_F(TimerTest, MoveToNewTaskRunnerWithoutTasks) {
  scoped_refptr<MainThreadTaskQueue> task_queue1(
      task_environment_.GetMainThreadScheduler()
          ->NewThrottleableTaskQueueForTest(nullptr));
  scoped_refptr<base::SingleThreadTaskRunner> task_runner1 =
      task_queue1->CreateTaskRunner(TaskType::kInternalTest);

  scoped_refptr<MainThreadTaskQueue> task_queue2(
      task_environment_.GetMainThreadScheduler()
          ->NewThrottleableTaskQueueForTest(nullptr));
  scoped_refptr<base::SingleThreadTaskRunner> task_runner2 =
      task_queue2->CreateTaskRunner(TaskType::kInternalTest);

  TimerForTest<TimerTest> timer(task_runner1, this, &TimerTest::CountingTask);

  task_environment_.RunUntilIdle();
  EXPECT_TRUE(!run_times_.size());
  EXPECT_TRUE(task_queue1->IsEmpty());
  EXPECT_TRUE(task_queue2->IsEmpty());
}

}  // namespace
}  // namespace blink
