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