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.
5 #include "ash/system/tray/system_tray.h"
9 #include "ash/root_window_controller.h"
10 #include "ash/shelf/shelf_widget.h"
11 #include "ash/system/status_area_widget.h"
12 #include "ash/system/tray/system_tray_item.h"
13 #include "ash/test/ash_test_base.h"
14 #include "base/utf_string_conversions.h"
15 #include "ui/views/controls/label.h"
16 #include "ui/views/layout/fill_layout.h"
17 #include "ui/views/view.h"
18 #include "ui/views/widget/widget.h"
21 #include "base/win/windows_version.h"
29 SystemTray
* GetSystemTray() {
30 return Shell::GetPrimaryRootWindowController()->shelf()->
31 status_area_widget()->system_tray();
34 // Trivial item implementation that tracks its views for testing.
35 class TestItem
: public SystemTrayItem
{
37 TestItem() : SystemTrayItem(GetSystemTray()), tray_view_(NULL
) {}
39 virtual views::View
* CreateTrayView(user::LoginStatus status
) OVERRIDE
{
40 tray_view_
= new views::View
;
41 // Add a label so it has non-zero width.
42 tray_view_
->SetLayoutManager(new views::FillLayout
);
43 tray_view_
->AddChildView(new views::Label(UTF8ToUTF16("Tray")));
47 virtual views::View
* CreateDefaultView(user::LoginStatus status
) OVERRIDE
{
48 default_view_
= new views::View
;
49 default_view_
->SetLayoutManager(new views::FillLayout
);
50 default_view_
->AddChildView(new views::Label(UTF8ToUTF16("Default")));
54 virtual views::View
* CreateDetailedView(user::LoginStatus status
) OVERRIDE
{
55 detailed_view_
= new views::View
;
56 detailed_view_
->SetLayoutManager(new views::FillLayout
);
57 detailed_view_
->AddChildView(new views::Label(UTF8ToUTF16("Detailed")));
58 return detailed_view_
;
61 virtual views::View
* CreateNotificationView(
62 user::LoginStatus status
) OVERRIDE
{
63 notification_view_
= new views::View
;
64 return notification_view_
;
67 virtual void DestroyTrayView() OVERRIDE
{
71 virtual void DestroyDefaultView() OVERRIDE
{
75 virtual void DestroyDetailedView() OVERRIDE
{
76 detailed_view_
= NULL
;
79 virtual void DestroyNotificationView() OVERRIDE
{
80 notification_view_
= NULL
;
83 virtual void UpdateAfterLoginStatusChange(
84 user::LoginStatus status
) OVERRIDE
{
87 views::View
* tray_view() const { return tray_view_
; }
88 views::View
* default_view() const { return default_view_
; }
89 views::View
* detailed_view() const { return detailed_view_
; }
90 views::View
* notification_view() const { return notification_view_
; }
93 views::View
* tray_view_
;
94 views::View
* default_view_
;
95 views::View
* detailed_view_
;
96 views::View
* notification_view_
;
99 // Trivial item implementation that returns NULL from tray/default/detailed
100 // view creation methods.
101 class TestNoViewItem
: public SystemTrayItem
{
103 TestNoViewItem() : SystemTrayItem(GetSystemTray()) {}
105 virtual views::View
* CreateTrayView(user::LoginStatus status
) OVERRIDE
{
109 virtual views::View
* CreateDefaultView(user::LoginStatus status
) OVERRIDE
{
113 virtual views::View
* CreateDetailedView(user::LoginStatus status
) OVERRIDE
{
117 virtual views::View
* CreateNotificationView(
118 user::LoginStatus status
) OVERRIDE
{
122 virtual void DestroyTrayView() OVERRIDE
{}
123 virtual void DestroyDefaultView() OVERRIDE
{}
124 virtual void DestroyDetailedView() OVERRIDE
{}
125 virtual void DestroyNotificationView() OVERRIDE
{}
126 virtual void UpdateAfterLoginStatusChange(
127 user::LoginStatus status
) OVERRIDE
{
133 typedef AshTestBase SystemTrayTest
;
135 TEST_F(SystemTrayTest
, SystemTrayDefaultView
) {
136 SystemTray
* tray
= GetSystemTray();
137 ASSERT_TRUE(tray
->GetWidget());
139 tray
->ShowDefaultView(BUBBLE_CREATE_NEW
);
141 // Ensure that closing the bubble destroys it.
142 ASSERT_TRUE(tray
->CloseSystemBubbleForTest());
143 RunAllPendingInMessageLoop();
144 ASSERT_FALSE(tray
->CloseSystemBubbleForTest());
147 TEST_F(SystemTrayTest
, SystemTrayTestItems
) {
148 SystemTray
* tray
= GetSystemTray();
149 ASSERT_TRUE(tray
->GetWidget());
151 TestItem
* test_item
= new TestItem
;
152 TestItem
* detailed_item
= new TestItem
;
153 tray
->AddTrayItem(test_item
);
154 tray
->AddTrayItem(detailed_item
);
156 // Ensure the tray views are created.
157 ASSERT_TRUE(test_item
->tray_view() != NULL
);
158 ASSERT_TRUE(detailed_item
->tray_view() != NULL
);
160 // Ensure a default views are created.
161 tray
->ShowDefaultView(BUBBLE_CREATE_NEW
);
162 ASSERT_TRUE(test_item
->default_view() != NULL
);
163 ASSERT_TRUE(detailed_item
->default_view() != NULL
);
165 // Show the detailed view, ensure it's created and the default view destroyed.
166 tray
->ShowDetailedView(detailed_item
, 0, false, BUBBLE_CREATE_NEW
);
167 RunAllPendingInMessageLoop();
168 ASSERT_TRUE(test_item
->default_view() == NULL
);
169 ASSERT_TRUE(detailed_item
->detailed_view() != NULL
);
171 // Show the default view, ensure it's created and the detailed view destroyed.
172 tray
->ShowDefaultView(BUBBLE_CREATE_NEW
);
173 RunAllPendingInMessageLoop();
174 ASSERT_TRUE(test_item
->default_view() != NULL
);
175 ASSERT_TRUE(detailed_item
->detailed_view() == NULL
);
178 TEST_F(SystemTrayTest
, SystemTrayNoViewItems
) {
179 SystemTray
* tray
= GetSystemTray();
180 ASSERT_TRUE(tray
->GetWidget());
182 // Verify that no crashes occur on items lacking some views.
183 TestNoViewItem
* no_view_item
= new TestNoViewItem
;
184 tray
->AddTrayItem(no_view_item
);
185 tray
->ShowDefaultView(BUBBLE_CREATE_NEW
);
186 tray
->ShowDetailedView(no_view_item
, 0, false, BUBBLE_USE_EXISTING
);
187 RunAllPendingInMessageLoop();
190 TEST_F(SystemTrayTest
, TrayWidgetAutoResizes
) {
191 SystemTray
* tray
= GetSystemTray();
192 ASSERT_TRUE(tray
->GetWidget());
194 // Add an initial tray item so that the tray gets laid out correctly.
195 TestItem
* initial_item
= new TestItem
;
196 tray
->AddTrayItem(initial_item
);
198 gfx::Size initial_size
= tray
->GetWidget()->GetWindowBoundsInScreen().size();
200 TestItem
* new_item
= new TestItem
;
201 tray
->AddTrayItem(new_item
);
203 gfx::Size new_size
= tray
->GetWidget()->GetWindowBoundsInScreen().size();
205 // Adding the new item should change the size of the tray.
206 EXPECT_NE(initial_size
.ToString(), new_size
.ToString());
208 // Hiding the tray view of the new item should also change the size of the
210 new_item
->tray_view()->SetVisible(false);
211 EXPECT_EQ(initial_size
.ToString(),
212 tray
->GetWidget()->GetWindowBoundsInScreen().size().ToString());
214 new_item
->tray_view()->SetVisible(true);
215 EXPECT_EQ(new_size
.ToString(),
216 tray
->GetWidget()->GetWindowBoundsInScreen().size().ToString());
219 TEST_F(SystemTrayTest
, SystemTrayNotifications
) {
220 SystemTray
* tray
= GetSystemTray();
221 ASSERT_TRUE(tray
->GetWidget());
223 TestItem
* test_item
= new TestItem
;
224 TestItem
* detailed_item
= new TestItem
;
225 tray
->AddTrayItem(test_item
);
226 tray
->AddTrayItem(detailed_item
);
228 // Ensure the tray views are created.
229 ASSERT_TRUE(test_item
->tray_view() != NULL
);
230 ASSERT_TRUE(detailed_item
->tray_view() != NULL
);
232 // Ensure a notification view is created.
233 tray
->ShowNotificationView(test_item
);
234 ASSERT_TRUE(test_item
->notification_view() != NULL
);
236 // Show the default view, notification view should remain.
237 tray
->ShowDefaultView(BUBBLE_CREATE_NEW
);
238 RunAllPendingInMessageLoop();
239 ASSERT_TRUE(test_item
->notification_view() != NULL
);
241 // Show the detailed view, ensure the notificaiton view remains.
242 tray
->ShowDetailedView(detailed_item
, 0, false, BUBBLE_CREATE_NEW
);
243 RunAllPendingInMessageLoop();
244 ASSERT_TRUE(detailed_item
->detailed_view() != NULL
);
245 ASSERT_TRUE(test_item
->notification_view() != NULL
);
247 // Hide the detailed view, ensure the notificaiton view still exists.
248 ASSERT_TRUE(tray
->CloseSystemBubbleForTest());
249 RunAllPendingInMessageLoop();
250 ASSERT_TRUE(detailed_item
->detailed_view() == NULL
);
251 ASSERT_TRUE(test_item
->notification_view() != NULL
);
254 TEST_F(SystemTrayTest
, BubbleCreationTypesTest
) {
255 SystemTray
* tray
= GetSystemTray();
256 ASSERT_TRUE(tray
->GetWidget());
258 TestItem
* test_item
= new TestItem
;
259 tray
->AddTrayItem(test_item
);
261 // Ensure the tray views are created.
262 ASSERT_TRUE(test_item
->tray_view() != NULL
);
264 // Show the default view, ensure the notification view is destroyed.
265 tray
->ShowDefaultView(BUBBLE_CREATE_NEW
);
266 RunAllPendingInMessageLoop();
268 views::Widget
* widget
= test_item
->default_view()->GetWidget();
269 gfx::Rect bubble_bounds
= widget
->GetWindowBoundsInScreen();
271 tray
->ShowDetailedView(test_item
, 0, true, BUBBLE_USE_EXISTING
);
272 RunAllPendingInMessageLoop();
274 EXPECT_FALSE(test_item
->default_view());
276 EXPECT_EQ(bubble_bounds
.ToString(), test_item
->detailed_view()->GetWidget()->
277 GetWindowBoundsInScreen().ToString());
278 EXPECT_EQ(widget
, test_item
->detailed_view()->GetWidget());
280 tray
->ShowDefaultView(BUBBLE_USE_EXISTING
);
281 RunAllPendingInMessageLoop();
283 EXPECT_EQ(bubble_bounds
.ToString(), test_item
->default_view()->GetWidget()->
284 GetWindowBoundsInScreen().ToString());
285 EXPECT_EQ(widget
, test_item
->default_view()->GetWidget());
288 // Tests that the tray is laid out properly in the widget to make sure that the
289 // tray extends to the correct edge of the screen.
290 TEST_F(SystemTrayTest
, TrayBoundsInWidget
) {
291 internal::StatusAreaWidget
* widget
=
292 Shell::GetPrimaryRootWindowController()->shelf()->status_area_widget();
293 SystemTray
* tray
= widget
->system_tray();
295 // Test in bottom alignment. Bottom and right edges of the view should be
296 // aligned with the widget.
297 widget
->SetShelfAlignment(SHELF_ALIGNMENT_BOTTOM
);
298 gfx::Rect window_bounds
= widget
->GetWindowBoundsInScreen();
299 gfx::Rect tray_bounds
= tray
->GetBoundsInScreen();
300 EXPECT_EQ(window_bounds
.bottom(), tray_bounds
.bottom());
301 EXPECT_EQ(window_bounds
.right(), tray_bounds
.right());
303 // Test in the top alignment. Top and right edges should match.
304 widget
->SetShelfAlignment(SHELF_ALIGNMENT_TOP
);
305 window_bounds
= widget
->GetWindowBoundsInScreen();
306 tray_bounds
= tray
->GetBoundsInScreen();
307 EXPECT_EQ(window_bounds
.y(), tray_bounds
.y());
308 EXPECT_EQ(window_bounds
.right(), tray_bounds
.right());
310 // Test in the left alignment. Left and bottom edges should match.
311 widget
->SetShelfAlignment(SHELF_ALIGNMENT_LEFT
);
312 window_bounds
= widget
->GetWindowBoundsInScreen();
313 tray_bounds
= tray
->GetBoundsInScreen();
314 EXPECT_EQ(window_bounds
.bottom(), tray_bounds
.bottom());
315 EXPECT_EQ(window_bounds
.x(), tray_bounds
.x());
317 // Test in the right alignment. Right and bottom edges should match.
318 widget
->SetShelfAlignment(SHELF_ALIGNMENT_LEFT
);
319 window_bounds
= widget
->GetWindowBoundsInScreen();
320 tray_bounds
= tray
->GetBoundsInScreen();
321 EXPECT_EQ(window_bounds
.bottom(), tray_bounds
.bottom());
322 EXPECT_EQ(window_bounds
.right(), tray_bounds
.right());