base: Change DCHECK_IS_ON to a macro DCHECK_IS_ON().
[chromium-blink-merge.git] / ui / message_center / cocoa / popup_collection_unittest.mm
blobd2990bc9228780250668941b33dab076239191a3
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 #import "ui/message_center/cocoa/popup_collection.h"
7 #include "base/mac/scoped_nsobject.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/run_loop.h"
11 #include "base/strings/sys_string_conversions.h"
12 #include "base/strings/utf_string_conversions.h"
13 #import "ui/gfx/test/ui_cocoa_test_helper.h"
14 #import "ui/message_center/cocoa/notification_controller.h"
15 #import "ui/message_center/cocoa/popup_controller.h"
16 #include "ui/message_center/message_center.h"
17 #include "ui/message_center/message_center_style.h"
18 #include "ui/message_center/notification.h"
20 using base::ASCIIToUTF16;
22 namespace message_center {
24 class PopupCollectionTest : public ui::CocoaTest {
25  public:
26   PopupCollectionTest() {
27     message_center::MessageCenter::Initialize();
28     center_ = message_center::MessageCenter::Get();
29     collection_.reset(
30         [[MCPopupCollection alloc] initWithMessageCenter:center_]);
31     [collection_ setAnimationDuration:0.001];
32     [collection_ setAnimationEndedCallback:^{
33         if (nested_run_loop_.get())
34           nested_run_loop_->Quit();
35     }];
36   }
38   virtual void TearDown() override {
39     collection_.reset();  // Close all popups.
40     ui::CocoaTest::TearDown();
41   }
43   virtual ~PopupCollectionTest() {
44     message_center::MessageCenter::Shutdown();
45   }
47   message_center::NotifierId DummyNotifierId() {
48     return message_center::NotifierId();
49   }
51   void AddThreeNotifications() {
52     scoped_ptr<message_center::Notification> notification;
53     notification.reset(new message_center::Notification(
54         message_center::NOTIFICATION_TYPE_SIMPLE,
55         "1",
56         ASCIIToUTF16("One"),
57         ASCIIToUTF16("This is the first notification to"
58                      " be displayed"),
59         gfx::Image(),
60         base::string16(),
61         DummyNotifierId(),
62         message_center::RichNotificationData(),
63         NULL));
64     center_->AddNotification(notification.Pass());
66     notification.reset(new message_center::Notification(
67         message_center::NOTIFICATION_TYPE_SIMPLE,
68         "2",
69         ASCIIToUTF16("Two"),
70         ASCIIToUTF16("This is the second notification."),
71         gfx::Image(),
72         base::string16(),
73         DummyNotifierId(),
74         message_center::RichNotificationData(),
75         NULL));
76     center_->AddNotification(notification.Pass());
78     notification.reset(new message_center::Notification(
79         message_center::NOTIFICATION_TYPE_SIMPLE,
80         "3",
81         ASCIIToUTF16("Three"),
82         ASCIIToUTF16("This is the third notification "
83                      "that has a much longer body "
84                      "than the other notifications. It "
85                      "may not fit on the screen if we "
86                      "set the screen size too small or "
87                      "if the notification is way too big"),
88         gfx::Image(),
89         base::string16(),
90         DummyNotifierId(),
91         message_center::RichNotificationData(),
92         NULL));
93     center_->AddNotification(notification.Pass());
94     WaitForAnimationEnded();
95   }
97   bool CheckSpacingBetween(MCPopupController* upper, MCPopupController* lower) {
98     CGFloat minY = NSMinY([[upper window] frame]);
99     CGFloat maxY = NSMaxY([[lower window] frame]);
100     CGFloat delta = minY - maxY;
101     EXPECT_EQ(message_center::kMarginBetweenItems, delta);
102     return delta == message_center::kMarginBetweenItems;
103   }
105   void WaitForAnimationEnded() {
106     if (![collection_ isAnimating])
107       return;
108     nested_run_loop_.reset(new base::RunLoop());
109     nested_run_loop_->Run();
110     nested_run_loop_.reset();
111   }
113   base::MessageLoopForUI message_loop_;
114   scoped_ptr<base::RunLoop> nested_run_loop_;
115   message_center::MessageCenter* center_;
116   base::scoped_nsobject<MCPopupCollection> collection_;
119 TEST_F(PopupCollectionTest, AddThreeCloseOne) {
120   EXPECT_EQ(0u, [[collection_ popups] count]);
121   AddThreeNotifications();
122   EXPECT_EQ(3u, [[collection_ popups] count]);
124   center_->RemoveNotification("2", true);
125   WaitForAnimationEnded();
126   EXPECT_EQ(2u, [[collection_ popups] count]);
129 TEST_F(PopupCollectionTest, AttemptFourOneOffscreen) {
130   [collection_ setScreenFrame:NSMakeRect(0, 0, 800, 300)];
132   EXPECT_EQ(0u, [[collection_ popups] count]);
133   AddThreeNotifications();
134   EXPECT_EQ(2u, [[collection_ popups] count]);  // "3" does not fit on screen.
136   scoped_ptr<message_center::Notification> notification;
138   notification.reset(new message_center::Notification(
139       message_center::NOTIFICATION_TYPE_SIMPLE,
140       "4",
141       ASCIIToUTF16("Four"),
142       ASCIIToUTF16("This is the fourth notification."),
143       gfx::Image(),
144       base::string16(),
145       DummyNotifierId(),
146       message_center::RichNotificationData(),
147       NULL));
148   center_->AddNotification(notification.Pass());
149   WaitForAnimationEnded();
151   // Remove "1" and "3" should fit on screen.
152   center_->RemoveNotification("1", true);
153   WaitForAnimationEnded();
154   ASSERT_EQ(2u, [[collection_ popups] count]);
156   EXPECT_EQ("2", [[[collection_ popups] objectAtIndex:0] notificationID]);
157   EXPECT_EQ("3", [[[collection_ popups] objectAtIndex:1] notificationID]);
159   // Remove "2" and "4" should fit on screen.
160   center_->RemoveNotification("2", true);
161   WaitForAnimationEnded();
162   ASSERT_EQ(2u, [[collection_ popups] count]);
164   EXPECT_EQ("3", [[[collection_ popups] objectAtIndex:0] notificationID]);
165   EXPECT_EQ("4", [[[collection_ popups] objectAtIndex:1] notificationID]);
168 TEST_F(PopupCollectionTest, LayoutSpacing) {
169   const CGFloat kScreenSize = 500;
170   [collection_ setScreenFrame:NSMakeRect(0, 0, kScreenSize, kScreenSize)];
172   AddThreeNotifications();
173   NSArray* popups = [collection_ popups];
175   EXPECT_EQ(message_center::kMarginBetweenItems,
176             kScreenSize - NSMaxY([[[popups objectAtIndex:0] window] frame]));
178   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:0],
179                                   [popups objectAtIndex:1]));
180   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:1],
181                                   [popups objectAtIndex:2]));
183   // Set priority so that kMaxVisiblePopupNotifications does not hide it.
184   message_center::RichNotificationData optional;
185   optional.priority = message_center::HIGH_PRIORITY;
186   scoped_ptr<message_center::Notification> notification;
187   notification.reset(new message_center::Notification(
188       message_center::NOTIFICATION_TYPE_SIMPLE,
189       "4",
190       ASCIIToUTF16("Four"),
191       ASCIIToUTF16("This is the fourth notification."),
192       gfx::Image(),
193       base::string16(),
194       DummyNotifierId(),
195       optional,
196       NULL));
197   center_->AddNotification(notification.Pass());
198   WaitForAnimationEnded();
199   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:2],
200                                   [popups objectAtIndex:3]));
202   // Remove "2".
203   center_->RemoveNotification("2", true);
204   WaitForAnimationEnded();
205   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:0],
206                                   [popups objectAtIndex:1]));
207   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:1],
208                                   [popups objectAtIndex:2]));
210   // Remove "1".
211   center_->RemoveNotification("2", true);
212   WaitForAnimationEnded();
213   EXPECT_EQ(message_center::kMarginBetweenItems,
214             kScreenSize - NSMaxY([[[popups objectAtIndex:0] window] frame]));
215   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:0],
216                                   [popups objectAtIndex:1]));
219 TEST_F(PopupCollectionTest, TinyScreen) {
220   [collection_ setScreenFrame:NSMakeRect(0, 0, 800, 100)];
222   EXPECT_EQ(0u, [[collection_ popups] count]);
223   scoped_ptr<message_center::Notification> notification;
224   notification.reset(new message_center::Notification(
225       message_center::NOTIFICATION_TYPE_SIMPLE,
226       "1",
227       ASCIIToUTF16("One"),
228       ASCIIToUTF16("This is the first notification to"
229               " be displayed"),
230       gfx::Image(),
231       base::string16(),
232       DummyNotifierId(),
233       message_center::RichNotificationData(),
234       NULL));
235   center_->AddNotification(notification.Pass());
236   WaitForAnimationEnded();
237   EXPECT_EQ(1u, [[collection_ popups] count]);
239   // Now give the notification a longer message so that it no longer fits.
240   notification.reset(new message_center::Notification(
241       message_center::NOTIFICATION_TYPE_SIMPLE,
242       "1",
243       ASCIIToUTF16("One"),
244       ASCIIToUTF16("This is now a very very very very "
245               "very very very very very very very "
246               "very very very very very very very "
247               "very very very very very very very "
248               "very very very very very very very "
249               "very very very very very very very "
250               "very very very very very very very "
251               "long notification."),
252       gfx::Image(),
253       base::string16(),
254       DummyNotifierId(),
255       message_center::RichNotificationData(),
256       NULL));
257   center_->UpdateNotification("1", notification.Pass());
258   WaitForAnimationEnded();
259   EXPECT_EQ(0u, [[collection_ popups] count]);
262 TEST_F(PopupCollectionTest, UpdateIconAndBody) {
263   AddThreeNotifications();
264   NSArray* popups = [collection_ popups];
266   EXPECT_EQ(3u, [popups count]);
268   // Update "2" icon.
269   MCNotificationController* controller =
270       [[popups objectAtIndex:1] notificationController];
271   EXPECT_FALSE([[controller iconView] image]);
272   center_->SetNotificationIcon("2",
273       gfx::Image([[NSImage imageNamed:NSImageNameUser] retain]));
274   WaitForAnimationEnded();
275   EXPECT_TRUE([[controller iconView] image]);
277   EXPECT_EQ(3u, [popups count]);
278   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:0],
279                                   [popups objectAtIndex:1]));
280   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:1],
281                                   [popups objectAtIndex:2]));
283   // Replace "1".
284   controller = [[popups objectAtIndex:0] notificationController];
285   NSRect old_frame = [[controller view] frame];
286   scoped_ptr<message_center::Notification> notification;
287   notification.reset(new message_center::Notification(
288       message_center::NOTIFICATION_TYPE_SIMPLE,
289       "1",
290       ASCIIToUTF16("One is going to get a much longer "
291               "title than it previously had."),
292       ASCIIToUTF16("This is the first notification to "
293               "be displayed, but it will also be "
294               "updated to have a significantly "
295               "longer body"),
296       gfx::Image(),
297       base::string16(),
298       DummyNotifierId(),
299       message_center::RichNotificationData(),
300       NULL));
301   center_->AddNotification(notification.Pass());
302   WaitForAnimationEnded();
303   EXPECT_GT(NSHeight([[controller view] frame]), NSHeight(old_frame));
305   // Test updated spacing.
306   EXPECT_EQ(3u, [popups count]);
307   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:0],
308                                   [popups objectAtIndex:1]));
309   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:1],
310                                   [popups objectAtIndex:2]));
311   EXPECT_EQ("1", [[popups objectAtIndex:0] notificationID]);
312   EXPECT_EQ("2", [[popups objectAtIndex:1] notificationID]);
313   EXPECT_EQ("3", [[popups objectAtIndex:2] notificationID]);
316 TEST_F(PopupCollectionTest, UpdatePriority) {
317   scoped_ptr<message_center::Notification> notification;
318   notification.reset(new message_center::Notification(
319       message_center::NOTIFICATION_TYPE_SIMPLE,
320       "1",
321       ASCIIToUTF16("One"),
322       ASCIIToUTF16("This notification should not yet toast."),
323       gfx::Image(),
324       base::string16(),
325       DummyNotifierId(),
326       message_center::RichNotificationData(),
327       NULL));
328   notification->set_priority(-1);
330   center_->AddNotification(notification.Pass());
331   WaitForAnimationEnded();
332   NSArray* popups = [collection_ popups];
333   EXPECT_EQ(0u, [popups count]);
335   // Raise priority -1 to 1. Notification should display.
336   notification.reset(new message_center::Notification(
337       message_center::NOTIFICATION_TYPE_SIMPLE,
338       "1",
339       ASCIIToUTF16("One"),
340       ASCIIToUTF16("This notification should now toast"),
341       gfx::Image(),
342       base::string16(),
343       DummyNotifierId(),
344       message_center::RichNotificationData(),
345       NULL));
346   notification->set_priority(1);
348   center_->UpdateNotification("1", notification.Pass());
349   WaitForAnimationEnded();
350   EXPECT_EQ(1u, [popups count]);
353 TEST_F(PopupCollectionTest, CloseCollectionBeforeNewPopupAnimationEnds) {
354   // Add a notification and don't wait for the animation to finish.
355   scoped_ptr<message_center::Notification> notification;
356   notification.reset(new message_center::Notification(
357       message_center::NOTIFICATION_TYPE_SIMPLE,
358       "1",
359       ASCIIToUTF16("One"),
360       ASCIIToUTF16("This is the first notification to"
361                    " be displayed"),
362       gfx::Image(),
363       base::string16(),
364       DummyNotifierId(),
365       message_center::RichNotificationData(),
366       NULL));
367   center_->AddNotification(notification.Pass());
369   // Release the popup collection before the animation ends. No crash should
370   // be expected.
371   collection_.reset();
374 TEST_F(PopupCollectionTest, CloseCollectionBeforeClosePopupAnimationEnds) {
375   AddThreeNotifications();
377   // Remove a notification and don't wait for the animation to finish.
378   center_->RemoveNotification("1", true);
380   // Release the popup collection before the animation ends. No crash should
381   // be expected.
382   collection_.reset();
385 TEST_F(PopupCollectionTest, CloseCollectionBeforeUpdatePopupAnimationEnds) {
386   AddThreeNotifications();
388   // Update a notification and don't wait for the animation to finish.
389   scoped_ptr<message_center::Notification> notification;
390   notification.reset(new message_center::Notification(
391       message_center::NOTIFICATION_TYPE_SIMPLE,
392       "1",
393       ASCIIToUTF16("One"),
394       ASCIIToUTF16("New message."),
395       gfx::Image(),
396       base::string16(),
397       DummyNotifierId(),
398       message_center::RichNotificationData(),
399       NULL));
400   center_->UpdateNotification("1", notification.Pass());
402   // Release the popup collection before the animation ends. No crash should
403   // be expected.
404   collection_.reset();
407 // This test reproduces bug.
408 // https://code.google.com/p/chromium/issues/detail?id=418053
409 // It will timeout if problem exists.
410 TEST_F(PopupCollectionTest, AnimationDidEndOutOfOrder) {
411   // Set animation duration to 100 ms.
412   [collection_ setAnimationDuration:0.1];
414   // Add three notifications.
415   AddThreeNotifications();
417   // Wait for animation end.
418   WaitForAnimationEnded();
420   // Add fourth notification.
421   scoped_ptr<message_center::Notification> notification;
422   notification.reset(new message_center::Notification(
423       message_center::NOTIFICATION_TYPE_SIMPLE,
424       "4",
425       ASCIIToUTF16("Fourth"),
426       ASCIIToUTF16("This is the Fourth notification to be displayed"),
427       gfx::Image(),
428       base::string16(),
429       DummyNotifierId(),
430       message_center::RichNotificationData(),
431       NULL));
432   center_->AddNotification(notification.Pass());
434   // Mark first notifications as shown,
435   // just as from TimerFinished callback.
436   // Mark it while fourth animation is in progress so old notification
437   // will be marked as pending updates.
438   center_->MarkSinglePopupAsShown("1", false);
439   center_->MarkSinglePopupAsShown("2", false);
440   center_->MarkSinglePopupAsShown("3", false);
442   WaitForAnimationEnded();
443   // Assert that isAnimating flag is false ater animation ends.
444   // If it will be true after all animaions ends,
445   // no new notification can be added and collection will be blocked.
446   EXPECT_FALSE([collection_ isAnimating]);
448   // Release the popup collection before the animation ends. No crash should
449   // be expected.
450   collection_.reset();
453 }  // namespace message_center