Zoraiz Naeem | 6a49a56 | 2023-03-06 20:33:13 | [diff] [blame] | 1 | // Copyright 2023 The Chromium Authors |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "ash/rounded_display/rounded_display_provider.h" |
| 6 | |
| 7 | #include <algorithm> |
| 8 | #include <memory> |
| 9 | #include <string> |
| 10 | #include <vector> |
| 11 | |
| 12 | #include "ash/rounded_display/rounded_display_gutter.h" |
| 13 | #include "ash/rounded_display/rounded_display_provider_test_api.h" |
| 14 | #include "ash/shell.h" |
| 15 | #include "ash/test/ash_test_base.h" |
| 16 | #include "base/test/gtest_util.h" |
| 17 | #include "testing/gmock/include/gmock/gmock.h" |
| 18 | #include "testing/gtest/include/gtest/gtest.h" |
| 19 | #include "ui/display/display.h" |
| 20 | #include "ui/display/manager/display_manager.h" |
| 21 | #include "ui/gfx/geometry/rounded_corners_f.h" |
| 22 | |
| 23 | namespace ash { |
| 24 | namespace { |
| 25 | |
| 26 | using Gutters = std::vector<RoundedDisplayGutter*>; |
| 27 | using RoundedCorner = RoundedDisplayGutter::RoundedCorner; |
| 28 | using RoundedCornerPosition = RoundedDisplayGutter::RoundedCorner::Position; |
| 29 | using ProviderStrategy = RoundedDisplayProvider::Strategy; |
| 30 | |
| 31 | constexpr ProviderStrategy kDefaultTestStrategy = ProviderStrategy::kScanout; |
| 32 | |
| 33 | gfx::RoundedCornersF CreateHorizontallyUniformRadii(int radius_a, |
| 34 | int radius_b) { |
| 35 | return gfx::RoundedCornersF(radius_a, radius_a, radius_b, radius_b); |
| 36 | } |
| 37 | |
| 38 | // The matcher matches a RoundedDisplayGutter that has the rounded corners of |
| 39 | // `positions`. |
| 40 | template <typename... Matchers> |
| 41 | auto GutterWithMatchingCorners(Matchers&&... positions) { |
| 42 | return testing::ResultOf( |
| 43 | "positions", |
| 44 | [](const RoundedDisplayGutter* gutter) { |
| 45 | std::vector<RoundedCornerPosition> positions; |
| 46 | const std::vector<RoundedCorner>& corners = gutter->GetGutterCorners(); |
| 47 | base::ranges::transform( |
| 48 | corners.begin(), corners.end(), std::back_inserter(positions), |
| 49 | [](const RoundedCorner& corner) { return corner.position(); }); |
| 50 | return positions; |
| 51 | }, |
| 52 | testing::UnorderedElementsAre(positions...)); |
| 53 | } |
| 54 | |
| 55 | class RoundedDisplayProviderTest : public AshTestBase { |
| 56 | public: |
| 57 | RoundedDisplayProviderTest() = default; |
| 58 | |
| 59 | RoundedDisplayProviderTest(const RoundedDisplayProviderTest&) = delete; |
| 60 | RoundedDisplayProviderTest& operator=(const RoundedDisplayProviderTest&) = |
| 61 | delete; |
| 62 | |
| 63 | ~RoundedDisplayProviderTest() override = default; |
| 64 | |
| 65 | void SetUp() override { |
| 66 | AshTestBase::SetUp(); |
| 67 | // Create the rounded display provider for the primary display. |
| 68 | provider_ = RoundedDisplayProvider::Create(GetPrimaryDisplay().id()); |
| 69 | } |
| 70 | |
| 71 | void TearDown() override { |
| 72 | provider_.reset(); |
| 73 | AshTestBase::TearDown(); |
| 74 | } |
| 75 | |
| 76 | protected: |
| 77 | const display::Display& GetDisplay(int64_t display_id) { |
| 78 | return display_manager()->GetDisplayForId(display_id); |
| 79 | } |
| 80 | |
| 81 | std::unique_ptr<RoundedDisplayProvider> provider_; |
| 82 | }; |
| 83 | |
| 84 | TEST_F(RoundedDisplayProviderTest, InitializeWithNonUniformRadii) { |
| 85 | RoundedDisplayProviderTestApi test_api(provider_.get()); |
| 86 | |
| 87 | const gfx::RoundedCornersF not_valid_radii(10, 11, 12, 12); |
| 88 | |
| 89 | EXPECT_DCHECK_DEATH( |
| 90 | { provider_->Init(not_valid_radii, kDefaultTestStrategy); }); |
| 91 | } |
| 92 | |
| 93 | TEST_F(RoundedDisplayProviderTest, CorrectNumberOfGuttersAreProvided) { |
| 94 | RoundedDisplayProviderTestApi test_api(provider_.get()); |
| 95 | const gfx::RoundedCornersF radii = CreateHorizontallyUniformRadii(10, 12); |
| 96 | |
Zoraiz Naeem | 3fff1f26 | 2023-04-21 13:21:51 | [diff] [blame] | 97 | // We expect 2 overlay gutters to be created. |
Zoraiz Naeem | 6a49a56 | 2023-03-06 20:33:13 | [diff] [blame] | 98 | provider_->Init(radii, kDefaultTestStrategy); |
Zoraiz Naeem | 3fff1f26 | 2023-04-21 13:21:51 | [diff] [blame] | 99 | EXPECT_EQ(test_api.GetGutters().size(), 2u); |
Zoraiz Naeem | 6a49a56 | 2023-03-06 20:33:13 | [diff] [blame] | 100 | } |
| 101 | |
| 102 | TEST_F(RoundedDisplayProviderTest, |
| 103 | CorrectGutterCreatedForStrategy_ScanoutDirection) { |
| 104 | RoundedDisplayProviderTestApi test_api(provider_.get()); |
| 105 | const gfx::RoundedCornersF radii = CreateHorizontallyUniformRadii(10, 12); |
| 106 | |
| 107 | provider_->Init(radii, ProviderStrategy::kScanout); |
| 108 | |
| 109 | const auto& gutters = test_api.GetGutters(); |
| 110 | |
Zoraiz Naeem | 3fff1f26 | 2023-04-21 13:21:51 | [diff] [blame] | 111 | EXPECT_EQ(gutters.size(), 2u); |
Zoraiz Naeem | 6a49a56 | 2023-03-06 20:33:13 | [diff] [blame] | 112 | |
| 113 | // Check that we have two overlay gutters that in the scanout direction. |
| 114 | EXPECT_THAT(gutters, testing::Contains(GutterWithMatchingCorners( |
| 115 | RoundedCornerPosition::kUpperLeft, |
| 116 | RoundedCornerPosition::kUpperRight))); |
| 117 | EXPECT_THAT(gutters, testing::Contains(GutterWithMatchingCorners( |
| 118 | RoundedCornerPosition::kLowerLeft, |
| 119 | RoundedCornerPosition::kLowerRight))); |
| 120 | } |
| 121 | |
| 122 | TEST_F(RoundedDisplayProviderTest, |
| 123 | CorrectGutterCreatedForStrategy_OtherDirection) { |
| 124 | RoundedDisplayProviderTestApi test_api(provider_.get()); |
| 125 | const gfx::RoundedCornersF radii = CreateHorizontallyUniformRadii(10, 12); |
| 126 | |
| 127 | provider_->Init(radii, ProviderStrategy::kOther); |
| 128 | |
| 129 | const auto& gutters = test_api.GetGutters(); |
| 130 | |
Zoraiz Naeem | 3fff1f26 | 2023-04-21 13:21:51 | [diff] [blame] | 131 | EXPECT_EQ(gutters.size(), 2u); |
Zoraiz Naeem | 6a49a56 | 2023-03-06 20:33:13 | [diff] [blame] | 132 | |
| 133 | // Check that we have two overlay gutters that in the scanout direction. |
| 134 | EXPECT_THAT(gutters, testing::Contains(GutterWithMatchingCorners( |
| 135 | RoundedCornerPosition::kUpperLeft, |
| 136 | RoundedCornerPosition::kLowerLeft))); |
| 137 | // Right overlay gutter. |
| 138 | EXPECT_THAT(gutters, testing::Contains(GutterWithMatchingCorners( |
| 139 | RoundedCornerPosition::kUpperRight, |
| 140 | RoundedCornerPosition::kLowerRight))); |
| 141 | } |
| 142 | |
| 143 | class RoundedDisplayProviderSurfaceUpdateTest |
| 144 | : public RoundedDisplayProviderTest, |
| 145 | public ::testing::WithParamInterface< |
| 146 | std::tuple<std::string, std::string, bool>> { |
| 147 | public: |
| 148 | RoundedDisplayProviderSurfaceUpdateTest() |
| 149 | : initial_display_spec_(std::get<0>(GetParam())), |
| 150 | updated_display_spec_(std::get<1>(GetParam())), |
| 151 | expect_update_(std::get<2>(GetParam())) {} |
| 152 | |
| 153 | RoundedDisplayProviderSurfaceUpdateTest( |
| 154 | const RoundedDisplayProviderSurfaceUpdateTest&) = delete; |
| 155 | RoundedDisplayProviderSurfaceUpdateTest& operator=( |
| 156 | const RoundedDisplayProviderSurfaceUpdateTest&) = delete; |
| 157 | |
| 158 | ~RoundedDisplayProviderSurfaceUpdateTest() override = default; |
| 159 | |
| 160 | protected: |
| 161 | std::string initial_display_spec_; |
| 162 | std::string updated_display_spec_; |
| 163 | bool expect_update_; |
| 164 | }; |
| 165 | |
| 166 | TEST_P(RoundedDisplayProviderSurfaceUpdateTest, |
| 167 | UpdatesSurfaceOnlyWhenNecessary) { |
| 168 | RoundedDisplayProviderTestApi test_api(provider_.get()); |
| 169 | |
| 170 | display::Display primary_display = |
| 171 | display::Screen::GetScreen()->GetPrimaryDisplay(); |
| 172 | |
| 173 | auto display_id = primary_display.id(); |
| 174 | |
| 175 | UpdateDisplay(initial_display_spec_); |
| 176 | |
| 177 | gfx::RoundedCornersF radii = |
Zoraiz Naeem | 72bf416 | 2023-04-26 21:43:55 | [diff] [blame] | 178 | display_manager()->GetDisplayInfo(display_id).panel_corners_radii(); |
Zoraiz Naeem | 6a49a56 | 2023-03-06 20:33:13 | [diff] [blame] | 179 | |
| 180 | provider_->Init(radii, kDefaultTestStrategy); |
| 181 | |
| 182 | ASSERT_TRUE(provider_->UpdateRoundedDisplaySurface()); |
| 183 | |
| 184 | UpdateDisplay(updated_display_spec_); |
| 185 | |
| 186 | Gutters before_update_gutters = test_api.GetGutters(); |
| 187 | ASSERT_EQ(provider_->UpdateRoundedDisplaySurface(), expect_update_); |
| 188 | Gutters after_update_gutters = test_api.GetGutters(); |
| 189 | |
| 190 | // Confirm that we did not change gutters. |
| 191 | EXPECT_EQ(before_update_gutters, after_update_gutters); |
| 192 | } |
| 193 | |
| 194 | const std::string kInitialDisplaySpec = "500x400~15"; |
| 195 | const std::string kInitialDisplaySpecWithRotation = "500x400/r~15"; |
| 196 | |
| 197 | INSTANTIATE_TEST_SUITE_P( |
| 198 | /* no prefix */, |
| 199 | RoundedDisplayProviderSurfaceUpdateTest, |
| 200 | testing::Values( |
| 201 | // If nothing changes, we should skip surface update. |
| 202 | std::make_tuple(kInitialDisplaySpec, |
| 203 | "500x400~15", |
| 204 | /*expect_update=*/false), |
| 205 | // Change in device scale factor, should only cause a surface update. |
| 206 | std::make_tuple(kInitialDisplaySpec, |
| 207 | "500x400*2~15", |
| 208 | /*expect_update=*/true), |
| 209 | // Further display rotation, should only cause a surface update. |
| 210 | std::make_tuple(kInitialDisplaySpecWithRotation, |
| 211 | "500x400/u~15", |
| 212 | /*expect_update=*/true), |
| 213 | // Multiple spec changes, should appropriately cause a surface update. |
| 214 | std::make_tuple(kInitialDisplaySpec, |
| 215 | "500x400*2~15", |
| 216 | /*expect_update=*/true))); |
| 217 | |
| 218 | } // namespace |
| 219 | } // namespace ash |