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.
5 #include <X11/extensions/Xrandr.h>
10 #include "base/test/simple_test_tick_clock.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12 #include "ui/display/chromeos/x11/display_mode_x11.h"
13 #include "ui/display/chromeos/x11/display_snapshot_x11.h"
14 #include "ui/display/chromeos/x11/native_display_delegate_x11.h"
15 #include "ui/display/chromeos/x11/native_display_event_dispatcher_x11.h"
21 DisplaySnapshotX11
* CreateOutput(int64_t id
,
22 DisplayConnectionType type
,
25 static const DisplayModeX11
* kDefaultDisplayMode
=
26 new DisplayModeX11(gfx::Size(1, 1), false, 60.0f
, 20);
28 DisplaySnapshotX11
* snapshot
= new DisplaySnapshotX11(
36 std::vector
<const DisplayMode
*>(1, kDefaultDisplayMode
),
46 DisplaySnapshotX11
* CreateExternalOutput(RROutput output
, RRCrtc crtc
) {
47 return CreateOutput(static_cast<int64_t>(output
),
48 DISPLAY_CONNECTION_TYPE_UNKNOWN
,
53 DisplaySnapshotX11
* CreateInternalOutput(RROutput output
, RRCrtc crtc
) {
54 return CreateOutput(0,
55 DISPLAY_CONNECTION_TYPE_INTERNAL
,
60 class TestHelperDelegate
: public NativeDisplayDelegateX11::HelperDelegate
{
63 ~TestHelperDelegate() override
;
65 int num_calls_update_xrandr_config() const {
66 return num_calls_update_xrandr_config_
;
69 int num_calls_notify_observers() const { return num_calls_notify_observers_
; }
71 void set_cached_outputs(const std::vector
<DisplaySnapshot
*>& outputs
) {
72 cached_outputs_
= outputs
;
75 // NativeDisplayDelegateX11::HelperDelegate overrides:
76 void UpdateXRandRConfiguration(const base::NativeEvent
& event
) override
;
77 const std::vector
<DisplaySnapshot
*>& GetCachedDisplays() const override
;
78 void NotifyDisplayObservers() override
;
81 int num_calls_update_xrandr_config_
;
82 int num_calls_notify_observers_
;
84 std::vector
<DisplaySnapshot
*> cached_outputs_
;
86 DISALLOW_COPY_AND_ASSIGN(TestHelperDelegate
);
89 TestHelperDelegate::TestHelperDelegate()
90 : num_calls_update_xrandr_config_(0), num_calls_notify_observers_(0) {}
92 TestHelperDelegate::~TestHelperDelegate() {}
94 void TestHelperDelegate::UpdateXRandRConfiguration(
95 const base::NativeEvent
& event
) {
96 ++num_calls_update_xrandr_config_
;
99 const std::vector
<DisplaySnapshot
*>& TestHelperDelegate::GetCachedDisplays()
101 return cached_outputs_
;
104 void TestHelperDelegate::NotifyDisplayObservers() {
105 ++num_calls_notify_observers_
;
108 ////////////////////////////////////////////////////////////////////////////////
109 // NativeDisplayEventDispatcherX11Test
111 class NativeDisplayEventDispatcherX11Test
: public testing::Test
{
113 NativeDisplayEventDispatcherX11Test();
114 ~NativeDisplayEventDispatcherX11Test() override
;
117 void DispatchScreenChangeEvent();
118 void DispatchOutputChangeEvent(RROutput output
,
123 int xrandr_event_base_
;
124 scoped_ptr
<TestHelperDelegate
> helper_delegate_
;
125 scoped_ptr
<NativeDisplayEventDispatcherX11
> dispatcher_
;
126 base::SimpleTestTickClock
* test_tick_clock_
; // Owned by |dispatcher_|.
129 DISALLOW_COPY_AND_ASSIGN(NativeDisplayEventDispatcherX11Test
);
132 NativeDisplayEventDispatcherX11Test::NativeDisplayEventDispatcherX11Test()
133 : xrandr_event_base_(10),
134 helper_delegate_(new TestHelperDelegate()),
135 dispatcher_(new NativeDisplayEventDispatcherX11(helper_delegate_
.get(),
136 xrandr_event_base_
)),
137 test_tick_clock_(new base::SimpleTestTickClock
) {
138 test_tick_clock_
->Advance(base::TimeDelta::FromMilliseconds(1));
139 dispatcher_
->SetTickClockForTest(
140 scoped_ptr
<base::TickClock
>(test_tick_clock_
));
143 NativeDisplayEventDispatcherX11Test::~NativeDisplayEventDispatcherX11Test() {}
145 void NativeDisplayEventDispatcherX11Test::DispatchScreenChangeEvent() {
146 XRRScreenChangeNotifyEvent event
= {0};
147 event
.type
= xrandr_event_base_
+ RRScreenChangeNotify
;
149 dispatcher_
->DispatchEvent(reinterpret_cast<const PlatformEvent
>(&event
));
152 void NativeDisplayEventDispatcherX11Test::DispatchOutputChangeEvent(
157 XRROutputChangeNotifyEvent event
= {0};
158 event
.type
= xrandr_event_base_
+ RRNotify
;
159 event
.subtype
= RRNotify_OutputChange
;
160 event
.output
= output
;
163 event
.connection
= connected
? RR_Connected
: RR_Disconnected
;
165 dispatcher_
->DispatchEvent(reinterpret_cast<const PlatformEvent
>(&event
));
170 TEST_F(NativeDisplayEventDispatcherX11Test
, OnScreenChangedEvent
) {
171 DispatchScreenChangeEvent();
172 EXPECT_EQ(1, helper_delegate_
->num_calls_update_xrandr_config());
173 EXPECT_EQ(0, helper_delegate_
->num_calls_notify_observers());
176 TEST_F(NativeDisplayEventDispatcherX11Test
, CheckNotificationOnFirstEvent
) {
177 DispatchOutputChangeEvent(1, 10, 20, true);
178 EXPECT_EQ(0, helper_delegate_
->num_calls_update_xrandr_config());
179 EXPECT_EQ(1, helper_delegate_
->num_calls_notify_observers());
182 TEST_F(NativeDisplayEventDispatcherX11Test
, CheckNotificationAfterSecondEvent
) {
183 DispatchOutputChangeEvent(1, 10, 20, true);
185 // Simulate addition of the first output to the cached output list.
186 ScopedVector
<DisplaySnapshot
> outputs
;
187 outputs
.push_back(CreateExternalOutput(1, 10));
188 helper_delegate_
->set_cached_outputs(outputs
.get());
190 DispatchOutputChangeEvent(2, 11, 20, true);
191 EXPECT_EQ(2, helper_delegate_
->num_calls_notify_observers());
194 TEST_F(NativeDisplayEventDispatcherX11Test
, CheckNotificationOnDisconnect
) {
195 ScopedVector
<DisplaySnapshot
> outputs
;
196 outputs
.push_back(CreateExternalOutput(1, 10));
197 helper_delegate_
->set_cached_outputs(outputs
.get());
199 DispatchOutputChangeEvent(1, 10, 20, false);
200 EXPECT_EQ(1, helper_delegate_
->num_calls_notify_observers());
203 TEST_F(NativeDisplayEventDispatcherX11Test
, CheckNotificationOnModeChange
) {
204 ScopedVector
<DisplaySnapshot
> outputs
;
205 outputs
.push_back(CreateExternalOutput(1, 10));
206 helper_delegate_
->set_cached_outputs(outputs
.get());
208 DispatchOutputChangeEvent(1, 10, 21, true);
209 EXPECT_EQ(1, helper_delegate_
->num_calls_notify_observers());
212 TEST_F(NativeDisplayEventDispatcherX11Test
, CheckNotificationOnSecondOutput
) {
213 ScopedVector
<DisplaySnapshot
> outputs
;
214 outputs
.push_back(CreateExternalOutput(1, 10));
215 helper_delegate_
->set_cached_outputs(outputs
.get());
217 DispatchOutputChangeEvent(2, 11, 20, true);
218 EXPECT_EQ(1, helper_delegate_
->num_calls_notify_observers());
221 TEST_F(NativeDisplayEventDispatcherX11Test
, CheckNotificationOnDifferentCrtc
) {
222 ScopedVector
<DisplaySnapshot
> outputs
;
223 outputs
.push_back(CreateExternalOutput(1, 10));
224 helper_delegate_
->set_cached_outputs(outputs
.get());
226 DispatchOutputChangeEvent(1, 11, 20, true);
227 EXPECT_EQ(1, helper_delegate_
->num_calls_notify_observers());
230 TEST_F(NativeDisplayEventDispatcherX11Test
,
231 CheckNotificationOnSecondOutputDisconnect
) {
232 ScopedVector
<DisplaySnapshot
> outputs
;
233 outputs
.push_back(CreateExternalOutput(1, 10));
234 outputs
.push_back(CreateExternalOutput(2, 11));
235 helper_delegate_
->set_cached_outputs(outputs
.get());
237 DispatchOutputChangeEvent(2, 11, 20, false);
238 EXPECT_EQ(1, helper_delegate_
->num_calls_notify_observers());
241 TEST_F(NativeDisplayEventDispatcherX11Test
,
242 AvoidDuplicateNotificationOnSecondOutputDisconnect
) {
243 ScopedVector
<DisplaySnapshot
> outputs
;
244 outputs
.push_back(CreateExternalOutput(1, 10));
245 outputs
.push_back(CreateExternalOutput(2, 11));
246 helper_delegate_
->set_cached_outputs(outputs
.get());
248 DispatchOutputChangeEvent(2, 11, 20, false);
249 EXPECT_EQ(1, helper_delegate_
->num_calls_notify_observers());
251 // Simulate removal of second output from cached output list.
252 outputs
.erase(outputs
.begin() + 1);
253 helper_delegate_
->set_cached_outputs(outputs
.get());
255 DispatchOutputChangeEvent(2, 11, 20, false);
256 EXPECT_EQ(1, helper_delegate_
->num_calls_notify_observers());
259 TEST_F(NativeDisplayEventDispatcherX11Test
,
260 ForceUpdateAfterCacheExpiration
) {
261 // +1 to compenstate a possible rounding error.
262 const int kHalfOfExpirationMs
=
263 NativeDisplayEventDispatcherX11::kUseCacheAfterStartupMs
/ 2 + 1;
265 ScopedVector
<DisplaySnapshot
> outputs
;
266 outputs
.push_back(CreateExternalOutput(1, 10));
267 outputs
.push_back(CreateExternalOutput(2, 11));
268 helper_delegate_
->set_cached_outputs(outputs
.get());
270 EXPECT_EQ(0, helper_delegate_
->num_calls_notify_observers());
272 // Duplicated event will be ignored during the startup.
273 DispatchOutputChangeEvent(2, 11, 20, true);
274 EXPECT_EQ(0, helper_delegate_
->num_calls_notify_observers());
276 test_tick_clock_
->Advance(base::TimeDelta::FromMilliseconds(
277 kHalfOfExpirationMs
));
279 // Duplicated event will still be ignored.
280 DispatchOutputChangeEvent(2, 11, 20, true);
281 EXPECT_EQ(0, helper_delegate_
->num_calls_notify_observers());
283 // The startup timeout has been elapsed. Duplicated event
284 // should not be ignored.
285 test_tick_clock_
->Advance(
286 base::TimeDelta::FromMilliseconds(kHalfOfExpirationMs
));
287 DispatchOutputChangeEvent(2, 11, 20, true);
288 EXPECT_EQ(1, helper_delegate_
->num_calls_notify_observers());
290 // Sending the same event immediately shoudldn't be ignored.
291 DispatchOutputChangeEvent(2, 11, 20, true);
292 EXPECT_EQ(2, helper_delegate_
->num_calls_notify_observers());
294 // Advancing time further should not change the behavior.
295 test_tick_clock_
->Advance(base::TimeDelta::FromMilliseconds(
296 kHalfOfExpirationMs
));
297 DispatchOutputChangeEvent(2, 11, 20, true);
298 EXPECT_EQ(3, helper_delegate_
->num_calls_notify_observers());
300 test_tick_clock_
->Advance(
301 base::TimeDelta::FromMilliseconds(kHalfOfExpirationMs
));
302 DispatchOutputChangeEvent(2, 11, 20, true);
303 EXPECT_EQ(4, helper_delegate_
->num_calls_notify_observers());
306 TEST_F(NativeDisplayEventDispatcherX11Test
,
307 UpdateMissingExternalDisplayId
) {
308 ScopedVector
<DisplaySnapshot
> outputs
;
309 outputs
.push_back(CreateInternalOutput(1, 10));
310 helper_delegate_
->set_cached_outputs(outputs
.get());
312 ASSERT_EQ(0, helper_delegate_
->num_calls_notify_observers());
314 // Internal display's ID can be zero and not updated.
315 DispatchOutputChangeEvent(1, 10, 20, true);
316 EXPECT_EQ(0, helper_delegate_
->num_calls_notify_observers());
319 outputs
.push_back(CreateOutput(0, DISPLAY_CONNECTION_TYPE_UNKNOWN
, 2, 11));
320 helper_delegate_
->set_cached_outputs(outputs
.get());
322 // External display should be updated if the id is zero.
323 DispatchOutputChangeEvent(2, 11, 20, true);
324 EXPECT_EQ(1, helper_delegate_
->num_calls_notify_observers());