Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ui / message_center / views / message_popup_collection_unittest.cc
blob3c67e3cc0a1dad02b7f18faffb5f79a0dbd83832
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"
7 #include <list>
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/events/event_utils.h"
16 #include "ui/gfx/display.h"
17 #include "ui/gfx/geometry/rect.h"
18 #include "ui/message_center/fake_message_center.h"
19 #include "ui/message_center/views/desktop_popup_alignment_delegate.h"
20 #include "ui/message_center/views/toast_contents_view.h"
21 #include "ui/views/test/views_test_base.h"
22 #include "ui/views/widget/widget.h"
23 #include "ui/views/widget/widget_delegate.h"
25 namespace message_center {
26 namespace test {
28 class MessagePopupCollectionTest : public views::ViewsTestBase {
29 public:
30 void SetUp() override {
31 views::ViewsTestBase::SetUp();
32 MessageCenter::Initialize();
33 MessageCenter::Get()->DisableTimersForTest();
34 alignment_delegate_.reset(new DesktopPopupAlignmentDelegate);
35 collection_.reset(new MessagePopupCollection(
36 GetContext(), MessageCenter::Get(), NULL, alignment_delegate_.get()));
37 // This size fits test machines resolution and also can keep a few toasts
38 // w/o ill effects of hitting the screen overflow. This allows us to assume
39 // and verify normal layout of the toast stack.
40 SetDisplayInfo(gfx::Rect(0, 0, 600, 390), // taskbar at the bottom.
41 gfx::Rect(0, 0, 600, 400));
42 id_ = 0;
43 PrepareForWait();
46 void TearDown() override {
47 collection_.reset();
48 MessageCenter::Shutdown();
49 views::ViewsTestBase::TearDown();
52 protected:
53 MessagePopupCollection* collection() { return collection_.get(); }
55 size_t GetToastCounts() {
56 return collection_->toasts_.size();
59 bool MouseInCollection() {
60 return collection_->latest_toast_entered_ != NULL;
63 bool IsToastShown(const std::string& id) {
64 views::Widget* widget = collection_->GetWidgetForTest(id);
65 return widget && widget->IsVisible();
68 views::Widget* GetWidget(const std::string& id) {
69 return collection_->GetWidgetForTest(id);
72 void SetDisplayInfo(const gfx::Rect& work_area,
73 const gfx::Rect& display_bounds) {
74 gfx::Display dummy_display;
75 dummy_display.set_bounds(display_bounds);
76 dummy_display.set_work_area(work_area);
77 alignment_delegate_->RecomputeAlignment(dummy_display);
78 PrepareForWait();
81 gfx::Rect GetWorkArea() {
82 return alignment_delegate_->work_area_;
85 ToastContentsView* GetToast(const std::string& id) {
86 for (MessagePopupCollection::Toasts::iterator iter =
87 collection_->toasts_.begin();
88 iter != collection_->toasts_.end(); ++iter) {
89 if ((*iter)->id() == id)
90 return *iter;
92 return NULL;
95 std::string AddNotification() {
96 std::string id = base::IntToString(id_++);
97 scoped_ptr<Notification> notification(new Notification(
98 NOTIFICATION_TYPE_BASE_FORMAT, id, base::UTF8ToUTF16("test title"),
99 base::UTF8ToUTF16("test message"), gfx::Image(),
100 base::string16() /* display_source */, GURL(), NotifierId(),
101 message_center::RichNotificationData(), NULL /* delegate */));
102 MessageCenter::Get()->AddNotification(notification.Pass());
103 return id;
106 void PrepareForWait() { collection_->CreateRunLoopForTest(); }
108 // Assumes there is non-zero pending work.
109 void WaitForTransitionsDone() {
110 collection_->WaitForTest();
111 collection_->CreateRunLoopForTest();
114 void CloseAllToasts() {
115 // Assumes there is at least one toast to close.
116 EXPECT_TRUE(GetToastCounts() > 0);
117 MessageCenter::Get()->RemoveAllNotifications(false);
120 gfx::Rect GetToastRectAt(size_t index) {
121 return collection_->GetToastRectAt(index);
124 private:
125 scoped_ptr<MessagePopupCollection> collection_;
126 scoped_ptr<DesktopPopupAlignmentDelegate> alignment_delegate_;
127 int id_;
130 TEST_F(MessagePopupCollectionTest, DismissOnClick) {
132 std::string id1 = AddNotification();
133 std::string id2 = AddNotification();
134 WaitForTransitionsDone();
136 EXPECT_EQ(2u, GetToastCounts());
137 EXPECT_TRUE(IsToastShown(id1));
138 EXPECT_TRUE(IsToastShown(id2));
140 MessageCenter::Get()->ClickOnNotification(id2);
141 WaitForTransitionsDone();
143 EXPECT_EQ(1u, GetToastCounts());
144 EXPECT_TRUE(IsToastShown(id1));
145 EXPECT_FALSE(IsToastShown(id2));
147 MessageCenter::Get()->ClickOnNotificationButton(id1, 0);
148 WaitForTransitionsDone();
149 EXPECT_EQ(0u, GetToastCounts());
150 EXPECT_FALSE(IsToastShown(id1));
151 EXPECT_FALSE(IsToastShown(id2));
154 TEST_F(MessagePopupCollectionTest, ShutdownDuringShowing) {
155 std::string id1 = AddNotification();
156 std::string id2 = AddNotification();
157 WaitForTransitionsDone();
158 EXPECT_EQ(2u, GetToastCounts());
159 EXPECT_TRUE(IsToastShown(id1));
160 EXPECT_TRUE(IsToastShown(id2));
162 // Finish without cleanup of notifications, which may cause use-after-free.
163 // See crbug.com/236448
164 GetWidget(id1)->CloseNow();
165 collection()->OnMouseExited(GetToast(id2));
167 GetWidget(id2)->CloseNow();
170 TEST_F(MessagePopupCollectionTest, DefaultPositioning) {
171 std::string id0 = AddNotification();
172 std::string id1 = AddNotification();
173 std::string id2 = AddNotification();
174 std::string id3 = AddNotification();
175 WaitForTransitionsDone();
177 gfx::Rect r0 = GetToastRectAt(0);
178 gfx::Rect r1 = GetToastRectAt(1);
179 gfx::Rect r2 = GetToastRectAt(2);
180 gfx::Rect r3 = GetToastRectAt(3);
182 // 3 toasts are shown, equal size, vertical stack.
183 EXPECT_TRUE(IsToastShown(id0));
184 EXPECT_TRUE(IsToastShown(id1));
185 EXPECT_TRUE(IsToastShown(id2));
187 EXPECT_EQ(r0.width(), r1.width());
188 EXPECT_EQ(r1.width(), r2.width());
190 EXPECT_EQ(r0.height(), r1.height());
191 EXPECT_EQ(r1.height(), r2.height());
193 EXPECT_GT(r0.y(), r1.y());
194 EXPECT_GT(r1.y(), r2.y());
196 EXPECT_EQ(r0.x(), r1.x());
197 EXPECT_EQ(r1.x(), r2.x());
199 // The 4th toast is not shown yet.
200 EXPECT_FALSE(IsToastShown(id3));
201 EXPECT_EQ(0, r3.width());
202 EXPECT_EQ(0, r3.height());
204 CloseAllToasts();
205 EXPECT_EQ(0u, GetToastCounts());
206 WaitForTransitionsDone();
209 TEST_F(MessagePopupCollectionTest, DefaultPositioningWithRightTaskbar) {
210 // If taskbar is on the right we show the toasts bottom to top as usual.
212 // Simulate a taskbar at the right.
213 SetDisplayInfo(gfx::Rect(0, 0, 590, 400), // Work-area.
214 gfx::Rect(0, 0, 600, 400)); // Display-bounds.
215 std::string id0 = AddNotification();
216 std::string id1 = AddNotification();
217 WaitForTransitionsDone();
219 gfx::Rect r0 = GetToastRectAt(0);
220 gfx::Rect r1 = GetToastRectAt(1);
222 // 2 toasts are shown, equal size, vertical stack.
223 EXPECT_TRUE(IsToastShown(id0));
224 EXPECT_TRUE(IsToastShown(id1));
226 EXPECT_EQ(r0.width(), r1.width());
227 EXPECT_EQ(r0.height(), r1.height());
228 EXPECT_GT(r0.y(), r1.y());
229 EXPECT_EQ(r0.x(), r1.x());
231 CloseAllToasts();
232 EXPECT_EQ(0u, GetToastCounts());
234 // Restore simulated taskbar position to bottom.
235 SetDisplayInfo(gfx::Rect(0, 0, 600, 390), // Work-area.
236 gfx::Rect(0, 0, 600, 400)); // Display-bounds.
238 WaitForTransitionsDone();
241 TEST_F(MessagePopupCollectionTest, TopDownPositioningWithTopTaskbar) {
242 // Simulate a taskbar at the top.
243 SetDisplayInfo(gfx::Rect(0, 10, 600, 390), // Work-area.
244 gfx::Rect(0, 0, 600, 400)); // Display-bounds.
245 std::string id0 = AddNotification();
246 std::string id1 = AddNotification();
247 WaitForTransitionsDone();
249 gfx::Rect r0 = GetToastRectAt(0);
250 gfx::Rect r1 = GetToastRectAt(1);
252 // 2 toasts are shown, equal size, vertical stack.
253 EXPECT_TRUE(IsToastShown(id0));
254 EXPECT_TRUE(IsToastShown(id1));
256 EXPECT_EQ(r0.width(), r1.width());
257 EXPECT_EQ(r0.height(), r1.height());
258 EXPECT_LT(r0.y(), r1.y());
259 EXPECT_EQ(r0.x(), r1.x());
261 CloseAllToasts();
262 EXPECT_EQ(0u, GetToastCounts());
263 WaitForTransitionsDone();
265 // Restore simulated taskbar position to bottom.
266 SetDisplayInfo(gfx::Rect(0, 0, 600, 390), // Work-area.
267 gfx::Rect(0, 0, 600, 400)); // Display-bounds.
270 TEST_F(MessagePopupCollectionTest, TopDownPositioningWithLeftAndTopTaskbar) {
271 // If there "seems" to be a taskbar on left and top (like in Unity), it is
272 // assumed that the actual taskbar is the top one.
274 // Simulate a taskbar at the top and left.
275 SetDisplayInfo(gfx::Rect(10, 10, 590, 390), // Work-area.
276 gfx::Rect(0, 0, 600, 400)); // Display-bounds.
277 std::string id0 = AddNotification();
278 std::string id1 = AddNotification();
279 WaitForTransitionsDone();
281 gfx::Rect r0 = GetToastRectAt(0);
282 gfx::Rect r1 = GetToastRectAt(1);
284 // 2 toasts are shown, equal size, vertical stack.
285 EXPECT_TRUE(IsToastShown(id0));
286 EXPECT_TRUE(IsToastShown(id1));
288 EXPECT_EQ(r0.width(), r1.width());
289 EXPECT_EQ(r0.height(), r1.height());
290 EXPECT_LT(r0.y(), r1.y());
291 EXPECT_EQ(r0.x(), r1.x());
293 CloseAllToasts();
294 EXPECT_EQ(0u, GetToastCounts());
295 WaitForTransitionsDone();
297 // Restore simulated taskbar position to bottom.
298 SetDisplayInfo(gfx::Rect(0, 0, 600, 390), // Work-area.
299 gfx::Rect(0, 0, 600, 400)); // Display-bounds.
302 TEST_F(MessagePopupCollectionTest, TopDownPositioningWithBottomAndTopTaskbar) {
303 // If there "seems" to be a taskbar on bottom and top (like in Gnome), it is
304 // assumed that the actual taskbar is the top one.
306 // Simulate a taskbar at the top and bottom.
307 SetDisplayInfo(gfx::Rect(0, 10, 580, 400), // Work-area.
308 gfx::Rect(0, 0, 600, 400)); // Display-bounds.
309 std::string id0 = AddNotification();
310 std::string id1 = AddNotification();
311 WaitForTransitionsDone();
313 gfx::Rect r0 = GetToastRectAt(0);
314 gfx::Rect r1 = GetToastRectAt(1);
316 // 2 toasts are shown, equal size, vertical stack.
317 EXPECT_TRUE(IsToastShown(id0));
318 EXPECT_TRUE(IsToastShown(id1));
320 EXPECT_EQ(r0.width(), r1.width());
321 EXPECT_EQ(r0.height(), r1.height());
322 EXPECT_LT(r0.y(), r1.y());
323 EXPECT_EQ(r0.x(), r1.x());
325 CloseAllToasts();
326 EXPECT_EQ(0u, GetToastCounts());
327 WaitForTransitionsDone();
329 // Restore simulated taskbar position to bottom.
330 SetDisplayInfo(gfx::Rect(0, 0, 600, 390), // Work-area.
331 gfx::Rect(0, 0, 600, 400)); // Display-bounds.
334 TEST_F(MessagePopupCollectionTest, LeftPositioningWithLeftTaskbar) {
335 // Simulate a taskbar at the left.
336 SetDisplayInfo(gfx::Rect(10, 0, 590, 400), // Work-area.
337 gfx::Rect(0, 0, 600, 400)); // Display-bounds.
338 std::string id0 = AddNotification();
339 std::string id1 = AddNotification();
340 WaitForTransitionsDone();
342 gfx::Rect r0 = GetToastRectAt(0);
343 gfx::Rect r1 = GetToastRectAt(1);
345 // 2 toasts are shown, equal size, vertical stack.
346 EXPECT_TRUE(IsToastShown(id0));
347 EXPECT_TRUE(IsToastShown(id1));
349 EXPECT_EQ(r0.width(), r1.width());
350 EXPECT_EQ(r0.height(), r1.height());
351 EXPECT_GT(r0.y(), r1.y());
352 EXPECT_EQ(r0.x(), r1.x());
354 // Ensure that toasts are on the left.
355 EXPECT_LT(r1.x(), GetWorkArea().CenterPoint().x());
357 CloseAllToasts();
358 EXPECT_EQ(0u, GetToastCounts());
359 WaitForTransitionsDone();
361 // Restore simulated taskbar position to bottom.
362 SetDisplayInfo(gfx::Rect(0, 0, 600, 390), // Work-area.
363 gfx::Rect(0, 0, 600, 400)); // Display-bounds.
366 TEST_F(MessagePopupCollectionTest, DetectMouseHover) {
367 std::string id0 = AddNotification();
368 std::string id1 = AddNotification();
369 WaitForTransitionsDone();
371 views::WidgetDelegateView* toast0 = GetToast(id0);
372 EXPECT_TRUE(toast0 != NULL);
373 views::WidgetDelegateView* toast1 = GetToast(id1);
374 EXPECT_TRUE(toast1 != NULL);
376 ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(),
377 ui::EventTimeForNow(), 0, 0);
379 // Test that mouse detection logic works in presence of out-of-order events.
380 toast0->OnMouseEntered(event);
381 EXPECT_TRUE(MouseInCollection());
382 toast1->OnMouseEntered(event);
383 EXPECT_TRUE(MouseInCollection());
384 toast0->OnMouseExited(event);
385 EXPECT_TRUE(MouseInCollection());
386 toast1->OnMouseExited(event);
387 EXPECT_FALSE(MouseInCollection());
389 // Test that mouse detection logic works in presence of WindowClosing events.
390 toast0->OnMouseEntered(event);
391 EXPECT_TRUE(MouseInCollection());
392 toast1->OnMouseEntered(event);
393 EXPECT_TRUE(MouseInCollection());
394 toast0->GetWidget()->CloseNow();
395 EXPECT_TRUE(MouseInCollection());
396 toast1->GetWidget()->CloseNow();
397 EXPECT_FALSE(MouseInCollection());
400 // TODO(dimich): Test repositioning - both normal one and when user is closing
401 // the toasts.
402 TEST_F(MessagePopupCollectionTest, DetectMouseHoverWithUserClose) {
403 std::string id0 = AddNotification();
404 std::string id1 = AddNotification();
405 WaitForTransitionsDone();
407 views::WidgetDelegateView* toast0 = GetToast(id0);
408 EXPECT_TRUE(toast0 != NULL);
409 views::WidgetDelegateView* toast1 = GetToast(id1);
410 ASSERT_TRUE(toast1 != NULL);
412 ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(),
413 ui::EventTimeForNow(), 0, 0);
414 toast1->OnMouseEntered(event);
415 static_cast<MessageCenterObserver*>(collection())->OnNotificationRemoved(
416 id1, true);
418 EXPECT_FALSE(MouseInCollection());
419 std::string id2 = AddNotification();
421 WaitForTransitionsDone();
422 views::WidgetDelegateView* toast2 = GetToast(id2);
423 EXPECT_TRUE(toast2 != NULL);
425 CloseAllToasts();
426 WaitForTransitionsDone();
430 } // namespace test
431 } // namespace message_center