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