Home | History | Annotate | Download | only in views
      1 // Copyright (c) 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 "ui/message_center/views/message_popup_collection.h"
      6 
      7 #include <list>
      8 
      9 #include "base/message_loop/message_loop.h"
     10 #include "base/strings/string_number_conversions.h"
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "testing/gtest/include/gtest/gtest.h"
     13 #include "ui/base/events/event.h"
     14 #include "ui/base/events/event_constants.h"
     15 #include "ui/gfx/display.h"
     16 #include "ui/gfx/rect.h"
     17 #include "ui/message_center/fake_message_center.h"
     18 #include "ui/message_center/views/toast_contents_view.h"
     19 #include "ui/views/test/views_test_base.h"
     20 #include "ui/views/widget/widget.h"
     21 #include "ui/views/widget/widget_delegate.h"
     22 
     23 namespace message_center {
     24 namespace test {
     25 
     26 class MessagePopupCollectionTest : public views::ViewsTestBase {
     27  public:
     28   virtual void SetUp() OVERRIDE {
     29     views::ViewsTestBase::SetUp();
     30     MessageCenter::Initialize();
     31     collection_.reset(new MessagePopupCollection(
     32         GetContext(), MessageCenter::Get(), NULL, false));
     33     // This size fits test machines resolution and also can keep a few toasts
     34     // w/o ill effects of hitting the screen overflow. This allows us to assume
     35     // and verify normal layout of the toast stack.
     36     collection_->SetDisplayInfo(gfx::Rect(0, 0, 600, 390),
     37                                 gfx::Rect(0, 0, 600, 400));  // Simulate a
     38                                                              // taskbar at the
     39                                                              // bottom.
     40     id_ = 0;
     41   }
     42 
     43   virtual void TearDown() OVERRIDE {
     44     collection_->CloseAllWidgets();
     45     collection_.reset();
     46     MessageCenter::Shutdown();
     47     views::ViewsTestBase::TearDown();
     48   }
     49 
     50  protected:
     51   MessagePopupCollection* collection() { return collection_.get(); }
     52 
     53   size_t GetToastCounts() {
     54     return collection_->toasts_.size();
     55   }
     56 
     57   bool MouseInCollection() {
     58     return collection_->latest_toast_entered_ != NULL;
     59   }
     60 
     61   bool IsToastShown(const std::string& id) {
     62     views::Widget* widget = collection_->GetWidgetForTest(id);
     63     return widget && widget->IsVisible();
     64   }
     65 
     66   views::Widget* GetWidget(const std::string& id) {
     67     return collection_->GetWidgetForTest(id);
     68   }
     69 
     70   gfx::Rect GetWorkArea() {
     71     return collection_->work_area_;
     72   }
     73 
     74   ToastContentsView* GetToast(const std::string& id) {
     75     for (MessagePopupCollection::Toasts::iterator iter =
     76              collection_->toasts_.begin();
     77          iter != collection_->toasts_.end(); ++iter) {
     78       if ((*iter)->id() == id)
     79         return *iter;
     80     }
     81     return NULL;
     82   }
     83 
     84   std::string AddNotification() {
     85     std::string id = base::IntToString(id_++);
     86     scoped_ptr<Notification> notification(
     87         new Notification(NOTIFICATION_TYPE_BASE_FORMAT,
     88                          id,
     89                          UTF8ToUTF16("test title"),
     90                          UTF8ToUTF16("test message"),
     91                          gfx::Image(),
     92                          string16() /* display_source */,
     93                          "" /* extension_id */,
     94                          message_center::RichNotificationData(),
     95                          NULL /* delegate */));
     96     MessageCenter::Get()->AddNotification(notification.Pass());
     97     return id;
     98   }
     99 
    100   // Assumes there is non-zero pending work.
    101   void WaitForTransitionsDone() {
    102     collection_->RunLoopForTest();
    103   }
    104 
    105   void CloseAllToastsAndWait() {
    106     // Assumes there is at least one toast to close.
    107     EXPECT_TRUE(GetToastCounts() > 0);
    108     MessageCenter::Get()->RemoveAllNotifications(false);
    109     WaitForTransitionsDone();
    110   }
    111 
    112   gfx::Rect GetToastRectAt(size_t index) {
    113     return collection_->GetToastRectAt(index);
    114   }
    115 
    116  private:
    117   scoped_ptr<MessagePopupCollection> collection_;
    118   int id_;
    119 };
    120 
    121 TEST_F(MessagePopupCollectionTest, DismissOnClick) {
    122   std::string id1 = AddNotification();
    123   std::string id2 = AddNotification();
    124   WaitForTransitionsDone();
    125   EXPECT_EQ(2u, GetToastCounts());
    126   EXPECT_TRUE(IsToastShown(id1));
    127   EXPECT_TRUE(IsToastShown(id2));
    128 
    129   MessageCenter::Get()->ClickOnNotification(id2);
    130   WaitForTransitionsDone();
    131   EXPECT_EQ(1u, GetToastCounts());
    132   EXPECT_TRUE(IsToastShown(id1));
    133   EXPECT_FALSE(IsToastShown(id2));
    134 
    135   MessageCenter::Get()->ClickOnNotificationButton(id1, 0);
    136   WaitForTransitionsDone();
    137   EXPECT_EQ(0u, GetToastCounts());
    138   EXPECT_FALSE(IsToastShown(id1));
    139   EXPECT_FALSE(IsToastShown(id2));
    140 }
    141 
    142 TEST_F(MessagePopupCollectionTest, ShutdownDuringShowing) {
    143   std::string id1 = AddNotification();
    144   std::string id2 = AddNotification();
    145   WaitForTransitionsDone();
    146   EXPECT_EQ(2u, GetToastCounts());
    147   EXPECT_TRUE(IsToastShown(id1));
    148   EXPECT_TRUE(IsToastShown(id2));
    149 
    150   // Finish without cleanup of notifications, which may cause use-after-free.
    151   // See crbug.com/236448
    152   GetWidget(id1)->CloseNow();
    153   collection()->OnMouseExited(GetToast(id2));
    154 }
    155 
    156 TEST_F(MessagePopupCollectionTest, DefaultPositioning) {
    157   std::string id0 = AddNotification();
    158   std::string id1 = AddNotification();
    159   std::string id2 = AddNotification();
    160   std::string id3 = AddNotification();
    161   WaitForTransitionsDone();
    162 
    163   gfx::Rect r0 = GetToastRectAt(0);
    164   gfx::Rect r1 = GetToastRectAt(1);
    165   gfx::Rect r2 = GetToastRectAt(2);
    166   gfx::Rect r3 = GetToastRectAt(3);
    167 
    168   // 3 toasts are shown, equal size, vertical stack.
    169   EXPECT_TRUE(IsToastShown(id0));
    170   EXPECT_TRUE(IsToastShown(id1));
    171   EXPECT_TRUE(IsToastShown(id2));
    172 
    173   EXPECT_EQ(r0.width(), r1.width());
    174   EXPECT_EQ(r1.width(), r2.width());
    175 
    176   EXPECT_EQ(r0.height(), r1.height());
    177   EXPECT_EQ(r1.height(), r2.height());
    178 
    179   EXPECT_GT(r0.y(), r1.y());
    180   EXPECT_GT(r1.y(), r2.y());
    181 
    182   EXPECT_EQ(r0.x(), r1.x());
    183   EXPECT_EQ(r1.x(), r2.x());
    184 
    185   // The 4th toast is not shown yet.
    186   EXPECT_FALSE(IsToastShown(id3));
    187   EXPECT_EQ(0, r3.width());
    188   EXPECT_EQ(0, r3.height());
    189 
    190   CloseAllToastsAndWait();
    191   EXPECT_EQ(0u, GetToastCounts());
    192 }
    193 
    194 TEST_F(MessagePopupCollectionTest, DefaultPositioningWithRightTaskbar) {
    195   // If taskbar is on the right we show the toasts bottom to top as usual.
    196 
    197   // Simulate a taskbar at the right.
    198   collection()->SetDisplayInfo(gfx::Rect(0, 0, 590, 400),   // Work-area.
    199                                gfx::Rect(0, 0, 600, 400));  // Display-bounds.
    200   std::string id0 = AddNotification();
    201   std::string id1 = AddNotification();
    202   WaitForTransitionsDone();
    203 
    204   gfx::Rect r0 = GetToastRectAt(0);
    205   gfx::Rect r1 = GetToastRectAt(1);
    206 
    207   // 2 toasts are shown, equal size, vertical stack.
    208   EXPECT_TRUE(IsToastShown(id0));
    209   EXPECT_TRUE(IsToastShown(id1));
    210 
    211   EXPECT_EQ(r0.width(), r1.width());
    212   EXPECT_EQ(r0.height(), r1.height());
    213   EXPECT_GT(r0.y(), r1.y());
    214   EXPECT_EQ(r0.x(), r1.x());
    215 
    216   CloseAllToastsAndWait();
    217   EXPECT_EQ(0u, GetToastCounts());
    218 
    219   // Restore simulated taskbar position to bottom.
    220   collection()->SetDisplayInfo(gfx::Rect(0, 0, 600, 390),  // Work-area.
    221                                gfx::Rect(0, 0, 600, 400)); // Display-bounds.
    222 }
    223 
    224 TEST_F(MessagePopupCollectionTest, TopDownPositioningWithTopTaskbar) {
    225   // Simulate a taskbar at the top.
    226   collection()->SetDisplayInfo(gfx::Rect(0, 10, 600, 390),  // Work-area.
    227                                gfx::Rect(0, 0, 600, 400));  // Display-bounds.
    228   std::string id0 = AddNotification();
    229   std::string id1 = AddNotification();
    230   WaitForTransitionsDone();
    231 
    232   gfx::Rect r0 = GetToastRectAt(0);
    233   gfx::Rect r1 = GetToastRectAt(1);
    234 
    235   // 2 toasts are shown, equal size, vertical stack.
    236   EXPECT_TRUE(IsToastShown(id0));
    237   EXPECT_TRUE(IsToastShown(id1));
    238 
    239   EXPECT_EQ(r0.width(), r1.width());
    240   EXPECT_EQ(r0.height(), r1.height());
    241   EXPECT_LT(r0.y(), r1.y());
    242   EXPECT_EQ(r0.x(), r1.x());
    243 
    244   CloseAllToastsAndWait();
    245   EXPECT_EQ(0u, GetToastCounts());
    246 
    247   // Restore simulated taskbar position to bottom.
    248   collection()->SetDisplayInfo(gfx::Rect(0, 0, 600, 390),   // Work-area.
    249                                gfx::Rect(0, 0, 600, 400));  // Display-bounds.
    250 }
    251 
    252 TEST_F(MessagePopupCollectionTest, TopDownPositioningWithLeftAndTopTaskbar) {
    253   // If there "seems" to be a taskbar on left and top (like in Unity), it is
    254   // assumed that the actual taskbar is the top one.
    255 
    256   // Simulate a taskbar at the top and left.
    257   collection()->SetDisplayInfo(gfx::Rect(10, 10, 590, 390),  // Work-area.
    258                                gfx::Rect(0, 0, 600, 400));   // Display-bounds.
    259   std::string id0 = AddNotification();
    260   std::string id1 = AddNotification();
    261   WaitForTransitionsDone();
    262 
    263   gfx::Rect r0 = GetToastRectAt(0);
    264   gfx::Rect r1 = GetToastRectAt(1);
    265 
    266   // 2 toasts are shown, equal size, vertical stack.
    267   EXPECT_TRUE(IsToastShown(id0));
    268   EXPECT_TRUE(IsToastShown(id1));
    269 
    270   EXPECT_EQ(r0.width(), r1.width());
    271   EXPECT_EQ(r0.height(), r1.height());
    272   EXPECT_LT(r0.y(), r1.y());
    273   EXPECT_EQ(r0.x(), r1.x());
    274 
    275   CloseAllToastsAndWait();
    276   EXPECT_EQ(0u, GetToastCounts());
    277 
    278   // Restore simulated taskbar position to bottom.
    279   collection()->SetDisplayInfo(gfx::Rect(0, 0, 600, 390),   // Work-area.
    280                                gfx::Rect(0, 0, 600, 400));  // Display-bounds.
    281 }
    282 
    283 TEST_F(MessagePopupCollectionTest, TopDownPositioningWithBottomAndTopTaskbar) {
    284   // If there "seems" to be a taskbar on bottom and top (like in Gnome), it is
    285   // assumed that the actual taskbar is the top one.
    286 
    287   // Simulate a taskbar at the top and bottom.
    288   collection()->SetDisplayInfo(gfx::Rect(0, 10, 580, 400),  // Work-area.
    289                                gfx::Rect(0, 0, 600, 400));  // Display-bounds.
    290   std::string id0 = AddNotification();
    291   std::string id1 = AddNotification();
    292   WaitForTransitionsDone();
    293 
    294   gfx::Rect r0 = GetToastRectAt(0);
    295   gfx::Rect r1 = GetToastRectAt(1);
    296 
    297   // 2 toasts are shown, equal size, vertical stack.
    298   EXPECT_TRUE(IsToastShown(id0));
    299   EXPECT_TRUE(IsToastShown(id1));
    300 
    301   EXPECT_EQ(r0.width(), r1.width());
    302   EXPECT_EQ(r0.height(), r1.height());
    303   EXPECT_LT(r0.y(), r1.y());
    304   EXPECT_EQ(r0.x(), r1.x());
    305 
    306   CloseAllToastsAndWait();
    307   EXPECT_EQ(0u, GetToastCounts());
    308 
    309   // Restore simulated taskbar position to bottom.
    310   collection()->SetDisplayInfo(gfx::Rect(0, 0, 600, 390),   // Work-area.
    311                                gfx::Rect(0, 0, 600, 400));  // Display-bounds.
    312 }
    313 
    314 TEST_F(MessagePopupCollectionTest, LeftPositioningWithLeftTaskbar) {
    315   // Simulate a taskbar at the left.
    316   collection()->SetDisplayInfo(gfx::Rect(10, 0, 590, 400),  // Work-area.
    317                                gfx::Rect(0, 0, 600, 400));  // Display-bounds.
    318   std::string id0 = AddNotification();
    319   std::string id1 = AddNotification();
    320   WaitForTransitionsDone();
    321 
    322   gfx::Rect r0 = GetToastRectAt(0);
    323   gfx::Rect r1 = GetToastRectAt(1);
    324 
    325   // 2 toasts are shown, equal size, vertical stack.
    326   EXPECT_TRUE(IsToastShown(id0));
    327   EXPECT_TRUE(IsToastShown(id1));
    328 
    329   EXPECT_EQ(r0.width(), r1.width());
    330   EXPECT_EQ(r0.height(), r1.height());
    331   EXPECT_GT(r0.y(), r1.y());
    332   EXPECT_EQ(r0.x(), r1.x());
    333 
    334   // Ensure that toasts are on the left.
    335   EXPECT_LT(r1.x(), GetWorkArea().CenterPoint().x());
    336 
    337   CloseAllToastsAndWait();
    338   EXPECT_EQ(0u, GetToastCounts());
    339 
    340   // Restore simulated taskbar position to bottom.
    341   collection()->SetDisplayInfo(gfx::Rect(0, 0, 600, 390),   // Work-area.
    342                                gfx::Rect(0, 0, 600, 400));  // Display-bounds.
    343 }
    344 
    345 TEST_F(MessagePopupCollectionTest, DetectMouseHover) {
    346   std::string id0 = AddNotification();
    347   std::string id1 = AddNotification();
    348   WaitForTransitionsDone();
    349 
    350   views::WidgetDelegateView* toast0 = GetToast(id0);
    351   EXPECT_TRUE(toast0 != NULL);
    352   views::WidgetDelegateView* toast1 = GetToast(id1);
    353   EXPECT_TRUE(toast1 != NULL);
    354 
    355   ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), 0);
    356 
    357   // Test that mouse detection logic works in presence of out-of-order events.
    358   toast0->OnMouseEntered(event);
    359   EXPECT_TRUE(MouseInCollection());
    360   toast1->OnMouseEntered(event);
    361   EXPECT_TRUE(MouseInCollection());
    362   toast0->OnMouseExited(event);
    363   EXPECT_TRUE(MouseInCollection());
    364   toast1->OnMouseExited(event);
    365   EXPECT_FALSE(MouseInCollection());
    366 
    367   // Test that mouse detection logic works in presence of WindowClosing events.
    368   toast0->OnMouseEntered(event);
    369   EXPECT_TRUE(MouseInCollection());
    370   toast1->OnMouseEntered(event);
    371   EXPECT_TRUE(MouseInCollection());
    372   toast0->WindowClosing();
    373   EXPECT_TRUE(MouseInCollection());
    374   toast1->WindowClosing();
    375   EXPECT_FALSE(MouseInCollection());
    376 }
    377 
    378 // TODO(dimich): Test repositioning - both normal one and when user is closing
    379 // the toasts.
    380 
    381 }  // namespace test
    382 }  // namespace message_center
    383