Home | History | Annotate | Download | only in tray
      1 // Copyright (c) 2012 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/system/tray/system_tray.h"
      6 
      7 #include <vector>
      8 
      9 #include "ash/root_window_controller.h"
     10 #include "ash/shelf/shelf_layout_manager.h"
     11 #include "ash/shelf/shelf_widget.h"
     12 #include "ash/shell.h"
     13 #include "ash/system/status_area_widget.h"
     14 #include "ash/system/tray/system_tray_item.h"
     15 #include "ash/test/ash_test_base.h"
     16 #include "ash/wm/window_util.h"
     17 #include "base/run_loop.h"
     18 #include "base/strings/utf_string_conversions.h"
     19 #include "ui/aura/test/event_generator.h"
     20 #include "ui/aura/window.h"
     21 #include "ui/views/controls/label.h"
     22 #include "ui/views/layout/fill_layout.h"
     23 #include "ui/views/view.h"
     24 #include "ui/views/widget/widget.h"
     25 
     26 #if defined(OS_WIN)
     27 #include "base/win/windows_version.h"
     28 #endif
     29 
     30 namespace ash {
     31 namespace test {
     32 
     33 namespace {
     34 
     35 SystemTray* GetSystemTray() {
     36   return Shell::GetPrimaryRootWindowController()->shelf()->
     37       status_area_widget()->system_tray();
     38 }
     39 
     40 // Trivial item implementation that tracks its views for testing.
     41 class TestItem : public SystemTrayItem {
     42  public:
     43   TestItem() : SystemTrayItem(GetSystemTray()), tray_view_(NULL) {}
     44 
     45   virtual views::View* CreateTrayView(user::LoginStatus status) OVERRIDE {
     46     tray_view_ = new views::View;
     47     // Add a label so it has non-zero width.
     48     tray_view_->SetLayoutManager(new views::FillLayout);
     49     tray_view_->AddChildView(new views::Label(UTF8ToUTF16("Tray")));
     50     return tray_view_;
     51   }
     52 
     53   virtual views::View* CreateDefaultView(user::LoginStatus status) OVERRIDE {
     54     default_view_ = new views::View;
     55     default_view_->SetLayoutManager(new views::FillLayout);
     56     default_view_->AddChildView(new views::Label(UTF8ToUTF16("Default")));
     57     return default_view_;
     58   }
     59 
     60   virtual views::View* CreateDetailedView(user::LoginStatus status) OVERRIDE {
     61     detailed_view_ = new views::View;
     62     detailed_view_->SetLayoutManager(new views::FillLayout);
     63     detailed_view_->AddChildView(new views::Label(UTF8ToUTF16("Detailed")));
     64     return detailed_view_;
     65   }
     66 
     67   virtual views::View* CreateNotificationView(
     68       user::LoginStatus status) OVERRIDE {
     69     notification_view_ = new views::View;
     70     return notification_view_;
     71   }
     72 
     73   virtual void DestroyTrayView() OVERRIDE {
     74     tray_view_ = NULL;
     75   }
     76 
     77   virtual void DestroyDefaultView() OVERRIDE {
     78     default_view_ = NULL;
     79   }
     80 
     81   virtual void DestroyDetailedView() OVERRIDE {
     82     detailed_view_ = NULL;
     83   }
     84 
     85   virtual void DestroyNotificationView() OVERRIDE {
     86     notification_view_ = NULL;
     87   }
     88 
     89   virtual void UpdateAfterLoginStatusChange(
     90       user::LoginStatus status) OVERRIDE {
     91   }
     92 
     93   views::View* tray_view() const { return tray_view_; }
     94   views::View* default_view() const { return default_view_; }
     95   views::View* detailed_view() const { return detailed_view_; }
     96   views::View* notification_view() const { return notification_view_; }
     97 
     98  private:
     99   views::View* tray_view_;
    100   views::View* default_view_;
    101   views::View* detailed_view_;
    102   views::View* notification_view_;
    103 };
    104 
    105 // Trivial item implementation that returns NULL from tray/default/detailed
    106 // view creation methods.
    107 class TestNoViewItem : public SystemTrayItem {
    108  public:
    109   TestNoViewItem() : SystemTrayItem(GetSystemTray()) {}
    110 
    111   virtual views::View* CreateTrayView(user::LoginStatus status) OVERRIDE {
    112     return NULL;
    113   }
    114 
    115   virtual views::View* CreateDefaultView(user::LoginStatus status) OVERRIDE {
    116     return NULL;
    117   }
    118 
    119   virtual views::View* CreateDetailedView(user::LoginStatus status) OVERRIDE {
    120     return NULL;
    121   }
    122 
    123   virtual views::View* CreateNotificationView(
    124       user::LoginStatus status) OVERRIDE {
    125     return NULL;
    126   }
    127 
    128   virtual void DestroyTrayView() OVERRIDE {}
    129   virtual void DestroyDefaultView() OVERRIDE {}
    130   virtual void DestroyDetailedView() OVERRIDE {}
    131   virtual void DestroyNotificationView() OVERRIDE {}
    132   virtual void UpdateAfterLoginStatusChange(
    133       user::LoginStatus status) OVERRIDE {
    134   }
    135 };
    136 
    137 }  // namespace
    138 
    139 typedef AshTestBase SystemTrayTest;
    140 
    141 TEST_F(SystemTrayTest, SystemTrayDefaultView) {
    142   SystemTray* tray = GetSystemTray();
    143   ASSERT_TRUE(tray->GetWidget());
    144 
    145   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
    146 
    147   // Ensure that closing the bubble destroys it.
    148   ASSERT_TRUE(tray->CloseSystemBubble());
    149   RunAllPendingInMessageLoop();
    150   ASSERT_FALSE(tray->CloseSystemBubble());
    151 }
    152 
    153 // Opening and closing the bubble should change the coloring of the tray.
    154 TEST_F(SystemTrayTest, SystemTrayColoring) {
    155   SystemTray* tray = GetSystemTray();
    156   ASSERT_TRUE(tray->GetWidget());
    157   // At the beginning the tray coloring is not active.
    158   ASSERT_FALSE(tray->draw_background_as_active());
    159 
    160   // Showing the system bubble should show the background as active.
    161   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
    162   ASSERT_TRUE(tray->draw_background_as_active());
    163 
    164   // Closing the system menu should change the coloring back to normal.
    165   ASSERT_TRUE(tray->CloseSystemBubble());
    166   RunAllPendingInMessageLoop();
    167   ASSERT_FALSE(tray->draw_background_as_active());
    168 }
    169 
    170 // Closing the system bubble through an alignment change should change the
    171 // system tray coloring back to normal.
    172 TEST_F(SystemTrayTest, SystemTrayColoringAfterAlignmentChange) {
    173   SystemTray* tray = GetSystemTray();
    174   ASSERT_TRUE(tray->GetWidget());
    175   internal::ShelfLayoutManager* manager =
    176       Shell::GetPrimaryRootWindowController()->shelf()->shelf_layout_manager();
    177   manager->SetAlignment(SHELF_ALIGNMENT_BOTTOM);
    178   // At the beginning the tray coloring is not active.
    179   ASSERT_FALSE(tray->draw_background_as_active());
    180 
    181   // Showing the system bubble should show the background as active.
    182   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
    183   ASSERT_TRUE(tray->draw_background_as_active());
    184 
    185   // Changing the alignment should close the system bubble and change the
    186   // background color.
    187   manager->SetAlignment(SHELF_ALIGNMENT_LEFT);
    188   ASSERT_FALSE(tray->draw_background_as_active());
    189   RunAllPendingInMessageLoop();
    190   // The bubble should already be closed by now.
    191   ASSERT_FALSE(tray->CloseSystemBubble());
    192 }
    193 
    194 TEST_F(SystemTrayTest, SystemTrayTestItems) {
    195   SystemTray* tray = GetSystemTray();
    196   ASSERT_TRUE(tray->GetWidget());
    197 
    198   TestItem* test_item = new TestItem;
    199   TestItem* detailed_item = new TestItem;
    200   tray->AddTrayItem(test_item);
    201   tray->AddTrayItem(detailed_item);
    202 
    203   // Check items have been added
    204   const std::vector<SystemTrayItem*>& items = tray->GetTrayItems();
    205   ASSERT_TRUE(
    206       std::find(items.begin(), items.end(), test_item) != items.end());
    207   ASSERT_TRUE(
    208       std::find(items.begin(), items.end(), detailed_item) != items.end());
    209 
    210   // Ensure the tray views are created.
    211   ASSERT_TRUE(test_item->tray_view() != NULL);
    212   ASSERT_TRUE(detailed_item->tray_view() != NULL);
    213 
    214   // Ensure a default views are created.
    215   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
    216   ASSERT_TRUE(test_item->default_view() != NULL);
    217   ASSERT_TRUE(detailed_item->default_view() != NULL);
    218 
    219   // Show the detailed view, ensure it's created and the default view destroyed.
    220   tray->ShowDetailedView(detailed_item, 0, false, BUBBLE_CREATE_NEW);
    221   RunAllPendingInMessageLoop();
    222   ASSERT_TRUE(test_item->default_view() == NULL);
    223   ASSERT_TRUE(detailed_item->detailed_view() != NULL);
    224 
    225   // Show the default view, ensure it's created and the detailed view destroyed.
    226   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
    227   RunAllPendingInMessageLoop();
    228   ASSERT_TRUE(test_item->default_view() != NULL);
    229   ASSERT_TRUE(detailed_item->detailed_view() == NULL);
    230 }
    231 
    232 TEST_F(SystemTrayTest, SystemTrayNoViewItems) {
    233   SystemTray* tray = GetSystemTray();
    234   ASSERT_TRUE(tray->GetWidget());
    235 
    236   // Verify that no crashes occur on items lacking some views.
    237   TestNoViewItem* no_view_item = new TestNoViewItem;
    238   tray->AddTrayItem(no_view_item);
    239   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
    240   tray->ShowDetailedView(no_view_item, 0, false, BUBBLE_USE_EXISTING);
    241   RunAllPendingInMessageLoop();
    242 }
    243 
    244 TEST_F(SystemTrayTest, TrayWidgetAutoResizes) {
    245   SystemTray* tray = GetSystemTray();
    246   ASSERT_TRUE(tray->GetWidget());
    247 
    248   // Add an initial tray item so that the tray gets laid out correctly.
    249   TestItem* initial_item = new TestItem;
    250   tray->AddTrayItem(initial_item);
    251 
    252   gfx::Size initial_size = tray->GetWidget()->GetWindowBoundsInScreen().size();
    253 
    254   TestItem* new_item = new TestItem;
    255   tray->AddTrayItem(new_item);
    256 
    257   gfx::Size new_size = tray->GetWidget()->GetWindowBoundsInScreen().size();
    258 
    259   // Adding the new item should change the size of the tray.
    260   EXPECT_NE(initial_size.ToString(), new_size.ToString());
    261 
    262   // Hiding the tray view of the new item should also change the size of the
    263   // tray.
    264   new_item->tray_view()->SetVisible(false);
    265   EXPECT_EQ(initial_size.ToString(),
    266             tray->GetWidget()->GetWindowBoundsInScreen().size().ToString());
    267 
    268   new_item->tray_view()->SetVisible(true);
    269   EXPECT_EQ(new_size.ToString(),
    270             tray->GetWidget()->GetWindowBoundsInScreen().size().ToString());
    271 }
    272 
    273 TEST_F(SystemTrayTest, SystemTrayNotifications) {
    274   SystemTray* tray = GetSystemTray();
    275   ASSERT_TRUE(tray->GetWidget());
    276 
    277   TestItem* test_item = new TestItem;
    278   TestItem* detailed_item = new TestItem;
    279   tray->AddTrayItem(test_item);
    280   tray->AddTrayItem(detailed_item);
    281 
    282   // Ensure the tray views are created.
    283   ASSERT_TRUE(test_item->tray_view() != NULL);
    284   ASSERT_TRUE(detailed_item->tray_view() != NULL);
    285 
    286   // Ensure a notification view is created.
    287   tray->ShowNotificationView(test_item);
    288   ASSERT_TRUE(test_item->notification_view() != NULL);
    289 
    290   // Show the default view, notification view should remain.
    291   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
    292   RunAllPendingInMessageLoop();
    293   ASSERT_TRUE(test_item->notification_view() != NULL);
    294 
    295   // Show the detailed view, ensure the notificaiton view remains.
    296   tray->ShowDetailedView(detailed_item, 0, false, BUBBLE_CREATE_NEW);
    297   RunAllPendingInMessageLoop();
    298   ASSERT_TRUE(detailed_item->detailed_view() != NULL);
    299   ASSERT_TRUE(test_item->notification_view() != NULL);
    300 
    301   // Hide the detailed view, ensure the notification view still exists.
    302   ASSERT_TRUE(tray->CloseSystemBubble());
    303   RunAllPendingInMessageLoop();
    304   ASSERT_TRUE(detailed_item->detailed_view() == NULL);
    305   ASSERT_TRUE(test_item->notification_view() != NULL);
    306 }
    307 
    308 TEST_F(SystemTrayTest, BubbleCreationTypesTest) {
    309   SystemTray* tray = GetSystemTray();
    310   ASSERT_TRUE(tray->GetWidget());
    311 
    312   TestItem* test_item = new TestItem;
    313   tray->AddTrayItem(test_item);
    314 
    315   // Ensure the tray views are created.
    316   ASSERT_TRUE(test_item->tray_view() != NULL);
    317 
    318   // Show the default view, ensure the notification view is destroyed.
    319   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
    320   RunAllPendingInMessageLoop();
    321 
    322   views::Widget* widget = test_item->default_view()->GetWidget();
    323   gfx::Rect bubble_bounds = widget->GetWindowBoundsInScreen();
    324 
    325   tray->ShowDetailedView(test_item, 0, true, BUBBLE_USE_EXISTING);
    326   RunAllPendingInMessageLoop();
    327 
    328   EXPECT_FALSE(test_item->default_view());
    329 
    330   EXPECT_EQ(bubble_bounds.ToString(), test_item->detailed_view()->GetWidget()->
    331       GetWindowBoundsInScreen().ToString());
    332   EXPECT_EQ(widget, test_item->detailed_view()->GetWidget());
    333 
    334   tray->ShowDefaultView(BUBBLE_USE_EXISTING);
    335   RunAllPendingInMessageLoop();
    336 
    337   EXPECT_EQ(bubble_bounds.ToString(), test_item->default_view()->GetWidget()->
    338       GetWindowBoundsInScreen().ToString());
    339   EXPECT_EQ(widget, test_item->default_view()->GetWidget());
    340 }
    341 
    342 // Tests that the tray is laid out properly and is fully contained within
    343 // the shelf.
    344 TEST_F(SystemTrayTest, TrayBoundsInWidget) {
    345   internal::ShelfLayoutManager* manager =
    346       Shell::GetPrimaryRootWindowController()->shelf()->shelf_layout_manager();
    347   internal::StatusAreaWidget* widget =
    348       Shell::GetPrimaryRootWindowController()->shelf()->status_area_widget();
    349   SystemTray* tray = widget->system_tray();
    350 
    351   // Test in bottom alignment.
    352   manager->SetAlignment(SHELF_ALIGNMENT_BOTTOM);
    353   gfx::Rect window_bounds = widget->GetWindowBoundsInScreen();
    354   gfx::Rect tray_bounds = tray->GetBoundsInScreen();
    355   EXPECT_TRUE(window_bounds.bottom() >= tray_bounds.bottom());
    356   EXPECT_TRUE(window_bounds.right() >= tray_bounds.right());
    357   EXPECT_TRUE(window_bounds.x() >= tray_bounds.x());
    358   EXPECT_TRUE(window_bounds.y() >= tray_bounds.y());
    359 
    360   // Test in the left alignment.
    361   manager->SetAlignment(SHELF_ALIGNMENT_LEFT);
    362   window_bounds = widget->GetWindowBoundsInScreen();
    363   tray_bounds = tray->GetBoundsInScreen();
    364   EXPECT_TRUE(window_bounds.bottom() >= tray_bounds.bottom());
    365   EXPECT_TRUE(window_bounds.right() >= tray_bounds.right());
    366   EXPECT_TRUE(window_bounds.x() >= tray_bounds.x());
    367   EXPECT_TRUE(window_bounds.y() >= tray_bounds.y());
    368 
    369   // Test in the right alignment.
    370   manager->SetAlignment(SHELF_ALIGNMENT_LEFT);
    371   window_bounds = widget->GetWindowBoundsInScreen();
    372   tray_bounds = tray->GetBoundsInScreen();
    373   EXPECT_TRUE(window_bounds.bottom() >= tray_bounds.bottom());
    374   EXPECT_TRUE(window_bounds.right() >= tray_bounds.right());
    375   EXPECT_TRUE(window_bounds.x() >= tray_bounds.x());
    376   EXPECT_TRUE(window_bounds.y() >= tray_bounds.y());
    377 }
    378 
    379 TEST_F(SystemTrayTest, PersistentBubble) {
    380   SystemTray* tray = GetSystemTray();
    381   ASSERT_TRUE(tray->GetWidget());
    382 
    383   TestItem* test_item = new TestItem;
    384   tray->AddTrayItem(test_item);
    385 
    386   scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
    387 
    388   // Tests for usual default view.
    389   // Activating window.
    390   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
    391   ASSERT_TRUE(tray->HasSystemBubble());
    392   wm::ActivateWindow(window.get());
    393   base::RunLoop().RunUntilIdle();
    394   ASSERT_FALSE(tray->HasSystemBubble());
    395 
    396   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
    397   ASSERT_TRUE(tray->HasSystemBubble());
    398   {
    399     aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
    400                                          gfx::Point(5, 5));
    401     generator.ClickLeftButton();
    402     ASSERT_FALSE(tray->HasSystemBubble());
    403   }
    404 
    405   // Same tests for persistent default view.
    406   tray->ShowPersistentDefaultView();
    407   ASSERT_TRUE(tray->HasSystemBubble());
    408   wm::ActivateWindow(window.get());
    409   base::RunLoop().RunUntilIdle();
    410   ASSERT_TRUE(tray->HasSystemBubble());
    411 
    412   {
    413     aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
    414                                          gfx::Point(5, 5));
    415     generator.ClickLeftButton();
    416     ASSERT_TRUE(tray->HasSystemBubble());
    417   }
    418 }
    419 
    420 }  // namespace test
    421 }  // namespace ash
    422