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.
5 #include "ash/display/resolution_notification_controller.h"
7 #include "ash/display/display_manager.h"
8 #include "ash/screen_util.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/geometry/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"
23 base::string16
ExpectedNotificationMessage(int64 display_id
,
24 const gfx::Size
& new_resolution
) {
25 return l10n_util::GetStringFUTF16(
26 IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED
,
28 Shell::GetInstance()->display_manager()->GetDisplayNameForId(
30 base::UTF8ToUTF16(new_resolution
.ToString()));
33 base::string16
ExpectedFallbackNotificationMessage(
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
,
40 Shell::GetInstance()->display_manager()->GetDisplayNameForId(
42 base::UTF8ToUTF16(specified_resolution
.ToString()),
43 base::UTF8ToUTF16(fallback_resolution
.ToString()));
48 class ResolutionNotificationControllerTest
: public ash::test::AshTestBase
{
50 ResolutionNotificationControllerTest()
54 ~ResolutionNotificationControllerTest() override
{}
57 void SetUp() override
{
58 ash::test::AshTestBase::SetUp();
59 ResolutionNotificationController::SuppressTimerForTest();
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();
68 const DisplayInfo
& info
= display_manager
->GetDisplayInfo(display
.id());
69 DisplayMode
old_mode(info
.size_in_pixel(),
70 60 /* refresh_rate */,
71 false /* interlaced */,
73 DisplayMode new_mode
= old_mode
;
74 new_mode
.size
= new_resolution
;
76 if (display_manager
->SetDisplayMode(display
.id(), new_mode
)) {
77 controller()->PrepareNotification(
81 base::Bind(&ResolutionNotificationControllerTest::OnAccepted
,
82 base::Unretained(this)));
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
);
96 info_list
.push_back(info
);
98 display_manager
->OnNativeDisplaysChanged(info_list
);
99 RunAllPendingInMessageLoop();
102 void SetDisplayResolutionAndNotify(const gfx::Display
& display
,
103 const gfx::Size
& new_resolution
) {
104 SetDisplayResolutionAndNotifyWithResolution(
105 display
, new_resolution
, new_resolution
);
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();
117 return base::string16();
120 static void ClickOnNotification() {
121 message_center::MessageCenter::Get()->ClickOnNotification(
122 ResolutionNotificationController::kNotificationId
);
125 static void ClickOnNotificationButton(int index
) {
126 message_center::MessageCenter::Get()->ClickOnNotificationButton(
127 ResolutionNotificationController::kNotificationId
, index
);
130 static void CloseNotification() {
131 message_center::MessageCenter::Get()->RemoveNotification(
132 ResolutionNotificationController::kNotificationId
, true /* by_user */);
135 static bool IsNotificationVisible() {
136 return message_center::MessageCenter::Get()->FindVisibleNotificationById(
137 ResolutionNotificationController::kNotificationId
);
140 static void TickTimer() {
141 controller()->OnTimerTick();
144 static ResolutionNotificationController
* controller() {
145 return Shell::GetInstance()->resolution_notification_controller();
148 int accept_count() const {
149 return accept_count_
;
154 EXPECT_FALSE(controller()->DoesNotificationTimeout());
160 DISALLOW_COPY_AND_ASSIGN(ResolutionNotificationControllerTest
);
163 // Basic behaviors and verifies it doesn't cause crashes.
164 TEST_F(ResolutionNotificationControllerTest
, Basic
) {
165 if (!SupportsMultipleDisplays())
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());
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());
183 EXPECT_TRUE(display_manager
->GetSelectedModeForDisplayId(id2
, &mode
));
184 EXPECT_EQ("200x200", mode
.size
.ToString());
185 EXPECT_EQ(60.0, mode
.refresh_rate
);
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
);
197 TEST_F(ResolutionNotificationControllerTest
, ClickMeansAccept
) {
198 if (!SupportsMultipleDisplays())
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());
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());
214 EXPECT_TRUE(display_manager
->GetSelectedModeForDisplayId(id2
, &mode
));
215 EXPECT_EQ("200x200", mode
.size
.ToString());
216 EXPECT_EQ(60.0, mode
.refresh_rate
);
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
);
228 TEST_F(ResolutionNotificationControllerTest
, AcceptButton
) {
229 if (!SupportsMultipleDisplays())
232 ash::DisplayManager
* display_manager
=
233 ash::Shell::GetInstance()->display_manager();
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());
240 // If there's a single display only, it will have timeout and the first button
242 EXPECT_TRUE(controller()->DoesNotificationTimeout());
243 ClickOnNotificationButton(0);
244 EXPECT_FALSE(IsNotificationVisible());
245 EXPECT_EQ(1, accept_count());
248 display_manager
->GetSelectedModeForDisplayId(display
.id(), &mode
));
249 EXPECT_EQ("200x200", mode
.size
.ToString());
250 EXPECT_EQ(60.0f
, mode
.refresh_rate
);
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());
257 EXPECT_TRUE(controller()->DoesNotificationTimeout());
258 ClickOnNotificationButton(1);
259 EXPECT_FALSE(IsNotificationVisible());
260 EXPECT_EQ(1, accept_count());
262 display_manager
->GetSelectedModeForDisplayId(display
.id(), &mode
));
263 EXPECT_EQ("300x300", mode
.size
.ToString());
264 EXPECT_EQ(59.0f
, mode
.refresh_rate
);
267 TEST_F(ResolutionNotificationControllerTest
, Close
) {
268 if (!SupportsMultipleDisplays())
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());
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());
284 EXPECT_TRUE(display_manager
->GetSelectedModeForDisplayId(id2
, &mode
));
285 EXPECT_EQ("200x200", mode
.size
.ToString());
286 EXPECT_EQ(60.0f
, mode
.refresh_rate
);
288 // Close the notification (imitates clicking [x] button). Also verifies if
289 // this does not cause a crash. See crbug.com/271784
291 RunAllPendingInMessageLoop();
292 EXPECT_FALSE(IsNotificationVisible());
293 EXPECT_EQ(1, accept_count());
296 TEST_F(ResolutionNotificationControllerTest
, Timeout
) {
297 if (!SupportsMultipleDisplays())
300 UpdateDisplay("300x300#300x300%59|200x200%60");
301 const gfx::Display
& display
= ash::Shell::GetScreen()->GetPrimaryDisplay();
302 SetDisplayResolutionAndNotify(display
, gfx::Size(200, 200));
304 for (int i
= 0; i
< ResolutionNotificationController::kTimeoutInSec
; ++i
) {
305 EXPECT_TRUE(IsNotificationVisible()) << "notification is closed after "
306 << i
<< "-th timer tick";
308 RunAllPendingInMessageLoop();
310 EXPECT_FALSE(IsNotificationVisible());
311 EXPECT_EQ(0, accept_count());
312 ash::DisplayManager
* display_manager
=
313 ash::Shell::GetInstance()->display_manager();
316 display_manager
->GetSelectedModeForDisplayId(display
.id(), &mode
));
317 EXPECT_EQ("300x300", mode
.size
.ToString());
318 EXPECT_EQ(59.0f
, mode
.refresh_rate
);
321 TEST_F(ResolutionNotificationControllerTest
, DisplayDisconnected
) {
322 if (!SupportsMultipleDisplays())
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());
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());
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
);
346 TEST_F(ResolutionNotificationControllerTest
, MultipleResolutionChange
) {
347 if (!SupportsMultipleDisplays())
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();
356 SetDisplayResolutionAndNotify(
357 ScreenUtil::GetSecondaryDisplay(), gfx::Size(200, 200));
358 EXPECT_TRUE(IsNotificationVisible());
359 EXPECT_FALSE(controller()->DoesNotificationTimeout());
361 EXPECT_TRUE(display_manager
->GetSelectedModeForDisplayId(id2
, &mode
));
362 EXPECT_EQ("200x200", mode
.size
.ToString());
363 EXPECT_EQ(59.0f
, mode
.refresh_rate
);
365 // Invokes SetDisplayResolutionAndNotify during the previous notification is
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
);
373 // Then, click the revert button. Although |old_resolution| for the second
374 // SetDisplayResolutionAndNotify is 200x200, it should revert to the original
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
);
385 TEST_F(ResolutionNotificationControllerTest
, Fallback
) {
386 if (!SupportsMultipleDisplays())
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());
397 // Changes the resolution and apply the result.
398 SetDisplayResolutionAndNotifyWithResolution(
399 ScreenUtil::GetSecondaryDisplay(),
401 gfx::Size(200, 200));
402 EXPECT_TRUE(IsNotificationVisible());
403 EXPECT_FALSE(controller()->DoesNotificationTimeout());
405 ExpectedFallbackNotificationMessage(
406 id2
, gfx::Size(220, 220), gfx::Size(200, 200)),
407 GetNotificationMessage());
409 EXPECT_TRUE(display_manager
->GetSelectedModeForDisplayId(id2
, &mode
));
410 EXPECT_EQ("200x200", mode
.size
.ToString());
411 EXPECT_EQ(60.0f
, mode
.refresh_rate
);
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
);