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_widget.h"
     11 #include "ash/shell.h"
     12 #include "ash/system/status_area_widget.h"
     13 #include "ash/system/tray/system_tray_item.h"
     14 #include "ash/test/ash_test_base.h"
     15 #include "base/strings/utf_string_conversions.h"
     16 #include "ui/views/controls/label.h"
     17 #include "ui/views/layout/fill_layout.h"
     18 #include "ui/views/view.h"
     19 #include "ui/views/widget/widget.h"
     20 
     21 #if defined(OS_WIN)
     22 #include "base/win/windows_version.h"
     23 #endif
     24 
     25 namespace ash {
     26 namespace test {
     27 
     28 namespace {
     29 
     30 const int kStatusTrayOffsetFromScreenEdgeForTest = 4;
     31 
     32 SystemTray* GetSystemTray() {
     33   return Shell::GetPrimaryRootWindowController()->shelf()->
     34       status_area_widget()->system_tray();
     35 }
     36 
     37 // Trivial item implementation that tracks its views for testing.
     38 class TestItem : public SystemTrayItem {
     39  public:
     40   TestItem() : SystemTrayItem(GetSystemTray()), tray_view_(NULL) {}
     41 
     42   virtual views::View* CreateTrayView(user::LoginStatus status) OVERRIDE {
     43     tray_view_ = new views::View;
     44     // Add a label so it has non-zero width.
     45     tray_view_->SetLayoutManager(new views::FillLayout);
     46     tray_view_->AddChildView(new views::Label(UTF8ToUTF16("Tray")));
     47     return tray_view_;
     48   }
     49 
     50   virtual views::View* CreateDefaultView(user::LoginStatus status) OVERRIDE {
     51     default_view_ = new views::View;
     52     default_view_->SetLayoutManager(new views::FillLayout);
     53     default_view_->AddChildView(new views::Label(UTF8ToUTF16("Default")));
     54     return default_view_;
     55   }
     56 
     57   virtual views::View* CreateDetailedView(user::LoginStatus status) OVERRIDE {
     58     detailed_view_ = new views::View;
     59     detailed_view_->SetLayoutManager(new views::FillLayout);
     60     detailed_view_->AddChildView(new views::Label(UTF8ToUTF16("Detailed")));
     61     return detailed_view_;
     62   }
     63 
     64   virtual views::View* CreateNotificationView(
     65       user::LoginStatus status) OVERRIDE {
     66     notification_view_ = new views::View;
     67     return notification_view_;
     68   }
     69 
     70   virtual void DestroyTrayView() OVERRIDE {
     71     tray_view_ = NULL;
     72   }
     73 
     74   virtual void DestroyDefaultView() OVERRIDE {
     75     default_view_ = NULL;
     76   }
     77 
     78   virtual void DestroyDetailedView() OVERRIDE {
     79     detailed_view_ = NULL;
     80   }
     81 
     82   virtual void DestroyNotificationView() OVERRIDE {
     83     notification_view_ = NULL;
     84   }
     85 
     86   virtual void UpdateAfterLoginStatusChange(
     87       user::LoginStatus status) OVERRIDE {
     88   }
     89 
     90   views::View* tray_view() const { return tray_view_; }
     91   views::View* default_view() const { return default_view_; }
     92   views::View* detailed_view() const { return detailed_view_; }
     93   views::View* notification_view() const { return notification_view_; }
     94 
     95  private:
     96   views::View* tray_view_;
     97   views::View* default_view_;
     98   views::View* detailed_view_;
     99   views::View* notification_view_;
    100 };
    101 
    102 // Trivial item implementation that returns NULL from tray/default/detailed
    103 // view creation methods.
    104 class TestNoViewItem : public SystemTrayItem {
    105  public:
    106   TestNoViewItem() : SystemTrayItem(GetSystemTray()) {}
    107 
    108   virtual views::View* CreateTrayView(user::LoginStatus status) OVERRIDE {
    109     return NULL;
    110   }
    111 
    112   virtual views::View* CreateDefaultView(user::LoginStatus status) OVERRIDE {
    113     return NULL;
    114   }
    115 
    116   virtual views::View* CreateDetailedView(user::LoginStatus status) OVERRIDE {
    117     return NULL;
    118   }
    119 
    120   virtual views::View* CreateNotificationView(
    121       user::LoginStatus status) OVERRIDE {
    122     return NULL;
    123   }
    124 
    125   virtual void DestroyTrayView() OVERRIDE {}
    126   virtual void DestroyDefaultView() OVERRIDE {}
    127   virtual void DestroyDetailedView() OVERRIDE {}
    128   virtual void DestroyNotificationView() OVERRIDE {}
    129   virtual void UpdateAfterLoginStatusChange(
    130       user::LoginStatus status) OVERRIDE {
    131   }
    132 };
    133 
    134 }  // namespace
    135 
    136 typedef AshTestBase SystemTrayTest;
    137 
    138 TEST_F(SystemTrayTest, SystemTrayDefaultView) {
    139   SystemTray* tray = GetSystemTray();
    140   ASSERT_TRUE(tray->GetWidget());
    141 
    142   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
    143 
    144   // Ensure that closing the bubble destroys it.
    145   ASSERT_TRUE(tray->CloseSystemBubble());
    146   RunAllPendingInMessageLoop();
    147   ASSERT_FALSE(tray->CloseSystemBubble());
    148 }
    149 
    150 TEST_F(SystemTrayTest, SystemTrayTestItems) {
    151   SystemTray* tray = GetSystemTray();
    152   ASSERT_TRUE(tray->GetWidget());
    153 
    154   TestItem* test_item = new TestItem;
    155   TestItem* detailed_item = new TestItem;
    156   tray->AddTrayItem(test_item);
    157   tray->AddTrayItem(detailed_item);
    158 
    159   // Check items have been added
    160   const std::vector<SystemTrayItem*>& items = tray->GetTrayItems();
    161   ASSERT_TRUE(
    162       std::find(items.begin(), items.end(), test_item) != items.end());
    163   ASSERT_TRUE(
    164       std::find(items.begin(), items.end(), detailed_item) != items.end());
    165 
    166   // Ensure the tray views are created.
    167   ASSERT_TRUE(test_item->tray_view() != NULL);
    168   ASSERT_TRUE(detailed_item->tray_view() != NULL);
    169 
    170   // Ensure a default views are created.
    171   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
    172   ASSERT_TRUE(test_item->default_view() != NULL);
    173   ASSERT_TRUE(detailed_item->default_view() != NULL);
    174 
    175   // Show the detailed view, ensure it's created and the default view destroyed.
    176   tray->ShowDetailedView(detailed_item, 0, false, BUBBLE_CREATE_NEW);
    177   RunAllPendingInMessageLoop();
    178   ASSERT_TRUE(test_item->default_view() == NULL);
    179   ASSERT_TRUE(detailed_item->detailed_view() != NULL);
    180 
    181   // Show the default view, ensure it's created and the detailed view destroyed.
    182   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
    183   RunAllPendingInMessageLoop();
    184   ASSERT_TRUE(test_item->default_view() != NULL);
    185   ASSERT_TRUE(detailed_item->detailed_view() == NULL);
    186 }
    187 
    188 TEST_F(SystemTrayTest, SystemTrayNoViewItems) {
    189   SystemTray* tray = GetSystemTray();
    190   ASSERT_TRUE(tray->GetWidget());
    191 
    192   // Verify that no crashes occur on items lacking some views.
    193   TestNoViewItem* no_view_item = new TestNoViewItem;
    194   tray->AddTrayItem(no_view_item);
    195   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
    196   tray->ShowDetailedView(no_view_item, 0, false, BUBBLE_USE_EXISTING);
    197   RunAllPendingInMessageLoop();
    198 }
    199 
    200 TEST_F(SystemTrayTest, TrayWidgetAutoResizes) {
    201   SystemTray* tray = GetSystemTray();
    202   ASSERT_TRUE(tray->GetWidget());
    203 
    204   // Add an initial tray item so that the tray gets laid out correctly.
    205   TestItem* initial_item = new TestItem;
    206   tray->AddTrayItem(initial_item);
    207 
    208   gfx::Size initial_size = tray->GetWidget()->GetWindowBoundsInScreen().size();
    209 
    210   TestItem* new_item = new TestItem;
    211   tray->AddTrayItem(new_item);
    212 
    213   gfx::Size new_size = tray->GetWidget()->GetWindowBoundsInScreen().size();
    214 
    215   // Adding the new item should change the size of the tray.
    216   EXPECT_NE(initial_size.ToString(), new_size.ToString());
    217 
    218   // Hiding the tray view of the new item should also change the size of the
    219   // tray.
    220   new_item->tray_view()->SetVisible(false);
    221   EXPECT_EQ(initial_size.ToString(),
    222             tray->GetWidget()->GetWindowBoundsInScreen().size().ToString());
    223 
    224   new_item->tray_view()->SetVisible(true);
    225   EXPECT_EQ(new_size.ToString(),
    226             tray->GetWidget()->GetWindowBoundsInScreen().size().ToString());
    227 }
    228 
    229 TEST_F(SystemTrayTest, SystemTrayNotifications) {
    230   SystemTray* tray = GetSystemTray();
    231   ASSERT_TRUE(tray->GetWidget());
    232 
    233   TestItem* test_item = new TestItem;
    234   TestItem* detailed_item = new TestItem;
    235   tray->AddTrayItem(test_item);
    236   tray->AddTrayItem(detailed_item);
    237 
    238   // Ensure the tray views are created.
    239   ASSERT_TRUE(test_item->tray_view() != NULL);
    240   ASSERT_TRUE(detailed_item->tray_view() != NULL);
    241 
    242   // Ensure a notification view is created.
    243   tray->ShowNotificationView(test_item);
    244   ASSERT_TRUE(test_item->notification_view() != NULL);
    245 
    246   // Show the default view, notification view should remain.
    247   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
    248   RunAllPendingInMessageLoop();
    249   ASSERT_TRUE(test_item->notification_view() != NULL);
    250 
    251   // Show the detailed view, ensure the notificaiton view remains.
    252   tray->ShowDetailedView(detailed_item, 0, false, BUBBLE_CREATE_NEW);
    253   RunAllPendingInMessageLoop();
    254   ASSERT_TRUE(detailed_item->detailed_view() != NULL);
    255   ASSERT_TRUE(test_item->notification_view() != NULL);
    256 
    257   // Hide the detailed view, ensure the notification view still exists.
    258   ASSERT_TRUE(tray->CloseSystemBubble());
    259   RunAllPendingInMessageLoop();
    260   ASSERT_TRUE(detailed_item->detailed_view() == NULL);
    261   ASSERT_TRUE(test_item->notification_view() != NULL);
    262 }
    263 
    264 TEST_F(SystemTrayTest, BubbleCreationTypesTest) {
    265   SystemTray* tray = GetSystemTray();
    266   ASSERT_TRUE(tray->GetWidget());
    267 
    268   TestItem* test_item = new TestItem;
    269   tray->AddTrayItem(test_item);
    270 
    271   // Ensure the tray views are created.
    272   ASSERT_TRUE(test_item->tray_view() != NULL);
    273 
    274   // Show the default view, ensure the notification view is destroyed.
    275   tray->ShowDefaultView(BUBBLE_CREATE_NEW);
    276   RunAllPendingInMessageLoop();
    277 
    278   views::Widget* widget = test_item->default_view()->GetWidget();
    279   gfx::Rect bubble_bounds = widget->GetWindowBoundsInScreen();
    280 
    281   tray->ShowDetailedView(test_item, 0, true, BUBBLE_USE_EXISTING);
    282   RunAllPendingInMessageLoop();
    283 
    284   EXPECT_FALSE(test_item->default_view());
    285 
    286   EXPECT_EQ(bubble_bounds.ToString(), test_item->detailed_view()->GetWidget()->
    287       GetWindowBoundsInScreen().ToString());
    288   EXPECT_EQ(widget, test_item->detailed_view()->GetWidget());
    289 
    290   tray->ShowDefaultView(BUBBLE_USE_EXISTING);
    291   RunAllPendingInMessageLoop();
    292 
    293   EXPECT_EQ(bubble_bounds.ToString(), test_item->default_view()->GetWidget()->
    294       GetWindowBoundsInScreen().ToString());
    295   EXPECT_EQ(widget, test_item->default_view()->GetWidget());
    296 }
    297 
    298 // Tests that the tray is laid out properly in the widget to make sure that the
    299 // tray extends to the correct edge of the screen.
    300 TEST_F(SystemTrayTest, TrayBoundsInWidget) {
    301   internal::StatusAreaWidget* widget =
    302       Shell::GetPrimaryRootWindowController()->shelf()->status_area_widget();
    303   SystemTray* tray = widget->system_tray();
    304 
    305   // Test in bottom alignment. Bottom and right edges of the view should be
    306   // aligned with the widget.
    307   widget->SetShelfAlignment(SHELF_ALIGNMENT_BOTTOM);
    308   gfx::Rect window_bounds = widget->GetWindowBoundsInScreen();
    309   gfx::Rect tray_bounds = tray->GetBoundsInScreen();
    310   EXPECT_EQ(window_bounds.bottom(),
    311             tray_bounds.bottom() + kStatusTrayOffsetFromScreenEdgeForTest);
    312   EXPECT_EQ(window_bounds.right(), tray_bounds.right());
    313 
    314   // Test in the top alignment. Top and right edges should match.
    315   widget->SetShelfAlignment(SHELF_ALIGNMENT_TOP);
    316   window_bounds = widget->GetWindowBoundsInScreen();
    317   tray_bounds = tray->GetBoundsInScreen();
    318   EXPECT_EQ(window_bounds.y(),
    319             tray_bounds.y() - kStatusTrayOffsetFromScreenEdgeForTest);
    320   EXPECT_EQ(window_bounds.right(), tray_bounds.right());
    321 
    322   // Test in the left alignment. Left and bottom edges should match.
    323   widget->SetShelfAlignment(SHELF_ALIGNMENT_LEFT);
    324   window_bounds = widget->GetWindowBoundsInScreen();
    325   tray_bounds = tray->GetBoundsInScreen();
    326   EXPECT_EQ(window_bounds.bottom(), tray_bounds.bottom());
    327   EXPECT_EQ(window_bounds.x(),
    328             tray_bounds.x() - kStatusTrayOffsetFromScreenEdgeForTest);
    329 
    330   // Test in the right alignment. Right and bottom edges should match.
    331   widget->SetShelfAlignment(SHELF_ALIGNMENT_LEFT);
    332   window_bounds = widget->GetWindowBoundsInScreen();
    333   tray_bounds = tray->GetBoundsInScreen();
    334   EXPECT_EQ(window_bounds.bottom(), tray_bounds.bottom());
    335   EXPECT_EQ(window_bounds.right(), tray_bounds.right());
    336 }
    337 
    338 }  // namespace test
    339 }  // namespace ash
    340