// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/viz/common/frame_sinks/begin_frame_source.h"

#include <stdint.h>

#include <memory>
#include <string>
#include <utility>

#include "base/feature_list.h"
#include "base/memory/raw_ptr.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_mock_time_task_runner.h"
#include "components/viz/common/display/display_scheduler_draw_result.h"
#include "components/viz/common/features.h"
#include "components/viz/test/begin_frame_args_test.h"
#include "components/viz/test/begin_frame_source_test.h"
#include "components/viz/test/fake_delay_based_time_source.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using testing::NiceMock;
using testing::_;

namespace viz {
namespace {

// Returns a fake TimeTicks based on the given microsecond offset.
base::TimeTicks TicksFromMicroseconds(int64_t micros) {
  return base::TimeTicks() + base::Microseconds(micros);
}

// BeginFrameSource testing ----------------------------------------------------
TEST(BeginFrameSourceTest, SourceIdsAreUnique) {
  StubBeginFrameSource source1;
  StubBeginFrameSource source2;
  StubBeginFrameSource source3;
  EXPECT_NE(source1.source_id(), source2.source_id());
  EXPECT_NE(source1.source_id(), source3.source_id());
  EXPECT_NE(source2.source_id(), source3.source_id());
}

class TestTaskRunner : public base::TestMockTimeTaskRunner {
 public:
  TestTaskRunner()
      : base::TestMockTimeTaskRunner(
            base::TestMockTimeTaskRunner::Type::kStandalone) {
    AdvanceMockTickClock(base::Microseconds(1000));
  }

  TestTaskRunner(const TestTaskRunner&) = delete;
  TestTaskRunner& operator=(const TestTaskRunner&) = delete;

  void FastForwardTo(base::TimeTicks end_time) {
    base::TimeDelta offset = end_time - NowTicks();
    DCHECK_GE(offset, base::TimeDelta());
    FastForwardBy(offset);
  }

 private:
  ~TestTaskRunner() override = default;  // Ref-counted.
};

// BackToBackBeginFrameSource testing
// ------------------------------------------
class BackToBackBeginFrameSourceTest : public ::testing::Test {
 protected:
  static const int64_t kDeadline;
  static const int64_t kInterval;

  void SetUp() override {
    task_runner_ = base::MakeRefCounted<TestTaskRunner>();
    std::unique_ptr<FakeDelayBasedTimeSource> time_source =
        std::make_unique<FakeDelayBasedTimeSource>(
            task_runner_->GetMockTickClock(), task_runner_.get());

    delay_based_time_source_ = time_source.get();
    source_ =
        std::make_unique<BackToBackBeginFrameSource>(std::move(time_source));
    obs_ = std::make_unique<::testing::NiceMock<MockBeginFrameObserver>>();
  }

  void TearDown() override { obs_.reset(); }

  scoped_refptr<TestTaskRunner> task_runner_;
  std::unique_ptr<BackToBackBeginFrameSource> source_;
  std::unique_ptr<MockBeginFrameObserver> obs_;
  raw_ptr<FakeDelayBasedTimeSource>
      delay_based_time_source_;  // Owned by |source_|.
};

const int64_t BackToBackBeginFrameSourceTest::kDeadline =
    BeginFrameArgs::DefaultInterval().InMicroseconds();

const int64_t BackToBackBeginFrameSourceTest::kInterval =
    BeginFrameArgs::DefaultInterval().InMicroseconds();

TEST_F(BackToBackBeginFrameSourceTest, AddObserverSendsBeginFrame) {
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  source_->AddObserver(obs_.get());
  EXPECT_TRUE(task_runner_->HasPendingTask());
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
                          1000 + kDeadline, kInterval);
  task_runner_->RunUntilIdle();

  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1100,
                          1100 + kDeadline, kInterval);
  task_runner_->AdvanceMockTickClock(base::Microseconds(100));
  source_->DidFinishFrame(obs_.get(), DisplaySchedulerDrawResult::kUnknown);
  task_runner_->RunUntilIdle();
}

TEST_F(BackToBackBeginFrameSourceTest,
       RemoveObserverThenDidFinishFrameProducesNoFrame) {
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  source_->AddObserver(obs_.get());
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
                          1000 + kDeadline, kInterval);
  task_runner_->RunUntilIdle();

  source_->RemoveObserver(obs_.get());
  source_->DidFinishFrame(obs_.get(), DisplaySchedulerDrawResult::kUnknown);

  // Verify no BeginFrame is sent to |obs_|. There is a pending task in the
  // task_runner_ as a BeginFrame was posted, but it gets aborted since |obs_|
  // is removed.
  task_runner_->RunUntilIdle();
  EXPECT_FALSE(task_runner_->HasPendingTask());
}

TEST_F(BackToBackBeginFrameSourceTest,
       DidFinishFrameThenRemoveObserverProducesNoFrame) {
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  source_->AddObserver(obs_.get());
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
                          1000 + kDeadline, kInterval);
  task_runner_->RunUntilIdle();

  task_runner_->AdvanceMockTickClock(base::Microseconds(100));
  source_->DidFinishFrame(obs_.get(), DisplaySchedulerDrawResult::kUnknown);
  source_->RemoveObserver(obs_.get());

  // Task gets cancelled so it doesn't count as a pending task.
  EXPECT_FALSE(task_runner_->HasPendingTask());
}

TEST_F(BackToBackBeginFrameSourceTest,
       TogglingObserverThenDidFinishFrameProducesCorrectFrame) {
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  source_->AddObserver(obs_.get());
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
                          1000 + kDeadline, kInterval);
  task_runner_->RunUntilIdle();

  task_runner_->AdvanceMockTickClock(base::Microseconds(100));
  source_->RemoveObserver(obs_.get());

  task_runner_->AdvanceMockTickClock(base::Microseconds(10));
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  source_->AddObserver(obs_.get());

  task_runner_->AdvanceMockTickClock(base::Microseconds(10));
  source_->DidFinishFrame(obs_.get(), DisplaySchedulerDrawResult::kUnknown);

  task_runner_->AdvanceMockTickClock(base::Microseconds(10));
  // The begin frame is posted at the time when the observer was added,
  // so it ignores changes to "now" afterward.
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1110,
                          1110 + kDeadline, kInterval);
  EXPECT_TRUE(task_runner_->HasPendingTask());
  task_runner_->RunUntilIdle();
}

TEST_F(BackToBackBeginFrameSourceTest,
       DidFinishFrameThenTogglingObserverProducesCorrectFrame) {
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  source_->AddObserver(obs_.get());
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
                          1000 + kDeadline, kInterval);
  task_runner_->RunUntilIdle();

  task_runner_->AdvanceMockTickClock(base::Microseconds(100));
  source_->DidFinishFrame(obs_.get(), DisplaySchedulerDrawResult::kUnknown);

  task_runner_->AdvanceMockTickClock(base::Microseconds(10));
  source_->RemoveObserver(obs_.get());

  task_runner_->AdvanceMockTickClock(base::Microseconds(10));
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  source_->AddObserver(obs_.get());

  task_runner_->AdvanceMockTickClock(base::Microseconds(10));
  // Ticks at the time at which the observer was added, ignoring the
  // last change to "now".
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1120,
                          1120 + kDeadline, kInterval);
  EXPECT_TRUE(task_runner_->HasPendingTask());
  task_runner_->RunUntilIdle();
}

TEST_F(BackToBackBeginFrameSourceTest, DidFinishFrameNoObserver) {
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  source_->AddObserver(obs_.get());
  source_->RemoveObserver(obs_.get());
  source_->DidFinishFrame(obs_.get(), DisplaySchedulerDrawResult::kUnknown);
  task_runner_->RunUntilIdle();
  EXPECT_FALSE(task_runner_->HasPendingTask());
}

TEST_F(BackToBackBeginFrameSourceTest, DidFinishFrameMultipleCallsIdempotent) {
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  source_->AddObserver(obs_.get());
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
                          1000 + kDeadline, kInterval);
  task_runner_->RunUntilIdle();

  task_runner_->AdvanceMockTickClock(base::Microseconds(100));
  source_->DidFinishFrame(obs_.get(), DisplaySchedulerDrawResult::kUnknown);
  source_->DidFinishFrame(obs_.get(), DisplaySchedulerDrawResult::kUnknown);
  source_->DidFinishFrame(obs_.get(), DisplaySchedulerDrawResult::kUnknown);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1100,
                          1100 + kDeadline, kInterval);
  task_runner_->RunUntilIdle();

  task_runner_->AdvanceMockTickClock(base::Microseconds(100));
  source_->DidFinishFrame(obs_.get(), DisplaySchedulerDrawResult::kUnknown);
  source_->DidFinishFrame(obs_.get(), DisplaySchedulerDrawResult::kUnknown);
  source_->DidFinishFrame(obs_.get(), DisplaySchedulerDrawResult::kUnknown);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 1200,
                          1200 + kDeadline, kInterval);
  task_runner_->RunUntilIdle();
}

TEST_F(BackToBackBeginFrameSourceTest, DelayInPostedTaskProducesCorrectFrame) {
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  source_->AddObserver(obs_.get());
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
                          1000 + kDeadline, kInterval);
  task_runner_->RunUntilIdle();

  task_runner_->AdvanceMockTickClock(base::Microseconds(100));
  source_->DidFinishFrame(obs_.get(), DisplaySchedulerDrawResult::kUnknown);
  task_runner_->AdvanceMockTickClock(base::Microseconds(50));
  // Ticks at the time the last frame finished, so ignores the last change to
  // "now".
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1100,
                          1100 + kDeadline, kInterval);

  EXPECT_TRUE(task_runner_->HasPendingTask());
  task_runner_->RunUntilIdle();
}

TEST_F(BackToBackBeginFrameSourceTest, MultipleObserversSynchronized) {
  NiceMock<MockBeginFrameObserver> obs1, obs2;

  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs1, false);
  source_->AddObserver(&obs1);
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs2, false);
  source_->AddObserver(&obs2);

  EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 1, 1000, 1000 + kDeadline,
                          kInterval);
  EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 1, 1000, 1000 + kDeadline,
                          kInterval);
  task_runner_->RunUntilIdle();

  task_runner_->AdvanceMockTickClock(base::Microseconds(100));
  source_->DidFinishFrame(&obs1, DisplaySchedulerDrawResult::kUnknown);
  source_->DidFinishFrame(&obs2, DisplaySchedulerDrawResult::kUnknown);
  EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 2, 1100, 1100 + kDeadline,
                          kInterval);
  EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 2, 1100, 1100 + kDeadline,
                          kInterval);
  task_runner_->RunUntilIdle();

  task_runner_->AdvanceMockTickClock(base::Microseconds(100));
  source_->DidFinishFrame(&obs1, DisplaySchedulerDrawResult::kUnknown);
  source_->DidFinishFrame(&obs2, DisplaySchedulerDrawResult::kUnknown);
  EXPECT_TRUE(task_runner_->HasPendingTask());
  source_->RemoveObserver(&obs1);
  source_->RemoveObserver(&obs2);
  task_runner_->RunUntilIdle();
}

TEST_F(BackToBackBeginFrameSourceTest, MultipleObserversInterleaved) {
  NiceMock<MockBeginFrameObserver> obs1, obs2;

  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs1, false);
  source_->AddObserver(&obs1);
  EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 1, 1000, 1000 + kDeadline,
                          kInterval);
  task_runner_->RunUntilIdle();

  task_runner_->AdvanceMockTickClock(base::Microseconds(100));
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs2, false);
  source_->AddObserver(&obs2);
  EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 2, 1100, 1100 + kDeadline,
                          kInterval);
  task_runner_->RunUntilIdle();

  task_runner_->AdvanceMockTickClock(base::Microseconds(100));
  source_->DidFinishFrame(&obs1, DisplaySchedulerDrawResult::kUnknown);
  EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 3, 1200, 1200 + kDeadline,
                          kInterval);
  task_runner_->RunUntilIdle();

  source_->DidFinishFrame(&obs1, DisplaySchedulerDrawResult::kUnknown);
  source_->RemoveObserver(&obs1);
  // Removing all finished observers should disable the time source.
  EXPECT_FALSE(delay_based_time_source_->Active());
  // Finishing the frame for |obs1| posts a begin frame task, which will be
  // aborted since |obs1| is removed. Clear that from the task runner.
  task_runner_->RunUntilIdle();

  task_runner_->AdvanceMockTickClock(base::Microseconds(100));
  source_->DidFinishFrame(&obs2, DisplaySchedulerDrawResult::kUnknown);
  EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 4, 1300, 1300 + kDeadline,
                          kInterval);
  task_runner_->RunUntilIdle();

  source_->DidFinishFrame(&obs2, DisplaySchedulerDrawResult::kUnknown);
  source_->RemoveObserver(&obs2);
}

TEST_F(BackToBackBeginFrameSourceTest, MultipleObserversAtOnce) {
  NiceMock<MockBeginFrameObserver> obs1, obs2;

  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs1, false);
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs2, false);
  source_->AddObserver(&obs1);
  source_->AddObserver(&obs2);
  EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 1, 1000, 1000 + kDeadline,
                          kInterval);
  EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 1, 1000, 1000 + kDeadline,
                          kInterval);
  task_runner_->RunUntilIdle();

  // |obs1| finishes first.
  task_runner_->AdvanceMockTickClock(base::Microseconds(100));
  source_->DidFinishFrame(&obs1, DisplaySchedulerDrawResult::kUnknown);

  // |obs2| finishes also, before getting to the newly posted begin frame.
  task_runner_->AdvanceMockTickClock(base::Microseconds(100));
  source_->DidFinishFrame(&obs2, DisplaySchedulerDrawResult::kUnknown);

  // Because the begin frame source already ticked when |obs1| finished,
  // we see it as the frame time for both observers.
  EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 2, 1100, 1100 + kDeadline,
                          kInterval);
  EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 2, 1100, 1100 + kDeadline,
                          kInterval);
  task_runner_->RunUntilIdle();

  source_->DidFinishFrame(&obs1, DisplaySchedulerDrawResult::kUnknown);
  source_->RemoveObserver(&obs1);
  source_->DidFinishFrame(&obs2, DisplaySchedulerDrawResult::kUnknown);
  source_->RemoveObserver(&obs2);
}

TEST_F(BackToBackBeginFrameSourceTest, UnthrottledInterval) {
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
                          1000 + kDeadline, kInterval);
  source_->AddObserver(obs_.get());
  task_runner_->RunUntilIdle();

  // Initially unthrottled_interval should match interval.
  EXPECT_EQ(obs_->last_begin_frame_args.interval,
            BeginFrameArgs::DefaultInterval());
  EXPECT_EQ(obs_->last_begin_frame_args.unthrottled_interval,
            BeginFrameArgs::DefaultInterval());

  // Throttling the interval should not change unthrottled_interval.
  base::TimeDelta throttled_interval = base::Microseconds(20000);
  source_->SetMaxVrrInterval(throttled_interval);

  task_runner_->AdvanceMockTickClock(base::Microseconds(100));
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1100,
                          1100 + throttled_interval.InMicroseconds(),
                          throttled_interval.InMicroseconds(), kInterval);
  source_->DidFinishFrame(obs_.get(), DisplaySchedulerDrawResult::kUnknown);
  task_runner_->RunUntilIdle();

  EXPECT_EQ(obs_->last_begin_frame_args.interval, throttled_interval);
  EXPECT_EQ(obs_->last_begin_frame_args.unthrottled_interval,
            BeginFrameArgs::DefaultInterval());
}

// There may not be any observers available when the OnGpuNoLongerBusy()
// function is invoked on the BackToBackBeginFrameSource. For e.g.
// consider the following sequence.
// 1. An observer is added to the source.
// 2. One BeginFrame request is generated by the timer.
// 3. The clock advances and then the DidFrameFinish() callback is invoked.
//    The timer is marked as active.
// 4. Any pending BeginFrame requests are generated and then the observer
//    unsubscribes.
// 5. If the OnGpuNoLongerBusy() notification is invoked after this step
//    before any new observer is added, a DCHECK about pending observers
//    being empty fires which crashes in debug mode and also in release
//    builds with DCHECK on.
//    With the suggested fix of checking in OnTimerTick() whether the timer
//    is active, this DCHECK should not fire.
TEST_F(BackToBackBeginFrameSourceTest, OnGpuNoLongerBusyWithNoObservers) {
  // Initial setup.
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  source_->AddObserver(obs_.get());
  EXPECT_TRUE(task_runner_->HasPendingTask());

  // Expectations for two BeginFrames.
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
                          1000 + kDeadline, kInterval);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1100,
                          1100 + kDeadline, kInterval);

  // Advance the clock to ensure that the second BeginFrame has a different
  // timestamp
  task_runner_->AdvanceMockTickClock(base::Microseconds(100));
  task_runner_->RunUntilIdle();

  // Activate the timer so the second BeginFrame can be generated.
  source_->DidFinishFrame(obs_.get(), DisplaySchedulerDrawResult::kUnknown);
  task_runner_->RunUntilIdle();

  // Observer is unsubscribed from the source here.
  source_->RemoveObserver(obs_.get());

  // DCHECK for pending observers list being empty should not fire.
  source_->OnGpuNoLongerBusy();
}

// DelayBasedBeginFrameSource testing
// ------------------------------------------
class DelayBasedBeginFrameSourceTest
    : public ::testing::Test,
      public testing::WithParamInterface<bool> {
 public:
  bool NoLateBeginFrames() const { return GetParam(); }

  void SetUp() override {
    if (NoLateBeginFrames()) {
      scoped_feature_list_.InitAndEnableFeature(features::kNoLateBeginFrames);
    } else {
      scoped_feature_list_.InitAndDisableFeature(features::kNoLateBeginFrames);
    }
    task_runner_ = base::MakeRefCounted<TestTaskRunner>();
    std::unique_ptr<FakeDelayBasedTimeSource> time_source =
        std::make_unique<FakeDelayBasedTimeSource>(
            task_runner_->GetMockTickClock(), task_runner_.get());

    time_source->SetTimebaseAndInterval(base::TimeTicks(),
                                        base::Microseconds(10000));
    source_ = std::make_unique<DelayBasedBeginFrameSource>(
        std::move(time_source), BeginFrameSource::kNotRestartableId);
    obs_ = std::make_unique<MockBeginFrameObserver>();
  }

  void TearDown() override { obs_.reset(); }

  scoped_refptr<TestTaskRunner> task_runner_;
  std::unique_ptr<DelayBasedBeginFrameSource> source_;
  std::unique_ptr<MockBeginFrameObserver> obs_;
  base::test::ScopedFeatureList scoped_feature_list_;
};

TEST_P(DelayBasedBeginFrameSourceTest,
       AddObserverCallsOnBeginFrameWithMissedTick) {
  if (NoLateBeginFrames()) {
    return;
  }
  task_runner_->AdvanceMockTickClock(base::Microseconds(9010));
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 10000, 20000,
                                 10000);
  source_->AddObserver(obs_.get());  // Should cause the last tick to be sent
  // No tasks should need to be run for this to occur.
}

TEST_P(DelayBasedBeginFrameSourceTest, AddObserverCallsCausesOnBeginFrame) {
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  if (!NoLateBeginFrames()) {
    EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000,
                                   10000);
  }
  source_->AddObserver(obs_.get());
  EXPECT_EQ(TicksFromMicroseconds(10000),
            task_runner_->NowTicks() + task_runner_->NextPendingTaskDelay());

  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000);
  task_runner_->AdvanceMockTickClock(base::Microseconds(9010));
  task_runner_->RunUntilIdle();
}

TEST_P(DelayBasedBeginFrameSourceTest, BasicOperation) {
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  if (!NoLateBeginFrames()) {
    EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000,
                                   10000);
  }
  source_->AddObserver(obs_.get());
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20000, 30000, 10000);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30000, 40000, 10000);
  task_runner_->FastForwardTo(TicksFromMicroseconds(30001));

  source_->RemoveObserver(obs_.get());
  // No new frames....
  task_runner_->FastForwardTo(TicksFromMicroseconds(60000));
}

TEST_P(DelayBasedBeginFrameSourceTest, VSyncChanges) {
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  if (!NoLateBeginFrames()) {
    EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000,
                                   10000);
  }
  source_->AddObserver(obs_.get());

  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20000, 30000, 10000);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30000, 40000, 10000);
  task_runner_->FastForwardTo(TicksFromMicroseconds(30001));

  // Update the vsync information
  source_->OnUpdateVSyncParameters(TicksFromMicroseconds(27500),
                                   base::Microseconds(10001));

  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 5, 40000, 47502, 10001);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 6, 47502, 57503, 10001);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 7, 57503, 67504, 10001);
  task_runner_->FastForwardTo(TicksFromMicroseconds(60000));
}

TEST_P(DelayBasedBeginFrameSourceTest, VSyncChangeTimebaseBeforeLastTick) {
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  if (!NoLateBeginFrames()) {
    EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000,
                                   10000);
  }
  source_->AddObserver(obs_.get());

  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20000, 30000, 10000);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30000, 40000, 10000);
  task_runner_->FastForwardTo(TicksFromMicroseconds(30000));

  // Update the vsync information such that timebase is before last tick time,
  // and next tick happens within less than the new interval of the following
  // tick (i.e. next_tick -> 40000, following_tick -> 41000)
  // Begin frame won't be used at 41000 because this is a double-tick.
  source_->OnUpdateVSyncParameters(TicksFromMicroseconds(26000),
                                   base::Microseconds(5000));
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 5, 40000, 41000, 5000);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 7, 46000, 51000, 5000);
  task_runner_->FastForwardTo(TicksFromMicroseconds(46000));

  // Update the vsync information such that timebase is before last tick time,
  // and next tick happens exactly one interval before the following tick
  // tick (i.e. next_tick -> 51000, following_tick -> 60000)
  source_->OnUpdateVSyncParameters(TicksFromMicroseconds(42000),
                                   base::Microseconds(9000));
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 8, 51000, 60000, 9000);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 9, 60000, 69000, 9000);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 10, 69000, 78000, 9000);
  task_runner_->FastForwardTo(TicksFromMicroseconds(70000));
}

TEST_P(DelayBasedBeginFrameSourceTest, VSyncChangeTimebaseAfterNextTick) {
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  if (!NoLateBeginFrames()) {
    EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000,
                                   10000);
  }
  source_->AddObserver(obs_.get());

  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20000, 30000, 10000);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30000, 40000, 10000);
  task_runner_->FastForwardTo(TicksFromMicroseconds(30000));

  // Update the vsync information such that timebase is after next tick time,
  // and next tick happens within less than one interval of the new timebase
  // Begin frame won't be used at 41000 because this is a double-tick.
  source_->OnUpdateVSyncParameters(TicksFromMicroseconds(41000),
                                   base::Microseconds(5000));
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 5, 40000, 41000, 5000);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 7, 46000, 51000, 5000);
  task_runner_->FastForwardTo(TicksFromMicroseconds(46000));

  // Update the vsync information such that timebase is after next tick time,
  // and next tick happens exactly one interval before the new timebase
  source_->OnUpdateVSyncParameters(TicksFromMicroseconds(60000),
                                   base::Microseconds(9000));
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 8, 51000, 60000, 9000);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 9, 60000, 69000, 9000);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 10, 69000, 78000, 9000);
  task_runner_->FastForwardTo(TicksFromMicroseconds(70000));

  // Update the vsync information such that timebase is after next tick time,
  // and next tick happens more than one interval before the new timebase
  // Begin frame won't be used at 80000 because this is a double-tick.
  source_->OnUpdateVSyncParameters(TicksFromMicroseconds(100000),
                                   base::Microseconds(5000));
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 11, 78000, 80000, 5000);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 13, 85000, 90000, 5000);
  task_runner_->FastForwardTo(TicksFromMicroseconds(85000));
}

TEST_P(DelayBasedBeginFrameSourceTest, VSyncChangeTimebaseBetweenTicks) {
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  if (!NoLateBeginFrames()) {
    EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000,
                                   10000);
  }
  source_->AddObserver(obs_.get());

  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20000, 30000, 10000);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30000, 40000, 10000);
  task_runner_->FastForwardTo(TicksFromMicroseconds(30000));

  // Update the vsync information such that timebase is between next tick time,
  // and last tick time.
  // Begin frame won't be used at 41000 because this is a double-tick.
  source_->OnUpdateVSyncParameters(TicksFromMicroseconds(35000),
                                   base::Microseconds(6000));
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 5, 40000, 41000, 6000);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 7, 47000, 53000, 6000);
  task_runner_->FastForwardTo(TicksFromMicroseconds(47000));

  source_->OnUpdateVSyncParameters(TicksFromMicroseconds(49000),
                                   base::Microseconds(10000));
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 8, 53000, 59000, 10000);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 9, 59000, 69000, 10000);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 10, 69000, 79000, 10000);
  task_runner_->FastForwardTo(TicksFromMicroseconds(70000));
}

TEST_P(DelayBasedBeginFrameSourceTest, VSyncSkipped) {
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  if (!NoLateBeginFrames()) {
    EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000,
                                   10000);
  }
  source_->AddObserver(obs_.get());

  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20000, 30000, 10000);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30000, 40000, 10000);
  task_runner_->FastForwardTo(TicksFromMicroseconds(30000));

  // Advancing tick time without creating begin frames.
  task_runner_->AdvanceMockTickClock(base::Microseconds(40000));
  source_->OnUpdateVSyncParameters(TicksFromMicroseconds(40000),
                                   base::Microseconds(11000));
  // By advancing tick time to 40000, we would be skipping sequence_number 5 at
  // 40000 and sequence_number 6 at 51000.
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 7, 62000, 73000, 11000);
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 8, 73000, 84000, 11000);
  task_runner_->FastForwardTo(TicksFromMicroseconds(75000));
}

TEST_P(DelayBasedBeginFrameSourceTest, MultipleObservers) {
  NiceMock<MockBeginFrameObserver> obs1, obs2;

  // Mock tick clock starts off at 1000.
  task_runner_->FastForwardBy(base::Microseconds(9010));
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs1, false);
  if (!NoLateBeginFrames()) {
    EXPECT_BEGIN_FRAME_USED_MISSED(obs1, source_->source_id(), 1, 10000, 20000,
                                   10000);
  }
  source_->AddObserver(&obs1);  // Should cause the last tick to be sent
  // No tasks should need to be run for this to occur.

  EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 2, 20000, 30000, 10000);
  task_runner_->FastForwardBy(base::Microseconds(10000));

  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs2, false);
  // Sequence number unchanged for missed frame with time of last normal frame.
  if (!NoLateBeginFrames()) {
    EXPECT_BEGIN_FRAME_USED_MISSED(obs2, source_->source_id(), 2, 20000, 30000,
                                   10000);
  }
  source_->AddObserver(&obs2);  // Should cause the last tick to be sent
  // No tasks should need to be run for this to occur.

  EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 3, 30000, 40000, 10000);
  EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 3, 30000, 40000, 10000);
  task_runner_->FastForwardBy(base::Microseconds(10000));

  source_->RemoveObserver(&obs1);

  EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 4, 40000, 50000, 10000);
  task_runner_->FastForwardBy(base::Microseconds(10000));

  source_->RemoveObserver(&obs2);
  task_runner_->FastForwardTo(TicksFromMicroseconds(50000));
  EXPECT_FALSE(task_runner_->HasPendingTask());
}

TEST_P(DelayBasedBeginFrameSourceTest, DoubleTick) {
  NiceMock<MockBeginFrameObserver> obs;

  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
  if (!NoLateBeginFrames()) {
    EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 1, 0, 10000,
                                   10000);
  }
  source_->AddObserver(&obs);

  source_->OnUpdateVSyncParameters(TicksFromMicroseconds(5000),
                                   base::Microseconds(10000));
  task_runner_->AdvanceMockTickClock(base::Microseconds(4000));

  // No begin frame received.
  task_runner_->RunUntilIdle();

  // Begin frame received.
  source_->OnUpdateVSyncParameters(TicksFromMicroseconds(10000),
                                   base::Microseconds(10000));
  task_runner_->AdvanceMockTickClock(base::Microseconds(5000));
  EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 2, 10000, 20000, 10000);
  task_runner_->RunUntilIdle();
}

TEST_P(DelayBasedBeginFrameSourceTest, DoubleTickMissedFrame) {
  NiceMock<MockBeginFrameObserver> obs;

  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
  if (!NoLateBeginFrames()) {
    EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 1, 0, 10000,
                                   10000);
  }
  source_->AddObserver(&obs);
  source_->RemoveObserver(&obs);

  source_->OnUpdateVSyncParameters(TicksFromMicroseconds(5000),
                                   base::Microseconds(10000));
  task_runner_->AdvanceMockTickClock(base::Microseconds(4000));

  // No missed frame received.
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
  // This does not cause a missed BeginFrame because of double ticking
  // prevention. It does not produce a new sequence number.
  source_->AddObserver(&obs);
  source_->RemoveObserver(&obs);

  // Missed frame received.
  source_->OnUpdateVSyncParameters(TicksFromMicroseconds(10000),
                                   base::Microseconds(10000));
  task_runner_->AdvanceMockTickClock(base::Microseconds(5000));
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
  if (!NoLateBeginFrames()) {
    // Sequence number is incremented again, because sufficient time has passed.
    EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 2, 10000, 20000,
                                   10000);
  }
  source_->AddObserver(&obs);
  if (NoLateBeginFrames()) {
    EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 3, 20000, 30000, 10000);
    task_runner_->AdvanceMockTickClock(base::Microseconds(10010));
    task_runner_->RunUntilIdle();
  }
  source_->RemoveObserver(&obs);
}

TEST_P(DelayBasedBeginFrameSourceTest, MultipleArgsInSameInterval) {
  NiceMock<MockBeginFrameObserver> obs;
  NiceMock<MockBeginFrameObserver> obs2;

  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
  if (!NoLateBeginFrames()) {
    EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 1, 0, 10000,
                                   10000);
  }
  source_->AddObserver(&obs);
  task_runner_->RunUntilIdle();

  EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 2, 10000, 20000, 10000);
  task_runner_->AdvanceMockTickClock(base::Microseconds(9000));
  task_runner_->RunUntilIdle();

  if (!NoLateBeginFrames()) {
    // Sequence number should stay the same within same interval.
    EXPECT_BEGIN_FRAME_USED_MISSED(obs2, source_->source_id(), 2, 10000, 20000,
                                   10000);
  }
  source_->AddObserver(&obs2);

  EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 3, 20000, 30000, 10000);
  EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 3, 20000, 30000, 10000);
  task_runner_->AdvanceMockTickClock(base::Microseconds(10000));
  task_runner_->RunUntilIdle();
}

TEST_P(DelayBasedBeginFrameSourceTest, ConsecutiveArgsDelayedByMultipleVsyncs) {
  NiceMock<MockBeginFrameObserver> obs;

  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
  if (!NoLateBeginFrames()) {
    EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 1, 0, 10000,
                                   10000);
  }
  source_->AddObserver(&obs);
  task_runner_->RunUntilIdle();

  EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 2, 10000, 20000, 10000);
  task_runner_->AdvanceMockTickClock(base::Microseconds(9000));
  task_runner_->RunUntilIdle();
  source_->RemoveObserver(&obs);

  // New args created 8 intervals later.
  // Sequence number should increase by this much.
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
  if (!NoLateBeginFrames()) {
    EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 10, 90000, 100000,
                                   10000);
  }
  task_runner_->AdvanceMockTickClock(base::Microseconds(80000));
  source_->AddObserver(&obs);
  if (NoLateBeginFrames()) {
    EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 11, 100000, 110000,
                            10000);
    task_runner_->AdvanceMockTickClock(base::Microseconds(10010));
    task_runner_->RunUntilIdle();
  }
}

TEST_P(DelayBasedBeginFrameSourceTest, WithVrrInterval) {
  NiceMock<MockBeginFrameObserver> obs;
  source_->SetMaxVrrInterval(base::Microseconds(25000));

  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
  if (!NoLateBeginFrames()) {
    EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 1, 0, 25000,
                                   25000, 10000);
  }
  source_->AddObserver(&obs);

  EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 2, 10000, 35000, 25000,
                          10000);
  EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 3, 20000, 45000, 25000,
                          10000);
  task_runner_->FastForwardTo(TicksFromMicroseconds(21000));
  source_->OnUpdateVSyncParameters(TicksFromMicroseconds(21000),
                                   base::Microseconds(10000));
  EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 4, 30000, 46000, 25000,
                          10000);
  EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 5, 31000, 56000, 25000,
                          10000);
  task_runner_->FastForwardTo(TicksFromMicroseconds(32000));
  source_->OnUpdateVSyncParameters(TicksFromMicroseconds(32000),
                                   base::Microseconds(10000));
  EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 6, 41000, 57000, 25000,
                          10000);
  EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 7, 42000, 67000, 25000,
                          10000);
  EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 8, 52000, 77000, 25000,
                          10000);
  task_runner_->FastForwardTo(TicksFromMicroseconds(53000));
  source_->OnUpdateVSyncParameters(TicksFromMicroseconds(53000),
                                   base::Microseconds(10000));
  EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 9, 62000, 78000, 25000,
                          10000);
  EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 10, 63000, 88000, 25000,
                          10000);
  EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 11, 73000, 98000, 25000,
                          10000);
  task_runner_->FastForwardTo(TicksFromMicroseconds(73000));
}

TEST_P(DelayBasedBeginFrameSourceTest, UnthrottledInterval) {
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  if (!NoLateBeginFrames()) {
    EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000,
                                   10000);
  }
  source_->AddObserver(obs_.get());

  // Initially unthrottled_interval should match interval.
  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000);
  task_runner_->FastForwardBy(base::Microseconds(10000));
  EXPECT_EQ(obs_->last_begin_frame_args.interval, base::Microseconds(10000));
  EXPECT_EQ(obs_->last_begin_frame_args.unthrottled_interval,
            base::Microseconds(10000));

  // Throttling the interval should not change unthrottled_interval.
  base::TimeDelta throttled_interval = base::Microseconds(20000);
  source_->SetMaxVrrInterval(throttled_interval);

  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20000, 40000, 20000,
                          10000);
  task_runner_->FastForwardBy(base::Microseconds(10000));
  EXPECT_EQ(obs_->last_begin_frame_args.interval, throttled_interval);
  EXPECT_EQ(obs_->last_begin_frame_args.unthrottled_interval,
            base::Microseconds(10000));
}

INSTANTIATE_TEST_SUITE_P(
    ,
    DelayBasedBeginFrameSourceTest,
    testing::Bool(),
    [](const ::testing::TestParamInfo<bool>& info) -> std::string {
      return info.param ? "NoLateBeginFrames" : "LateBeginFrames";
    });
// ExternalBeginFrameSource testing
// --------------------------------------------
class MockExternalBeginFrameSourceClient
    : public ExternalBeginFrameSourceClient {
 public:
  MockExternalBeginFrameSourceClient() = default;
  virtual ~MockExternalBeginFrameSourceClient() = default;

  MOCK_METHOD1(OnNeedsBeginFrames, void(bool));
};

class ExternalBeginFrameSourceTest : public ::testing::Test {
 public:
  ExternalBeginFrameSourceTest()
      : client_(std::make_unique<MockExternalBeginFrameSourceClient>()),
        source_(std::make_unique<ExternalBeginFrameSource>(
            client_.get(),
            BeginFrameSource::kNotRestartableId)),
        obs_(std::make_unique<MockBeginFrameObserver>()) {}

  ~ExternalBeginFrameSourceTest() override = default;

  std::unique_ptr<MockExternalBeginFrameSourceClient> client_;
  std::unique_ptr<ExternalBeginFrameSource> source_;
  std::unique_ptr<MockBeginFrameObserver> obs_;
};

TEST_F(ExternalBeginFrameSourceTest, OnAnimateOnlyBeginFrameOptIn) {
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  EXPECT_CALL((*client_), OnNeedsBeginFrames(true)).Times(1);
  source_->AddObserver(obs_.get());

  // By default, an observer doesn't receive animate_only BeginFrames.
  BeginFrameArgs args = CreateBeginFrameArgsForTesting(
      BEGINFRAME_FROM_HERE, 0, 2, TicksFromMicroseconds(10000));
  args.animate_only = true;
  source_->OnBeginFrame(args);

  // When opting in, an observer receives animate_only BeginFrames.
  args = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 3,
                                        TicksFromMicroseconds(10001));
  args.animate_only = true;
  EXPECT_CALL(*obs_, WantsAnimateOnlyBeginFrames())
      .WillOnce(::testing::Return(true));
  EXPECT_BEGIN_FRAME_ARGS_USED(*obs_, args);
  source_->OnBeginFrame(args);

  EXPECT_CALL((*client_), OnNeedsBeginFrames(false)).Times(1);
  source_->RemoveObserver(obs_.get());
}

TEST_F(ExternalBeginFrameSourceTest, OnBeginFrameChecksBeginFrameContinuity) {
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  EXPECT_CALL((*client_), OnNeedsBeginFrames(true)).Times(1);
  source_->AddObserver(obs_.get());

  BeginFrameArgs args = CreateBeginFrameArgsForTesting(
      BEGINFRAME_FROM_HERE, 0, 2, TicksFromMicroseconds(10000));
  EXPECT_BEGIN_FRAME_ARGS_USED(*obs_, args);
  source_->OnBeginFrame(args);

  // Providing same args again to OnBeginFrame() should not notify observer.
  source_->OnBeginFrame(args);

  // Providing same args through a different ExternalBeginFrameSource also
  // does not notify observer.
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  EXPECT_CALL((*client_), OnNeedsBeginFrames(true)).Times(1);
  ExternalBeginFrameSource source2(client_.get());
  source2.AddObserver(obs_.get());
  source2.OnBeginFrame(args);

  EXPECT_CALL((*client_), OnNeedsBeginFrames(false)).Times(2);
  source_->RemoveObserver(obs_.get());
  source2.RemoveObserver(obs_.get());
}

TEST_F(ExternalBeginFrameSourceTest, GetMissedBeginFrameArgs) {
  BeginFrameArgs args = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0,
                                                       2, 10000, 10100, 100);
  source_->OnBeginFrame(args);

  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  if (!base::FeatureList::IsEnabled(features::kNoLateBeginFrames)) {
    EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, 0, 2, 10000, 10100, 100);
  }
  source_->AddObserver(obs_.get());
  source_->RemoveObserver(obs_.get());

  // Out of order frame_time. This might not be valid but still shouldn't
  // cause a DCHECK in ExternalBeginFrameSource code.
  args = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, 9999, 10100,
                                        101);
  source_->OnBeginFrame(args);

  EXPECT_CALL((*client_), OnNeedsBeginFrames(true)).Times(1);
  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
  EXPECT_CALL(*obs_, OnBeginFrame(_)).Times(0);
  source_->AddObserver(obs_.get());

  EXPECT_CALL((*client_), OnNeedsBeginFrames(false)).Times(1);
  source_->RemoveObserver(obs_.get());
}

// Tests that an InputClient is notified before observers.
TEST_F(ExternalBeginFrameSourceTest, InputClientNotifiedFirst) {
  using ::testing::InSequence;
  using ::testing::SaveArg;

  NiceMock<MockBeginFrameObserver> obs;
  MockInputClient input_client;

  EXPECT_CALL(*client_, OnNeedsBeginFrames(true)).Times(testing::AnyNumber());
  EXPECT_CALL(*client_, OnNeedsBeginFrames(false)).Times(testing::AnyNumber());

  source_->AddObserver(&obs);
  source_->SetInputClient(&input_client);

  {
    BeginFrameArgs args = CreateBeginFrameArgsForTesting(
        BEGINFRAME_FROM_HERE, 0, 1, 10000, 10100, 100);
    InSequence s;
    EXPECT_CALL(input_client, OnBeginFrameForInput(args));
    EXPECT_CALL(obs, OnBeginFrame(args))
        .WillOnce(SaveArg<0>(&(obs.last_begin_frame_args)));
    source_->OnBeginFrame(args);
  }

  source_->RemoveObserver(&obs);
  source_->SetInputClient(nullptr);
}

}  // namespace
}  // namespace viz
