chromium/ui/compositor/compositor_unittest.cc

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

#include <stdint.h>

#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/power_monitor_test.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "cc/metrics/frame_sequence_tracker.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/ui_base_features.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_delegate.h"
#include "ui/compositor/test/draw_waiter_for_test.h"
#include "ui/compositor/test/in_process_context_factory.h"
#include "ui/compositor/test/test_context_factories.h"
#include "ui/display/types/display_constants.h"

Mock;
_;

namespace ui {
namespace {

class MockCompositorObserver : public CompositorObserver {};

class CompositorTest : public testing::Test {};

// For tests that control time.
class CompositorTestWithMockedTime : public CompositorTest {};

// For tests that run on a real MessageLoop with real time.
class CompositorTestWithMessageLoop : public CompositorTest {};

class TestCompositorAnimationObserver : public CompositorAnimationObserver {};

}  // namespace

TEST_F(CompositorTestWithMockedTime, AnimationObserverBasic) {}

TEST_F(CompositorTestWithMockedTime, AnimationObserverResetAfterResume) {}

TEST_F(CompositorTestWithMessageLoop, ShouldUpdateDisplayProperties) {}

TEST_F(CompositorTestWithMockedTime,
       ReleaseWidgetWithOutputSurfaceNeverCreated) {}

TEST_F(CompositorTestWithMessageLoop, MoveThroughputTracker) {}

#if BUILDFLAG(IS_CHROMEOS)
// ui::ThroughputTracker is only supported on ChromeOS
TEST_F(CompositorTestWithMessageLoop, ThroughputTracker) {
  auto root_layer = std::make_unique<Layer>(ui::LAYER_SOLID_COLOR);
  viz::ParentLocalSurfaceIdAllocator allocator;
  allocator.GenerateId();
  root_layer->SetBounds(gfx::Rect(10, 10));
  compositor()->SetRootLayer(root_layer.get());
  compositor()->SetScaleAndSize(1.0f, gfx::Size(10, 10),
                                allocator.GetCurrentLocalSurfaceId());
  ASSERT_TRUE(compositor()->IsVisible());

  ThroughputTracker tracker = compositor()->RequestNewThroughputTracker();

  base::RunLoop run_loop;
  tracker.Start(base::BindLambdaForTesting(
      [&](const cc::FrameSequenceMetrics::CustomReportData& data) {
        EXPECT_GT(data.frames_expected_v3, 0u);
        EXPECT_GT(data.frames_expected_v3 - data.frames_dropped_v3, 0u);
        run_loop.Quit();
      }));

  // Generates a few frames after tracker starts to have some data collected.
  for (int i = 0; i < 5; ++i) {
    compositor()->ScheduleFullRedraw();
    DrawWaiterForTest::WaitForCompositingEnded(compositor());
  }

  EXPECT_TRUE(tracker.Stop());

  // Generates a few frames after tracker stops. Note the number of frames
  // must be at least two: one to trigger underlying cc::FrameSequenceTracker to
  // be scheduled for termination and one to report data.
  for (int i = 0; i < 5; ++i) {
    compositor()->ScheduleFullRedraw();
    DrawWaiterForTest::WaitForCompositingEnded(compositor());
  }

  run_loop.Run();
}

TEST_F(CompositorTestWithMessageLoop, ThroughputTrackerOutliveCompositor) {
  auto tracker = compositor()->RequestNewThroughputTracker();
  tracker.Start(base::BindLambdaForTesting(
      [&](const cc::FrameSequenceMetrics::CustomReportData& data) {
        ADD_FAILURE() << "No report should happen";
      }));

  DestroyCompositor();

  // Stop() fails but no crash, no use-after-free and no report.
  EXPECT_FALSE(tracker.Stop());
}

TEST_F(CompositorTestWithMessageLoop, ThroughputTrackerCallbackStateChange) {
  auto root_layer = std::make_unique<Layer>(ui::LAYER_SOLID_COLOR);
  viz::ParentLocalSurfaceIdAllocator allocator;
  allocator.GenerateId();
  root_layer->SetBounds(gfx::Rect(10, 10));
  compositor()->SetRootLayer(root_layer.get());
  compositor()->SetScaleAndSize(1.0f, gfx::Size(10, 10),
                                allocator.GetCurrentLocalSurfaceId());
  ASSERT_TRUE(compositor()->IsVisible());

  ThroughputTracker tracker = compositor()->RequestNewThroughputTracker();

  base::RunLoop run_loop;
  tracker.Start(base::BindLambdaForTesting(
      [&](const cc::FrameSequenceMetrics::CustomReportData& data) {
        // The following Cancel() call should not DCHECK or crash.
        tracker.Cancel();

        // Starting another tracker should not DCHECK or crash.
        ThroughputTracker another_tracker =
            compositor()->RequestNewThroughputTracker();
        another_tracker.Start(base::DoNothing());

        run_loop.Quit();
      }));

  // Generates a few frames after tracker starts to have some data collected.
  for (int i = 0; i < 5; ++i) {
    compositor()->ScheduleFullRedraw();
    DrawWaiterForTest::WaitForCompositingEnded(compositor());
  }

  EXPECT_TRUE(tracker.Stop());

  // Generates a few frames after tracker stops. Note the number of frames
  // must be at least two: one to trigger underlying cc::FrameSequenceTracker to
  // be scheduled for termination and one to report data.
  for (int i = 0; i < 5; ++i) {
    compositor()->ScheduleFullRedraw();
    DrawWaiterForTest::WaitForCompositingEnded(compositor());
  }

  run_loop.Run();
}

TEST_F(CompositorTestWithMessageLoop, ThroughputTrackerInvoluntaryReport) {
  auto root_layer = std::make_unique<Layer>(ui::LAYER_SOLID_COLOR);
  viz::ParentLocalSurfaceIdAllocator allocator;
  allocator.GenerateId();
  root_layer->SetBounds(gfx::Rect(10, 10));
  compositor()->SetRootLayer(root_layer.get());
  compositor()->SetScaleAndSize(1.0f, gfx::Size(10, 10),
                                allocator.GetCurrentLocalSurfaceId());
  ASSERT_TRUE(compositor()->IsVisible());

  ThroughputTracker tracker = compositor()->RequestNewThroughputTracker();

  tracker.Start(base::BindLambdaForTesting(
      [&](const cc::FrameSequenceMetrics::CustomReportData& data) {
        ADD_FAILURE() << "No report should happen";
      }));

  // Generates a few frames after tracker starts to have some data collected.
  for (int i = 0; i < 5; ++i) {
    compositor()->ScheduleFullRedraw();
    DrawWaiterForTest::WaitForCompositingEnded(compositor());
  }

  // ReleaseAcceleratedWidget() destroys underlying cc::FrameSequenceTracker
  // and triggers reports before Stop(). Such reports are dropped.
  compositor()->SetVisible(false);
  compositor()->ReleaseAcceleratedWidget();

  // Stop() fails but no DCHECK or crash.
  EXPECT_FALSE(tracker.Stop());
}
#endif  // BUILDFLAG(IS_CHROMEOS)

#if BUILDFLAG(IS_WIN)
// TODO(crbug.com/40467610): Flaky on windows trybots
#define MAYBE_CreateAndReleaseOutputSurface
#else
#define MAYBE_CreateAndReleaseOutputSurface
#endif
TEST_F(CompositorTestWithMessageLoop, MAYBE_CreateAndReleaseOutputSurface) {}

class LayerDelegateThatAddsDuringUpdateVisualState : public LayerDelegate {};

TEST_F(CompositorTestWithMessageLoop, AddLayerDuringUpdateVisualState) {}

TEST_F(CompositorTestWithMessageLoop, CompositorVisibilityChanges) {}

}  // namespace ui