resolution_notification_controller_unittest.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
1// Copyright 2013 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 "ash/display/resolution_notification_controller.h"
6
7#include "ash/display/display_manager.h"
8#include "ash/screen_util.h"
9#include "ash/shell.h"
10#include "ash/test/ash_test_base.h"
11#include "base/bind.h"
12#include "base/strings/utf_string_conversions.h"
13#include "grit/ash_strings.h"
14#include "ui/base/l10n/l10n_util.h"
15#include "ui/gfx/size.h"
16#include "ui/message_center/message_center.h"
17#include "ui/message_center/notification.h"
18#include "ui/message_center/notification_list.h"
19
20namespace ash {
21namespace {
22
23base::string16 ExpectedNotificationMessage(int64 display_id,
24                                           const gfx::Size& new_resolution) {
25  return l10n_util::GetStringFUTF16(
26      IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED,
27      base::UTF8ToUTF16(
28          Shell::GetInstance()->display_manager()->GetDisplayNameForId(
29              display_id)),
30      base::UTF8ToUTF16(new_resolution.ToString()));
31}
32
33base::string16 ExpectedFallbackNotificationMessage(
34    int64 display_id,
35    const gfx::Size& specified_resolution,
36    const gfx::Size& fallback_resolution) {
37  return l10n_util::GetStringFUTF16(
38      IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED_TO_UNSUPPORTED,
39      base::UTF8ToUTF16(
40          Shell::GetInstance()->display_manager()->GetDisplayNameForId(
41              display_id)),
42      base::UTF8ToUTF16(specified_resolution.ToString()),
43      base::UTF8ToUTF16(fallback_resolution.ToString()));
44}
45
46}  // namespace
47
48class ResolutionNotificationControllerTest : public ash::test::AshTestBase {
49 public:
50  ResolutionNotificationControllerTest()
51      : accept_count_(0) {
52  }
53
54  virtual ~ResolutionNotificationControllerTest() {}
55
56 protected:
57  virtual void SetUp() OVERRIDE {
58    ash::test::AshTestBase::SetUp();
59    ResolutionNotificationController::SuppressTimerForTest();
60  }
61
62  void SetDisplayResolutionAndNotifyWithResolution(
63      const gfx::Display& display,
64      const gfx::Size& new_resolution,
65      const gfx::Size& actual_new_resolution) {
66    DisplayManager* display_manager = Shell::GetInstance()->display_manager();
67    const DisplayInfo& info = display_manager->GetDisplayInfo(display.id());
68    controller()->SetDisplayResolutionAndNotify(
69        display.id(),
70        info.size_in_pixel(),
71        new_resolution,
72        base::Bind(&ResolutionNotificationControllerTest::OnAccepted,
73                   base::Unretained(this)));
74
75    // OnConfigurationChanged event won't be emitted in the test environment,
76    // so invoke UpdateDisplay() to emit that event explicitly.
77    std::vector<DisplayInfo> info_list;
78    for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
79      int64 id = display_manager->GetDisplayAt(i).id();
80      DisplayInfo info = display_manager->GetDisplayInfo(id);
81      if (display.id() == id) {
82        gfx::Rect bounds = info.bounds_in_native();
83        bounds.set_size(actual_new_resolution);
84        info.SetBounds(bounds);
85      }
86      info_list.push_back(info);
87    }
88    display_manager->OnNativeDisplaysChanged(info_list);
89    RunAllPendingInMessageLoop();
90  }
91
92  void SetDisplayResolutionAndNotify(const gfx::Display& display,
93                                     const gfx::Size& new_resolution) {
94    SetDisplayResolutionAndNotifyWithResolution(
95        display, new_resolution, new_resolution);
96  }
97
98  static base::string16 GetNotificationMessage() {
99    const message_center::NotificationList::Notifications& notifications =
100        message_center::MessageCenter::Get()->GetVisibleNotifications();
101    for (message_center::NotificationList::Notifications::const_iterator iter =
102             notifications.begin(); iter != notifications.end(); ++iter) {
103      if ((*iter)->id() == ResolutionNotificationController::kNotificationId)
104        return (*iter)->title();
105    }
106
107    return base::string16();
108  }
109
110  static void ClickOnNotification() {
111    message_center::MessageCenter::Get()->ClickOnNotification(
112        ResolutionNotificationController::kNotificationId);
113  }
114
115  static void ClickOnNotificationButton(int index) {
116    message_center::MessageCenter::Get()->ClickOnNotificationButton(
117        ResolutionNotificationController::kNotificationId, index);
118  }
119
120  static void CloseNotification() {
121    message_center::MessageCenter::Get()->RemoveNotification(
122        ResolutionNotificationController::kNotificationId, true /* by_user */);
123  }
124
125  static bool IsNotificationVisible() {
126    return message_center::MessageCenter::Get()->FindVisibleNotificationById(
127        ResolutionNotificationController::kNotificationId);
128  }
129
130  static void TickTimer() {
131    controller()->OnTimerTick();
132  }
133
134  static ResolutionNotificationController* controller() {
135    return Shell::GetInstance()->resolution_notification_controller();
136  }
137
138  int accept_count() const {
139    return accept_count_;
140  }
141
142 private:
143  void OnAccepted() {
144    EXPECT_FALSE(controller()->DoesNotificationTimeout());
145    accept_count_++;
146  }
147
148  int accept_count_;
149
150  DISALLOW_COPY_AND_ASSIGN(ResolutionNotificationControllerTest);
151};
152
153// Basic behaviors and verifies it doesn't cause crashes.
154TEST_F(ResolutionNotificationControllerTest, Basic) {
155  if (!SupportsMultipleDisplays())
156    return;
157
158  UpdateDisplay("300x300#300x300%57|200x200%58,250x250#250x250%59|200x200%60");
159  int64 id2 = ash::ScreenUtil::GetSecondaryDisplay().id();
160  ash::DisplayManager* display_manager =
161      ash::Shell::GetInstance()->display_manager();
162  ASSERT_EQ(0, accept_count());
163  EXPECT_FALSE(IsNotificationVisible());
164
165  // Changes the resolution and apply the result.
166  SetDisplayResolutionAndNotify(
167      ScreenUtil::GetSecondaryDisplay(), gfx::Size(200, 200));
168  EXPECT_TRUE(IsNotificationVisible());
169  EXPECT_FALSE(controller()->DoesNotificationTimeout());
170  EXPECT_EQ(ExpectedNotificationMessage(id2, gfx::Size(200, 200)),
171            GetNotificationMessage());
172  DisplayMode mode;
173  EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
174  EXPECT_EQ("200x200", mode.size.ToString());
175  EXPECT_EQ(60.0, mode.refresh_rate);
176
177  // Click the revert button, which reverts to the best resolution.
178  ClickOnNotificationButton(0);
179  RunAllPendingInMessageLoop();
180  EXPECT_FALSE(IsNotificationVisible());
181  EXPECT_EQ(0, accept_count());
182  EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
183  EXPECT_EQ("250x250", mode.size.ToString());
184  EXPECT_EQ(59.0, mode.refresh_rate);
185}
186
187TEST_F(ResolutionNotificationControllerTest, ClickMeansAccept) {
188  if (!SupportsMultipleDisplays())
189    return;
190
191  UpdateDisplay("300x300#300x300%57|200x200%58,250x250#250x250%59|200x200%60");
192  int64 id2 = ash::ScreenUtil::GetSecondaryDisplay().id();
193  ash::DisplayManager* display_manager =
194      ash::Shell::GetInstance()->display_manager();
195  ASSERT_EQ(0, accept_count());
196  EXPECT_FALSE(IsNotificationVisible());
197
198  // Changes the resolution and apply the result.
199  SetDisplayResolutionAndNotify(
200      ScreenUtil::GetSecondaryDisplay(), gfx::Size(200, 200));
201  EXPECT_TRUE(IsNotificationVisible());
202  EXPECT_FALSE(controller()->DoesNotificationTimeout());
203  DisplayMode mode;
204  EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
205  EXPECT_EQ("200x200", mode.size.ToString());
206  EXPECT_EQ(60.0, mode.refresh_rate);
207
208  // Click the revert button, which reverts the resolution.
209  ClickOnNotification();
210  RunAllPendingInMessageLoop();
211  EXPECT_FALSE(IsNotificationVisible());
212  EXPECT_EQ(1, accept_count());
213  EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
214  EXPECT_EQ("200x200", mode.size.ToString());
215  EXPECT_EQ(60.0, mode.refresh_rate);
216}
217
218TEST_F(ResolutionNotificationControllerTest, AcceptButton) {
219  if (!SupportsMultipleDisplays())
220    return;
221
222  ash::DisplayManager* display_manager =
223      ash::Shell::GetInstance()->display_manager();
224
225  UpdateDisplay("300x300#300x300%59|200x200%60");
226  const gfx::Display& display = ash::Shell::GetScreen()->GetPrimaryDisplay();
227  SetDisplayResolutionAndNotify(display, gfx::Size(200, 200));
228  EXPECT_TRUE(IsNotificationVisible());
229
230  // If there's a single display only, it will have timeout and the first button
231  // becomes accept.
232  EXPECT_TRUE(controller()->DoesNotificationTimeout());
233  ClickOnNotificationButton(0);
234  EXPECT_FALSE(IsNotificationVisible());
235  EXPECT_EQ(1, accept_count());
236  DisplayMode mode;
237  EXPECT_TRUE(
238      display_manager->GetSelectedModeForDisplayId(display.id(), &mode));
239  EXPECT_EQ("200x200", mode.size.ToString());
240  EXPECT_EQ(60.0f, mode.refresh_rate);
241
242  // In that case the second button is revert.
243  UpdateDisplay("300x300#300x300%59|200x200%60");
244  SetDisplayResolutionAndNotify(display, gfx::Size(200, 200));
245  EXPECT_TRUE(IsNotificationVisible());
246
247  EXPECT_TRUE(controller()->DoesNotificationTimeout());
248  ClickOnNotificationButton(1);
249  EXPECT_FALSE(IsNotificationVisible());
250  EXPECT_EQ(1, accept_count());
251  EXPECT_TRUE(
252      display_manager->GetSelectedModeForDisplayId(display.id(), &mode));
253  EXPECT_EQ("300x300", mode.size.ToString());
254  EXPECT_EQ(59.0f, mode.refresh_rate);
255}
256
257TEST_F(ResolutionNotificationControllerTest, Close) {
258  if (!SupportsMultipleDisplays())
259    return;
260
261  UpdateDisplay("100x100,150x150#150x150%59|200x200%60");
262  int64 id2 = ash::ScreenUtil::GetSecondaryDisplay().id();
263  ash::DisplayManager* display_manager =
264      ash::Shell::GetInstance()->display_manager();
265  ASSERT_EQ(0, accept_count());
266  EXPECT_FALSE(IsNotificationVisible());
267
268  // Changes the resolution and apply the result.
269  SetDisplayResolutionAndNotify(
270      ScreenUtil::GetSecondaryDisplay(), gfx::Size(200, 200));
271  EXPECT_TRUE(IsNotificationVisible());
272  EXPECT_FALSE(controller()->DoesNotificationTimeout());
273  DisplayMode mode;
274  EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
275  EXPECT_EQ("200x200", mode.size.ToString());
276  EXPECT_EQ(60.0f, mode.refresh_rate);
277
278  // Close the notification (imitates clicking [x] button). Also verifies if
279  // this does not cause a crash.  See crbug.com/271784
280  CloseNotification();
281  RunAllPendingInMessageLoop();
282  EXPECT_FALSE(IsNotificationVisible());
283  EXPECT_EQ(1, accept_count());
284}
285
286TEST_F(ResolutionNotificationControllerTest, Timeout) {
287  if (!SupportsMultipleDisplays())
288    return;
289
290  UpdateDisplay("300x300#300x300%59|200x200%60");
291  const gfx::Display& display = ash::Shell::GetScreen()->GetPrimaryDisplay();
292  SetDisplayResolutionAndNotify(display, gfx::Size(200, 200));
293
294  for (int i = 0; i < ResolutionNotificationController::kTimeoutInSec; ++i) {
295    EXPECT_TRUE(IsNotificationVisible()) << "notification is closed after "
296                                         << i << "-th timer tick";
297    TickTimer();
298    RunAllPendingInMessageLoop();
299  }
300  EXPECT_FALSE(IsNotificationVisible());
301  EXPECT_EQ(0, accept_count());
302  ash::DisplayManager* display_manager =
303      ash::Shell::GetInstance()->display_manager();
304  DisplayMode mode;
305  EXPECT_TRUE(
306      display_manager->GetSelectedModeForDisplayId(display.id(), &mode));
307  EXPECT_EQ("300x300", mode.size.ToString());
308  EXPECT_EQ(59.0f, mode.refresh_rate);
309}
310
311TEST_F(ResolutionNotificationControllerTest, DisplayDisconnected) {
312  if (!SupportsMultipleDisplays())
313    return;
314
315  UpdateDisplay("300x300#300x300%56|200x200%57,"
316                "200x200#250x250%58|200x200%59|100x100%60");
317  int64 id2 = ash::ScreenUtil::GetSecondaryDisplay().id();
318  ash::DisplayManager* display_manager =
319      ash::Shell::GetInstance()->display_manager();
320  SetDisplayResolutionAndNotify(
321      ScreenUtil::GetSecondaryDisplay(), gfx::Size(100, 100));
322  ASSERT_TRUE(IsNotificationVisible());
323
324  // Disconnects the secondary display and verifies it doesn't cause crashes.
325  UpdateDisplay("300x300#300x300%56|200x200%57");
326  RunAllPendingInMessageLoop();
327  EXPECT_FALSE(IsNotificationVisible());
328  EXPECT_EQ(0, accept_count());
329  DisplayMode mode;
330  EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
331  gfx::Size resolution;
332  EXPECT_EQ("200x200", mode.size.ToString());
333  EXPECT_EQ(59.0f, mode.refresh_rate);
334}
335
336TEST_F(ResolutionNotificationControllerTest, MultipleResolutionChange) {
337  if (!SupportsMultipleDisplays())
338    return;
339
340  UpdateDisplay("300x300#300x300%56|200x200%57,"
341                "250x250#250x250%58|200x200%59");
342  int64 id2 = ash::ScreenUtil::GetSecondaryDisplay().id();
343  ash::DisplayManager* display_manager =
344      ash::Shell::GetInstance()->display_manager();
345
346  SetDisplayResolutionAndNotify(
347      ScreenUtil::GetSecondaryDisplay(), gfx::Size(200, 200));
348  EXPECT_TRUE(IsNotificationVisible());
349  EXPECT_FALSE(controller()->DoesNotificationTimeout());
350  DisplayMode mode;
351  EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
352  EXPECT_EQ("200x200", mode.size.ToString());
353  EXPECT_EQ(59.0f, mode.refresh_rate);
354
355  // Invokes SetDisplayResolutionAndNotify during the previous notification is
356  // visible.
357  SetDisplayResolutionAndNotify(
358      ScreenUtil::GetSecondaryDisplay(), gfx::Size(250, 250));
359  EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
360  EXPECT_EQ("250x250", mode.size.ToString());
361  EXPECT_EQ(58.0f, mode.refresh_rate);
362
363  // Then, click the revert button. Although |old_resolution| for the second
364  // SetDisplayResolutionAndNotify is 200x200, it should revert to the original
365  // size 250x250.
366  ClickOnNotificationButton(0);
367  RunAllPendingInMessageLoop();
368  EXPECT_FALSE(IsNotificationVisible());
369  EXPECT_EQ(0, accept_count());
370  EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
371  EXPECT_EQ("250x250", mode.size.ToString());
372  EXPECT_EQ(58.0f, mode.refresh_rate);
373}
374
375TEST_F(ResolutionNotificationControllerTest, Fallback) {
376  if (!SupportsMultipleDisplays())
377    return;
378
379  UpdateDisplay("300x300#300x300%56|200x200%57,"
380                "250x250#250x250%58|220x220%59|200x200%60");
381  int64 id2 = ash::ScreenUtil::GetSecondaryDisplay().id();
382  ash::DisplayManager* display_manager =
383      ash::Shell::GetInstance()->display_manager();
384  ASSERT_EQ(0, accept_count());
385  EXPECT_FALSE(IsNotificationVisible());
386
387  // Changes the resolution and apply the result.
388  SetDisplayResolutionAndNotifyWithResolution(
389      ScreenUtil::GetSecondaryDisplay(),
390      gfx::Size(220, 220),
391      gfx::Size(200, 200));
392  EXPECT_TRUE(IsNotificationVisible());
393  EXPECT_FALSE(controller()->DoesNotificationTimeout());
394  EXPECT_EQ(
395      ExpectedFallbackNotificationMessage(
396          id2, gfx::Size(220, 220), gfx::Size(200, 200)),
397      GetNotificationMessage());
398  DisplayMode mode;
399  EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
400  EXPECT_EQ("200x200", mode.size.ToString());
401  EXPECT_EQ(60.0f, mode.refresh_rate);
402
403  // Click the revert button, which reverts to the best resolution.
404  ClickOnNotificationButton(0);
405  RunAllPendingInMessageLoop();
406  EXPECT_FALSE(IsNotificationVisible());
407  EXPECT_EQ(0, accept_count());
408  EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
409  EXPECT_EQ("250x250", mode.size.ToString());
410  EXPECT_EQ(58.0f, mode.refresh_rate);
411}
412
413}  // namespace ash
414