Reland "[Rounded Display] Adding RoundedDisplayProvider class."
This is a reland of commit 5009a3fd61c6ee3a67995f6eea20adb5a2092386
Reason for revert: It was reverted because host_window was leaking.
It was leaking because it was removed from the window hierarchy.
We've fixed it by leaving it attached to the window hierarchy.
Original change's description:
> [Rounded Display] Adding RoundedDisplayProvider class.
>
> This class provides an API through which we can add software
> rounded corner to a display.
>
> Bug: 1262970
> Test: Covered by unittests
> Change-Id: I11368af7b6899aefc196f11138a4bab9a9957c5a
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3900676
> Reviewed-by: Sean Kau <skau@chromium.org>
> Reviewed-by: Mitsuru Oshima <oshima@chromium.org>
> Commit-Queue: Zoraiz Naeem <zoraiznaeem@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1112427}
Bug: 1262970
Change-Id: I582b311ca5d7bd81f7c1484dcc7f83454fa7f7bb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4309922
Commit-Queue: Zoraiz Naeem <zoraiznaeem@chromium.org>
Reviewed-by: Sean Kau <skau@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1113545}
diff --git a/ash/rounded_display/rounded_display_provider_unittest.cc b/ash/rounded_display/rounded_display_provider_unittest.cc
new file mode 100644
index 0000000..efab93a
--- /dev/null
+++ b/ash/rounded_display/rounded_display_provider_unittest.cc
@@ -0,0 +1,219 @@
+// 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.
+
+#include "ash/rounded_display/rounded_display_provider.h"
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "ash/rounded_display/rounded_display_gutter.h"
+#include "ash/rounded_display/rounded_display_provider_test_api.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "base/test/gtest_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/display/display.h"
+#include "ui/display/manager/display_manager.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
+
+namespace ash {
+namespace {
+
+using Gutters = std::vector<RoundedDisplayGutter*>;
+using RoundedCorner = RoundedDisplayGutter::RoundedCorner;
+using RoundedCornerPosition = RoundedDisplayGutter::RoundedCorner::Position;
+using ProviderStrategy = RoundedDisplayProvider::Strategy;
+
+constexpr ProviderStrategy kDefaultTestStrategy = ProviderStrategy::kScanout;
+
+gfx::RoundedCornersF CreateHorizontallyUniformRadii(int radius_a,
+ int radius_b) {
+ return gfx::RoundedCornersF(radius_a, radius_a, radius_b, radius_b);
+}
+
+// The matcher matches a RoundedDisplayGutter that has the rounded corners of
+// `positions`.
+template <typename... Matchers>
+auto GutterWithMatchingCorners(Matchers&&... positions) {
+ return testing::ResultOf(
+ "positions",
+ [](const RoundedDisplayGutter* gutter) {
+ std::vector<RoundedCornerPosition> positions;
+ const std::vector<RoundedCorner>& corners = gutter->GetGutterCorners();
+ base::ranges::transform(
+ corners.begin(), corners.end(), std::back_inserter(positions),
+ [](const RoundedCorner& corner) { return corner.position(); });
+ return positions;
+ },
+ testing::UnorderedElementsAre(positions...));
+}
+
+class RoundedDisplayProviderTest : public AshTestBase {
+ public:
+ RoundedDisplayProviderTest() = default;
+
+ RoundedDisplayProviderTest(const RoundedDisplayProviderTest&) = delete;
+ RoundedDisplayProviderTest& operator=(const RoundedDisplayProviderTest&) =
+ delete;
+
+ ~RoundedDisplayProviderTest() override = default;
+
+ void SetUp() override {
+ AshTestBase::SetUp();
+ // Create the rounded display provider for the primary display.
+ provider_ = RoundedDisplayProvider::Create(GetPrimaryDisplay().id());
+ }
+
+ void TearDown() override {
+ provider_.reset();
+ AshTestBase::TearDown();
+ }
+
+ protected:
+ const display::Display& GetDisplay(int64_t display_id) {
+ return display_manager()->GetDisplayForId(display_id);
+ }
+
+ std::unique_ptr<RoundedDisplayProvider> provider_;
+};
+
+TEST_F(RoundedDisplayProviderTest, InitializeWithNonUniformRadii) {
+ RoundedDisplayProviderTestApi test_api(provider_.get());
+
+ const gfx::RoundedCornersF not_valid_radii(10, 11, 12, 12);
+
+ EXPECT_DCHECK_DEATH(
+ { provider_->Init(not_valid_radii, kDefaultTestStrategy); });
+}
+
+TEST_F(RoundedDisplayProviderTest, CorrectNumberOfGuttersAreProvided) {
+ RoundedDisplayProviderTestApi test_api(provider_.get());
+ const gfx::RoundedCornersF radii = CreateHorizontallyUniformRadii(10, 12);
+
+ // We expect 4 non-overlays and 2 overlay gutters to be created.
+ provider_->Init(radii, kDefaultTestStrategy);
+ EXPECT_EQ(test_api.GetGutters().size(), 6u);
+}
+
+TEST_F(RoundedDisplayProviderTest,
+ CorrectGutterCreatedForStrategy_ScanoutDirection) {
+ RoundedDisplayProviderTestApi test_api(provider_.get());
+ const gfx::RoundedCornersF radii = CreateHorizontallyUniformRadii(10, 12);
+
+ provider_->Init(radii, ProviderStrategy::kScanout);
+
+ const auto& gutters = test_api.GetGutters();
+
+ EXPECT_EQ(gutters.size(), 6u);
+
+ // Check that we have two overlay gutters that in the scanout direction.
+ EXPECT_THAT(gutters, testing::Contains(GutterWithMatchingCorners(
+ RoundedCornerPosition::kUpperLeft,
+ RoundedCornerPosition::kUpperRight)));
+ EXPECT_THAT(gutters, testing::Contains(GutterWithMatchingCorners(
+ RoundedCornerPosition::kLowerLeft,
+ RoundedCornerPosition::kLowerRight)));
+}
+
+TEST_F(RoundedDisplayProviderTest,
+ CorrectGutterCreatedForStrategy_OtherDirection) {
+ RoundedDisplayProviderTestApi test_api(provider_.get());
+ const gfx::RoundedCornersF radii = CreateHorizontallyUniformRadii(10, 12);
+
+ provider_->Init(radii, ProviderStrategy::kOther);
+
+ const auto& gutters = test_api.GetGutters();
+
+ EXPECT_EQ(gutters.size(), 6u);
+
+ // Check that we have two overlay gutters that in the scanout direction.
+ EXPECT_THAT(gutters, testing::Contains(GutterWithMatchingCorners(
+ RoundedCornerPosition::kUpperLeft,
+ RoundedCornerPosition::kLowerLeft)));
+ // Right overlay gutter.
+ EXPECT_THAT(gutters, testing::Contains(GutterWithMatchingCorners(
+ RoundedCornerPosition::kUpperRight,
+ RoundedCornerPosition::kLowerRight)));
+}
+
+class RoundedDisplayProviderSurfaceUpdateTest
+ : public RoundedDisplayProviderTest,
+ public ::testing::WithParamInterface<
+ std::tuple<std::string, std::string, bool>> {
+ public:
+ RoundedDisplayProviderSurfaceUpdateTest()
+ : initial_display_spec_(std::get<0>(GetParam())),
+ updated_display_spec_(std::get<1>(GetParam())),
+ expect_update_(std::get<2>(GetParam())) {}
+
+ RoundedDisplayProviderSurfaceUpdateTest(
+ const RoundedDisplayProviderSurfaceUpdateTest&) = delete;
+ RoundedDisplayProviderSurfaceUpdateTest& operator=(
+ const RoundedDisplayProviderSurfaceUpdateTest&) = delete;
+
+ ~RoundedDisplayProviderSurfaceUpdateTest() override = default;
+
+ protected:
+ std::string initial_display_spec_;
+ std::string updated_display_spec_;
+ bool expect_update_;
+};
+
+TEST_P(RoundedDisplayProviderSurfaceUpdateTest,
+ UpdatesSurfaceOnlyWhenNecessary) {
+ RoundedDisplayProviderTestApi test_api(provider_.get());
+
+ display::Display primary_display =
+ display::Screen::GetScreen()->GetPrimaryDisplay();
+
+ auto display_id = primary_display.id();
+
+ UpdateDisplay(initial_display_spec_);
+
+ gfx::RoundedCornersF radii =
+ display_manager()->GetDisplayInfo(display_id).rounded_corners_radii();
+
+ provider_->Init(radii, kDefaultTestStrategy);
+
+ ASSERT_TRUE(provider_->UpdateRoundedDisplaySurface());
+
+ UpdateDisplay(updated_display_spec_);
+
+ Gutters before_update_gutters = test_api.GetGutters();
+ ASSERT_EQ(provider_->UpdateRoundedDisplaySurface(), expect_update_);
+ Gutters after_update_gutters = test_api.GetGutters();
+
+ // Confirm that we did not change gutters.
+ EXPECT_EQ(before_update_gutters, after_update_gutters);
+}
+
+const std::string kInitialDisplaySpec = "500x400~15";
+const std::string kInitialDisplaySpecWithRotation = "500x400/r~15";
+
+INSTANTIATE_TEST_SUITE_P(
+ /* no prefix */,
+ RoundedDisplayProviderSurfaceUpdateTest,
+ testing::Values(
+ // If nothing changes, we should skip surface update.
+ std::make_tuple(kInitialDisplaySpec,
+ "500x400~15",
+ /*expect_update=*/false),
+ // Change in device scale factor, should only cause a surface update.
+ std::make_tuple(kInitialDisplaySpec,
+ "500x400*2~15",
+ /*expect_update=*/true),
+ // Further display rotation, should only cause a surface update.
+ std::make_tuple(kInitialDisplaySpecWithRotation,
+ "500x400/u~15",
+ /*expect_update=*/true),
+ // Multiple spec changes, should appropriately cause a surface update.
+ std::make_tuple(kInitialDisplaySpec,
+ "500x400*2~15",
+ /*expect_update=*/true)));
+
+} // namespace
+} // namespace ash