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