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.
5 #include "ui/message_center/views/message_popup_collection.h"
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/events/event.h"
14 #include "ui/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"
23 namespace message_center {
26 class MessagePopupCollectionTest : public views::ViewsTestBase {
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
43 virtual void TearDown() OVERRIDE {
44 collection_->CloseAllWidgets();
46 MessageCenter::Shutdown();
47 views::ViewsTestBase::TearDown();
51 MessagePopupCollection* collection() { return collection_.get(); }
53 size_t GetToastCounts() {
54 return collection_->toasts_.size();
57 bool MouseInCollection() {
58 return collection_->latest_toast_entered_ != NULL;
61 bool IsToastShown(const std::string& id) {
62 views::Widget* widget = collection_->GetWidgetForTest(id);
63 return widget && widget->IsVisible();
66 views::Widget* GetWidget(const std::string& id) {
67 return collection_->GetWidgetForTest(id);
70 gfx::Rect GetWorkArea() {
71 return collection_->work_area_;
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)
84 std::string AddNotification() {
85 std::string id = base::IntToString(id_++);
86 scoped_ptr<Notification> notification(
87 new Notification(NOTIFICATION_TYPE_BASE_FORMAT,
89 UTF8ToUTF16("test title"),
90 UTF8ToUTF16("test message"),
92 string16() /* display_source */,
94 message_center::RichNotificationData(),
95 NULL /* delegate */));
96 MessageCenter::Get()->AddNotification(notification.Pass());
100 // Assumes there is non-zero pending work.
101 void WaitForTransitionsDone() {
102 collection_->RunLoopForTest();
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();
112 gfx::Rect GetToastRectAt(size_t index) {
113 return collection_->GetToastRectAt(index);
117 scoped_ptr<MessagePopupCollection> collection_;
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));
129 MessageCenter::Get()->ClickOnNotification(id2);
130 WaitForTransitionsDone();
131 EXPECT_EQ(1u, GetToastCounts());
132 EXPECT_TRUE(IsToastShown(id1));
133 EXPECT_FALSE(IsToastShown(id2));
135 MessageCenter::Get()->ClickOnNotificationButton(id1, 0);
136 WaitForTransitionsDone();
137 EXPECT_EQ(0u, GetToastCounts());
138 EXPECT_FALSE(IsToastShown(id1));
139 EXPECT_FALSE(IsToastShown(id2));
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));
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));
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();
163 gfx::Rect r0 = GetToastRectAt(0);
164 gfx::Rect r1 = GetToastRectAt(1);
165 gfx::Rect r2 = GetToastRectAt(2);
166 gfx::Rect r3 = GetToastRectAt(3);
168 // 3 toasts are shown, equal size, vertical stack.
169 EXPECT_TRUE(IsToastShown(id0));
170 EXPECT_TRUE(IsToastShown(id1));
171 EXPECT_TRUE(IsToastShown(id2));
173 EXPECT_EQ(r0.width(), r1.width());
174 EXPECT_EQ(r1.width(), r2.width());
176 EXPECT_EQ(r0.height(), r1.height());
177 EXPECT_EQ(r1.height(), r2.height());
179 EXPECT_GT(r0.y(), r1.y());
180 EXPECT_GT(r1.y(), r2.y());
182 EXPECT_EQ(r0.x(), r1.x());
183 EXPECT_EQ(r1.x(), r2.x());
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());
190 CloseAllToastsAndWait();
191 EXPECT_EQ(0u, GetToastCounts());
194 TEST_F(MessagePopupCollectionTest, DefaultPositioningWithRightTaskbar) {
195 // If taskbar is on the right we show the toasts bottom to top as usual.
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();
204 gfx::Rect r0 = GetToastRectAt(0);
205 gfx::Rect r1 = GetToastRectAt(1);
207 // 2 toasts are shown, equal size, vertical stack.
208 EXPECT_TRUE(IsToastShown(id0));
209 EXPECT_TRUE(IsToastShown(id1));
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());
216 CloseAllToastsAndWait();
217 EXPECT_EQ(0u, GetToastCounts());
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.
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();
232 gfx::Rect r0 = GetToastRectAt(0);
233 gfx::Rect r1 = GetToastRectAt(1);
235 // 2 toasts are shown, equal size, vertical stack.
236 EXPECT_TRUE(IsToastShown(id0));
237 EXPECT_TRUE(IsToastShown(id1));
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());
244 CloseAllToastsAndWait();
245 EXPECT_EQ(0u, GetToastCounts());
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.
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.
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();
263 gfx::Rect r0 = GetToastRectAt(0);
264 gfx::Rect r1 = GetToastRectAt(1);
266 // 2 toasts are shown, equal size, vertical stack.
267 EXPECT_TRUE(IsToastShown(id0));
268 EXPECT_TRUE(IsToastShown(id1));
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());
275 CloseAllToastsAndWait();
276 EXPECT_EQ(0u, GetToastCounts());
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.
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.
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();
294 gfx::Rect r0 = GetToastRectAt(0);
295 gfx::Rect r1 = GetToastRectAt(1);
297 // 2 toasts are shown, equal size, vertical stack.
298 EXPECT_TRUE(IsToastShown(id0));
299 EXPECT_TRUE(IsToastShown(id1));
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());
306 CloseAllToastsAndWait();
307 EXPECT_EQ(0u, GetToastCounts());
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.
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();
322 gfx::Rect r0 = GetToastRectAt(0);
323 gfx::Rect r1 = GetToastRectAt(1);
325 // 2 toasts are shown, equal size, vertical stack.
326 EXPECT_TRUE(IsToastShown(id0));
327 EXPECT_TRUE(IsToastShown(id1));
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());
334 // Ensure that toasts are on the left.
335 EXPECT_LT(r1.x(), GetWorkArea().CenterPoint().x());
337 CloseAllToastsAndWait();
338 EXPECT_EQ(0u, GetToastCounts());
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.
345 TEST_F(MessagePopupCollectionTest, DetectMouseHover) {
346 std::string id0 = AddNotification();
347 std::string id1 = AddNotification();
348 WaitForTransitionsDone();
350 views::WidgetDelegateView* toast0 = GetToast(id0);
351 EXPECT_TRUE(toast0 != NULL);
352 views::WidgetDelegateView* toast1 = GetToast(id1);
353 EXPECT_TRUE(toast1 != NULL);
355 ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), 0);
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());
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());
378 // TODO(dimich): Test repositioning - both normal one and when user is closing
380 TEST_F(MessagePopupCollectionTest, DetectMouseHoverWithUserClose) {
381 std::string id0 = AddNotification();
382 std::string id1 = AddNotification();
383 WaitForTransitionsDone();
385 views::WidgetDelegateView* toast0 = GetToast(id0);
386 EXPECT_TRUE(toast0 != NULL);
387 views::WidgetDelegateView* toast1 = GetToast(id1);
388 ASSERT_TRUE(toast1 != NULL);
390 ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), 0);
391 toast1->OnMouseEntered(event);
392 static_cast<MessageCenterObserver*>(collection())->OnNotificationRemoved(
395 EXPECT_FALSE(MouseInCollection());
396 std::string id2 = AddNotification();
398 WaitForTransitionsDone();
399 views::WidgetDelegateView* toast2 = GetToast(id2);
400 EXPECT_TRUE(toast2 != NULL);
405 } // namespace message_center