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/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 virtual ~ResolutionNotificationControllerTest() {}
57 virtual 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();
67 const DisplayInfo
& info
= display_manager
->GetDisplayInfo(display
.id());
68 controller()->SetDisplayResolutionAndNotify(
72 base::Bind(&ResolutionNotificationControllerTest::OnAccepted
,
73 base::Unretained(this)));
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
);
86 info_list
.push_back(info
);
88 display_manager
->OnNativeDisplaysChanged(info_list
);
89 RunAllPendingInMessageLoop();
92 void SetDisplayResolutionAndNotify(const gfx::Display
& display
,
93 const gfx::Size
& new_resolution
) {
94 SetDisplayResolutionAndNotifyWithResolution(
95 display
, new_resolution
, new_resolution
);
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();
107 return base::string16();
110 static void ClickOnNotification() {
111 message_center::MessageCenter::Get()->ClickOnNotification(
112 ResolutionNotificationController::kNotificationId
);
115 static void ClickOnNotificationButton(int index
) {
116 message_center::MessageCenter::Get()->ClickOnNotificationButton(
117 ResolutionNotificationController::kNotificationId
, index
);
120 static void CloseNotification() {
121 message_center::MessageCenter::Get()->RemoveNotification(
122 ResolutionNotificationController::kNotificationId
, true /* by_user */);
125 static bool IsNotificationVisible() {
126 return message_center::MessageCenter::Get()->FindVisibleNotificationById(
127 ResolutionNotificationController::kNotificationId
);
130 static void TickTimer() {
131 controller()->OnTimerTick();
134 static ResolutionNotificationController
* controller() {
135 return Shell::GetInstance()->resolution_notification_controller();
138 int accept_count() const {
139 return accept_count_
;
144 EXPECT_FALSE(controller()->DoesNotificationTimeout());
150 DISALLOW_COPY_AND_ASSIGN(ResolutionNotificationControllerTest
);
153 // Basic behaviors and verifies it doesn't cause crashes.
154 TEST_F(ResolutionNotificationControllerTest
, Basic
) {
155 if (!SupportsMultipleDisplays())
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());
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());
173 EXPECT_TRUE(display_manager
->GetSelectedModeForDisplayId(id2
, &mode
));
174 EXPECT_EQ("200x200", mode
.size
.ToString());
175 EXPECT_EQ(60.0, mode
.refresh_rate
);
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
);
187 TEST_F(ResolutionNotificationControllerTest
, ClickMeansAccept
) {
188 if (!SupportsMultipleDisplays())
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());
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());
204 EXPECT_TRUE(display_manager
->GetSelectedModeForDisplayId(id2
, &mode
));
205 EXPECT_EQ("200x200", mode
.size
.ToString());
206 EXPECT_EQ(60.0, mode
.refresh_rate
);
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
);
218 TEST_F(ResolutionNotificationControllerTest
, AcceptButton
) {
219 if (!SupportsMultipleDisplays())
222 ash::DisplayManager
* display_manager
=
223 ash::Shell::GetInstance()->display_manager();
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());
230 // If there's a single display only, it will have timeout and the first button
232 EXPECT_TRUE(controller()->DoesNotificationTimeout());
233 ClickOnNotificationButton(0);
234 EXPECT_FALSE(IsNotificationVisible());
235 EXPECT_EQ(1, accept_count());
238 display_manager
->GetSelectedModeForDisplayId(display
.id(), &mode
));
239 EXPECT_EQ("200x200", mode
.size
.ToString());
240 EXPECT_EQ(60.0f
, mode
.refresh_rate
);
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());
247 EXPECT_TRUE(controller()->DoesNotificationTimeout());
248 ClickOnNotificationButton(1);
249 EXPECT_FALSE(IsNotificationVisible());
250 EXPECT_EQ(1, accept_count());
252 display_manager
->GetSelectedModeForDisplayId(display
.id(), &mode
));
253 EXPECT_EQ("300x300", mode
.size
.ToString());
254 EXPECT_EQ(59.0f
, mode
.refresh_rate
);
257 TEST_F(ResolutionNotificationControllerTest
, Close
) {
258 if (!SupportsMultipleDisplays())
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());
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());
274 EXPECT_TRUE(display_manager
->GetSelectedModeForDisplayId(id2
, &mode
));
275 EXPECT_EQ("200x200", mode
.size
.ToString());
276 EXPECT_EQ(60.0f
, mode
.refresh_rate
);
278 // Close the notification (imitates clicking [x] button). Also verifies if
279 // this does not cause a crash. See crbug.com/271784
281 RunAllPendingInMessageLoop();
282 EXPECT_FALSE(IsNotificationVisible());
283 EXPECT_EQ(1, accept_count());
286 TEST_F(ResolutionNotificationControllerTest
, Timeout
) {
287 if (!SupportsMultipleDisplays())
290 UpdateDisplay("300x300#300x300%59|200x200%60");
291 const gfx::Display
& display
= ash::Shell::GetScreen()->GetPrimaryDisplay();
292 SetDisplayResolutionAndNotify(display
, gfx::Size(200, 200));
294 for (int i
= 0; i
< ResolutionNotificationController::kTimeoutInSec
; ++i
) {
295 EXPECT_TRUE(IsNotificationVisible()) << "notification is closed after "
296 << i
<< "-th timer tick";
298 RunAllPendingInMessageLoop();
300 EXPECT_FALSE(IsNotificationVisible());
301 EXPECT_EQ(0, accept_count());
302 ash::DisplayManager
* display_manager
=
303 ash::Shell::GetInstance()->display_manager();
306 display_manager
->GetSelectedModeForDisplayId(display
.id(), &mode
));
307 EXPECT_EQ("300x300", mode
.size
.ToString());
308 EXPECT_EQ(59.0f
, mode
.refresh_rate
);
311 TEST_F(ResolutionNotificationControllerTest
, DisplayDisconnected
) {
312 if (!SupportsMultipleDisplays())
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());
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());
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
);
336 TEST_F(ResolutionNotificationControllerTest
, MultipleResolutionChange
) {
337 if (!SupportsMultipleDisplays())
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();
346 SetDisplayResolutionAndNotify(
347 ScreenUtil::GetSecondaryDisplay(), gfx::Size(200, 200));
348 EXPECT_TRUE(IsNotificationVisible());
349 EXPECT_FALSE(controller()->DoesNotificationTimeout());
351 EXPECT_TRUE(display_manager
->GetSelectedModeForDisplayId(id2
, &mode
));
352 EXPECT_EQ("200x200", mode
.size
.ToString());
353 EXPECT_EQ(59.0f
, mode
.refresh_rate
);
355 // Invokes SetDisplayResolutionAndNotify during the previous notification is
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
);
363 // Then, click the revert button. Although |old_resolution| for the second
364 // SetDisplayResolutionAndNotify is 200x200, it should revert to the original
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
);
375 TEST_F(ResolutionNotificationControllerTest
, Fallback
) {
376 if (!SupportsMultipleDisplays())
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());
387 // Changes the resolution and apply the result.
388 SetDisplayResolutionAndNotifyWithResolution(
389 ScreenUtil::GetSecondaryDisplay(),
391 gfx::Size(200, 200));
392 EXPECT_TRUE(IsNotificationVisible());
393 EXPECT_FALSE(controller()->DoesNotificationTimeout());
395 ExpectedFallbackNotificationMessage(
396 id2
, gfx::Size(220, 220), gfx::Size(200, 200)),
397 GetNotificationMessage());
399 EXPECT_TRUE(display_manager
->GetSelectedModeForDisplayId(id2
, &mode
));
400 EXPECT_EQ("200x200", mode
.size
.ToString());
401 EXPECT_EQ(60.0f
, mode
.refresh_rate
);
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
);