910e62b5创建于 1月15日历史提交
// Copyright 2025 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/display/win/screen_win_headless.h"

#include <windows.h>

#include <algorithm>
#include <set>
#include <string>
#include <string_view>
#include <vector>

#include "base/containers/flat_map.h"
#include "components/headless/screen_info/headless_screen_info.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/display/win/screen_win_display.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/vector2d_f.h"
#include "ui/gfx/native_ui_types.h"

using headless::HeadlessScreenInfo;

namespace display::win {

namespace {

class TestScreenWinHeadless : public ScreenWinHeadless {
 public:
  explicit TestScreenWinHeadless(
      const std::vector<HeadlessScreenInfo>& screen_infos)
      : ScreenWinHeadless(screen_infos) {}

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

  ~TestScreenWinHeadless() override = default;

  gfx::NativeWindow AddWindow(const gfx::Rect& bounds) {
    HWND hwnd = reinterpret_cast<HWND>(++last_hwnd_);
    windows_.insert({hwnd, bounds});
    return GetNativeWindowFromHWND(hwnd);
  }

  // win::ScreenWin:
  HWND GetHWNDFromNativeWindow(gfx::NativeWindow window) const override {
    return reinterpret_cast<HWND>(window);
  }

  gfx::NativeWindow GetNativeWindowFromHWND(HWND hwnd) const override {
    return reinterpret_cast<gfx::NativeWindow>(hwnd);
  }

  // win::ScreenWinHeadless:
  gfx::NativeWindow GetNativeWindowAtScreenPoint(
      const gfx::Point& point,
      const std::set<gfx::NativeWindow>& ignore) const override {
    gfx::NativeWindow result = nullptr;
    // Assume that more recently created windows are higher in z-order.
    for (const auto& [hwnd, bounds] : windows_) {
      gfx::NativeWindow window = GetNativeWindowFromHWND(hwnd);
      if (bounds.Contains(point) && ignore.find(window) == ignore.cend()) {
        result = window;
      }
    }

    return result;
  }

  gfx::Rect GetNativeWindowBoundsInScreen(
      gfx::NativeWindow window) const override {
    return GetWindowBounds(GetHWNDFromNativeWindow(window));
  }

  gfx::Rect GetHeadlessWindowBounds(
      gfx::AcceleratedWidget window) const override {
    return GetWindowBounds(window);
  }

  gfx::NativeWindow GetRootWindow(gfx::NativeWindow window) const override {
    return window;
  }

 private:
  gfx::Rect GetWindowBounds(HWND hwnd) const {
    CHECK(hwnd);
    auto it = windows_.find(hwnd);
    CHECK(it != windows_.cend());
    return it->second;
  }

  // A sequentially increasing integer value used as a substitute for a window
  // handle.
  int last_hwnd_ = 0;

  base::flat_map<HWND, gfx::Rect> windows_;
};

class ScreenWinHeadlessTest : public testing::Test {
 public:
  ScreenWinHeadlessTest(const ScreenWinHeadlessTest&) = delete;
  ScreenWinHeadlessTest& operator=(const ScreenWinHeadlessTest&) = delete;

 protected:
  ScreenWinHeadlessTest() = default;
  ~ScreenWinHeadlessTest() override = default;

  // Return specified or default headless screen configuration.
  std::vector<HeadlessScreenInfo> GetScreenInfos(
      std::string_view screen_info_spec) {
    std::vector<HeadlessScreenInfo> screen_infos;

    if (!screen_info_spec.empty()) {
      auto screen_info_or_error =
          HeadlessScreenInfo::FromString(screen_info_spec);
      CHECK(screen_info_or_error.has_value()) << screen_info_or_error.error();
      screen_infos = screen_info_or_error.value();
    } else {
      screen_infos.push_back(HeadlessScreenInfo());
    }

    return screen_infos;
  }

  std::unique_ptr<TestScreenWinHeadless> CreateHeadlessScreen(
      std::string_view screen_info_spec) {
    return std::make_unique<TestScreenWinHeadless>(
        GetScreenInfos(screen_info_spec));
  }
};

TEST_F(ScreenWinHeadlessTest, DefaultScreen) {
  auto screen = CreateHeadlessScreen("");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  const Display& display = screen->GetAllDisplays()[0];
  EXPECT_THAT(display.bounds(), testing::Eq(gfx::Rect(0, 0, 800, 600)));
  EXPECT_THAT(display.work_area(), testing::Eq(gfx::Rect(0, 0, 800, 600)));
  EXPECT_THAT(display.device_scale_factor(), testing::Eq(1.0f));
  EXPECT_THAT(display.rotation(), testing::Eq(Display::Rotation::ROTATE_0));
  EXPECT_THAT(display.color_depth(), testing::Eq(24));
  EXPECT_TRUE(display.label().empty());
  EXPECT_FALSE(display.IsInternal());
}

TEST_F(ScreenWinHeadlessTest, SpecifiedScreen) {
  auto screen = CreateHeadlessScreen("{1600x1200 label='Primary Screen'}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  const Display& display = screen->GetAllDisplays()[0];
  EXPECT_THAT(display.bounds(), testing::Eq(gfx::Rect(0, 0, 1600, 1200)));
  EXPECT_THAT(display.work_area(), testing::Eq(gfx::Rect(0, 0, 1600, 1200)));
  EXPECT_THAT(display.device_scale_factor(), testing::Eq(1.0f));
  EXPECT_THAT(display.rotation(), testing::Eq(Display::Rotation::ROTATE_0));
  EXPECT_THAT(display.color_depth(), testing::Eq(24));
  EXPECT_THAT(display.label(), testing::Eq("Primary Screen"));
  EXPECT_FALSE(display.IsInternal());
}

TEST_F(ScreenWinHeadlessTest, MultipleScreens) {
  auto screen = CreateHeadlessScreen("{label=#1}{600x800 label=#2}");
  ASSERT_EQ(screen->GetNumDisplays(), 2);

  const Display& display1 = screen->GetAllDisplays()[0];
  EXPECT_THAT(display1.bounds(), testing::Eq(gfx::Rect(0, 0, 800, 600)));
  EXPECT_THAT(display1.rotation(), testing::Eq(Display::Rotation::ROTATE_0));
  EXPECT_THAT(display1.label(), testing::Eq("#1"));

  const Display& display2 = screen->GetAllDisplays()[1];
  EXPECT_THAT(display2.bounds(), testing::Eq(gfx::Rect(800, 0, 600, 800)));
  EXPECT_THAT(display2.rotation(), testing::Eq(Display::Rotation::ROTATE_0));
  EXPECT_THAT(display2.label(), testing::Eq("#2"));
}

// ScreenWin::MonitorInfoFrom*() overrides tests ----------------------

TEST_F(ScreenWinHeadlessTest, MonitorInfoFromScreenPoint) {
  auto screen = CreateHeadlessScreen("{}{}");
  ASSERT_EQ(screen->GetNumDisplays(), 2);

  EXPECT_EQ(screen->MonitorInfoFromScreenPoint(gfx::Point(-1, -1))->dwFlags,
            static_cast<DWORD>(MONITORINFOF_PRIMARY));

  EXPECT_EQ(screen->MonitorInfoFromScreenPoint(gfx::Point(0, 0))->dwFlags,
            static_cast<DWORD>(MONITORINFOF_PRIMARY));

  EXPECT_EQ(screen->MonitorInfoFromScreenPoint(gfx::Point(799, 300))->dwFlags,
            static_cast<DWORD>(MONITORINFOF_PRIMARY));

  EXPECT_EQ(screen->MonitorInfoFromScreenPoint(gfx::Point(1599, 599))->dwFlags,
            static_cast<DWORD>(0));

  EXPECT_EQ(screen->MonitorInfoFromScreenPoint(gfx::Point(1601, 601))->dwFlags,
            static_cast<DWORD>(0));
}

TEST_F(ScreenWinHeadlessTest, MonitorInfoFromScreenRect) {
  auto screen = CreateHeadlessScreen("{}{}");
  ASSERT_EQ(screen->GetNumDisplays(), 2);

  EXPECT_EQ(screen->MonitorInfoFromScreenRect(gfx::Rect(-200, -100, 200, 100))
                ->dwFlags,
            static_cast<DWORD>(MONITORINFOF_PRIMARY));

  EXPECT_EQ(
      screen->MonitorInfoFromScreenRect(gfx::Rect(0, 0, 200, 100))->dwFlags,
      static_cast<DWORD>(MONITORINFOF_PRIMARY));

  EXPECT_EQ(
      screen->MonitorInfoFromScreenRect(gfx::Rect(400, 300, 200, 100))->dwFlags,
      static_cast<DWORD>(MONITORINFOF_PRIMARY));

  EXPECT_EQ(screen->MonitorInfoFromScreenRect(gfx::Rect(1500, 500, 200, 100))
                ->dwFlags,
            static_cast<DWORD>(0));

  EXPECT_EQ(screen->MonitorInfoFromScreenRect(gfx::Rect(1601, 601, 200, 100))
                ->dwFlags,
            static_cast<DWORD>(0));
}

TEST_F(ScreenWinHeadlessTest, MonitorInfoFromWindow) {
  auto screen = CreateHeadlessScreen("{}{}");
  ASSERT_EQ(screen->GetNumDisplays(), 2);

  // Window is on the primary screen.
  HWND hwnd1 = screen->GetHWNDFromNativeWindow(
      screen->AddWindow(gfx::Rect(0, 0, 400, 300)));
  EXPECT_EQ(
      screen->MonitorInfoFromWindow(hwnd1, MONITOR_DEFAULTTONULL)->dwFlags,
      static_cast<DWORD>(MONITORINFOF_PRIMARY));

  // Window is on the secondary screen.
  HWND hwnd2 = screen->GetHWNDFromNativeWindow(
      screen->AddWindow(gfx::Rect(800, 0, 400, 300)));
  EXPECT_EQ(
      screen->MonitorInfoFromWindow(hwnd2, MONITOR_DEFAULTTONULL)->dwFlags,
      static_cast<DWORD>(0));

  // Window is north west of the primary screen.
  HWND hwnd3 = screen->GetHWNDFromNativeWindow(
      screen->AddWindow(gfx::Rect(-400, -300, 400, 300)));
  EXPECT_FALSE(
      screen->MonitorInfoFromWindow(hwnd3, MONITOR_DEFAULTTONULL).has_value());
  EXPECT_EQ(
      screen->MonitorInfoFromWindow(hwnd3, MONITOR_DEFAULTTONEAREST)->dwFlags,
      static_cast<DWORD>(MONITORINFOF_PRIMARY));

  // Window is south east of the secondary screen.
  HWND hwnd4 = screen->GetHWNDFromNativeWindow(
      screen->AddWindow(gfx::Rect(1600, 600, 400, 300)));
  EXPECT_FALSE(
      screen->MonitorInfoFromWindow(hwnd4, MONITOR_DEFAULTTONULL).has_value());
  EXPECT_EQ(
      screen->MonitorInfoFromWindow(hwnd4, MONITOR_DEFAULTTONEAREST)->dwFlags,
      static_cast<DWORD>(0));
  EXPECT_EQ(
      screen->MonitorInfoFromWindow(hwnd4, MONITOR_DEFAULTTOPRIMARY)->dwFlags,
      static_cast<DWORD>(MONITORINFOF_PRIMARY));
}

// display::win::ScreenWin static methods tests -----------------------

TEST_F(ScreenWinHeadlessTest, ScreenToDIPPoint) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=1.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  EXPECT_EQ(GetScreenWin()->ScreenToDIPPoint(gfx::PointF(100, 200)),
            gfx::PointF(100, 200));
}

TEST_F(ScreenWinHeadlessTest, ScreenToDIPPoint2x) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=2.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  EXPECT_EQ(GetScreenWin()->ScreenToDIPPoint(gfx::PointF(100, 200)),
            gfx::PointF(50, 100));
}

TEST_F(ScreenWinHeadlessTest, DIPToScreenPoint) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=1.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  EXPECT_EQ(GetScreenWin()->DIPToScreenPoint(gfx::Point(100, 200)),
            gfx::Point(100, 200));
}

TEST_F(ScreenWinHeadlessTest, DIPToScreenPoint2x) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=2.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  EXPECT_EQ(GetScreenWin()->DIPToScreenPoint(gfx::Point(100, 200)),
            gfx::Point(200, 400));
}

TEST_F(ScreenWinHeadlessTest, ClientToDIPPoint) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=1.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  HWND hwnd = screen->GetHWNDFromNativeWindow(
      screen->AddWindow(gfx::Rect(0, 0, 400, 300)));

  EXPECT_EQ(GetScreenWin()->ClientToDIPPoint(hwnd, gfx::Point(100, 200)),
            gfx::Point(100, 200));
}

TEST_F(ScreenWinHeadlessTest, ClientToDIPPoint2x) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=2.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  HWND hwnd = screen->GetHWNDFromNativeWindow(
      screen->AddWindow(gfx::Rect(0, 0, 400, 300)));

  EXPECT_EQ(GetScreenWin()->ClientToDIPPoint(hwnd, gfx::Point(100, 200)),
            gfx::Point(50, 100));
}

TEST_F(ScreenWinHeadlessTest, DIPToClientPoint) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=1.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  HWND hwnd = screen->GetHWNDFromNativeWindow(
      screen->AddWindow(gfx::Rect(0, 0, 400, 300)));

  EXPECT_EQ(GetScreenWin()->DIPToClientPoint(hwnd, gfx::Point(100, 200)),
            gfx::Point(100, 200));
}

TEST_F(ScreenWinHeadlessTest, DIPToClientPoint2x) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=2.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  HWND hwnd = screen->GetHWNDFromNativeWindow(
      screen->AddWindow(gfx::Rect(0, 0, 400, 300)));

  EXPECT_EQ(GetScreenWin()->DIPToClientPoint(hwnd, gfx::Point(50, 100)),
            gfx::Point(100, 200));
}

TEST_F(ScreenWinHeadlessTest, ScreenToDIPRect) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=1.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  HWND hwnd = screen->GetHWNDFromNativeWindow(
      screen->AddWindow(gfx::Rect(0, 0, 400, 300)));

  EXPECT_EQ(GetScreenWin()->ScreenToDIPRect(hwnd, gfx::Rect(0, 0, 100, 200)),
            gfx::Rect(0, 0, 100, 200));

  EXPECT_EQ(GetScreenWin()->ScreenToDIPRect(nullptr, gfx::Rect(0, 0, 100, 200)),
            gfx::Rect(0, 0, 100, 200));
}

TEST_F(ScreenWinHeadlessTest, ScreenToDIPRect2x) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=2.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  HWND hwnd = screen->GetHWNDFromNativeWindow(
      screen->AddWindow(gfx::Rect(0, 0, 400, 300)));

  EXPECT_EQ(GetScreenWin()->ScreenToDIPRect(hwnd, gfx::Rect(0, 0, 100, 200)),
            gfx::Rect(0, 0, 50, 100));

  EXPECT_EQ(GetScreenWin()->ScreenToDIPRect(nullptr, gfx::Rect(0, 0, 100, 200)),
            gfx::Rect(0, 0, 50, 100));
}

TEST_F(ScreenWinHeadlessTest, DIPToScreenRect) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=1.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  HWND hwnd = screen->GetHWNDFromNativeWindow(
      screen->AddWindow(gfx::Rect(0, 0, 400, 300)));

  EXPECT_EQ(GetScreenWin()->DIPToScreenRect(hwnd, gfx::Rect(0, 0, 100, 200)),
            gfx::Rect(0, 0, 100, 200));

  EXPECT_EQ(GetScreenWin()->DIPToScreenRect(nullptr, gfx::Rect(0, 0, 100, 200)),
            gfx::Rect(0, 0, 100, 200));
}

TEST_F(ScreenWinHeadlessTest, DIPToScreenRect2x) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=2.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  HWND hwnd = screen->GetHWNDFromNativeWindow(
      screen->AddWindow(gfx::Rect(0, 0, 400, 300)));

  EXPECT_EQ(GetScreenWin()->DIPToScreenRect(hwnd, gfx::Rect(0, 0, 50, 100)),
            gfx::Rect(0, 0, 100, 200));

  EXPECT_EQ(GetScreenWin()->DIPToScreenRect(nullptr, gfx::Rect(0, 0, 50, 100)),
            gfx::Rect(0, 0, 100, 200));
}

TEST_F(ScreenWinHeadlessTest, ClientToDIPRect) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=1.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  HWND hwnd = screen->GetHWNDFromNativeWindow(
      screen->AddWindow(gfx::Rect(0, 0, 400, 300)));

  EXPECT_EQ(GetScreenWin()->ClientToDIPRect(hwnd, gfx::Rect(0, 0, 100, 200)),
            gfx::Rect(0, 0, 100, 200));
}

TEST_F(ScreenWinHeadlessTest, ClientToDIPRect2x) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=2.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  HWND hwnd = screen->GetHWNDFromNativeWindow(
      screen->AddWindow(gfx::Rect(0, 0, 400, 300)));

  EXPECT_EQ(GetScreenWin()->ClientToDIPRect(hwnd, gfx::Rect(0, 0, 100, 200)),
            gfx::Rect(0, 0, 50, 100));
}

TEST_F(ScreenWinHeadlessTest, DIPToClientRect) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=1.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  HWND hwnd = screen->GetHWNDFromNativeWindow(
      screen->AddWindow(gfx::Rect(0, 0, 400, 300)));

  EXPECT_EQ(GetScreenWin()->DIPToClientRect(hwnd, gfx::Rect(0, 0, 100, 200)),
            gfx::Rect(0, 0, 100, 200));
}

TEST_F(ScreenWinHeadlessTest, DIPToClientRect2x) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=2.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  HWND hwnd = screen->GetHWNDFromNativeWindow(
      screen->AddWindow(gfx::Rect(0, 0, 400, 300)));

  EXPECT_EQ(GetScreenWin()->DIPToClientRect(hwnd, gfx::Rect(0, 0, 50, 100)),
            gfx::Rect(0, 0, 100, 200));
}

TEST_F(ScreenWinHeadlessTest, ScreenToDIPSize) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=1.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  HWND hwnd = screen->GetHWNDFromNativeWindow(
      screen->AddWindow(gfx::Rect(0, 0, 400, 300)));

  EXPECT_EQ(GetScreenWin()->ScreenToDIPSize(hwnd, gfx::Size(100, 200)),
            gfx::Size(100, 200));
}

TEST_F(ScreenWinHeadlessTest, ScreenToDIPSize2x) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=2.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  HWND hwnd = screen->GetHWNDFromNativeWindow(
      screen->AddWindow(gfx::Rect(0, 0, 400, 300)));

  EXPECT_EQ(GetScreenWin()->ScreenToDIPSize(hwnd, gfx::Size(100, 200)),
            gfx::Size(50, 100));
}

TEST_F(ScreenWinHeadlessTest, DIPToScreenSize) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=1.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  HWND hwnd = screen->GetHWNDFromNativeWindow(
      screen->AddWindow(gfx::Rect(0, 0, 400, 300)));

  EXPECT_EQ(GetScreenWin()->DIPToScreenSize(hwnd, gfx::Size(100, 200)),
            gfx::Size(100, 200));
}

TEST_F(ScreenWinHeadlessTest, DIPToScreenSize2x) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=2.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  HWND hwnd = screen->GetHWNDFromNativeWindow(
      screen->AddWindow(gfx::Rect(0, 0, 400, 300)));

  EXPECT_EQ(GetScreenWin()->DIPToScreenSize(hwnd, gfx::Size(50, 100)),
            gfx::Size(100, 200));
}

TEST_F(ScreenWinHeadlessTest, GetPixelsPerInch) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=1.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  EXPECT_EQ(GetScreenWin()->GetPixelsPerInch(gfx::PointF(400, 300)),
            gfx::Vector2dF(96, 96));
}

TEST_F(ScreenWinHeadlessTest, GetPixelsPerInch2x) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=2.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  EXPECT_EQ(GetScreenWin()->GetPixelsPerInch(gfx::PointF(400, 300)),
            gfx::Vector2dF(192, 192));
}

TEST_F(ScreenWinHeadlessTest, GetScreenWinDisplayWithDisplayId) {
  auto screen = CreateHeadlessScreen("{label=#1}{label=#2}");
  ASSERT_EQ(screen->GetNumDisplays(), 2);

  const int64_t id1 = screen->GetAllDisplays()[0].id();
  EXPECT_EQ(
      GetScreenWin()->GetScreenWinDisplayWithDisplayId(id1).display().id(),
      id1);

  const int64_t id2 = screen->GetAllDisplays()[1].id();
  EXPECT_EQ(
      GetScreenWin()->GetScreenWinDisplayWithDisplayId(id2).display().id(),
      id2);

  // Unknown display id should result in primary display.
  EXPECT_EQ(GetScreenWin()->GetScreenWinDisplayWithDisplayId(-1).display().id(),
            id1);
}

TEST_F(ScreenWinHeadlessTest, DisplayIdFromMonitorInfo) {
  auto screen = CreateHeadlessScreen("{}{}");
  ASSERT_EQ(screen->GetNumDisplays(), 2);

  const int64_t id1 = screen->GetAllDisplays()[0].id();
  auto monitor_info1 = screen->GetMONITORINFOFromDisplayIdForTest(id1);
  ASSERT_TRUE(monitor_info1.has_value());
  EXPECT_EQ(GetScreenWin()->DisplayIdFromMonitorInfo(*monitor_info1), id1);

  const int64_t id2 = screen->GetAllDisplays()[1].id();
  auto monitor_info2 = screen->GetMONITORINFOFromDisplayIdForTest(id2);
  ASSERT_TRUE(monitor_info2.has_value());
  EXPECT_EQ(GetScreenWin()->DisplayIdFromMonitorInfo(*monitor_info2), id2);
}

TEST_F(ScreenWinHeadlessTest, GetScaleFactorForHWND) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=1.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  HWND hwnd = screen->GetHWNDFromNativeWindow(
      screen->AddWindow(gfx::Rect(0, 0, 400, 300)));
  EXPECT_EQ(GetScreenWin()->GetScaleFactorForHWND(hwnd), 1.0);
}

TEST_F(ScreenWinHeadlessTest, GetScaleFactorForHWND2x) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=2.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  HWND hwnd = screen->GetHWNDFromNativeWindow(
      screen->AddWindow(gfx::Rect(0, 0, 400, 300)));
  EXPECT_EQ(GetScreenWin()->GetScaleFactorForHWND(hwnd), 2.0);
}

// display::Screen interface methods tests ----------------------------

TEST_F(ScreenWinHeadlessTest, GetCursorScreenPoint) {
  auto screen = CreateHeadlessScreen("");

  EXPECT_EQ(screen->GetCursorScreenPoint(), gfx::Point());

  static constexpr gfx::Point kCursorScreenPoint(123, 456);
  screen->SetCursorScreenPointForTesting(kCursorScreenPoint);
  EXPECT_EQ(screen->GetCursorScreenPoint(), kCursorScreenPoint);
}

TEST_F(ScreenWinHeadlessTest, IsWindowUnderCursor) {
  auto screen = CreateHeadlessScreen("");

  gfx::NativeWindow window1 = screen->AddWindow(gfx::Rect(0, 0, 400, 300));
  EXPECT_TRUE(screen->IsWindowUnderCursor(window1));

  gfx::NativeWindow window2 = screen->AddWindow(gfx::Rect(800, 0, 400, 300));
  EXPECT_FALSE(screen->IsWindowUnderCursor(window2));
}

TEST_F(ScreenWinHeadlessTest, GetWindowAtScreenPoint) {
  auto screen = CreateHeadlessScreen("");

  EXPECT_EQ(screen->GetWindowAtScreenPoint(gfx::Point(0, 0)), nullptr);

  gfx::NativeWindow window1 = screen->AddWindow(gfx::Rect(0, 0, 400, 300));
  gfx::NativeWindow window2 = screen->AddWindow(gfx::Rect(10, 10, 400, 300));

  EXPECT_EQ(screen->GetWindowAtScreenPoint(gfx::Point(0, 0)), window1);
  EXPECT_EQ(screen->GetWindowAtScreenPoint(gfx::Point(10, 10)), window2);
}

TEST_F(ScreenWinHeadlessTest, GetLocalProcessWindowAtPoint) {
  auto screen = CreateHeadlessScreen("");

  gfx::NativeWindow window1 = screen->AddWindow(gfx::Rect(0, 0, 400, 300));
  gfx::NativeWindow window2 = screen->AddWindow(gfx::Rect(10, 10, 400, 300));

  EXPECT_EQ(screen->GetLocalProcessWindowAtPoint(gfx::Point(0, 0),
                                                 std::set<gfx::NativeWindow>()),
            window1);

  EXPECT_EQ(screen->GetLocalProcessWindowAtPoint(gfx::Point(10, 10),
                                                 std::set<gfx::NativeWindow>()),
            window2);

  EXPECT_EQ(screen->GetLocalProcessWindowAtPoint(
                gfx::Point(10, 10), std::set<gfx::NativeWindow>({window2})),
            window1);

  EXPECT_EQ(
      screen->GetLocalProcessWindowAtPoint(
          gfx::Point(10, 10), std::set<gfx::NativeWindow>({window1, window2})),
      nullptr);
}

TEST_F(ScreenWinHeadlessTest, GetNumDisplays) {
  auto screen = CreateHeadlessScreen("{label='#1'}{label='#2'}");
  EXPECT_EQ(screen->GetNumDisplays(), 2);
}

TEST_F(ScreenWinHeadlessTest, GetAllDisplays) {
  auto screen = CreateHeadlessScreen("{label='#1'}{label='#2'}");

  const std::vector<Display>& displays = screen->GetAllDisplays();
  ASSERT_THAT(displays, testing::SizeIs(2));

  EXPECT_THAT(displays[0].label(), testing::StrEq("#1"));
  EXPECT_THAT(displays[1].label(), testing::StrEq("#2"));
}

TEST_F(ScreenWinHeadlessTest, GetDisplayNearestWindow) {
  auto screen = CreateHeadlessScreen("{label='#1'}{label='#2'}");
  ASSERT_EQ(screen->GetNumDisplays(), 2);

  gfx::NativeWindow window1 = screen->AddWindow(gfx::Rect(0, 0, 400, 300));
  Display display1 = screen->GetDisplayNearestWindow(window1);
  EXPECT_THAT(display1.label(), testing::StrEq("#1"));

  gfx::NativeWindow window2 = screen->AddWindow(gfx::Rect(800, 0, 400, 300));
  Display display2 = screen->GetDisplayNearestWindow(window2);
  EXPECT_THAT(display2.label(), testing::StrEq("#2"));
}

TEST_F(ScreenWinHeadlessTest, GetDisplayNearestPoint) {
  auto screen = CreateHeadlessScreen(
      "{label='#1'}{label='#2'}{0,600 label='#3'}{label='#4'}");
  ASSERT_EQ(screen->GetNumDisplays(), 4);

  EXPECT_THAT(screen->GetDisplayNearestPoint(gfx::Point(1, 1)).label(),
              testing::StrEq("#1"));
  EXPECT_THAT(screen->GetDisplayNearestPoint(gfx::Point(801, 1)).label(),
              testing::StrEq("#2"));
  EXPECT_THAT(screen->GetDisplayNearestPoint(gfx::Point(1, 601)).label(),
              testing::StrEq("#3"));
  EXPECT_THAT(screen->GetDisplayNearestPoint(gfx::Point(801, 601)).label(),
              testing::StrEq("#4"));
}

TEST_F(ScreenWinHeadlessTest, GetDisplayMatching) {
  auto screen = CreateHeadlessScreen(
      "{label='#1'}{label='#2'}{0,600 label='#3'}{label='#4'}");
  ASSERT_EQ(screen->GetNumDisplays(), 4);

  EXPECT_THAT(screen->GetDisplayMatching(gfx::Rect(1, 1, 400, 300)).label(),
              testing::StrEq("#1"));
  EXPECT_THAT(screen->GetDisplayMatching(gfx::Rect(801, 1, 400, 300)).label(),
              testing::StrEq("#2"));
  EXPECT_THAT(screen->GetDisplayMatching(gfx::Rect(1, 601, 400, 300)).label(),
              testing::StrEq("#3"));
  EXPECT_THAT(screen->GetDisplayMatching(gfx::Rect(801, 601, 400, 300)).label(),
              testing::StrEq("#4"));
}

TEST_F(ScreenWinHeadlessTest, GetPrimaryDisplay) {
  auto screen =
      CreateHeadlessScreen("{label='#1'}{label='#2'}{label='#3'}{label='#4'}");
  ASSERT_EQ(screen->GetNumDisplays(), 4);

  Display display = screen->GetPrimaryDisplay();
  EXPECT_THAT(display.label(), testing::StrEq("#1"));
}

TEST_F(ScreenWinHeadlessTest, ScreenToDIPRectInWindow) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=1.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  gfx::NativeWindow window = screen->AddWindow(gfx::Rect(10, 20, 400, 300));
  EXPECT_EQ(
      screen->ScreenToDIPRectInWindow(window, gfx::Rect(10, 20, 100, 200)),
      gfx::Rect(10, 20, 100, 200));
}

TEST_F(ScreenWinHeadlessTest, ScreenToDIPRectInWindow2x) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=2.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  gfx::NativeWindow window = screen->AddWindow(gfx::Rect(10, 20, 400, 300));
  EXPECT_EQ(
      screen->ScreenToDIPRectInWindow(window, gfx::Rect(10, 20, 100, 200)),
      gfx::Rect(5, 10, 50, 100));
}

TEST_F(ScreenWinHeadlessTest, DIPToScreenRectInWindow) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=1.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  gfx::NativeWindow window = screen->AddWindow(gfx::Rect(10, 20, 400, 300));
  EXPECT_EQ(
      screen->DIPToScreenRectInWindow(window, gfx::Rect(10, 20, 100, 200)),
      gfx::Rect(10, 20, 100, 200));
}

TEST_F(ScreenWinHeadlessTest, DIPToScreenRectInWindow2x) {
  auto screen = CreateHeadlessScreen("{devicePixelRatio=2.0}");
  ASSERT_EQ(screen->GetNumDisplays(), 1);

  gfx::NativeWindow window = screen->AddWindow(gfx::Rect(10, 20, 400, 300));
  EXPECT_EQ(screen->DIPToScreenRectInWindow(window, gfx::Rect(5, 10, 50, 100)),
            gfx::Rect(10, 20, 100, 200));
}

}  // namespace

}  // namespace display::win