| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 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 <stdint.h> | |
| 6 #include <X11/extensions/Xrandr.h> | |
| 7 | |
| 8 #undef Bool | |
| 9 #undef None | |
| 10 | |
| 11 #include "base/macros.h" | |
| 12 #include "base/test/simple_test_tick_clock.h" | |
| 13 #include "testing/gtest/include/gtest/gtest.h" | |
| 14 #include "ui/display/chromeos/x11/display_mode_x11.h" | |
| 15 #include "ui/display/chromeos/x11/display_snapshot_x11.h" | |
| 16 #include "ui/display/chromeos/x11/native_display_delegate_x11.h" | |
| 17 #include "ui/display/chromeos/x11/native_display_event_dispatcher_x11.h" | |
| 18 | |
| 19 namespace ui { | |
| 20 | |
| 21 namespace { | |
| 22 | |
| 23 DisplaySnapshotX11* CreateOutput(int64_t id, | |
| 24 DisplayConnectionType type, | |
| 25 RROutput output, | |
| 26 RRCrtc crtc) { | |
| 27 static const DisplayModeX11 kDefaultDisplayMode(gfx::Size(1, 1), | |
| 28 false, | |
| 29 60.0f, | |
| 30 20); | |
| 31 std::vector<std::unique_ptr<const DisplayMode>> modes; | |
| 32 const DisplayMode* mode; | |
| 33 | |
| 34 modes.push_back(kDefaultDisplayMode.Clone()); | |
| 35 mode = modes.front().get(); | |
| 36 | |
| 37 DisplaySnapshotX11* snapshot = new DisplaySnapshotX11( | |
| 38 id, | |
| 39 gfx::Point(0, 0), | |
| 40 gfx::Size(0, 0), | |
| 41 type, | |
| 42 false, | |
| 43 false, | |
| 44 std::string(), | |
| 45 std::move(modes), | |
| 46 std::vector<uint8_t>(), | |
| 47 mode, | |
| 48 NULL, | |
| 49 output, | |
| 50 crtc, | |
| 51 0); | |
| 52 | |
| 53 return snapshot; | |
| 54 } | |
| 55 | |
| 56 DisplaySnapshotX11* CreateExternalOutput(RROutput output, RRCrtc crtc) { | |
| 57 return CreateOutput(static_cast<int64_t>(output), | |
| 58 DISPLAY_CONNECTION_TYPE_UNKNOWN, | |
| 59 output, | |
| 60 crtc); | |
| 61 } | |
| 62 | |
| 63 DisplaySnapshotX11* CreateInternalOutput(RROutput output, RRCrtc crtc) { | |
| 64 return CreateOutput(0, | |
| 65 DISPLAY_CONNECTION_TYPE_INTERNAL, | |
| 66 output, | |
| 67 crtc); | |
| 68 } | |
| 69 | |
| 70 class TestHelperDelegate : public NativeDisplayDelegateX11::HelperDelegate { | |
| 71 public: | |
| 72 TestHelperDelegate(); | |
| 73 ~TestHelperDelegate() override; | |
| 74 | |
| 75 int num_calls_update_xrandr_config() const { | |
| 76 return num_calls_update_xrandr_config_; | |
| 77 } | |
| 78 | |
| 79 int num_calls_notify_observers() const { return num_calls_notify_observers_; } | |
| 80 | |
| 81 void set_cached_outputs(const std::vector<DisplaySnapshot*>& outputs) { | |
| 82 cached_outputs_ = outputs; | |
| 83 } | |
| 84 | |
| 85 // NativeDisplayDelegateX11::HelperDelegate overrides: | |
| 86 void UpdateXRandRConfiguration(const base::NativeEvent& event) override; | |
| 87 const std::vector<DisplaySnapshot*>& GetCachedDisplays() const override; | |
| 88 void NotifyDisplayObservers() override; | |
| 89 | |
| 90 private: | |
| 91 int num_calls_update_xrandr_config_; | |
| 92 int num_calls_notify_observers_; | |
| 93 | |
| 94 std::vector<DisplaySnapshot*> cached_outputs_; | |
| 95 | |
| 96 DISALLOW_COPY_AND_ASSIGN(TestHelperDelegate); | |
| 97 }; | |
| 98 | |
| 99 TestHelperDelegate::TestHelperDelegate() | |
| 100 : num_calls_update_xrandr_config_(0), num_calls_notify_observers_(0) {} | |
| 101 | |
| 102 TestHelperDelegate::~TestHelperDelegate() {} | |
| 103 | |
| 104 void TestHelperDelegate::UpdateXRandRConfiguration( | |
| 105 const base::NativeEvent& event) { | |
| 106 ++num_calls_update_xrandr_config_; | |
| 107 } | |
| 108 | |
| 109 const std::vector<DisplaySnapshot*>& TestHelperDelegate::GetCachedDisplays() | |
| 110 const { | |
| 111 return cached_outputs_; | |
| 112 } | |
| 113 | |
| 114 void TestHelperDelegate::NotifyDisplayObservers() { | |
| 115 ++num_calls_notify_observers_; | |
| 116 } | |
| 117 | |
| 118 //////////////////////////////////////////////////////////////////////////////// | |
| 119 // NativeDisplayEventDispatcherX11Test | |
| 120 | |
| 121 class NativeDisplayEventDispatcherX11Test : public testing::Test { | |
| 122 public: | |
| 123 NativeDisplayEventDispatcherX11Test(); | |
| 124 ~NativeDisplayEventDispatcherX11Test() override; | |
| 125 | |
| 126 protected: | |
| 127 void DispatchScreenChangeEvent(); | |
| 128 void DispatchOutputChangeEvent(RROutput output, | |
| 129 RRCrtc crtc, | |
| 130 RRMode mode, | |
| 131 bool connected); | |
| 132 | |
| 133 int xrandr_event_base_; | |
| 134 std::unique_ptr<TestHelperDelegate> helper_delegate_; | |
| 135 std::unique_ptr<NativeDisplayEventDispatcherX11> dispatcher_; | |
| 136 base::SimpleTestTickClock* test_tick_clock_; // Owned by |dispatcher_|. | |
| 137 | |
| 138 private: | |
| 139 DISALLOW_COPY_AND_ASSIGN(NativeDisplayEventDispatcherX11Test); | |
| 140 }; | |
| 141 | |
| 142 NativeDisplayEventDispatcherX11Test::NativeDisplayEventDispatcherX11Test() | |
| 143 : xrandr_event_base_(10), | |
| 144 helper_delegate_(new TestHelperDelegate()), | |
| 145 dispatcher_(new NativeDisplayEventDispatcherX11(helper_delegate_.get(), | |
| 146 xrandr_event_base_)), | |
| 147 test_tick_clock_(new base::SimpleTestTickClock) { | |
| 148 test_tick_clock_->Advance(base::TimeDelta::FromMilliseconds(1)); | |
| 149 dispatcher_->SetTickClockForTest( | |
| 150 std::unique_ptr<base::TickClock>(test_tick_clock_)); | |
| 151 } | |
| 152 | |
| 153 NativeDisplayEventDispatcherX11Test::~NativeDisplayEventDispatcherX11Test() {} | |
| 154 | |
| 155 void NativeDisplayEventDispatcherX11Test::DispatchScreenChangeEvent() { | |
| 156 XRRScreenChangeNotifyEvent event = {0}; | |
| 157 event.type = xrandr_event_base_ + RRScreenChangeNotify; | |
| 158 | |
| 159 dispatcher_->DispatchEvent(reinterpret_cast<const PlatformEvent>(&event)); | |
| 160 } | |
| 161 | |
| 162 void NativeDisplayEventDispatcherX11Test::DispatchOutputChangeEvent( | |
| 163 RROutput output, | |
| 164 RRCrtc crtc, | |
| 165 RRMode mode, | |
| 166 bool connected) { | |
| 167 XRROutputChangeNotifyEvent event = {0}; | |
| 168 event.type = xrandr_event_base_ + RRNotify; | |
| 169 event.subtype = RRNotify_OutputChange; | |
| 170 event.output = output; | |
| 171 event.crtc = crtc; | |
| 172 event.mode = mode; | |
| 173 event.connection = connected ? RR_Connected : RR_Disconnected; | |
| 174 | |
| 175 dispatcher_->DispatchEvent(reinterpret_cast<const PlatformEvent>(&event)); | |
| 176 } | |
| 177 | |
| 178 } // namespace | |
| 179 | |
| 180 TEST_F(NativeDisplayEventDispatcherX11Test, OnScreenChangedEvent) { | |
| 181 DispatchScreenChangeEvent(); | |
| 182 EXPECT_EQ(1, helper_delegate_->num_calls_update_xrandr_config()); | |
| 183 EXPECT_EQ(0, helper_delegate_->num_calls_notify_observers()); | |
| 184 } | |
| 185 | |
| 186 TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationOnFirstEvent) { | |
| 187 DispatchOutputChangeEvent(1, 10, 20, true); | |
| 188 EXPECT_EQ(0, helper_delegate_->num_calls_update_xrandr_config()); | |
| 189 EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers()); | |
| 190 } | |
| 191 | |
| 192 TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationAfterSecondEvent) { | |
| 193 DispatchOutputChangeEvent(1, 10, 20, true); | |
| 194 | |
| 195 // Simulate addition of the first output to the cached output list. | |
| 196 ScopedVector<DisplaySnapshot> outputs; | |
| 197 outputs.push_back(CreateExternalOutput(1, 10)); | |
| 198 helper_delegate_->set_cached_outputs(outputs.get()); | |
| 199 | |
| 200 DispatchOutputChangeEvent(2, 11, 20, true); | |
| 201 EXPECT_EQ(2, helper_delegate_->num_calls_notify_observers()); | |
| 202 } | |
| 203 | |
| 204 TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationOnDisconnect) { | |
| 205 ScopedVector<DisplaySnapshot> outputs; | |
| 206 outputs.push_back(CreateExternalOutput(1, 10)); | |
| 207 helper_delegate_->set_cached_outputs(outputs.get()); | |
| 208 | |
| 209 DispatchOutputChangeEvent(1, 10, 20, false); | |
| 210 EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers()); | |
| 211 } | |
| 212 | |
| 213 TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationOnModeChange) { | |
| 214 ScopedVector<DisplaySnapshot> outputs; | |
| 215 outputs.push_back(CreateExternalOutput(1, 10)); | |
| 216 helper_delegate_->set_cached_outputs(outputs.get()); | |
| 217 | |
| 218 DispatchOutputChangeEvent(1, 10, 21, true); | |
| 219 EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers()); | |
| 220 } | |
| 221 | |
| 222 TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationOnSecondOutput) { | |
| 223 ScopedVector<DisplaySnapshot> outputs; | |
| 224 outputs.push_back(CreateExternalOutput(1, 10)); | |
| 225 helper_delegate_->set_cached_outputs(outputs.get()); | |
| 226 | |
| 227 DispatchOutputChangeEvent(2, 11, 20, true); | |
| 228 EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers()); | |
| 229 } | |
| 230 | |
| 231 TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationOnDifferentCrtc) { | |
| 232 ScopedVector<DisplaySnapshot> outputs; | |
| 233 outputs.push_back(CreateExternalOutput(1, 10)); | |
| 234 helper_delegate_->set_cached_outputs(outputs.get()); | |
| 235 | |
| 236 DispatchOutputChangeEvent(1, 11, 20, true); | |
| 237 EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers()); | |
| 238 } | |
| 239 | |
| 240 TEST_F(NativeDisplayEventDispatcherX11Test, | |
| 241 CheckNotificationOnSecondOutputDisconnect) { | |
| 242 ScopedVector<DisplaySnapshot> outputs; | |
| 243 outputs.push_back(CreateExternalOutput(1, 10)); | |
| 244 outputs.push_back(CreateExternalOutput(2, 11)); | |
| 245 helper_delegate_->set_cached_outputs(outputs.get()); | |
| 246 | |
| 247 DispatchOutputChangeEvent(2, 11, 20, false); | |
| 248 EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers()); | |
| 249 } | |
| 250 | |
| 251 TEST_F(NativeDisplayEventDispatcherX11Test, | |
| 252 AvoidDuplicateNotificationOnSecondOutputDisconnect) { | |
| 253 ScopedVector<DisplaySnapshot> outputs; | |
| 254 outputs.push_back(CreateExternalOutput(1, 10)); | |
| 255 outputs.push_back(CreateExternalOutput(2, 11)); | |
| 256 helper_delegate_->set_cached_outputs(outputs.get()); | |
| 257 | |
| 258 DispatchOutputChangeEvent(2, 11, 20, false); | |
| 259 EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers()); | |
| 260 | |
| 261 // Simulate removal of second output from cached output list. | |
| 262 outputs.erase(outputs.begin() + 1); | |
| 263 helper_delegate_->set_cached_outputs(outputs.get()); | |
| 264 | |
| 265 DispatchOutputChangeEvent(2, 11, 20, false); | |
| 266 EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers()); | |
| 267 } | |
| 268 | |
| 269 TEST_F(NativeDisplayEventDispatcherX11Test, | |
| 270 ForceUpdateAfterCacheExpiration) { | |
| 271 // +1 to compenstate a possible rounding error. | |
| 272 const int kHalfOfExpirationMs = | |
| 273 NativeDisplayEventDispatcherX11::kUseCacheAfterStartupMs / 2 + 1; | |
| 274 | |
| 275 ScopedVector<DisplaySnapshot> outputs; | |
| 276 outputs.push_back(CreateExternalOutput(1, 10)); | |
| 277 outputs.push_back(CreateExternalOutput(2, 11)); | |
| 278 helper_delegate_->set_cached_outputs(outputs.get()); | |
| 279 | |
| 280 EXPECT_EQ(0, helper_delegate_->num_calls_notify_observers()); | |
| 281 | |
| 282 // Duplicated event will be ignored during the startup. | |
| 283 DispatchOutputChangeEvent(2, 11, 20, true); | |
| 284 EXPECT_EQ(0, helper_delegate_->num_calls_notify_observers()); | |
| 285 | |
| 286 test_tick_clock_->Advance(base::TimeDelta::FromMilliseconds( | |
| 287 kHalfOfExpirationMs)); | |
| 288 | |
| 289 // Duplicated event will still be ignored. | |
| 290 DispatchOutputChangeEvent(2, 11, 20, true); | |
| 291 EXPECT_EQ(0, helper_delegate_->num_calls_notify_observers()); | |
| 292 | |
| 293 // The startup timeout has been elapsed. Duplicated event | |
| 294 // should not be ignored. | |
| 295 test_tick_clock_->Advance( | |
| 296 base::TimeDelta::FromMilliseconds(kHalfOfExpirationMs)); | |
| 297 DispatchOutputChangeEvent(2, 11, 20, true); | |
| 298 EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers()); | |
| 299 | |
| 300 // Sending the same event immediately shoudldn't be ignored. | |
| 301 DispatchOutputChangeEvent(2, 11, 20, true); | |
| 302 EXPECT_EQ(2, helper_delegate_->num_calls_notify_observers()); | |
| 303 | |
| 304 // Advancing time further should not change the behavior. | |
| 305 test_tick_clock_->Advance(base::TimeDelta::FromMilliseconds( | |
| 306 kHalfOfExpirationMs)); | |
| 307 DispatchOutputChangeEvent(2, 11, 20, true); | |
| 308 EXPECT_EQ(3, helper_delegate_->num_calls_notify_observers()); | |
| 309 | |
| 310 test_tick_clock_->Advance( | |
| 311 base::TimeDelta::FromMilliseconds(kHalfOfExpirationMs)); | |
| 312 DispatchOutputChangeEvent(2, 11, 20, true); | |
| 313 EXPECT_EQ(4, helper_delegate_->num_calls_notify_observers()); | |
| 314 } | |
| 315 | |
| 316 TEST_F(NativeDisplayEventDispatcherX11Test, | |
| 317 UpdateMissingExternalDisplayId) { | |
| 318 ScopedVector<DisplaySnapshot> outputs; | |
| 319 outputs.push_back(CreateInternalOutput(1, 10)); | |
| 320 helper_delegate_->set_cached_outputs(outputs.get()); | |
| 321 | |
| 322 ASSERT_EQ(0, helper_delegate_->num_calls_notify_observers()); | |
| 323 | |
| 324 // Internal display's ID can be zero and not updated. | |
| 325 DispatchOutputChangeEvent(1, 10, 20, true); | |
| 326 EXPECT_EQ(0, helper_delegate_->num_calls_notify_observers()); | |
| 327 | |
| 328 outputs.clear(); | |
| 329 outputs.push_back(CreateOutput(0, DISPLAY_CONNECTION_TYPE_UNKNOWN, 2, 11)); | |
| 330 helper_delegate_->set_cached_outputs(outputs.get()); | |
| 331 | |
| 332 // External display should be updated if the id is zero. | |
| 333 DispatchOutputChangeEvent(2, 11, 20, true); | |
| 334 EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers()); | |
| 335 } | |
| 336 | |
| 337 } // namespace ui | |
| OLD | NEW |