chromium/content/browser/renderer_host/input/scroll_tracing_browsertest.cc

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

#include <memory>
#include <string_view>

#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_trace_processor.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/input/synthetic_gesture_controller.h"
#include "content/common/input/synthetic_smooth_scroll_gesture.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
#include "net/dns/mock_host_resolver.h"
#include "services/metrics/public/cpp/metrics_utils.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "third_party/blink/public/common/input/synthetic_web_input_event_builders.h"

namespace content {

class ScrollTracingBrowserTest : public ContentBrowserTest {};

// NOTE:  Mac doesn't support touch events, and will not record scrolls with
// touch input. Linux bots are inconsistent.
#if BUILDFLAG(IS_ANDROID)
// Basic parity matching test between trace events and UKM if both are recorded
// during a scroll.
IN_PROC_BROWSER_TEST_F(ScrollTracingBrowserTest, ScrollingMetricsParity) {
  base::test::TestTraceProcessor ttp_;
  ttp_.StartTrace("input.scrolling");

  ASSERT_TRUE(embedded_test_server()->Start());

  GURL url(
      embedded_test_server()->GetURL("/scrollable_page_with_content.html"));
  EXPECT_TRUE(NavigateToURL(shell(), url));
  ASSERT_TRUE(WaitForLoadStop(shell()->web_contents()));

  // Scroll with 3 updates to ensure:
  //  1) One frame is missed (maximum 2 may be missed).
  //  2) Predictor jank occurs.
  DoScroll(gfx::Point(10, 10), {gfx::Vector2d(0, 100), gfx::Vector2d(0, -100)},
           content::mojom::GestureSourceType::kTouchInput);

  RunUntilInputProcessed(GetRenderWidgetHostImpl());
  EXPECT_EQ(true, EvalJs(shell()->web_contents(), "did_scroll;"));

  absl::Status status = ttp_.StopAndParseTrace();
  ASSERT_TRUE(status.ok()) << status.message();

  // Use the values in chrome_scroll_interactions to validate values in UKM.
  auto result = ttp_.RunQuery(R"(
    INCLUDE PERFETTO MODULE chrome.scroll_interactions;

    SELECT
      frame_count,
      vsync_count,
      missed_vsync_max,
      missed_vsync_sum,
      delayed_frame_count,
      predictor_janky_frame_count
    FROM chrome_scroll_interactions
    LIMIT 1;
  )");

  ASSERT_TRUE(result.has_value()) << result.error();
  auto scroll_metrics_result = result.value();

  // Validate that there are two rows in the output. The first row is the
  // column names, the second row is the values. If there is only one row,
  // then the query produced no data (a.k.a the data model was not populated).
  EXPECT_EQ(scroll_metrics_result.size(), 2u);

  // Validate that each column of the query is present.
  ASSERT_TRUE(scroll_metrics_result[1].size() == 6);

  auto scroll_metrics = scroll_metrics_result[1];

  // frame_count
  EXPECT_GE(ConvertToHistogramValue(scroll_metrics[0]), 1);
  // vsync_count
  EXPECT_GE(ConvertToHistogramValue(scroll_metrics[1]), 1);

  ValidateUkm(
      url, ukm::builders::Event_Scroll::kEntryName,
      {
          {ukm::builders::Event_Scroll::kFrameCountName,
           ConvertToHistogramValue(scroll_metrics[0])},
          {ukm::builders::Event_Scroll::kVsyncCountName,
           ConvertToHistogramValue(scroll_metrics[1])},
          {ukm::builders::Event_Scroll::kScrollJank_MissedVsyncsMaxName,
           ConvertToHistogramValue(scroll_metrics[2])},
          {ukm::builders::Event_Scroll::kScrollJank_MissedVsyncsSumName,
           ConvertToHistogramValue(scroll_metrics[3])},
          {ukm::builders::Event_Scroll::kScrollJank_DelayedFrameCountName,
           ConvertToHistogramValue(scroll_metrics[4])},
          {ukm::builders::Event_Scroll::kPredictorJankyFrameCountName,
           ConvertToHistogramValue(scroll_metrics[5])},
      });
}
#endif  // !BUILDFLAG(IS_ANDROID)

}  // namespace content