Home | History | Annotate | Download | only in display
      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 
     20 namespace ash {
     21 namespace {
     22 
     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,
     27       base::UTF8ToUTF16(
     28           Shell::GetInstance()->display_manager()->GetDisplayNameForId(
     29               display_id)),
     30       base::UTF8ToUTF16(new_resolution.ToString()));
     31 }
     32 
     33 base::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 
     48 class 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     const DisplayInfo& info = display_manager->GetDisplayInfo(display.id());
     68     controller()->SetDisplayResolutionAndNotify(
     69         display.id(),
     70         info.size_in_pixel(),
     71         new_resolution,
     72         base::Bind(&ResolutionNotificationControllerTest::OnAccepted,
     73                    base::Unretained(this)));
     74 
     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);
     85       }
     86       info_list.push_back(info);
     87     }
     88     display_manager->OnNativeDisplaysChanged(info_list);
     89     RunAllPendingInMessageLoop();
     90   }
     91 
     92   void SetDisplayResolutionAndNotify(const gfx::Display& display,
     93                                      const gfx::Size& new_resolution) {
     94     SetDisplayResolutionAndNotifyWithResolution(
     95         display, new_resolution, new_resolution);
     96   }
     97 
     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();
    105     }
    106 
    107     return base::string16();
    108   }
    109 
    110   static void ClickOnNotification() {
    111     message_center::MessageCenter::Get()->ClickOnNotification(
    112         ResolutionNotificationController::kNotificationId);
    113   }
    114 
    115   static void ClickOnNotificationButton(int index) {
    116     message_center::MessageCenter::Get()->ClickOnNotificationButton(
    117         ResolutionNotificationController::kNotificationId, index);
    118   }
    119 
    120   static void CloseNotification() {
    121     message_center::MessageCenter::Get()->RemoveNotification(
    122         ResolutionNotificationController::kNotificationId, true /* by_user */);
    123   }
    124 
    125   static bool IsNotificationVisible() {
    126     return message_center::MessageCenter::Get()->FindVisibleNotificationById(
    127         ResolutionNotificationController::kNotificationId);
    128   }
    129 
    130   static void TickTimer() {
    131     controller()->OnTimerTick();
    132   }
    133 
    134   static ResolutionNotificationController* controller() {
    135     return Shell::GetInstance()->resolution_notification_controller();
    136   }
    137 
    138   int accept_count() const {
    139     return accept_count_;
    140   }
    141 
    142  private:
    143   void OnAccepted() {
    144     EXPECT_FALSE(controller()->DoesNotificationTimeout());
    145     accept_count_++;
    146   }
    147 
    148   int accept_count_;
    149 
    150   DISALLOW_COPY_AND_ASSIGN(ResolutionNotificationControllerTest);
    151 };
    152 
    153 // Basic behaviors and verifies it doesn't cause crashes.
    154 TEST_F(ResolutionNotificationControllerTest, Basic) {
    155   if (!SupportsMultipleDisplays())
    156     return;
    157 
    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());
    164 
    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());
    172   DisplayMode mode;
    173   EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
    174   EXPECT_EQ("200x200", mode.size.ToString());
    175   EXPECT_EQ(60.0, mode.refresh_rate);
    176 
    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);
    185 }
    186 
    187 TEST_F(ResolutionNotificationControllerTest, ClickMeansAccept) {
    188   if (!SupportsMultipleDisplays())
    189     return;
    190 
    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());
    197 
    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());
    203   DisplayMode mode;
    204   EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
    205   EXPECT_EQ("200x200", mode.size.ToString());
    206   EXPECT_EQ(60.0, mode.refresh_rate);
    207 
    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);
    216 }
    217 
    218 TEST_F(ResolutionNotificationControllerTest, AcceptButton) {
    219   if (!SupportsMultipleDisplays())
    220     return;
    221 
    222   ash::DisplayManager* display_manager =
    223       ash::Shell::GetInstance()->display_manager();
    224 
    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());
    229 
    230   // If there's a single display only, it will have timeout and the first button
    231   // becomes accept.
    232   EXPECT_TRUE(controller()->DoesNotificationTimeout());
    233   ClickOnNotificationButton(0);
    234   EXPECT_FALSE(IsNotificationVisible());
    235   EXPECT_EQ(1, accept_count());
    236   DisplayMode mode;
    237   EXPECT_TRUE(
    238       display_manager->GetSelectedModeForDisplayId(display.id(), &mode));
    239   EXPECT_EQ("200x200", mode.size.ToString());
    240   EXPECT_EQ(60.0f, mode.refresh_rate);
    241 
    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());
    246 
    247   EXPECT_TRUE(controller()->DoesNotificationTimeout());
    248   ClickOnNotificationButton(1);
    249   EXPECT_FALSE(IsNotificationVisible());
    250   EXPECT_EQ(1, accept_count());
    251   EXPECT_TRUE(
    252       display_manager->GetSelectedModeForDisplayId(display.id(), &mode));
    253   EXPECT_EQ("300x300", mode.size.ToString());
    254   EXPECT_EQ(59.0f, mode.refresh_rate);
    255 }
    256 
    257 TEST_F(ResolutionNotificationControllerTest, Close) {
    258   if (!SupportsMultipleDisplays())
    259     return;
    260 
    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());
    267 
    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());
    273   DisplayMode mode;
    274   EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
    275   EXPECT_EQ("200x200", mode.size.ToString());
    276   EXPECT_EQ(60.0f, mode.refresh_rate);
    277 
    278   // Close the notification (imitates clicking [x] button). Also verifies if
    279   // this does not cause a crash.  See crbug.com/271784
    280   CloseNotification();
    281   RunAllPendingInMessageLoop();
    282   EXPECT_FALSE(IsNotificationVisible());
    283   EXPECT_EQ(1, accept_count());
    284 }
    285 
    286 TEST_F(ResolutionNotificationControllerTest, Timeout) {
    287   if (!SupportsMultipleDisplays())
    288     return;
    289 
    290   UpdateDisplay("300x300#300x300%59|200x200%60");
    291   const gfx::Display& display = ash::Shell::GetScreen()->GetPrimaryDisplay();
    292   SetDisplayResolutionAndNotify(display, gfx::Size(200, 200));
    293 
    294   for (int i = 0; i < ResolutionNotificationController::kTimeoutInSec; ++i) {
    295     EXPECT_TRUE(IsNotificationVisible()) << "notification is closed after "
    296                                          << i << "-th timer tick";
    297     TickTimer();
    298     RunAllPendingInMessageLoop();
    299   }
    300   EXPECT_FALSE(IsNotificationVisible());
    301   EXPECT_EQ(0, accept_count());
    302   ash::DisplayManager* display_manager =
    303       ash::Shell::GetInstance()->display_manager();
    304   DisplayMode mode;
    305   EXPECT_TRUE(
    306       display_manager->GetSelectedModeForDisplayId(display.id(), &mode));
    307   EXPECT_EQ("300x300", mode.size.ToString());
    308   EXPECT_EQ(59.0f, mode.refresh_rate);
    309 }
    310 
    311 TEST_F(ResolutionNotificationControllerTest, DisplayDisconnected) {
    312   if (!SupportsMultipleDisplays())
    313     return;
    314 
    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());
    323 
    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());
    329   DisplayMode mode;
    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);
    334 }
    335 
    336 TEST_F(ResolutionNotificationControllerTest, MultipleResolutionChange) {
    337   if (!SupportsMultipleDisplays())
    338     return;
    339 
    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();
    345 
    346   SetDisplayResolutionAndNotify(
    347       ScreenUtil::GetSecondaryDisplay(), gfx::Size(200, 200));
    348   EXPECT_TRUE(IsNotificationVisible());
    349   EXPECT_FALSE(controller()->DoesNotificationTimeout());
    350   DisplayMode mode;
    351   EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
    352   EXPECT_EQ("200x200", mode.size.ToString());
    353   EXPECT_EQ(59.0f, mode.refresh_rate);
    354 
    355   // Invokes SetDisplayResolutionAndNotify during the previous notification is
    356   // visible.
    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);
    362 
    363   // Then, click the revert button. Although |old_resolution| for the second
    364   // SetDisplayResolutionAndNotify is 200x200, it should revert to the original
    365   // size 250x250.
    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);
    373 }
    374 
    375 TEST_F(ResolutionNotificationControllerTest, Fallback) {
    376   if (!SupportsMultipleDisplays())
    377     return;
    378 
    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());
    386 
    387   // Changes the resolution and apply the result.
    388   SetDisplayResolutionAndNotifyWithResolution(
    389       ScreenUtil::GetSecondaryDisplay(),
    390       gfx::Size(220, 220),
    391       gfx::Size(200, 200));
    392   EXPECT_TRUE(IsNotificationVisible());
    393   EXPECT_FALSE(controller()->DoesNotificationTimeout());
    394   EXPECT_EQ(
    395       ExpectedFallbackNotificationMessage(
    396           id2, gfx::Size(220, 220), gfx::Size(200, 200)),
    397       GetNotificationMessage());
    398   DisplayMode mode;
    399   EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
    400   EXPECT_EQ("200x200", mode.size.ToString());
    401   EXPECT_EQ(60.0f, mode.refresh_rate);
    402 
    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);
    411 }
    412 
    413 }  // namespace ash
    414