chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager_unittest.cc

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

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#ifndef UI_OZONE_PLATFORM_DRM_GPU_DRM_GPU_DISPLAY_MANAGER_UNITTEST_CC_
#define UI_OZONE_PLATFORM_DRM_GPU_DRM_GPU_DISPLAY_MANAGER_UNITTEST_CC_

#include "ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h"

#include <xf86drm.h>

#include "base/files/file_path.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/display/display_features.h"
#include "ui/display/types/display_configuration_params.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/linux/test/mock_gbm_device.h"
#include "ui/ozone/platform/drm/common/display_types.h"
#include "ui/ozone/platform/drm/common/drm_util.h"
#include "ui/ozone/platform/drm/common/scoped_drm_types.h"
#include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
#include "ui/ozone/platform/drm/gpu/drm_display.h"
#include "ui/ozone/platform/drm/gpu/fake_drm_device.h"
#include "ui/ozone/platform/drm/gpu/fake_drm_device_generator.h"
#include "ui/ozone/platform/drm/gpu/mock_drm_device.h"
#include "ui/ozone/platform/drm/gpu/screen_manager.h"

namespace ui {

namespace {

using ::testing::_;
using ::testing::AllOf;
using ::testing::AtLeast;
using ::testing::Eq;
using ::testing::Exactly;
using ::testing::Field;
using ::testing::Gt;
using ::testing::IsEmpty;
using ::testing::Pointee;
using ::testing::Property;
using ::testing::Return;
using ::testing::SizeIs;
using ::testing::UnorderedElementsAre;

// HP z32x monitor.
constexpr unsigned char kHPz32x[] =
    "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x22\xF0\x75\x32\x01\x01\x01\x01"
    "\x1B\x1B\x01\x04\xB5\x46\x27\x78\x3A\x8D\x15\xAC\x51\x32\xB8\x26"
    "\x0B\x50\x54\x21\x08\x00\xD1\xC0\xA9\xC0\x81\xC0\xD1\x00\xB3\x00"
    "\x95\x00\xA9\x40\x81\x80\x4D\xD0\x00\xA0\xF0\x70\x3E\x80\x30\x20"
    "\x35\x00\xB9\x88\x21\x00\x00\x1A\x00\x00\x00\xFD\x00\x18\x3C\x1E"
    "\x87\x3C\x00\x0A\x20\x20\x20\x20\x20\x20\x00\x00\x00\xFC\x00\x48"
    "\x50\x20\x5A\x33\x32\x78\x0A\x20\x20\x20\x20\x20\x00\x00\x00\xFF"
    "\x00\x43\x4E\x43\x37\x32\x37\x30\x4D\x57\x30\x0A\x20\x20\x01\x46"
    "\x02\x03\x18\xF1\x4B\x10\x1F\x04\x13\x03\x12\x02\x11\x01\x05\x14"
    "\x23\x09\x07\x07\x83\x01\x00\x00\xA3\x66\x00\xA0\xF0\x70\x1F\x80"
    "\x30\x20\x35\x00\xB9\x88\x21\x00\x00\x1A\x56\x5E\x00\xA0\xA0\xA0"
    "\x29\x50\x30\x20\x35\x00\xB9\x88\x21\x00\x00\x1A\xEF\x51\x00\xA0"
    "\xF0\x70\x19\x80\x30\x20\x35\x00\xB9\x88\x21\x00\x00\x1A\xE2\x68"
    "\x00\xA0\xA0\x40\x2E\x60\x20\x30\x63\x00\xB9\x88\x21\x00\x00\x1C"
    "\x28\x3C\x80\xA0\x70\xB0\x23\x40\x30\x20\x36\x00\xB9\x88\x21\x00"
    "\x00\x1A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3E";
constexpr size_t kHPz32xLength = std::size(kHPz32x);

// An EDID that can be found on an internal panel.
constexpr unsigned char kInternalDisplay[] =
    "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\xa3\x42\x31\x00\x00\x00\x00"
    "\x00\x15\x01\x03\x80\x1a\x10\x78\x0a\xd3\xe5\x95\x5c\x60\x90\x27"
    "\x19\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
    "\x01\x01\x01\x01\x01\x01\x9e\x1b\x00\xa0\x50\x20\x12\x30\x10\x30"
    "\x13\x00\x05\xa3\x10\x00\x00\x19\x00\x00\x00\x0f\x00\x00\x00\x00"
    "\x00\x00\x00\x00\x00\x23\x87\x02\x64\x00\x00\x00\x00\xfe\x00\x53"
    "\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x00\x00\x00\xfe"
    "\x00\x31\x32\x31\x41\x54\x31\x31\x2d\x38\x30\x31\x0a\x20\x00\x45";
constexpr size_t kInternalDisplayLength = std::size(kInternalDisplay);

// Serial number is unavailable. Omitted from bytes 12-15 of block zero and SN
// descriptor (tag: 0xff). Displays like this will produce colliding EDID-based
// display IDs.
constexpr unsigned char kNoSerialNumberDisplay[] =
    "\x00\xff\xff\xff\xff\xff\xff\x00\x22\xf0\x6c\x28\x00\x00\x00\x00"
    "\x02\x16\x01\x04\xb5\x40\x28\x78\xe2\x8d\x85\xad\x4f\x35\xb1\x25"
    "\x0e\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
    "\x01\x01\x01\x01\x01\x01\xe2\x68\x00\xa0\xa0\x40\x2e\x60\x30\x20"
    "\x36\x00\x81\x90\x21\x00\x00\x1a\xbc\x1b\x00\xa0\x50\x20\x17\x30"
    "\x30\x20\x36\x00\x81\x90\x21\x00\x00\x1a\x00\x00\x00\xfc\x00\x48"
    "\x50\x20\x5a\x52\x33\x30\x77\x0a\x20\x20\x20\x20\x00\x00\x00\x10"
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x71";
constexpr size_t kNoSerialNumberDisplayLength =
    std::size(kNoSerialNumberDisplay);

constexpr unsigned char kTiledDisplay[] =
    "\x00\xff\xff\xff\xff\xff\xff\x00\x10\xac\x47\x41\x4c\x34\x37\x41"
    "\x0b\x21\x01\x04\xb5\x46\x27\x78\x3a\x76\x45\xae\x51\x33\xba\x26"
    "\x0d\x50\x54\xa5\x4b\x00\x81\x00\xb3\x00\xd1\x00\xa9\x40\x81\x80"
    "\xd1\xc0\x01\x01\x01\x01\x4d\xd0\x00\xa0\xf0\x70\x3e\x80\x30\x20"
    "\x35\x00\xba\x89\x21\x00\x00\x1a\x00\x00\x00\xff\x00\x4a\x48\x4e"
    "\x34\x4a\x33\x33\x47\x41\x37\x34\x4c\x0a\x00\x00\x00\xfc\x00\x44"
    "\x45\x4c\x4c\x20\x55\x50\x33\x32\x31\x38\x4b\x0a\x00\x00\x00\xfd"
    "\x00\x18\x4b\x1e\xb4\x6c\x01\x0a\x20\x20\x20\x20\x20\x20\x02\x79"
    "\x02\x03\x1d\xf1\x50\x10\x1f\x20\x05\x14\x04\x13\x12\x11\x03\x02"
    "\x16\x15\x07\x06\x01\x23\x09\x1f\x07\x83\x01\x00\x00\xa3\x66\x00"
    "\xa0\xf0\x70\x1f\x80\x30\x20\x35\x00\xba\x89\x21\x00\x00\x1a\x56"
    "\x5e\x00\xa0\xa0\xa0\x29\x50\x30\x20\x35\x00\xba\x89\x21\x00\x00"
    "\x1a\x7c\x39\x00\xa0\x80\x38\x1f\x40\x30\x20\x3a\x00\xba\x89\x21"
    "\x00\x00\x1a\xa8\x16\x00\xa0\x80\x38\x13\x40\x30\x20\x3a\x00\xba"
    "\x89\x21\x00\x00\x1a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x47"
    "\x70\x12\x79\x00\x00\x12\x00\x16\x82\x10\x10\x00\xff\x0e\xdf\x10"
    "\x00\x00\x00\x00\x00\x44\x45\x4c\x47\x41\x4c\x34\x37\x41\x03\x01"
    "\x50\x70\x92\x01\x84\xff\x1d\xc7\x00\x1d\x80\x09\x00\xdf\x10\x2f"
    "\x00\x02\x00\x04\x00\xc1\x42\x01\x84\xff\x1d\xc7\x00\x2f\x80\x1f"
    "\x00\xdf\x10\x30\x00\x02\x00\x04\x00\xa8\x4e\x01\x04\xff\x0e\xc7"
    "\x00\x2f\x80\x1f\x00\xdf\x10\x61\x00\x02\x00\x09\x00\x97\x9d\x01"
    "\x04\xff\x0e\xc7\x00\x2f\x80\x1f\x00\xdf\x10\x2f\x00\x02\x00\x09"
    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x78\x90";
constexpr size_t kTiledDisplayLength = std::size(kTiledDisplay);

constexpr char kDefaultTestGraphicsCardPattern[] = "/test/dri/card%d";

constexpr char kTestOnlyModesetOutcomeOneDisplay[] =
    "ConfigureDisplays.Modeset.Test.OneDisplay.Outcome";
constexpr char kTestOnlyModesetOutcomeTwoDisplays[] =
    "ConfigureDisplays.Modeset.Test.TwoDisplays.Outcome";
constexpr char kTestOnlyModesetFallbacksAttemptedTwoDisplaysMetric[] =
    "ConfigureDisplays.Modeset.Test.DynamicCRTCs.TwoDisplays."
    "PermutationsAttempted";

const std::vector<ResolutionAndRefreshRate> kStandardModes = {
    ResolutionAndRefreshRate{gfx::Size(3840, 2160), 60u},
    ResolutionAndRefreshRate{gfx::Size(3840, 2160), 50u},
    ResolutionAndRefreshRate{gfx::Size(3840, 2160), 30u},
    ResolutionAndRefreshRate{gfx::Size(1920, 1080), 60u},
    ResolutionAndRefreshRate{gfx::Size(1920, 1080), 50u},
    ResolutionAndRefreshRate{gfx::Size(1920, 1080), 30u}};

enum class TestOnlyModesetOutcome {
  kSuccess = 0,
  kFallbackSuccess = 1,
  kFailure = 2,
  kMaxValue = kFailure,
};

// arg: drmModeAtomicReq*, pairings: CrtcConnectorPairs
MATCHER_P(AtomicRequestHasCrtcConnectorPairs, pairings, "") {
  if (arg == nullptr) {
    return false;
  }

  std::vector<drmModeAtomicReqItem> arg_items;
  for (uint32_t i = 0; i < arg->cursor; ++i) {
    arg_items.push_back(arg->items[i]);
  }

  for (const auto& [crtc, connector] : pairings) {
    if (std::find_if(arg_items.begin(), arg_items.end(),
                     [crtc, connector](const drmModeAtomicReqItem& item) {
                       return item.object_id == connector && item.value == crtc;
                     }) == arg_items.end()) {
      return false;
    }
  }

  return true;
}

class MockDrmDeviceGenerator : public DrmDeviceGenerator {
  scoped_refptr<DrmDevice> CreateDevice(const base::FilePath& path,
                                        base::ScopedFD fd,
                                        bool is_primary_device) override {
    auto gbm_device = std::make_unique<MockGbmDevice>();
    DCHECK(!path.empty());
    return base::MakeRefCounted<MockDrmDevice>(
        std::move(path), std::move(gbm_device), is_primary_device);
  }
};

ScopedDrmPropertyBlob CreateTilePropertyBlob(FakeDrmDevice& drm,
                                             const TileProperty& property) {
  // "group_id:tile_is_single_monitor:num_h_tile:num_v_tile:tile_h_loc
  // :tile_v_loc:tile_h_size:tile_v_size"
  std::string tile_property_str = base::StringPrintf(
      "%d:1:%d:%d:%d:%d:%d:%d\0", property.group_id,
      property.tile_layout.width(), property.tile_layout.height(),
      property.location.x(), property.location.y(), property.tile_size.width(),
      property.tile_size.height());

  return drm.CreatePropertyBlob(tile_property_str.data(),
                                tile_property_str.size());
}

testing::Matcher<display::DisplayMode> EqResAndRefresh(
    const ResolutionAndRefreshRate& mode) {
  return AllOf(Property(&display::DisplayMode::size, Eq(mode.first)),
               Property(&display::DisplayMode::refresh_rate, Eq(mode.second)));
}

// Verifies that two vectors contain equal requests, excluding certain
// properties that are permitted to change during a configuration. Assumes that
// the vectors maintain the same ordering w.r.t. the requests' `id` properties.
void ExpectEqualRequestsWithExceptions(
    const std::vector<display::DisplayConfigurationParams>& a,
    const std::vector<display::DisplayConfigurationParams>& b) {
  EXPECT_EQ(a.size(), b.size());
  for (size_t i = 0; i < a.size(); ++i) {
    EXPECT_EQ(a[i].id, b[i].id);
    EXPECT_EQ(a[i].origin, b[i].origin);
    EXPECT_EQ(a[i].enable_vrr, b[i].enable_vrr);
    EXPECT_EQ(a[i].mode->size(), b[i].mode->size());
    EXPECT_EQ(a[i].mode->is_interlaced(), b[i].mode->is_interlaced());
    // mode->refresh_rate() excepted because it can update after configuration.
    // mode->vsync_rate_min() excepted because it can update after
    // configuration.
  }
}

}  // namespace

class DrmGpuDisplayManagerTest : public testing::Test {
 protected:
  void SetUp() override {
    std::unique_ptr<FakeDrmDeviceGenerator> fake_device_generator =
        std::make_unique<FakeDrmDeviceGenerator>();
    device_manager_ =
        std::make_unique<DrmDeviceManager>(std::move(fake_device_generator));
    screen_manager_ = std::make_unique<ScreenManager>();
    drm_gpu_display_manager_ = std::make_unique<DrmGpuDisplayManager>(
        screen_manager_.get(), device_manager_.get());
  }

  void TearDown() override {
    drm_gpu_display_manager_ = nullptr;
    screen_manager_ = nullptr;
    for (auto& device : device_manager_->GetDrmDevices()) {
      FakeDrmDevice* fake_drm = static_cast<FakeDrmDevice*>(device.get());
      fake_drm->ResetPlaneManagerForTesting();
    }
    device_manager_ = nullptr;
    next_drm_device_number_ = 0u;
  }

  // Note: the first device added will be marked as the primary device.
  scoped_refptr<FakeDrmDevice> AddDrmDevice() {
    std::string card_path = base::StringPrintf(kDefaultTestGraphicsCardPattern,
                                               next_drm_device_number_++);
    base::FilePath file_path(card_path);

    device_manager_->AddDrmDevice(file_path, base::ScopedFD());
    FakeDrmDevice* fake_drm = static_cast<FakeDrmDevice*>(
        device_manager_->GetDrmDevices().back().get());
    return scoped_refptr<FakeDrmDevice>(fake_drm);
  }

  bool ConfigureDisplays(const MovableDisplaySnapshots& display_snapshots,
                         display::ModesetFlags modeset_flag) {
    std::vector<display::DisplayConfigurationParams> config_requests;
    for (const auto& snapshot : display_snapshots) {
      config_requests.emplace_back(snapshot->display_id(), snapshot->origin(),
                                   snapshot->native_mode());
    }
    std::vector<display::DisplayConfigurationParams> out_requests;
    return drm_gpu_display_manager_->ConfigureDisplays(
        config_requests, modeset_flag, out_requests);
  }

  DrmDisplay* FindDisplay(int64_t display_id) {
    return drm_gpu_display_manager_->FindDisplay(display_id);
  }

  DrmDisplay* FindDisplayByConnectorId(uint32_t connector) {
    return drm_gpu_display_manager_->FindDisplayByConnectorId(connector);
  }

  size_t next_drm_device_number_ = 0u;

  std::unique_ptr<DrmDeviceManager> device_manager_;
  std::unique_ptr<ScreenManager> screen_manager_;
  std::unique_ptr<DrmGpuDisplayManager> drm_gpu_display_manager_;
  base::HistogramTester histogram_tester_;
};

TEST_F(DrmGpuDisplayManagerTest, CapOutOnMaxDrmDeviceCount) {
  // Add |kMaxDrmCount| + 1 DRM devices, each with one active display.
  for (size_t i = 0; i < kMaxDrmCount + 1; ++i) {
    auto fake_drm = AddDrmDevice();
    fake_drm->ResetStateWithAllProperties();

    // Add 1 CRTC
    fake_drm->AddCrtcWithPrimaryAndCursorPlanes();

    // Add one encoder
    auto& encoder = fake_drm->AddEncoder();
    encoder.possible_crtcs = 0b1;

    // Add 1 Connector
    auto& connector = fake_drm->AddConnector();
    connector.connection = true;
    connector.modes = std::vector<ResolutionAndRefreshRate>{kStandardModes[0]};
    connector.encoders = std::vector<uint32_t>{encoder.id};
    connector.edid_blob =
        std::vector<uint8_t>(kHPz32x, kHPz32x + kHPz32xLength);

    fake_drm->InitializeState(/* use_atomic */ true);
  }

  ASSERT_EQ(drm_gpu_display_manager_->GetDisplays().size(), kMaxDrmCount);
}

TEST_F(DrmGpuDisplayManagerTest, CapOutOnMaxConnectorCount) {
  // One DRM device.
  auto fake_drm = AddDrmDevice();
  fake_drm->ResetStateWithAllProperties();

  // Add |kMaxDrmConnectors| + 1 connector, each with one active display.
  for (size_t i = 0; i < kMaxDrmConnectors + 1; ++i) {
    // Add 1 CRTC
    fake_drm->AddCrtcWithPrimaryAndCursorPlanes();

    // Add one encoder
    auto& encoder = fake_drm->AddEncoder();
    encoder.possible_crtcs = 1 << i;

    // Add 1 Connector
    auto& connector = fake_drm->AddConnector();
    connector.connection = true;
    connector.modes = std::vector<ResolutionAndRefreshRate>{kStandardModes[0]};
    connector.encoders = std::vector<uint32_t>{encoder.id};
    connector.edid_blob =
        std::vector<uint8_t>(kHPz32x, kHPz32x + kHPz32xLength);
  }
  fake_drm->InitializeState(/* use_atomic */ true);

  ASSERT_EQ(drm_gpu_display_manager_->GetDisplays().size(), kMaxDrmConnectors);
}

TEST_F(DrmGpuDisplayManagerTest, FindAndConfigureDisplaysOnSameDrmDevice) {
  // One DRM device.
  auto drm = AddDrmDevice();
  drm->ResetStateWithAllProperties();

  // Add 3 connectors, each with one active display.
  for (size_t i = 0; i < 3; ++i) {
    drm->AddCrtcWithPrimaryAndCursorPlanes();

    auto& encoder = drm->AddEncoder();
    encoder.possible_crtcs = 1 << i;

    auto& connector = drm->AddConnector();
    connector.connection = true;
    connector.modes = std::vector<ResolutionAndRefreshRate>{
        kStandardModes[i % kStandardModes.size()]};
    connector.encoders = std::vector<uint32_t>{encoder.id};
    connector.edid_blob =
        std::vector<uint8_t>(kHPz32x, kHPz32x + kHPz32xLength);
  }
  drm->InitializeState(/* use_atomic */ true);

  MovableDisplaySnapshots display_snapshots =
      drm_gpu_display_manager_->GetDisplays();
  ASSERT_EQ(display_snapshots.size(), 3u);

  // Make sure the displays are successfully found on the only existing DRM
  // device.
  DrmDisplay* display1 = FindDisplay(display_snapshots[0]->display_id());
  ASSERT_TRUE(display1);
  ASSERT_EQ(display1->drm().get(), drm.get());

  DrmDisplay* display2 = FindDisplay(display_snapshots[1]->display_id());
  ASSERT_TRUE(display2);
  ASSERT_EQ(display2->drm().get(), drm.get());

  DrmDisplay* display3 = FindDisplay(display_snapshots[2]->display_id());
  ASSERT_TRUE(display3);
  ASSERT_EQ(display3->drm().get(), drm.get());

  // Make sure configuration on the returned snapshots is possible.
  ASSERT_TRUE(ConfigureDisplays(display_snapshots,
                                {display::ModesetFlag::kTestModeset,
                                 display::ModesetFlag::kCommitModeset}));
}

// This case tests scenarios in which a display ID is searched across multiple
// DRM devices, such as in DisplayLink hubs.
TEST_F(DrmGpuDisplayManagerTest,
       FindAndConfigureDisplaysAcrossDifferentDrmDevices) {
  // Add 3 DRM devices, each with one active display.
  for (size_t i = 0; i < 3; ++i) {
    auto fake_drm = AddDrmDevice();
    fake_drm->ResetStateWithAllProperties();

    fake_drm->AddCrtcWithPrimaryAndCursorPlanes();

    auto& encoder = fake_drm->AddEncoder();
    encoder.possible_crtcs = 0b1;

    auto& connector = fake_drm->AddConnector();
    connector.connection = true;
    connector.modes = std::vector<ResolutionAndRefreshRate>{
        kStandardModes[i % kStandardModes.size()]};
    connector.encoders = std::vector<uint32_t>{encoder.id};
    connector.edid_blob =
        std::vector<uint8_t>(kHPz32x, kHPz32x + kHPz32xLength);

    fake_drm->InitializeState(/* use_atomic */ true);
  }

  MovableDisplaySnapshots display_snapshots =
      drm_gpu_display_manager_->GetDisplays();
  ASSERT_EQ(display_snapshots.size(), 3u);

  // Make sure the displays are successfully found across all existing DRM
  // devices, and that the returned devices are indeed different.
  DrmDisplay* display1 = FindDisplay(display_snapshots[0]->display_id());
  ASSERT_TRUE(display1);

  DrmDisplay* display2 = FindDisplay(display_snapshots[1]->display_id());
  ASSERT_TRUE(display2);

  DrmDisplay* display3 = FindDisplay(display_snapshots[2]->display_id());
  ASSERT_TRUE(display3);

  ASSERT_NE(display1->drm().get(), display2->drm().get());
  ASSERT_NE(display1->drm().get(), display3->drm().get());
  ASSERT_NE(display2->drm().get(), display3->drm().get());

  // Make sure configuration on the returned snapshots is possible.
  ASSERT_TRUE(ConfigureDisplays(display_snapshots,
                                {display::ModesetFlag::kTestModeset,
                                 display::ModesetFlag::kCommitModeset}));
}

TEST_F(DrmGpuDisplayManagerTest,
       OriginsPersistThroughSimilarExtendedModeConfigurations) {
  // One DRM device.
  auto fake_drm = AddDrmDevice();
  fake_drm->ResetStateWithAllProperties();

  // Add three connectors, each with one active display.
  for (size_t i = 0; i < 3; ++i) {
    // Add 1 CRTC
    fake_drm->AddCrtcWithPrimaryAndCursorPlanes();

    // Add one encoder
    auto& encoder = fake_drm->AddEncoder();
    encoder.possible_crtcs = 1 << i;

    // Add 1 Connector
    auto& connector = fake_drm->AddConnector();
    connector.connection = true;
    connector.modes = std::vector<ResolutionAndRefreshRate>{kStandardModes[0]};
    connector.encoders = std::vector<uint32_t>{encoder.id};
    connector.edid_blob =
        std::vector<uint8_t>(kHPz32x, kHPz32x + kHPz32xLength);
  }
  fake_drm->InitializeState(/* use_atomic */ true);

  auto display_snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_EQ(display_snapshots.size(), 3u);

  // Set the first set of Display's origins to imitate the process that happens
  // in the DisplayConfigurator during a full display configuration. Moving on,
  // these origins should persist while moving between extended and SW mirror
  // mode, since they're both technically the same (i.e. extended mode).
  DrmDisplay* display1 = FindDisplay(display_snapshots[0]->display_id());
  ASSERT_TRUE(display1);
  const gfx::Point display1_origin = gfx::Point(0, 0);
  display_snapshots[0]->set_origin(display1_origin);
  display1->SetOrigin(display_snapshots[0]->origin());

  DrmDisplay* display2 = FindDisplay(display_snapshots[1]->display_id());
  ASSERT_TRUE(display2);
  const gfx::Point display2_origin =
      gfx::Point(0, display_snapshots[0]->native_mode()->size().height());
  display_snapshots[1]->set_origin(display2_origin);
  display2->SetOrigin(display_snapshots[1]->origin());

  DrmDisplay* display3 = FindDisplay(display_snapshots[2]->display_id());
  ASSERT_TRUE(display3);
  const gfx::Point display3_origin =
      gfx::Point(0, display_snapshots[0]->native_mode()->size().height() +
                        display_snapshots[1]->native_mode()->size().height());
  display_snapshots[2]->set_origin(display3_origin);
  display3->SetOrigin(display_snapshots[2]->origin());

  ASSERT_TRUE(ConfigureDisplays(display_snapshots,
                                {display::ModesetFlag::kTestModeset,
                                 display::ModesetFlag::kCommitModeset}));

  // "Switch" to mirror mode - so nothing changes as far as display resources
  // go.
  display_snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_TRUE(!display_snapshots.empty());

  display1 = FindDisplay(display_snapshots[0]->display_id());
  ASSERT_TRUE(display1);
  ASSERT_EQ(display1->origin(), display_snapshots[0]->origin());
  ASSERT_EQ(display1->origin(), display1_origin);

  display2 = FindDisplay(display_snapshots[1]->display_id());
  ASSERT_TRUE(display2);
  ASSERT_EQ(display2->origin(), display_snapshots[1]->origin());
  ASSERT_EQ(display2->origin(), display2_origin);

  display3 = FindDisplay(display_snapshots[2]->display_id());
  ASSERT_TRUE(display3);
  ASSERT_EQ(display3->origin(), display_snapshots[2]->origin());
  ASSERT_EQ(display3->origin(), display3_origin);

  ASSERT_TRUE(ConfigureDisplays(display_snapshots,
                                {display::ModesetFlag::kTestModeset,
                                 display::ModesetFlag::kCommitModeset}));
}

TEST_F(DrmGpuDisplayManagerTest, TestEdidIdConflictResolution) {
  // One DRM device.
  auto fake_drm = AddDrmDevice();
  fake_drm->ResetStateWithAllProperties();

  // First, add the internal display.
  {
    fake_drm->AddCrtcWithPrimaryAndCursorPlanes();

    auto& encoder = fake_drm->AddEncoder();
    encoder.possible_crtcs = 0b1;

    auto& connector = fake_drm->AddConnector();
    connector.connection = true;
    connector.modes = std::vector<ResolutionAndRefreshRate>{kStandardModes[3]};
    connector.encoders = std::vector<uint32_t>{encoder.id};
    connector.edid_blob = std::vector<uint8_t>(
        kInternalDisplay, kInternalDisplay + kInternalDisplayLength);
  }

  // Next, add two external displays that will produce an EDID-based ID
  // collision, since their EDIDs do not include viable serial numbers.
  {
    fake_drm->AddCrtcWithPrimaryAndCursorPlanes();

    auto& encoder = fake_drm->AddEncoder();
    encoder.possible_crtcs = 0b10;

    auto& connector = fake_drm->AddConnector();
    connector.connection = true;
    connector.modes = kStandardModes;
    connector.encoders = std::vector<uint32_t>{encoder.id};
    connector.edid_blob = std::vector<uint8_t>(
        kNoSerialNumberDisplay,
        kNoSerialNumberDisplay + kNoSerialNumberDisplayLength);
  }

  {
    fake_drm->AddCrtcWithPrimaryAndCursorPlanes();

    auto& encoder = fake_drm->AddEncoder();
    encoder.possible_crtcs = 0b100;

    auto& connector = fake_drm->AddConnector();
    connector.connection = true;
    connector.modes = kStandardModes;
    connector.encoders = std::vector<uint32_t>{encoder.id};
    connector.edid_blob = std::vector<uint8_t>(
        kNoSerialNumberDisplay,
        kNoSerialNumberDisplay + kNoSerialNumberDisplayLength);
  }

  fake_drm->InitializeState(/* use_atomic */ true);

  auto display_snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_EQ(display_snapshots.size(), 3u);

  // First, ensure all display IDs are unique.
  ASSERT_NE(display_snapshots[0]->edid_display_id(),
            display_snapshots[1]->edid_display_id());
  ASSERT_NE(display_snapshots[0]->edid_display_id(),
            display_snapshots[2]->edid_display_id());
  ASSERT_NE(display_snapshots[1]->edid_display_id(),
            display_snapshots[2]->edid_display_id());

  // The EDID-based display IDs occupy the first 32 bits of the id field.
  // Extract unresolved EDID IDs from the snapshots and show that they are
  // equal, and thus have been successfully resolved.
  const int64_t unresolved_display_id1 =
      display_snapshots[1]->edid_display_id() & 0xffffffff;
  const int64_t unresolved_display_id2 =
      display_snapshots[2]->edid_display_id() & 0xffffffff;
  ASSERT_EQ(unresolved_display_id1, unresolved_display_id2);
}

class DrmGpuDisplayManagerMockedDeviceTest : public DrmGpuDisplayManagerTest {
 protected:
  void SetUp() override {
    std::unique_ptr<MockDrmDeviceGenerator> fake_device_generator =
        std::make_unique<MockDrmDeviceGenerator>();
    device_manager_ =
        std::make_unique<DrmDeviceManager>(std::move(fake_device_generator));
    screen_manager_ = std::make_unique<ScreenManager>();
    drm_gpu_display_manager_ = std::make_unique<DrmGpuDisplayManager>(
        screen_manager_.get(), device_manager_.get());
  }
};

TEST_F(DrmGpuDisplayManagerMockedDeviceTest,
       ConfigureDisplaysAlternateCrtcFallbackSuccess) {
  auto drm = AddDrmDevice();
  drm->ResetStateWithAllProperties();

  // Create a pool of 3 CRTCs
  const uint32_t crtc_1 = drm->AddCrtcWithPrimaryAndCursorPlanes().id;
  drm->AddCrtcWithPrimaryAndCursorPlanes();
  const uint32_t crtc_3 = drm->AddCrtcWithPrimaryAndCursorPlanes().id;

  uint32_t big_display_connector_id, small_display_connector_id;

  // First, add a "big" display with high bandwidth mode.
  {
    auto& encoder = drm->AddEncoder();
    // Can use all 3 CRTCs
    encoder.possible_crtcs = 0b111;

    auto& connector = drm->AddConnector();
    connector.connection = true;
    connector.modes = std::vector<ResolutionAndRefreshRate>{
        ResolutionAndRefreshRate{gfx::Size(7680, 4320), 144u}};
    connector.encoders = std::vector<uint32_t>{encoder.id};

    big_display_connector_id = connector.id;
  }
  // Add a "small" external display.
  {
    auto& encoder = drm->AddEncoder();
    encoder.possible_crtcs = 0b111;

    auto& connector = drm->AddConnector();
    connector.connection = true;
    connector.modes = kStandardModes;
    connector.encoders = std::vector<uint32_t>{encoder.id};

    small_display_connector_id = connector.id;
  }

  drm->InitializeState(/* use_atomic */ true);
  MockDrmDevice* mock_drm = static_cast<MockDrmDevice*>(drm.get());

  auto display_snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_EQ(display_snapshots.size(), 2u);

  DrmDisplay* big_display = FindDisplayByConnectorId(big_display_connector_id);
  const DrmDisplay::CrtcConnectorPair*
      original_big_display_crtc_connector_pair =
          big_display->GetCrtcConnectorPairForConnectorId(
              big_display_connector_id);
  ASSERT_NE(original_big_display_crtc_connector_pair, nullptr);

  DrmDisplay* small_display =
      FindDisplayByConnectorId(small_display_connector_id);
  const DrmDisplay::CrtcConnectorPair*
      original_small_display_crtc_connector_pair =
          small_display->GetCrtcConnectorPairForConnectorId(
              small_display_connector_id);
  ASSERT_NE(original_small_display_crtc_connector_pair, nullptr);

  // Modesets should fail by default, unless it is with CRTC-connector pairings
  // specified with |desired_pairings|.
  EXPECT_CALL(*mock_drm, CommitProperties)
      .Times(AtLeast(1))
      .WillRepeatedly(Return(false));

  CrtcConnectorPairs desired_pairings = {{crtc_1, big_display_connector_id},
                                         {crtc_3, small_display_connector_id}};
  EXPECT_CALL(
      *mock_drm,
      CommitProperties(AtomicRequestHasCrtcConnectorPairs(desired_pairings), _,
                       _, _))
      .WillOnce(Return(true));

  EXPECT_TRUE(ConfigureDisplays(display_snapshots,
                                {display::ModesetFlag::kTestModeset}));

  histogram_tester_.ExpectBucketCount(kTestOnlyModesetOutcomeTwoDisplays,
                                      TestOnlyModesetOutcome::kFallbackSuccess,
                                      /*expected_count=*/1);
  EXPECT_THAT(histogram_tester_.GetAllSamples(
                  kTestOnlyModesetFallbacksAttemptedTwoDisplaysMetric),
              UnorderedElementsAre(AllOf(Field(&base::Bucket::min, Gt(0)),
                                         Field(&base::Bucket::count, Eq(1)))));

  // Even if there is a successful fallback configuration, ozone abstractions
  // should not change for test modeset request.
  big_display = FindDisplayByConnectorId(big_display_connector_id);
  EXPECT_EQ(
      big_display->GetCrtcConnectorPairForConnectorId(big_display_connector_id)
          ->crtc_id,
      original_big_display_crtc_connector_pair->crtc_id);
  small_display = FindDisplayByConnectorId(small_display_connector_id);
  EXPECT_EQ(small_display
                ->GetCrtcConnectorPairForConnectorId(small_display_connector_id)
                ->crtc_id,
            original_small_display_crtc_connector_pair->crtc_id);

  // Check that commit modeset after fallback changes the CRTC assignment for
  // displays.
  ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(mock_drm));
  EXPECT_CALL(
      *mock_drm,
      CommitProperties(AtomicRequestHasCrtcConnectorPairs(desired_pairings), _,
                       _, _))
      .WillOnce(Return(true));

  EXPECT_TRUE(ConfigureDisplays(display_snapshots,
                                {display::ModesetFlag::kCommitModeset}));
  EXPECT_TRUE(big_display->ContainsCrtc(crtc_1));
  EXPECT_TRUE(small_display->ContainsCrtc(crtc_3));

  // DrmDevice seems to leak on successful configure in tests. Manually
  // checking for mock calls and allowing leak for now.
  ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(mock_drm));
  testing::Mock::AllowLeak(mock_drm);
}

TEST_F(DrmGpuDisplayManagerMockedDeviceTest,
       ConfigureDisplaysFallbackTestSuccessButCommitFailure) {
  auto drm = AddDrmDevice();
  drm->ResetStateWithAllProperties();

  // Create a pool of 3 CRTCs
  const uint32_t crtc_1 = drm->AddCrtcWithPrimaryAndCursorPlanes().id;
  drm->AddCrtcWithPrimaryAndCursorPlanes();
  const uint32_t crtc_3 = drm->AddCrtcWithPrimaryAndCursorPlanes().id;

  uint32_t primary_connector_id, secondary_connector_id;

  // First, add a display with high bandwidth mode.
  {
    auto& encoder = drm->AddEncoder();
    // Can use all 3 CRTCs
    encoder.possible_crtcs = 0b111;

    auto& connector = drm->AddConnector();
    connector.connection = true;
    connector.modes = std::vector<ResolutionAndRefreshRate>{
        ResolutionAndRefreshRate{gfx::Size(7680, 4320), 144u}};
    connector.encoders = std::vector<uint32_t>{encoder.id};

    primary_connector_id = connector.id;
  }
  // Add a normal external display.
  {
    auto& encoder = drm->AddEncoder();
    encoder.possible_crtcs = 0b111;

    auto& connector = drm->AddConnector();
    connector.connection = true;
    connector.modes = kStandardModes;
    connector.encoders = std::vector<uint32_t>{encoder.id};

    secondary_connector_id = connector.id;
  }

  drm->InitializeState(/* use_atomic */ true);
  MockDrmDevice* mock_drm = static_cast<MockDrmDevice*>(drm.get());

  auto display_snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_EQ(display_snapshots.size(), 2u);

  // Modesets should fail by default, unless it is with CRTC-connector pairings
  // specified with |desired_pairings|.
  EXPECT_CALL(*mock_drm, CommitProperties)
      .Times(AtLeast(1))
      .WillRepeatedly(Return(false));

  CrtcConnectorPairs desired_pairings = {{crtc_1, primary_connector_id},
                                         {crtc_3, secondary_connector_id}};
  EXPECT_CALL(
      *mock_drm,
      CommitProperties(AtomicRequestHasCrtcConnectorPairs(desired_pairings), _,
                       _, _))
      .WillRepeatedly(Return(true));

  // test should succeed.
  EXPECT_TRUE(ConfigureDisplays(display_snapshots,
                                {display::ModesetFlag::kTestModeset}));

  histogram_tester_.ExpectBucketCount(kTestOnlyModesetOutcomeTwoDisplays,
                                      TestOnlyModesetOutcome::kFallbackSuccess,
                                      /*expected_count=*/1);
  EXPECT_THAT(histogram_tester_.GetAllSamples(
                  kTestOnlyModesetFallbacksAttemptedTwoDisplaysMetric),
              UnorderedElementsAre(AllOf(Field(&base::Bucket::min, Gt(0)),
                                         Field(&base::Bucket::count, Eq(1)))));

  EXPECT_CALL(
      *mock_drm,
      CommitProperties(AtomicRequestHasCrtcConnectorPairs(desired_pairings),
                       // For non-test.
                       DRM_MODE_ATOMIC_ALLOW_MODESET, _, _))
      // Return false - even though testing with the exact same pairings
      // succeeded.
      .WillRepeatedly(Return(false));
  // Commit with exact config should fail.
  EXPECT_FALSE(ConfigureDisplays(display_snapshots,
                                 {display::ModesetFlag::kCommitModeset}));

  DrmDisplay* primary_display = FindDisplayByConnectorId(primary_connector_id);
  EXPECT_TRUE(primary_display->ContainsCrtc(crtc_1));
  DrmDisplay* secondary_display =
      FindDisplayByConnectorId(secondary_connector_id);
  EXPECT_TRUE(secondary_display->ContainsCrtc(crtc_3));
  histogram_tester_.ExpectBucketCount(kTestOnlyModesetOutcomeTwoDisplays,
                                      TestOnlyModesetOutcome::kFailure,
                                      /*expected_count=*/0);
}

TEST_F(DrmGpuDisplayManagerMockedDeviceTest,
       ConfigureDisplaysAlternateCrtcFallbackAllFailed) {
  auto drm = AddDrmDevice();
  drm->ResetStateWithAllProperties();

  // Create a pool of 3 CRTCs
  drm->AddCrtcWithPrimaryAndCursorPlanes();
  drm->AddCrtcWithPrimaryAndCursorPlanes();
  drm->AddCrtcWithPrimaryAndCursorPlanes();

  // First, add a display with high bandwidth mode.
  {
    auto& encoder = drm->AddEncoder();
    // Can use all 3 CRTCs
    encoder.possible_crtcs = 0b111;

    auto& connector = drm->AddConnector();
    connector.connection = true;
    connector.modes = std::vector<ResolutionAndRefreshRate>{
        ResolutionAndRefreshRate{gfx::Size(7680, 4320), 144u}};
    connector.encoders = std::vector<uint32_t>{encoder.id};
  }
  // Add a normal external display.
  {
    auto& encoder = drm->AddEncoder();
    encoder.possible_crtcs = 0b111;

    auto& connector = drm->AddConnector();
    connector.connection = true;
    connector.modes = kStandardModes;
    connector.encoders = std::vector<uint32_t>{encoder.id};
  }

  drm->InitializeState(/* use_atomic */ true);
  MockDrmDevice* mock_drm = static_cast<MockDrmDevice*>(drm.get());

  auto display_snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_EQ(display_snapshots.size(), 2u);
  EXPECT_CALL(*mock_drm, CommitProperties)
      // Expect at least 4 calls to ensure fallback is being triggered (2 from
      // the initial linear/preferred modifier test modeset, and another 2 for
      // the first fallback).
      .Times(AtLeast(4))
      .WillRepeatedly(Return(false));

  EXPECT_FALSE(ConfigureDisplays(display_snapshots,
                                 {display::ModesetFlag::kTestModeset}));
  histogram_tester_.ExpectBucketCount(kTestOnlyModesetOutcomeTwoDisplays,
                                      TestOnlyModesetOutcome::kFailure,
                                      /*expected_count=*/1);
  EXPECT_THAT(histogram_tester_.GetAllSamples(
                  kTestOnlyModesetFallbacksAttemptedTwoDisplaysMetric),
              UnorderedElementsAre(AllOf(Field(&base::Bucket::min, Gt(0)),
                                         Field(&base::Bucket::count, Eq(1)))));
}

TEST_F(DrmGpuDisplayManagerMockedDeviceTest,
       NoConfigureDisplaysAlternateCrtcFallbackWithHardwareMirroring) {
  // Enable hardware mirroring
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(
      display::features::kEnableHardwareMirrorMode);
  ASSERT_TRUE(display::features::IsHardwareMirrorModeEnabled());
  // Re-initialize DrmGpuDisplayManager with HW mirroring enabled.
  drm_gpu_display_manager_ = std::make_unique<DrmGpuDisplayManager>(
      screen_manager_.get(), device_manager_.get());

  auto drm = AddDrmDevice();
  drm->ResetStateWithAllProperties();

  // Create a pool of 2 CRTCs
  drm->AddCrtcWithPrimaryAndCursorPlanes();
  drm->AddCrtcWithPrimaryAndCursorPlanes();

  {
    auto& encoder = drm->AddEncoder();
    encoder.possible_crtcs = 0b11;
    auto& connector = drm->AddConnector();
    connector.connection = true;
    connector.modes = std::vector<ResolutionAndRefreshRate>{
        ResolutionAndRefreshRate{gfx::Size(7680, 4320), 144u}};
    connector.encoders = std::vector<uint32_t>{encoder.id};
  }

  drm->InitializeState(/* use_atomic */ true);
  MockDrmDevice* mock_drm = static_cast<MockDrmDevice*>(drm.get());

  auto display_snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_EQ(display_snapshots.size(), 1u);

  // Test-modeset should only be called twice - without any fallbacks attempted.
  EXPECT_CALL(*mock_drm, CommitProperties)
      // Expect 2 calls as ScreenManager tests modeset with preferred modifiers
      // and then linear modifiers on failure.
      .Times(Exactly(2))
      .WillRepeatedly(Return(false));

  EXPECT_FALSE(ConfigureDisplays(display_snapshots,
                                 {display::ModesetFlag::kTestModeset}));
  histogram_tester_.ExpectBucketCount(kTestOnlyModesetOutcomeOneDisplay,
                                      TestOnlyModesetOutcome::kFailure,
                                      /*expected_count=*/1);
  EXPECT_THAT(histogram_tester_.GetAllSamples(
                  kTestOnlyModesetFallbacksAttemptedTwoDisplaysMetric),
              IsEmpty());
}

TEST_F(DrmGpuDisplayManagerMockedDeviceTest,
       ConfigureDisplaysUseSuccessfulStateForCommit) {
  auto drm = AddDrmDevice();
  drm->ResetStateWithAllProperties();

  // Create a pool of 3 CRTCs
  const uint32_t crtc_1 = drm->AddCrtcWithPrimaryAndCursorPlanes().id;
  drm->AddCrtcWithPrimaryAndCursorPlanes();
  const uint32_t crtc_3 = drm->AddCrtcWithPrimaryAndCursorPlanes().id;

  uint32_t primary_connector_id, secondary_connector_id;

  // First, add a display with high bandwidth mode.
  {
    auto& encoder = drm->AddEncoder();
    // Can use all 3 CRTCs
    encoder.possible_crtcs = 0b111;

    auto& connector = drm->AddConnector();
    connector.connection = true;
    connector.modes = std::vector<ResolutionAndRefreshRate>{
        ResolutionAndRefreshRate{gfx::Size(7680, 4320), 144u}};
    connector.encoders = std::vector<uint32_t>{encoder.id};

    primary_connector_id = connector.id;
  }
  // Add a normal external display.
  {
    auto& encoder = drm->AddEncoder();
    encoder.possible_crtcs = 0b111;

    auto& connector = drm->AddConnector();
    connector.connection = true;
    connector.modes = kStandardModes;
    connector.encoders = std::vector<uint32_t>{encoder.id};

    secondary_connector_id = connector.id;
  }

  drm->InitializeState(/* use_atomic */ true);
  MockDrmDevice* mock_drm = static_cast<MockDrmDevice*>(drm.get());

  auto display_snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_EQ(display_snapshots.size(), 2u);

  CrtcConnectorPairs desired_pairings = {{crtc_1, primary_connector_id},
                                         {crtc_3, secondary_connector_id}};
  // First test modeset should fallback and succeed on |desired_pairings|.
  {
    // Modesets should fail by default, unless it is with CRTC-connector
    // pairings specified with |desired_pairings|.
    EXPECT_CALL(*mock_drm, CommitProperties)
        .Times(AtLeast(1))
        .WillRepeatedly(Return(false));

    EXPECT_CALL(
        *mock_drm,
        CommitProperties(AtomicRequestHasCrtcConnectorPairs(desired_pairings),
                         _, _, _))
        .WillOnce(Return(true));

    EXPECT_TRUE(ConfigureDisplays(display_snapshots,
                                  {display::ModesetFlag::kTestModeset}));

    histogram_tester_.ExpectBucketCount(
        kTestOnlyModesetOutcomeTwoDisplays,
        TestOnlyModesetOutcome::kFallbackSuccess,
        /*expected_count=*/1);
    EXPECT_THAT(
        histogram_tester_.GetAllSamples(
            kTestOnlyModesetFallbacksAttemptedTwoDisplaysMetric),
        UnorderedElementsAre(AllOf(Field(&base::Bucket::min, Gt(0)),
                                   Field(&base::Bucket::count, Eq(1)))));
    ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(mock_drm));
  }
  // Second test modeset with different config should fail, and leave the
  // DrmGpuDisplayManager with a failed CRTC-connector pairings.
  {
    std::vector<display::DisplayConfigurationParams> failing_request;
    for (const auto& snapshot : display_snapshots) {
      failing_request.emplace_back(snapshot->display_id(), snapshot->origin(),
                                   snapshot->modes().back().get());
    }

    std::vector<display::DisplayConfigurationParams> out_requests;
    EXPECT_CALL(*mock_drm, CommitProperties)
        .Times(AtLeast(1))
        .WillRepeatedly(Return(false));
    EXPECT_FALSE(drm_gpu_display_manager_->ConfigureDisplays(
        failing_request, {display::ModesetFlag::kTestModeset}, out_requests));

    histogram_tester_.ExpectBucketCount(kTestOnlyModesetOutcomeTwoDisplays,
                                        TestOnlyModesetOutcome::kFailure,
                                        /*expected_count=*/1);
    EXPECT_THAT(
        histogram_tester_.GetAllSamples(
            kTestOnlyModesetFallbacksAttemptedTwoDisplaysMetric),
        UnorderedElementsAre(AllOf(Field(&base::Bucket::min, Gt(0)),
                                   Field(&base::Bucket::count, Eq(1))),
                             AllOf(Field(&base::Bucket::min, Gt(0)),
                                   Field(&base::Bucket::count, Eq(1)))));
    ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(mock_drm));
  }
  // A commit call made with previously successful config (first config) should
  // restore the abstractions back to the state of its success.
  {
    // Only commit modeset should be called once.
    EXPECT_CALL(*mock_drm, CommitProperties).Times(0);
    EXPECT_CALL(
        *mock_drm,
        CommitProperties(AtomicRequestHasCrtcConnectorPairs(desired_pairings),
                         _, _, _))
        .WillOnce(Return(true));

    EXPECT_TRUE(ConfigureDisplays(display_snapshots,
                                  {display::ModesetFlag::kCommitModeset}));
  }

  DrmDisplay* primary_display = FindDisplayByConnectorId(primary_connector_id);
  EXPECT_TRUE(primary_display->ContainsCrtc(crtc_1));
  DrmDisplay* secondary_display =
      FindDisplayByConnectorId(secondary_connector_id);
  EXPECT_TRUE(secondary_display->ContainsCrtc(crtc_3));
}

// DrmGpuDisplayManagerGetSeamlessRefreshRateTest is a test fixture for tests
// related to testing seamless refresh rates.
class DrmGpuDisplayManagerGetSeamlessRefreshRateTest
    : public DrmGpuDisplayManagerMockedDeviceTest {
 protected:
  void SetUp() override {
    DrmGpuDisplayManagerMockedDeviceTest::SetUp();

    fake_drm_device_ = AddDrmDevice();
    fake_drm_device_->ResetStateWithAllProperties();

    // Add internal display with a possible downclock mode and without VRR
    // capability.
    {
      fake_drm_device_->AddCrtcWithPrimaryAndCursorPlanes();

      auto& encoder = fake_drm_device_->AddEncoder();
      encoder.possible_crtcs = 0b1;

      auto& connector = fake_drm_device_->AddConnector();
      connector.connection = true;
      connector.modes = {
          // native mode.
          ResolutionAndRefreshRate{gfx::Size(3840, 2160), 120u},
          // downclock mode.
          ResolutionAndRefreshRate{gfx::Size(3840, 2160), 60u},
      };
      connector.encoders = std::vector<uint32_t>{encoder.id};
      connector.edid_blob = std::vector<uint8_t>(
          kInternalDisplay, kInternalDisplay + kInternalDisplayLength);
    }

    // Add external display with a possible downclock mode and with VRR
    // capability.
    {
      fake_drm_device_->AddCrtcWithPrimaryAndCursorPlanes();

      auto& encoder = fake_drm_device_->AddEncoder();
      encoder.possible_crtcs = 0b10;

      auto& connector = fake_drm_device_->AddConnector();
      connector.connection = true;
      connector.modes = {
          // native mode.
          ResolutionAndRefreshRate{gfx::Size(3840, 2160), 120u},
          // downclock mode.
          ResolutionAndRefreshRate{gfx::Size(3840, 2160), 60u},
      };
      connector.encoders = std::vector<uint32_t>{encoder.id};
      // Use HPz32x because it sets vsync_rate_min=24Hz, which is required for
      // VRR capability.
      connector.edid_blob =
          std::vector<uint8_t>(kHPz32x, kHPz32x + kHPz32xLength);
      fake_drm_device_->AddProperty(connector.id,
                                    {.id = kVrrCapablePropId, .value = 1});
    }

    fake_drm_device_->InitializeState(/* use_atomic */ true);
    mock_drm_device_ = static_cast<MockDrmDevice*>(fake_drm_device_.get());

    // Do an initial configuration of the display.
    auto display_snapshots = drm_gpu_display_manager_->GetDisplays();
    CHECK_EQ(display_snapshots.size(), 2u);
    CHECK(ConfigureDisplays(display_snapshots,
                            {display::ModesetFlag::kCommitModeset}));
  }

  scoped_refptr<FakeDrmDevice> fake_drm_device_;
  raw_ptr<MockDrmDevice> mock_drm_device_;
};

TEST_F(DrmGpuDisplayManagerGetSeamlessRefreshRateTest,
       GetSeamlessRefreshRates) {
  // Get the display id for the display to probe.
  auto display_snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_TRUE(!display_snapshots.empty());
  int64_t display_id = display_snapshots[0]->display_id();

  // Expect two test commits to correspond to the two modes with
  // the same visible size as the currently configured mode.
  const uint32_t seamless_test_flags = DRM_MODE_ATOMIC_TEST_ONLY;
  EXPECT_CALL(*mock_drm_device_, CommitProperties(_, seamless_test_flags, _, _))
      .Times(2)
      .WillRepeatedly(Return(true));

  std::optional<std::vector<float>> refresh_rates =
      drm_gpu_display_manager_->GetSeamlessRefreshRates(display_id);
  ASSERT_TRUE(refresh_rates);
  EXPECT_EQ(refresh_rates->size(), 2u);
  EXPECT_EQ(std::count(refresh_rates->begin(), refresh_rates->end(), 120u), 1);
  EXPECT_EQ(std::count(refresh_rates->begin(), refresh_rates->end(), 60u), 1);
}

TEST_F(DrmGpuDisplayManagerGetSeamlessRefreshRateTest,
       GetSeamlessRefreshRatesWithInvalidId) {
  // Return std::nullopt for invalid display id.
  const int64_t wrong_display_id = 42;
  EXPECT_CALL(*mock_drm_device_, CommitProperties(_, _, _, _)).Times(0);
  std::optional<std::vector<float>> refresh_rates =
      drm_gpu_display_manager_->GetSeamlessRefreshRates(wrong_display_id);

  ASSERT_FALSE(refresh_rates);
}

TEST_F(DrmGpuDisplayManagerGetSeamlessRefreshRateTest,
       ConfigureDisplaysModeMatching_UnsetMode) {
  const auto snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_FALSE(snapshots.empty());
  const auto& snapshot = snapshots[0];
  const display::ModesetFlags flags = {display::ModesetFlag::kTestModeset};
  const std::vector<display::DisplayConfigurationParams> config_requests = {
      display::DisplayConfigurationParams(snapshot->display_id(),
                                          snapshot->origin(), nullptr)};

  std::vector<display::DisplayConfigurationParams> out_requests;
  EXPECT_TRUE(drm_gpu_display_manager_->ConfigureDisplays(config_requests,
                                                          flags, out_requests));
  EXPECT_EQ(config_requests, out_requests);
}

TEST_F(DrmGpuDisplayManagerGetSeamlessRefreshRateTest,
       ConfigureDisplaysModeMatching_ExistingMode) {
  const auto snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_FALSE(snapshots.empty());
  const auto& snapshot = snapshots[0];
  const display::ModesetFlags flags = {display::ModesetFlag::kTestModeset};
  const std::vector<display::DisplayConfigurationParams> config_requests = {
      display::DisplayConfigurationParams(
          snapshot->display_id(), snapshot->origin(), snapshot->native_mode())};

  std::vector<display::DisplayConfigurationParams> out_requests;
  EXPECT_TRUE(drm_gpu_display_manager_->ConfigureDisplays(config_requests,
                                                          flags, out_requests));
  EXPECT_EQ(config_requests, out_requests);
}

TEST_F(DrmGpuDisplayManagerGetSeamlessRefreshRateTest,
       ConfigureDisplaysModeMatching_NonExistingMode) {
  const auto snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_FALSE(snapshots.empty());
  const auto& snapshot = snapshots[0];
  const display::ModesetFlags flags = {display::ModesetFlag::kTestModeset};
  const display::DisplayMode nonmatching_mode(snapshot->native_mode()->size(),
                                              false, 600);
  const std::vector<display::DisplayConfigurationParams> config_requests = {
      display::DisplayConfigurationParams(
          snapshot->display_id(), snapshot->origin(), &nonmatching_mode)};

  std::vector<display::DisplayConfigurationParams> out_requests;
  EXPECT_FALSE(drm_gpu_display_manager_->ConfigureDisplays(
      config_requests, flags, out_requests));
  EXPECT_EQ(config_requests, out_requests);
}

TEST_F(DrmGpuDisplayManagerGetSeamlessRefreshRateTest,
       ConfigureDisplaysSeamlessModeMatching_UnsetMode) {
  const auto snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_FALSE(snapshots.empty());
  const auto& snapshot = snapshots[0];
  const display::ModesetFlags flags = {display::ModesetFlag::kTestModeset,
                                       display::ModesetFlag::kSeamlessModeset};
  const std::vector<display::DisplayConfigurationParams> config_requests = {
      display::DisplayConfigurationParams(snapshot->display_id(),
                                          snapshot->origin(), nullptr)};

  std::vector<display::DisplayConfigurationParams> out_requests;
  EXPECT_TRUE(drm_gpu_display_manager_->ConfigureDisplays(config_requests,
                                                          flags, out_requests));
  EXPECT_EQ(config_requests, out_requests);
}

TEST_F(DrmGpuDisplayManagerGetSeamlessRefreshRateTest,
       ConfigureDisplaysSeamlessModeMatching_ExistingSeamlessMode) {
  const auto snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_FALSE(snapshots.empty());
  const auto& snapshot = snapshots[0];
  const display::ModesetFlags flags = {display::ModesetFlag::kTestModeset,
                                       display::ModesetFlag::kSeamlessModeset};
  const std::vector<display::DisplayConfigurationParams> config_requests = {
      display::DisplayConfigurationParams(
          snapshot->display_id(), snapshot->origin(), snapshot->native_mode())};

  std::vector<display::DisplayConfigurationParams> out_requests;
  EXPECT_TRUE(drm_gpu_display_manager_->ConfigureDisplays(config_requests,
                                                          flags, out_requests));
  EXPECT_EQ(config_requests, out_requests);
}

TEST_F(
    DrmGpuDisplayManagerGetSeamlessRefreshRateTest,
    ConfigureDisplaysSeamlessModeMatching_ExistingModeFailingSeamlessVerification) {
  const auto snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_FALSE(snapshots.empty());
  const auto& snapshot = snapshots[0];
  const display::ModesetFlags flags = {display::ModesetFlag::kTestModeset,
                                       display::ModesetFlag::kSeamlessModeset};
  const display::DisplayMode* failing_mode = snapshot->modes().back().get();
  const std::vector<display::DisplayConfigurationParams> config_requests = {
      display::DisplayConfigurationParams(snapshot->display_id(),
                                          snapshot->origin(), failing_mode)};

  const uint32_t seamless_test_flags = DRM_MODE_ATOMIC_TEST_ONLY;
  std::vector<display::DisplayConfigurationParams> out_requests;
  // Override mock behavior to fail seamless verification.
  EXPECT_CALL(*mock_drm_device_, CommitProperties(_, seamless_test_flags, _, _))
      .Times(1)
      .WillOnce(Return(false));
  EXPECT_FALSE(drm_gpu_display_manager_->ConfigureDisplays(
      config_requests, flags, out_requests));
  EXPECT_EQ(config_requests, out_requests);
}

TEST_F(DrmGpuDisplayManagerGetSeamlessRefreshRateTest,
       ConfigureDisplaysSeamlessModeMatching_NonExistingMode) {
  const auto snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_FALSE(snapshots.empty());
  const auto& snapshot = snapshots[0];
  const display::ModesetFlags flags = {display::ModesetFlag::kTestModeset,
                                       display::ModesetFlag::kSeamlessModeset};
  const display::DisplayMode nonmatching_mode(snapshot->native_mode()->size(),
                                              false, 600);
  const std::vector<display::DisplayConfigurationParams> config_requests = {
      display::DisplayConfigurationParams(
          snapshot->display_id(), snapshot->origin(), &nonmatching_mode)};

  std::vector<display::DisplayConfigurationParams> out_requests;
  EXPECT_FALSE(drm_gpu_display_manager_->ConfigureDisplays(
      config_requests, flags, out_requests));
  EXPECT_EQ(config_requests, out_requests);
}

TEST_F(DrmGpuDisplayManagerGetSeamlessRefreshRateTest,
       ConfigureVrrDisplaysModeMatching_UnsetMode) {
  const auto snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_FALSE(snapshots.empty());
  const auto& snapshot = snapshots[1];
  const display::ModesetFlags flags = {display::ModesetFlag::kTestModeset};
  const std::vector<display::DisplayConfigurationParams> config_requests = {
      display::DisplayConfigurationParams(snapshot->display_id(),
                                          snapshot->origin(), nullptr)};

  std::vector<display::DisplayConfigurationParams> out_requests;
  EXPECT_TRUE(drm_gpu_display_manager_->ConfigureDisplays(config_requests,
                                                          flags, out_requests));
  EXPECT_EQ(config_requests, out_requests);
}

TEST_F(DrmGpuDisplayManagerGetSeamlessRefreshRateTest,
       ConfigureVrrDisplaysModeMatching_ExistingMode) {
  const auto snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_FALSE(snapshots.empty());
  const auto& snapshot = snapshots[1];
  const display::ModesetFlags flags = {display::ModesetFlag::kTestModeset};
  const std::vector<display::DisplayConfigurationParams> config_requests = {
      display::DisplayConfigurationParams(
          snapshot->display_id(), snapshot->origin(), snapshot->native_mode())};

  std::vector<display::DisplayConfigurationParams> out_requests;
  EXPECT_TRUE(drm_gpu_display_manager_->ConfigureDisplays(config_requests,
                                                          flags, out_requests));
  EXPECT_EQ(config_requests, out_requests);
}

TEST_F(DrmGpuDisplayManagerGetSeamlessRefreshRateTest,
       ConfigureVrrDisplaysModeMatching_NonExistingFasterMode) {
  const auto snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_FALSE(snapshots.empty());
  const auto& snapshot = snapshots[1];
  const display::ModesetFlags flags = {display::ModesetFlag::kTestModeset};
  const display::DisplayMode nonmatching_mode = display::DisplayMode(
      snapshot->native_mode()->size(), false, 600, std::nullopt);
  const std::vector<display::DisplayConfigurationParams> config_requests = {
      display::DisplayConfigurationParams(
          snapshot->display_id(), snapshot->origin(), &nonmatching_mode)};

  std::vector<display::DisplayConfigurationParams> out_requests;
  EXPECT_FALSE(drm_gpu_display_manager_->ConfigureDisplays(
      config_requests, flags, out_requests));
  EXPECT_EQ(config_requests, out_requests);
}

TEST_F(DrmGpuDisplayManagerGetSeamlessRefreshRateTest,
       ConfigureVrrDisplaysModeMatching_NonExistingSlowerMode) {
  const auto snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_FALSE(snapshots.empty());
  const auto& snapshot = snapshots[1];
  const display::ModesetFlags flags = {display::ModesetFlag::kTestModeset};
  const display::DisplayMode nonmatching_mode = display::DisplayMode(
      snapshot->native_mode()->size(), false, 51, std::nullopt);
  const std::vector<display::DisplayConfigurationParams> config_requests = {
      display::DisplayConfigurationParams(
          snapshot->display_id(), snapshot->origin(), &nonmatching_mode)};

  std::vector<display::DisplayConfigurationParams> out_requests;
  EXPECT_TRUE(drm_gpu_display_manager_->ConfigureDisplays(config_requests,
                                                          flags, out_requests));
  EXPECT_NE(config_requests, out_requests);
  ExpectEqualRequestsWithExceptions(config_requests, out_requests);
  EXPECT_EQ(51.00354f, out_requests[0].mode->refresh_rate());
  EXPECT_EQ(24.0f, out_requests[0].mode->vsync_rate_min());
}

TEST_F(DrmGpuDisplayManagerGetSeamlessRefreshRateTest,
       ConfigureVrrDisplaysModeMatching_NonExistingSlowerModeBelowVSyncMin) {
  const auto snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_FALSE(snapshots.empty());
  const auto& snapshot = snapshots[1];
  const display::ModesetFlags flags = {display::ModesetFlag::kTestModeset};
  const display::DisplayMode nonmatching_mode = display::DisplayMode(
      snapshot->native_mode()->size(), false, 20, std::nullopt);
  const std::vector<display::DisplayConfigurationParams> config_requests = {
      display::DisplayConfigurationParams(
          snapshot->display_id(), snapshot->origin(), &nonmatching_mode)};

  std::vector<display::DisplayConfigurationParams> out_requests;
  EXPECT_FALSE(drm_gpu_display_manager_->ConfigureDisplays(
      config_requests, flags, out_requests));
  EXPECT_EQ(config_requests, out_requests);
}

TEST_F(DrmGpuDisplayManagerGetSeamlessRefreshRateTest,
       ConfigureVrrDisplaysSeamlessModeMatching_UnsetMode) {
  const auto snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_FALSE(snapshots.empty());
  const auto& snapshot = snapshots[1];
  const display::ModesetFlags flags = {display::ModesetFlag::kTestModeset,
                                       display::ModesetFlag::kSeamlessModeset};
  const std::vector<display::DisplayConfigurationParams> config_requests = {
      display::DisplayConfigurationParams(snapshot->display_id(),
                                          snapshot->origin(), nullptr)};

  std::vector<display::DisplayConfigurationParams> out_requests;
  EXPECT_TRUE(drm_gpu_display_manager_->ConfigureDisplays(config_requests,
                                                          flags, out_requests));
  EXPECT_EQ(config_requests, out_requests);
}

TEST_F(DrmGpuDisplayManagerGetSeamlessRefreshRateTest,
       ConfigureVrrDisplaysSeamlessModeMatching_ExistingSeamlessMode) {
  const auto snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_FALSE(snapshots.empty());
  const auto& snapshot = snapshots[1];
  const display::ModesetFlags flags = {display::ModesetFlag::kTestModeset,
                                       display::ModesetFlag::kSeamlessModeset};
  const std::vector<display::DisplayConfigurationParams> config_requests = {
      display::DisplayConfigurationParams(
          snapshot->display_id(), snapshot->origin(), snapshot->native_mode())};

  std::vector<display::DisplayConfigurationParams> out_requests;
  EXPECT_TRUE(drm_gpu_display_manager_->ConfigureDisplays(config_requests,
                                                          flags, out_requests));
  EXPECT_EQ(config_requests, out_requests);
}

TEST_F(
    DrmGpuDisplayManagerGetSeamlessRefreshRateTest,
    ConfigureVrrDisplaysSeamlessModeMatching_ExistingModeFailingSeamlessVerification) {
  const auto snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_FALSE(snapshots.empty());
  const auto& snapshot = snapshots[1];
  const display::ModesetFlags flags = {display::ModesetFlag::kTestModeset,
                                       display::ModesetFlag::kSeamlessModeset};
  const display::DisplayMode* failing_mode = snapshot->modes().back().get();
  const std::vector<display::DisplayConfigurationParams> config_requests = {
      display::DisplayConfigurationParams(snapshot->display_id(),
                                          snapshot->origin(), failing_mode)};

  const uint32_t seamless_test_flags = DRM_MODE_ATOMIC_TEST_ONLY;
  std::vector<display::DisplayConfigurationParams> out_requests;
  // Override mock behavior to fail seamless verification.
  EXPECT_CALL(*mock_drm_device_, CommitProperties(_, seamless_test_flags, _, _))
      .Times(3)
      .WillRepeatedly(Return(false));
  EXPECT_FALSE(drm_gpu_display_manager_->ConfigureDisplays(
      config_requests, flags, out_requests));
  EXPECT_EQ(config_requests, out_requests);
}

TEST_F(DrmGpuDisplayManagerGetSeamlessRefreshRateTest,
       ConfigureVrrDisplaysSeamlessModeMatching_NonExistingFasterMode) {
  const auto snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_FALSE(snapshots.empty());
  const auto& snapshot = snapshots[1];
  const display::ModesetFlags flags = {display::ModesetFlag::kTestModeset,
                                       display::ModesetFlag::kSeamlessModeset};
  const display::DisplayMode nonmatching_mode = display::DisplayMode(
      snapshot->native_mode()->size(), false, 600, std::nullopt);
  const std::vector<display::DisplayConfigurationParams> config_requests = {
      display::DisplayConfigurationParams(
          snapshot->display_id(), snapshot->origin(), &nonmatching_mode)};

  std::vector<display::DisplayConfigurationParams> out_requests;
  EXPECT_FALSE(drm_gpu_display_manager_->ConfigureDisplays(
      config_requests, flags, out_requests));
  EXPECT_EQ(config_requests, out_requests);
}

TEST_F(DrmGpuDisplayManagerGetSeamlessRefreshRateTest,
       ConfigureVrrDisplaysSeamlessModeMatching_NonExistingSlowerMode) {
  const auto snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_FALSE(snapshots.empty());
  const auto& snapshot = snapshots[1];
  const display::ModesetFlags flags = {display::ModesetFlag::kTestModeset,
                                       display::ModesetFlag::kSeamlessModeset};
  const display::DisplayMode nonmatching_mode = display::DisplayMode(
      snapshot->native_mode()->size(), false, 50, std::nullopt);
  const std::vector<display::DisplayConfigurationParams> config_requests = {
      display::DisplayConfigurationParams(
          snapshot->display_id(), snapshot->origin(), &nonmatching_mode)};

  std::vector<display::DisplayConfigurationParams> out_requests;
  EXPECT_TRUE(drm_gpu_display_manager_->ConfigureDisplays(config_requests,
                                                          flags, out_requests));
  EXPECT_NE(config_requests, out_requests);
  ExpectEqualRequestsWithExceptions(config_requests, out_requests);
  EXPECT_EQ(50.0f, out_requests[0].mode->refresh_rate());
  EXPECT_EQ(24.0f, out_requests[0].mode->vsync_rate_min());
}

TEST_F(
    DrmGpuDisplayManagerGetSeamlessRefreshRateTest,
    ConfigureVrrDisplaysSeamlessModeMatching_NonExistingSlowerModeBelowVSyncMin) {
  const auto snapshots = drm_gpu_display_manager_->GetDisplays();
  ASSERT_FALSE(snapshots.empty());
  const auto& snapshot = snapshots[1];
  const display::ModesetFlags flags = {display::ModesetFlag::kTestModeset,
                                       display::ModesetFlag::kSeamlessModeset};
  const display::DisplayMode nonmatching_mode = display::DisplayMode(
      snapshot->native_mode()->size(), false, 20, std::nullopt);
  const std::vector<display::DisplayConfigurationParams> config_requests = {
      display::DisplayConfigurationParams(
          snapshot->display_id(), snapshot->origin(), &nonmatching_mode)};

  std::vector<display::DisplayConfigurationParams> out_requests;
  EXPECT_FALSE(drm_gpu_display_manager_->ConfigureDisplays(
      config_requests, flags, out_requests));
  EXPECT_EQ(config_requests, out_requests);
}

using TiledDisplayGetDisplaysTest = DrmGpuDisplayManagerTest;

TEST_F(TiledDisplayGetDisplaysTest, SingleTile) {
  auto fake_drm = AddDrmDevice();
  fake_drm->ResetStateWithAllProperties();

  // Add 1 CRTC
  fake_drm->AddCrtcWithPrimaryAndCursorPlanes();

  // Add one encoder
  auto& encoder = fake_drm->AddEncoder();
  encoder.possible_crtcs = 0b1;

  // Add 1 Connector with a tile blob.
  auto& connector = fake_drm->AddConnector();
  connector.connection = true;
  // One tile, one non-tile modes.
  connector.modes = std::vector<ResolutionAndRefreshRate>{
      {gfx::Size(3840, 4320), 60}, {gfx::Size(1920, 1080), 60}};
  connector.encoders = std::vector<uint32_t>{encoder.id};

  const TileProperty tile_property = {.group_id = 1,
                                      .scale_to_fit_display = true,
                                      .tile_size = gfx::Size(3840, 4320),
                                      .tile_layout = gfx::Size(2, 1),
                                      .location = gfx::Point(0, 0)};
  ScopedDrmPropertyBlob tile_property_blob =
      CreateTilePropertyBlob(*fake_drm, tile_property);
  fake_drm->AddProperty(
      connector.id, {.id = kTileBlobPropId, .value = tile_property_blob->id()});

  fake_drm->InitializeState(/* use_atomic */ true);

  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(display::features::kTiledDisplaySupport);
  ASSERT_TRUE(display::features::IsTiledDisplaySupportEnabled());

  MovableDisplaySnapshots displays = drm_gpu_display_manager_->GetDisplays();

  // Expect tiled modes to be pruned when not all tiles in a group are present.
  ASSERT_THAT(displays, SizeIs(1));
  EXPECT_THAT(displays[0]->modes(),
              UnorderedElementsAre(
                  Pointee(EqResAndRefresh({gfx::Size(1920, 1080), 60}))));
}

TEST_F(TiledDisplayGetDisplaysTest, AllTilesPresent) {
  auto fake_drm = AddDrmDevice();
  fake_drm->ResetStateWithAllProperties();

  const TileProperty expected_primary_tile_prop = {
      .group_id = 1,
      .scale_to_fit_display = true,
      .tile_size = gfx::Size(3840, 4320),
      .tile_layout = gfx::Size(2, 1),
      .location = gfx::Point(0, 0)};

  // Primary tile at (0,0)
  ScopedDrmPropertyBlob primary_tile_property_blob =
      CreateTilePropertyBlob(*fake_drm, expected_primary_tile_prop);
  {
    fake_drm->AddCrtcWithPrimaryAndCursorPlanes();

    auto& primary_encoder = fake_drm->AddEncoder();
    primary_encoder.possible_crtcs = 0b1;

    auto& primary_connector = fake_drm->AddConnector();
    primary_connector.connection = true;
    primary_connector.modes = std::vector<ResolutionAndRefreshRate>{
        {gfx::Size(3840, 4320), 60}, {gfx::Size(1920, 1080), 60}};
    primary_connector.encoders = std::vector<uint32_t>{primary_encoder.id};
    fake_drm->AddProperty(
        primary_connector.id,
        {.id = kTileBlobPropId, .value = primary_tile_property_blob->id()});
  }

  // Non-primary tile at (0,1) - Identical to the primary tile except for tile
  // location.
  TileProperty expected_nonprimary_tile_prop = expected_primary_tile_prop;
  expected_nonprimary_tile_prop.location = gfx::Point(1, 0);
  ScopedDrmPropertyBlob nonprimary_tile_property_blob =
      CreateTilePropertyBlob(*fake_drm, expected_nonprimary_tile_prop);
  {
    fake_drm->AddCrtcWithPrimaryAndCursorPlanes();

    auto& nonprimary_encoder = fake_drm->AddEncoder();
    nonprimary_encoder.possible_crtcs = 0b10;

    auto& nonprimary_connector = fake_drm->AddConnector();
    nonprimary_connector.connection = true;
    nonprimary_connector.modes = std::vector<ResolutionAndRefreshRate>{
        {gfx::Size(3840, 4320), 60}, {gfx::Size(1920, 1080), 60}};
    nonprimary_connector.encoders =
        std::vector<uint32_t>{nonprimary_encoder.id};
    fake_drm->AddProperty(
        nonprimary_connector.id,
        {.id = kTileBlobPropId, .value = nonprimary_tile_property_blob->id()});
  }

  fake_drm->InitializeState(/* use_atomic */ true);

  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(display::features::kTiledDisplaySupport);
  ASSERT_TRUE(display::features::IsTiledDisplaySupportEnabled());

  MovableDisplaySnapshots displays = drm_gpu_display_manager_->GetDisplays();

  // Expect the tiled mode to have composited resolution.
  ASSERT_THAT(displays, SizeIs(1));
  EXPECT_THAT(displays[0]->modes(),
              UnorderedElementsAre(
                  Pointee(EqResAndRefresh({gfx::Size(7680, 4320), 60})),
                  Pointee(EqResAndRefresh({gfx::Size(1920, 1080), 60}))));
}

TEST_F(TiledDisplayGetDisplaysTest, PrimaryCanStretchToFit) {
  auto fake_drm = AddDrmDevice();
  fake_drm->ResetStateWithAllProperties();

  // Primary tile at (1,0)
  const TileProperty expected_primary_tile_prop = {
      .group_id = 1,
      .scale_to_fit_display = true,
      .tile_size = gfx::Size(3840, 4320),
      .tile_layout = gfx::Size(2, 1),
      .location = gfx::Point(1, 0)};
  ScopedDrmPropertyBlob primary_tile_property_blob =
      CreateTilePropertyBlob(*fake_drm, expected_primary_tile_prop);
  {  // Add 1 CRTC
    fake_drm->AddCrtcWithPrimaryAndCursorPlanes();

    // Add an encoder
    auto& primary_encoder = fake_drm->AddEncoder();
    primary_encoder.possible_crtcs = 0b1;

    auto& primary_connector = fake_drm->AddConnector();
    primary_connector.connection = true;
    primary_connector.modes = std::vector<ResolutionAndRefreshRate>{
        {gfx::Size(3840, 4320), 60}, {gfx::Size(1920, 1080), 60}};
    primary_connector.encoders = std::vector<uint32_t>{primary_encoder.id};
    // |kTiledDisplay| contains tile display DisplayID extension block with
    // stretch to fit behavior.
    primary_connector.edid_blob = std::vector<uint8_t>(
        kTiledDisplay, kTiledDisplay + kTiledDisplayLength);
    fake_drm->AddProperty(
        primary_connector.id,
        {.id = kTileBlobPropId, .value = primary_tile_property_blob->id()});
  }

  // Non-primary tile at (0,0) - does not advertise stretch to fit behavior in
  // EDID.
  TileProperty expected_nonprimary_tile_prop = expected_primary_tile_prop;
  expected_nonprimary_tile_prop.scale_to_fit_display = false;
  expected_nonprimary_tile_prop.location = gfx::Point(0, 0);
  ScopedDrmPropertyBlob nonprimary_tile_property_blob =
      CreateTilePropertyBlob(*fake_drm, expected_nonprimary_tile_prop);
  {
    fake_drm->AddCrtcWithPrimaryAndCursorPlanes();

    auto& nonprimary_encoder = fake_drm->AddEncoder();
    nonprimary_encoder.possible_crtcs = 0b10;

    auto& nonprimary_connector = fake_drm->AddConnector();
    nonprimary_connector.connection = true;
    nonprimary_connector.modes = std::vector<ResolutionAndRefreshRate>{
        {gfx::Size(3840, 4320), 60}, {gfx::Size(1920, 1080), 60}};
    nonprimary_connector.encoders =
        std::vector<uint32_t>{nonprimary_encoder.id};
    // Only TileProperty::scale_to_fit is parsed by the EdidParser, meaning that
    // any EDID ID that doesn't have that bit would suffice for scale_to_fit to
    // be false.
    nonprimary_connector.edid_blob =
        std::vector<uint8_t>(kHPz32x, kHPz32x + kHPz32xLength);
    fake_drm->AddProperty(
        nonprimary_connector.id,
        {.id = kTileBlobPropId, .value = nonprimary_tile_property_blob->id()});
  }

  fake_drm->InitializeState(/* use_atomic */ true);

  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(display::features::kTiledDisplaySupport);
  ASSERT_TRUE(display::features::IsTiledDisplaySupportEnabled());

  MovableDisplaySnapshots displays = drm_gpu_display_manager_->GetDisplays();

  ASSERT_THAT(displays, SizeIs(1));

  // Expect the tiled mode to have composited resolution.
  EXPECT_THAT(displays[0]->modes(),
              UnorderedElementsAre(
                  Pointee(EqResAndRefresh({gfx::Size(7680, 4320), 60})),
                  Pointee(EqResAndRefresh({gfx::Size(1920, 1080), 60}))));
}

TEST_F(TiledDisplayGetDisplaysTest, MoreModesInOneTile) {
  auto fake_drm = AddDrmDevice();
  fake_drm->ResetStateWithAllProperties();

  // Primary tile at (0,1) - has 3 modes.
  const TileProperty expected_primary_tile_prop = {
      .group_id = 1,
      .scale_to_fit_display = false,
      .tile_size = gfx::Size(3840, 4320),
      .tile_layout = gfx::Size(2, 1),
      .location = gfx::Point(1, 0)};
  ScopedDrmPropertyBlob primary_tile_property_blob =
      CreateTilePropertyBlob(*fake_drm, expected_primary_tile_prop);
  {
    fake_drm->AddCrtcWithPrimaryAndCursorPlanes();

    auto& primary_encoder = fake_drm->AddEncoder();
    primary_encoder.possible_crtcs = 0b1;

    auto& primary_connector = fake_drm->AddConnector();
    primary_connector.connection = true;
    primary_connector.modes =
        std::vector<ResolutionAndRefreshRate>{{gfx::Size(3840, 4320), 60},
                                              {gfx::Size(1920, 1080), 60},
                                              {gfx::Size(2560, 1440), 60}};
    primary_connector.encoders = std::vector<uint32_t>{primary_encoder.id};
    fake_drm->AddProperty(
        primary_connector.id,
        {.id = kTileBlobPropId, .value = primary_tile_property_blob->id()});
  }

  // Non-primary tile at (0,0) - has 1 mode.
  TileProperty expected_nonprimary_tile_prop = expected_primary_tile_prop;
  expected_nonprimary_tile_prop.location = gfx::Point(0, 0);
  ScopedDrmPropertyBlob nonprimary_tile_property_blob =
      CreateTilePropertyBlob(*fake_drm, expected_nonprimary_tile_prop);
  {
    fake_drm->AddCrtcWithPrimaryAndCursorPlanes();

    auto& nonprimary_encoder = fake_drm->AddEncoder();
    nonprimary_encoder.possible_crtcs = 0b10;

    auto& nonprimary_connector = fake_drm->AddConnector();
    nonprimary_connector.connection = true;

    nonprimary_connector.modes =
        std::vector<ResolutionAndRefreshRate>{{gfx::Size(3840, 4320), 60}};
    nonprimary_connector.encoders =
        std::vector<uint32_t>{nonprimary_encoder.id};
    nonprimary_connector.properties.push_back(
        {.id = kTileBlobPropId, .value = kTileBlobId + 1});
    fake_drm->AddProperty(
        nonprimary_connector.id,
        {.id = kTileBlobPropId, .value = nonprimary_tile_property_blob->id()});
  }

  fake_drm->InitializeState(/* use_atomic */ true);

  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(display::features::kTiledDisplaySupport);
  ASSERT_TRUE(display::features::IsTiledDisplaySupportEnabled());

  MovableDisplaySnapshots displays = drm_gpu_display_manager_->GetDisplays();

  ASSERT_THAT(displays, SizeIs(1));

  // Expect the tiled mode to have composited resolution, and to have modes from
  // the primary tile.
  EXPECT_THAT(displays[0]->modes(),
              UnorderedElementsAre(
                  Pointee(EqResAndRefresh({gfx::Size(7680, 4320), 60})),
                  // The non-tile mode in the primary tile (but not in
                  // non-primary) should live.
                  Pointee(EqResAndRefresh({gfx::Size(1920, 1080), 60})),
                  Pointee(EqResAndRefresh({gfx::Size(2560, 1440), 60}))));
}

TEST_F(TiledDisplayGetDisplaysTest, PruneTileModesNotInAllTiles) {
  auto fake_drm = AddDrmDevice();
  fake_drm->ResetStateWithAllProperties();

  // Primary tile at (0,1) - has 2 modes - one of which is 3840x4320@48.
  const TileProperty expected_primary_tile_prop = {
      .group_id = 1,
      .scale_to_fit_display = false,
      .tile_size = gfx::Size(3840, 4320),
      .tile_layout = gfx::Size(2, 1),
      .location = gfx::Point(1, 0)};
  ScopedDrmPropertyBlob primary_tile_property_blob =
      CreateTilePropertyBlob(*fake_drm, expected_primary_tile_prop);
  {
    fake_drm->AddCrtcWithPrimaryAndCursorPlanes();

    auto& primary_encoder = fake_drm->AddEncoder();
    primary_encoder.possible_crtcs = 0b1;

    auto& primary_connector = fake_drm->AddConnector();
    primary_connector.connection = true;
    primary_connector.modes = std::vector<ResolutionAndRefreshRate>{
        {gfx::Size(3840, 4320), 60}, {gfx::Size(3840, 4320), 48}};
    primary_connector.encoders = std::vector<uint32_t>{primary_encoder.id};
    primary_connector.edid_blob =
        std::vector<uint8_t>(kHPz32x, kHPz32x + kHPz32xLength);
    fake_drm->AddProperty(
        primary_connector.id,
        {.id = kTileBlobPropId, .value = primary_tile_property_blob->id()});
  }

  // Non-primary tile at (0,0) - has 1 mode.
  TileProperty expected_nonprimary_tile_prop = expected_primary_tile_prop;
  expected_nonprimary_tile_prop.location = gfx::Point(0, 0);
  ScopedDrmPropertyBlob nonprimary_tile_property_blob =
      CreateTilePropertyBlob(*fake_drm, expected_nonprimary_tile_prop);
  {
    fake_drm->AddCrtcWithPrimaryAndCursorPlanes();

    auto& nonprimary_encoder = fake_drm->AddEncoder();
    nonprimary_encoder.possible_crtcs = 0b10;

    auto& nonprimary_connector = fake_drm->AddConnector();
    nonprimary_connector.connection = true;
    nonprimary_connector.modes =
        std::vector<ResolutionAndRefreshRate>{{gfx::Size(3840, 4320), 60}};
    nonprimary_connector.encoders =
        std::vector<uint32_t>{nonprimary_encoder.id};
    nonprimary_connector.edid_blob =
        std::vector<uint8_t>(kHPz32x, kHPz32x + kHPz32xLength);
    fake_drm->AddProperty(
        nonprimary_connector.id,
        {.id = kTileBlobPropId, .value = nonprimary_tile_property_blob->id()});
  }

  fake_drm->InitializeState(/* use_atomic */ true);

  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(display::features::kTiledDisplaySupport);
  ASSERT_TRUE(display::features::IsTiledDisplaySupportEnabled());

  MovableDisplaySnapshots displays = drm_gpu_display_manager_->GetDisplays();

  ASSERT_THAT(displays, SizeIs(1));

  // 3840x4320@48 is pruned since it is not in all the tiles.
  EXPECT_THAT(displays[0]->modes(),
              UnorderedElementsAre(
                  Pointee(EqResAndRefresh({gfx::Size(7680, 4320), 60}))));
}

TEST_F(TiledDisplayGetDisplaysTest, NonTileModeNotPruned) {
  auto fake_drm = AddDrmDevice();
  fake_drm->ResetStateWithAllProperties();

  const TileProperty expected_primary_tile_prop = {
      .group_id = 1,
      .scale_to_fit_display = false,
      .tile_size = gfx::Size(3840, 4320),
      .tile_layout = gfx::Size(2, 1),
      .location = gfx::Point(1, 0)};
  ScopedDrmPropertyBlob primary_tile_property_blob =
      CreateTilePropertyBlob(*fake_drm, expected_primary_tile_prop);
  {
    fake_drm->AddCrtcWithPrimaryAndCursorPlanes();

    auto& primary_encoder = fake_drm->AddEncoder();
    primary_encoder.possible_crtcs = 0b1;

    auto& primary_connector = fake_drm->AddConnector();
    primary_connector.connection = true;

    // 7680x4320@30 has the same size as the tile composited mode (the total
    // size of the tiled display), but is not a tile mode (3840x4320).
    primary_connector.modes = std::vector<ResolutionAndRefreshRate>{
        {gfx::Size(7680, 4320), 30}, {gfx::Size(3840, 4320), 60}};
    primary_connector.encoders = std::vector<uint32_t>{primary_encoder.id};
    primary_connector.edid_blob =
        std::vector<uint8_t>(kHPz32x, kHPz32x + kHPz32xLength);
    fake_drm->AddProperty(
        primary_connector.id,
        {.id = kTileBlobPropId, .value = primary_tile_property_blob->id()});
  }

  fake_drm->InitializeState(/* use_atomic */ true);

  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndEnableFeature(display::features::kTiledDisplaySupport);
  ASSERT_TRUE(display::features::IsTiledDisplaySupportEnabled());

  MovableDisplaySnapshots displays = drm_gpu_display_manager_->GetDisplays();

  ASSERT_THAT(displays, SizeIs(1));

  // 7680x4320@30 should not be pruned as it is not a tiled-composited mode
  // (3840x4320), so does not get pruned when it is not present in all tiles.
  EXPECT_THAT(displays[0]->modes(),
              UnorderedElementsAre(
                  Pointee(EqResAndRefresh({gfx::Size(7680, 4320), 30}))));
}
}  // namespace ui

#endif  // UI_OZONE_PLATFORM_DRM_GPU_DRM_GPU_DISPLAY_MANAGER_UNITTEST_CC_