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