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

#include "cc/metrics/event_metrics.h"

#include <vector>

#include "base/test/simple_test_tick_clock.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace cc {
namespace {

using TraceId = base::IdType64<class ui::LatencyInfo>;
class EventMetricsTest : public testing::Test {
 private:
  base::SimpleTestTickClock test_tick_clock_;

 protected:
  base::TimeTicks AdvanceNowByMs(int advance_ms) {
    test_tick_clock_.Advance(base::Microseconds(advance_ms));
    return test_tick_clock_.NowTicks();
  }
};

TEST_F(EventMetricsTest, ScrollBeginCreateWithNullBeginRwhTime) {
  // Arrange
  base::TimeTicks event_time = base::TimeTicks::Now() - base::Microseconds(100);
  base::TimeTicks blocking_touch_dispatched_to_renderer_timestamp;
  base::TimeTicks arrived_in_browser_main_timestamp;
  base::TimeTicks now = base::TimeTicks::Now();

  // Act
  std::unique_ptr<ScrollEventMetrics> scroll_event_metric =
      ScrollEventMetrics::Create(
          ui::EventType::kGestureScrollBegin, ui::ScrollInputType::kTouchscreen,
          /*is_inertial=*/false, event_time, arrived_in_browser_main_timestamp,
          blocking_touch_dispatched_to_renderer_timestamp, std::nullopt,
          /*scroll_begin_generated_timestamp=*/base::TimeTicks(),
          /*scroll_begin_arrival_timestamp=*/base::TimeTicks());

  // Assert
  EXPECT_EQ(event_time, scroll_event_metric->GetDispatchStageTimestamp(
                            EventMetrics::DispatchStage::kGenerated));
  EXPECT_LE(now,
            scroll_event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kArrivedInRendererCompositor));
  EXPECT_EQ(event_time,
            scroll_event_metric->scroll_begin_generated_timestamp());
  EXPECT_EQ(scroll_event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kArrivedInRendererCompositor),
            scroll_event_metric->scroll_begin_arrival_timestamp());
  // not set
  EXPECT_TRUE(scroll_event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::
                          kScrollsBlockingTouchDispatchedToRenderer)
                  .is_null());
  EXPECT_TRUE(scroll_event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::kArrivedInBrowserMain)
                  .is_null());
  EXPECT_TRUE(scroll_event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::kRendererCompositorStarted)
                  .is_null());
  EXPECT_TRUE(scroll_event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::kRendererCompositorFinished)
                  .is_null());
  EXPECT_TRUE(scroll_event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::kRendererMainStarted)
                  .is_null());
  EXPECT_TRUE(scroll_event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::kRendererMainFinished)
                  .is_null());
}

TEST_F(EventMetricsTest, ScrollBeginCreate) {
  // Arrange
  base::TimeTicks event_time = base::TimeTicks::Now() - base::Microseconds(100);
  base::TimeTicks blocking_touch_dispatched_to_renderer_timestamp =
      base::TimeTicks::Now() - base::Microseconds(70);
  base::TimeTicks arrived_in_browser_main_timestamp =
      base::TimeTicks::Now() - base::Microseconds(50);
  base::TimeTicks now = base::TimeTicks::Now();

  // Act
  std::unique_ptr<ScrollEventMetrics> scroll_event_metric =
      ScrollEventMetrics::Create(
          ui::EventType::kGestureScrollBegin, ui::ScrollInputType::kTouchscreen,
          /*is_inertial=*/false, event_time, arrived_in_browser_main_timestamp,
          blocking_touch_dispatched_to_renderer_timestamp, std::nullopt,
          /*scroll_begin_generated_timestamp=*/base::TimeTicks(),
          /*scroll_begin_arrival_timestamp=*/base::TimeTicks());

  // Assert
  EXPECT_EQ(event_time, scroll_event_metric->GetDispatchStageTimestamp(
                            EventMetrics::DispatchStage::kGenerated));
  EXPECT_EQ(blocking_touch_dispatched_to_renderer_timestamp,
            scroll_event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::
                    kScrollsBlockingTouchDispatchedToRenderer));
  EXPECT_EQ(arrived_in_browser_main_timestamp,
            scroll_event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kArrivedInBrowserMain));
  EXPECT_LE(now,
            scroll_event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kArrivedInRendererCompositor));
  EXPECT_EQ(event_time,
            scroll_event_metric->scroll_begin_generated_timestamp());
  EXPECT_EQ(scroll_event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kArrivedInRendererCompositor),
            scroll_event_metric->scroll_begin_arrival_timestamp());
  // not set
  EXPECT_TRUE(scroll_event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::kRendererCompositorStarted)
                  .is_null());
  EXPECT_TRUE(scroll_event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::kRendererCompositorFinished)
                  .is_null());
  EXPECT_TRUE(scroll_event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::kRendererMainStarted)
                  .is_null());
  EXPECT_TRUE(scroll_event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::kRendererMainFinished)
                  .is_null());
}

TEST_F(EventMetricsTest, ScrollBeginCreateFromExisting) {
  // Arrange
  base::TimeTicks event_time = base::TimeTicks::Now() - base::Microseconds(100);
  base::TimeTicks blocking_touch_dispatched_to_renderer_timestamp =
      base::TimeTicks::Now() - base::Microseconds(70);
  base::TimeTicks arrived_in_browser_main_timestamp =
      base::TimeTicks::Now() - base::Microseconds(50);
  std::unique_ptr<ScrollEventMetrics> scroll_metric =
      ScrollEventMetrics::Create(
          ui::EventType::kGestureScrollBegin, ui::ScrollInputType::kTouchscreen,
          /*is_inertial=*/false, event_time, arrived_in_browser_main_timestamp,
          blocking_touch_dispatched_to_renderer_timestamp, std::nullopt,
          /*scroll_begin_generated_timestamp=*/base::TimeTicks(),
          /*scroll_begin_arrival_timestamp=*/base::TimeTicks());

  // Act
  std::unique_ptr<ScrollEventMetrics> copy_scroll_metric =
      ScrollEventMetrics::CreateFromExisting(
          ui::EventType::kGestureScrollBegin, ui::ScrollInputType::kTouchscreen,
          /*is_inertial=*/false,
          EventMetrics::DispatchStage::kRendererMainFinished,
          scroll_metric.get(),
          /*scroll_begin_generated_timestamp=*/base::TimeTicks(),
          /*scroll_begin_arrival_timestamp=*/base::TimeTicks());

  // Assert
  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kGenerated),
            copy_scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kGenerated));
  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::
                    kScrollsBlockingTouchDispatchedToRenderer),
            copy_scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::
                    kScrollsBlockingTouchDispatchedToRenderer));
  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kArrivedInBrowserMain),
            copy_scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kArrivedInBrowserMain));

  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kArrivedInRendererCompositor),
            copy_scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kArrivedInRendererCompositor));

  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererCompositorStarted),
            copy_scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererCompositorStarted));

  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererCompositorFinished),
            copy_scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererCompositorFinished));

  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererMainStarted),
            copy_scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererMainStarted));

  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererMainFinished),
            copy_scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererMainFinished));

  EXPECT_EQ(event_time, copy_scroll_metric->scroll_begin_generated_timestamp());
  EXPECT_LE(scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kArrivedInRendererCompositor),
            copy_scroll_metric->scroll_begin_arrival_timestamp());
  EXPECT_GE(base::TimeTicks::Now(),
            copy_scroll_metric->scroll_begin_arrival_timestamp());
}

TEST_F(EventMetricsTest, ScrollEndCreateWithLaterScrollBeginTimestamp) {
  // Arrange
  // This is an edge case that can happen in an inertial scroll, where the
  // scroll end is synthesized from a timestamp older than the scroll begin's.
  base::TimeTicks now = base::TimeTicks::Now();
  base::TimeTicks event_time = now - base::Microseconds(400);
  base::TimeTicks scroll_begin_generated_timestamp =
      now - base::Microseconds(50);
  base::TimeTicks scroll_begin_arrival_timestamp = now - base::Microseconds(10);
  ASSERT_LT(event_time, scroll_begin_generated_timestamp);

  // Act
  std::unique_ptr<ScrollEventMetrics> scroll_event_metric =
      ScrollEventMetrics::Create(
          ui::EventType::kGestureScrollEnd, ui::ScrollInputType::kTouchscreen,
          /*is_inertial=*/true, event_time,
          /*arrived_in_browser_main_timestamp=*/base::TimeTicks(),
          /*blocking_touch_dispatched_to_renderer=*/base::TimeTicks(),
          std::nullopt, scroll_begin_generated_timestamp,
          scroll_begin_arrival_timestamp);

  // Assert
  EXPECT_EQ(event_time, scroll_event_metric->GetDispatchStageTimestamp(
                            EventMetrics::DispatchStage::kGenerated));
  EXPECT_EQ(scroll_begin_generated_timestamp,
            scroll_event_metric->scroll_begin_generated_timestamp());
  EXPECT_EQ(scroll_begin_arrival_timestamp,
            scroll_event_metric->scroll_begin_arrival_timestamp());
}

TEST_F(EventMetricsTest, ScrollUpdateCreateWithNullBeginRwhTime) {
  // Arrange
  base::TimeTicks begin_frame_arrival_timestamp =
      base::TimeTicks::Now() - base::Microseconds(200);
  base::TimeTicks scroll_begin_generated_timestamp =
      base::TimeTicks::Now() - base::Microseconds(300);
  base::TimeTicks event_time = base::TimeTicks::Now() - base::Microseconds(100);
  base::TimeTicks blocking_touch_dispatched_to_renderer_timestamp;
  base::TimeTicks arrived_in_browser_main_timestamp;
  base::TimeTicks now = base::TimeTicks::Now();
  TraceId trace_id(123);

  // Act
  std::unique_ptr<ScrollUpdateEventMetrics> scroll_event_metric =
      ScrollUpdateEventMetrics::Create(
          ui::EventType::kGestureScrollUpdate,
          ui::ScrollInputType::kTouchscreen,
          /*is_inertial=*/false,
          ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, /*delta=*/0.4,
          event_time, arrived_in_browser_main_timestamp,
          blocking_touch_dispatched_to_renderer_timestamp, trace_id,
          scroll_begin_generated_timestamp,
          /*scroll_begin_arrival_timestamp=*/begin_frame_arrival_timestamp);

  // Assert
  EXPECT_EQ(trace_id, scroll_event_metric->trace_id());
  EXPECT_EQ(event_time, scroll_event_metric->GetDispatchStageTimestamp(
                            EventMetrics::DispatchStage::kGenerated));
  EXPECT_LE(now,
            scroll_event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kArrivedInRendererCompositor));
  EXPECT_EQ(scroll_begin_generated_timestamp,
            scroll_event_metric->scroll_begin_generated_timestamp());
  EXPECT_EQ(begin_frame_arrival_timestamp,
            scroll_event_metric->scroll_begin_arrival_timestamp());
  // not set
  EXPECT_TRUE(scroll_event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::
                          kScrollsBlockingTouchDispatchedToRenderer)
                  .is_null());
  EXPECT_TRUE(scroll_event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::kArrivedInBrowserMain)
                  .is_null());
  EXPECT_TRUE(scroll_event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::kRendererCompositorStarted)
                  .is_null());
  EXPECT_TRUE(scroll_event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::kRendererCompositorFinished)
                  .is_null());
  EXPECT_TRUE(scroll_event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::kRendererMainStarted)
                  .is_null());
  EXPECT_TRUE(scroll_event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::kRendererMainFinished)
                  .is_null());
}

TEST_F(EventMetricsTest, ScrollUpdateCreate) {
  // Arrange
  base::TimeTicks begin_frame_arrival_timestamp =
      base::TimeTicks::Now() - base::Microseconds(200);
  base::TimeTicks scroll_begin_generated_timestamp =
      base::TimeTicks::Now() - base::Microseconds(300);
  base::TimeTicks event_time = base::TimeTicks::Now() - base::Microseconds(100);
  base::TimeTicks blocking_touch_dispatched_to_renderer_timestamp =
      base::TimeTicks::Now() - base::Microseconds(70);
  base::TimeTicks arrived_in_browser_main_timestamp =
      base::TimeTicks::Now() - base::Microseconds(50);
  base::TimeTicks now = base::TimeTicks::Now();
  TraceId trace_id(123);

  // Act
  std::unique_ptr<ScrollUpdateEventMetrics> scroll_event_metric =
      ScrollUpdateEventMetrics::Create(
          ui::EventType::kGestureScrollUpdate,
          ui::ScrollInputType::kTouchscreen,
          /*is_inertial=*/false,
          ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, /*delta=*/0.4,
          event_time, arrived_in_browser_main_timestamp,
          blocking_touch_dispatched_to_renderer_timestamp, TraceId(trace_id),
          scroll_begin_generated_timestamp,
          /*scroll_begin_arrival_timestamp=*/begin_frame_arrival_timestamp);

  // Assert
  EXPECT_EQ(trace_id, scroll_event_metric->trace_id());
  EXPECT_EQ(event_time, scroll_event_metric->GetDispatchStageTimestamp(
                            EventMetrics::DispatchStage::kGenerated));
  EXPECT_EQ(blocking_touch_dispatched_to_renderer_timestamp,
            scroll_event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::
                    kScrollsBlockingTouchDispatchedToRenderer));
  EXPECT_EQ(arrived_in_browser_main_timestamp,
            scroll_event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kArrivedInBrowserMain));
  EXPECT_LE(now,
            scroll_event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kArrivedInRendererCompositor));
  EXPECT_EQ(scroll_begin_generated_timestamp,
            scroll_event_metric->scroll_begin_generated_timestamp());
  EXPECT_EQ(begin_frame_arrival_timestamp,
            scroll_event_metric->scroll_begin_arrival_timestamp());
  // not set
  EXPECT_TRUE(scroll_event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::kRendererCompositorStarted)
                  .is_null());
  EXPECT_TRUE(scroll_event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::kRendererCompositorFinished)
                  .is_null());
  EXPECT_TRUE(scroll_event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::kRendererMainStarted)
                  .is_null());
  EXPECT_TRUE(scroll_event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::kRendererMainFinished)
                  .is_null());
}

TEST_F(EventMetricsTest, ScrollUpdateCoalesceWith) {
  // Arrange
  base::TimeTicks now = base::TimeTicks::Now();
  std::unique_ptr<ScrollUpdateEventMetrics> older_scroll_event_metric =
      ScrollUpdateEventMetrics::Create(
          ui::EventType::kGestureScrollUpdate,
          ui::ScrollInputType::kTouchscreen,
          /*is_inertial=*/false,
          ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, /*delta=*/444,
          /*timestamp=*/now - base::Microseconds(100),
          /*arrived_in_browser_main_timestamp=*/now - base::Microseconds(80),
          /*blocking_touch_dispatched_to_renderer=*/now -
              base::Microseconds(90),
          TraceId(123),
          /*scroll_begin_generated_timestamp=*/now - base::Microseconds(150),
          /*scroll_begin_arrival_timestamp=*/now - base::Microseconds(100));
  older_scroll_event_metric->set_predicted_delta(333);
  older_scroll_event_metric->set_caused_frame_update(false);
  older_scroll_event_metric->set_did_scroll(false);
  older_scroll_event_metric->AddAppliedScrollObservation(ElementId(1));
  std::unique_ptr<ScrollUpdateEventMetrics> newer_scroll_event_metric =
      ScrollUpdateEventMetrics::Create(
          ui::EventType::kGestureScrollUpdate,
          ui::ScrollInputType::kTouchscreen,
          /*is_inertial=*/false,
          ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, /*delta=*/22,
          /*timestamp=*/now - base::Microseconds(50),
          /*arrived_in_browser_main_timestamp=*/now - base::Microseconds(30),
          /*blocking_touch_dispatched_to_renderer=*/now -
              base::Microseconds(40),
          TraceId(456),
          /*scroll_begin_generated_timestamp=*/now - base::Microseconds(150),
          /*scroll_begin_arrival_timestamp=*/now - base::Microseconds(100));
  newer_scroll_event_metric->set_predicted_delta(11);
  newer_scroll_event_metric->set_caused_frame_update(true);
  newer_scroll_event_metric->set_did_scroll(true);
  newer_scroll_event_metric->AddAppliedScrollObservation(ElementId(2));
  newer_scroll_event_metric->AddAppliedScrollObservation(ElementId(3));

  // Act
  older_scroll_event_metric->CoalesceWith(*newer_scroll_event_metric);

  // Assert
  EXPECT_EQ(older_scroll_event_metric->last_timestamp(),
            now - base::Microseconds(50));
  EXPECT_EQ(older_scroll_event_metric->coalesced_event_count(), 2);
  EXPECT_EQ(older_scroll_event_metric->delta(), 444 + 22);
  EXPECT_EQ(older_scroll_event_metric->predicted_delta(), 333 + 11);
  EXPECT_TRUE(older_scroll_event_metric->caused_frame_update());
  EXPECT_TRUE(older_scroll_event_metric->did_scroll());
  EXPECT_EQ(older_scroll_event_metric->scroll_begin_generated_timestamp(),
            now - base::Microseconds(150));
  EXPECT_EQ(older_scroll_event_metric->scroll_begin_arrival_timestamp(),
            now - base::Microseconds(100));
  // Each observation carries the `kGenerated` timestamp of the event it was
  // recorded on.
  EXPECT_EQ(older_scroll_event_metric->applied_scroll_observations(),
            (std::vector<ScrollUpdateEventMetrics::AppliedScrollObservation>{
                {.update_input_timestamp = now - base::Microseconds(100),
                 .element_id = ElementId(1)},
                {.update_input_timestamp = now - base::Microseconds(50),
                 .element_id = ElementId(2)},
                {.update_input_timestamp = now - base::Microseconds(50),
                 .element_id = ElementId(3)}}));
}

TEST_F(EventMetricsTest, ScrollUpdateCloneAppliedScrollObservations) {
  // Arrange
  base::TimeTicks now = base::TimeTicks::Now();
  std::unique_ptr<ScrollUpdateEventMetrics> scroll_event_metric =
      ScrollUpdateEventMetrics::Create(
          ui::EventType::kGestureScrollUpdate,
          ui::ScrollInputType::kTouchscreen,
          /*is_inertial=*/false,
          ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, /*delta=*/444,
          /*timestamp=*/now - base::Microseconds(100),
          /*arrived_in_browser_main_timestamp=*/now - base::Microseconds(80),
          /*blocking_touch_dispatched_to_renderer=*/now -
              base::Microseconds(90),
          TraceId(123),
          /*scroll_begin_generated_timestamp=*/now - base::Microseconds(150),
          /*scroll_begin_arrival_timestamp=*/now - base::Microseconds(100));
  ASSERT_TRUE(scroll_event_metric->applied_scroll_observations().empty());
  scroll_event_metric->AddAppliedScrollObservation(ElementId(1));
  scroll_event_metric->AddAppliedScrollObservation(ElementId(2));

  // Act
  std::unique_ptr<EventMetrics> clone = scroll_event_metric->Clone();

  // Assert
  ASSERT_NE(clone->AsScrollUpdate(), nullptr);
  EXPECT_EQ(clone->AsScrollUpdate()->scroll_begin_generated_timestamp(),
            scroll_event_metric->scroll_begin_generated_timestamp());
  const std::vector<ScrollUpdateEventMetrics::AppliedScrollObservation>
      expected = {{.update_input_timestamp = now - base::Microseconds(100),
                   .element_id = ElementId(1)},
                  {.update_input_timestamp = now - base::Microseconds(100),
                   .element_id = ElementId(2)}};
  EXPECT_EQ(clone->AsScrollUpdate()->applied_scroll_observations(), expected);

  // The clone owns its observations, so mutating the original must not reach
  // it.
  scroll_event_metric->AddAppliedScrollObservation(ElementId(3));
  EXPECT_EQ(clone->AsScrollUpdate()->applied_scroll_observations(), expected);
}

TEST_F(EventMetricsTest, ScrollUpdateCreateFromExisting) {
  // Arrange
  base::TimeTicks event_time = base::TimeTicks::Now() - base::Microseconds(100);
  base::TimeTicks blocking_touch_dispatched_to_renderer_timestamp =
      base::TimeTicks::Now() - base::Microseconds(70);
  base::TimeTicks arrived_in_browser_main_timestamp =
      base::TimeTicks::Now() - base::Microseconds(50);
  TraceId trace_id(123);
  std::unique_ptr<ScrollUpdateEventMetrics> scroll_metric =
      ScrollUpdateEventMetrics::Create(
          ui::EventType::kGestureScrollUpdate,
          ui::ScrollInputType::kTouchscreen,
          /*is_inertial=*/false,
          ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, /*delta=*/0.4,
          event_time, arrived_in_browser_main_timestamp,
          blocking_touch_dispatched_to_renderer_timestamp, trace_id,
          /*scroll_begin_generated_timestamp=*/event_time -
              base::Microseconds(50),
          /*scroll_begin_arrival_timestamp=*/event_time);

  // Act
  std::unique_ptr<ScrollUpdateEventMetrics> copy_scroll_metric =
      ScrollUpdateEventMetrics::CreateFromExisting(
          ui::EventType::kGestureScrollUpdate,
          ui::ScrollInputType::kTouchscreen,
          /*is_inertial=*/false,
          ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, /*delta=*/0.4,
          EventMetrics::DispatchStage::kRendererMainFinished,
          scroll_metric.get(),
          scroll_metric->scroll_begin_generated_timestamp(),
          /*scroll_begin_arrival_timestamp=*/
          scroll_metric->scroll_begin_arrival_timestamp());

  // Assert
  EXPECT_NE(scroll_metric->trace_id(), copy_scroll_metric->trace_id());
  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kGenerated),
            copy_scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kGenerated));
  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::
                    kScrollsBlockingTouchDispatchedToRenderer),
            copy_scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::
                    kScrollsBlockingTouchDispatchedToRenderer));
  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kArrivedInBrowserMain),
            copy_scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kArrivedInBrowserMain));

  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kArrivedInRendererCompositor),
            copy_scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kArrivedInRendererCompositor));

  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererCompositorStarted),
            copy_scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererCompositorStarted));

  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererCompositorFinished),
            copy_scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererCompositorFinished));

  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererMainStarted),
            copy_scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererMainStarted));

  EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererMainFinished),
            copy_scroll_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererMainFinished));

  EXPECT_EQ(scroll_metric->scroll_begin_generated_timestamp(),
            copy_scroll_metric->scroll_begin_generated_timestamp());
}

TEST_F(EventMetricsTest, Create) {
  // Arrange
  base::TimeTicks event_time = base::TimeTicks::Now() - base::Microseconds(100);
  base::TimeTicks arrived_in_browser_main_timestamp =
      base::TimeTicks::Now() - base::Microseconds(50);
  base::TimeTicks now = base::TimeTicks::Now();

  // Act
  std::unique_ptr<EventMetrics> event_metric =
      EventMetrics::Create(ui::EventType::kTouchMoved, event_time,
                           arrived_in_browser_main_timestamp, std::nullopt);

  // Assert
  EXPECT_EQ(event_time, event_metric->GetDispatchStageTimestamp(
                            EventMetrics::DispatchStage::kGenerated));
  EXPECT_EQ(arrived_in_browser_main_timestamp,
            event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kArrivedInBrowserMain));
  EXPECT_LE(now,
            event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kArrivedInRendererCompositor));
  // not set
  EXPECT_TRUE(event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::kRendererCompositorStarted)
                  .is_null());
  EXPECT_TRUE(event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::kRendererCompositorFinished)
                  .is_null());
  EXPECT_TRUE(event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::kRendererMainStarted)
                  .is_null());
  EXPECT_TRUE(event_metric
                  ->GetDispatchStageTimestamp(
                      EventMetrics::DispatchStage::kRendererMainFinished)
                  .is_null());
}

TEST_F(EventMetricsTest, CreateFromExisting) {
  // Arrange
  base::TimeTicks event_time = base::TimeTicks::Now() - base::Microseconds(100);
  base::TimeTicks arrived_in_browser_main_timestamp =
      base::TimeTicks::Now() - base::Microseconds(50);
  std::unique_ptr<EventMetrics> event_metric =
      EventMetrics::Create(ui::EventType::kTouchMoved, event_time,
                           arrived_in_browser_main_timestamp, std::nullopt);

  // Act
  std::unique_ptr<EventMetrics> copy_event_metric =
      EventMetrics::CreateFromExisting(
          ui::EventType::kTouchMoved,
          EventMetrics::DispatchStage::kRendererMainFinished,
          event_metric.get());

  // Assert
  EXPECT_EQ(event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kGenerated),
            copy_event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kGenerated));
  EXPECT_EQ(event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kArrivedInBrowserMain),
            copy_event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kArrivedInBrowserMain));

  EXPECT_EQ(event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kArrivedInRendererCompositor),
            copy_event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kArrivedInRendererCompositor));

  EXPECT_EQ(event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererCompositorStarted),
            copy_event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererCompositorStarted));

  EXPECT_EQ(event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererCompositorFinished),
            copy_event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererCompositorFinished));

  EXPECT_EQ(event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererMainStarted),
            copy_event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererMainStarted));

  EXPECT_EQ(event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererMainFinished),
            copy_event_metric->GetDispatchStageTimestamp(
                EventMetrics::DispatchStage::kRendererMainFinished));
}

}  // namespace
}  // namespace cc
