Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ui / message_center / cocoa / popup_collection_unittest.mm
blob274d375b6acacc22f717e912f3e67da6a0c5930e
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   void TearDown() override {
39     collection_.reset();  // Close all popups.
40     ui::CocoaTest::TearDown();
41   }
43   ~PopupCollectionTest() override { message_center::MessageCenter::Shutdown(); }
45   message_center::NotifierId DummyNotifierId() {
46     return message_center::NotifierId();
47   }
49   void AddThreeNotifications() {
50     scoped_ptr<message_center::Notification> notification;
51     notification.reset(new message_center::Notification(
52         message_center::NOTIFICATION_TYPE_SIMPLE, "1", ASCIIToUTF16("One"),
53         ASCIIToUTF16("This is the first notification to"
54                      " be displayed"),
55         gfx::Image(), base::string16(), GURL(), DummyNotifierId(),
56         message_center::RichNotificationData(), NULL));
57     center_->AddNotification(notification.Pass());
59     notification.reset(new message_center::Notification(
60         message_center::NOTIFICATION_TYPE_SIMPLE, "2", ASCIIToUTF16("Two"),
61         ASCIIToUTF16("This is the second notification."), gfx::Image(),
62         base::string16(), GURL(), DummyNotifierId(),
63         message_center::RichNotificationData(), NULL));
64     center_->AddNotification(notification.Pass());
66     notification.reset(new message_center::Notification(
67         message_center::NOTIFICATION_TYPE_SIMPLE, "3", ASCIIToUTF16("Three"),
68         ASCIIToUTF16("This is the third notification "
69                      "that has a much longer body "
70                      "than the other notifications. It "
71                      "may not fit on the screen if we "
72                      "set the screen size too small or "
73                      "if the notification is way too big"),
74         gfx::Image(), base::string16(), GURL(), DummyNotifierId(),
75         message_center::RichNotificationData(), NULL));
76     center_->AddNotification(notification.Pass());
77     WaitForAnimationEnded();
78   }
80   bool CheckSpacingBetween(MCPopupController* upper, MCPopupController* lower) {
81     CGFloat minY = NSMinY([[upper window] frame]);
82     CGFloat maxY = NSMaxY([[lower window] frame]);
83     CGFloat delta = minY - maxY;
84     EXPECT_EQ(message_center::kMarginBetweenItems, delta);
85     return delta == message_center::kMarginBetweenItems;
86   }
88   void WaitForAnimationEnded() {
89     if (![collection_ isAnimating])
90       return;
91     nested_run_loop_.reset(new base::RunLoop());
92     nested_run_loop_->Run();
93     nested_run_loop_.reset();
94   }
96   base::MessageLoopForUI message_loop_;
97   scoped_ptr<base::RunLoop> nested_run_loop_;
98   message_center::MessageCenter* center_;
99   base::scoped_nsobject<MCPopupCollection> collection_;
102 TEST_F(PopupCollectionTest, AddThreeCloseOne) {
103   EXPECT_EQ(0u, [[collection_ popups] count]);
104   AddThreeNotifications();
105   EXPECT_EQ(3u, [[collection_ popups] count]);
107   center_->RemoveNotification("2", true);
108   WaitForAnimationEnded();
109   EXPECT_EQ(2u, [[collection_ popups] count]);
112 TEST_F(PopupCollectionTest, AttemptFourOneOffscreen) {
113   [collection_ setScreenFrame:NSMakeRect(0, 0, 800, 300)];
115   EXPECT_EQ(0u, [[collection_ popups] count]);
116   AddThreeNotifications();
117   EXPECT_EQ(2u, [[collection_ popups] count]);  // "3" does not fit on screen.
119   scoped_ptr<message_center::Notification> notification;
121   notification.reset(new message_center::Notification(
122       message_center::NOTIFICATION_TYPE_SIMPLE, "4", ASCIIToUTF16("Four"),
123       ASCIIToUTF16("This is the fourth notification."), gfx::Image(),
124       base::string16(), GURL(), DummyNotifierId(),
125       message_center::RichNotificationData(), NULL));
126   center_->AddNotification(notification.Pass());
127   WaitForAnimationEnded();
129   // Remove "1" and "3" should fit on screen.
130   center_->RemoveNotification("1", true);
131   WaitForAnimationEnded();
132   ASSERT_EQ(2u, [[collection_ popups] count]);
134   EXPECT_EQ("2", [[[collection_ popups] objectAtIndex:0] notificationID]);
135   EXPECT_EQ("3", [[[collection_ popups] objectAtIndex:1] notificationID]);
137   // Remove "2" and "4" should fit on screen.
138   center_->RemoveNotification("2", true);
139   WaitForAnimationEnded();
140   ASSERT_EQ(2u, [[collection_ popups] count]);
142   EXPECT_EQ("3", [[[collection_ popups] objectAtIndex:0] notificationID]);
143   EXPECT_EQ("4", [[[collection_ popups] objectAtIndex:1] notificationID]);
146 TEST_F(PopupCollectionTest, LayoutSpacing) {
147   const CGFloat kScreenSize = 500;
148   [collection_ setScreenFrame:NSMakeRect(0, 0, kScreenSize, kScreenSize)];
150   AddThreeNotifications();
151   NSArray* popups = [collection_ popups];
153   EXPECT_EQ(message_center::kMarginBetweenItems,
154             kScreenSize - NSMaxY([[[popups objectAtIndex:0] window] frame]));
156   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:0],
157                                   [popups objectAtIndex:1]));
158   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:1],
159                                   [popups objectAtIndex:2]));
161   // Set priority so that kMaxVisiblePopupNotifications does not hide it.
162   message_center::RichNotificationData optional;
163   optional.priority = message_center::HIGH_PRIORITY;
164   scoped_ptr<message_center::Notification> notification;
165   notification.reset(new message_center::Notification(
166       message_center::NOTIFICATION_TYPE_SIMPLE, "4", ASCIIToUTF16("Four"),
167       ASCIIToUTF16("This is the fourth notification."), gfx::Image(),
168       base::string16(), GURL(), DummyNotifierId(), optional, NULL));
169   center_->AddNotification(notification.Pass());
170   WaitForAnimationEnded();
171   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:2],
172                                   [popups objectAtIndex:3]));
174   // Remove "2".
175   center_->RemoveNotification("2", true);
176   WaitForAnimationEnded();
177   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:0],
178                                   [popups objectAtIndex:1]));
179   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:1],
180                                   [popups objectAtIndex:2]));
182   // Remove "1".
183   center_->RemoveNotification("2", true);
184   WaitForAnimationEnded();
185   EXPECT_EQ(message_center::kMarginBetweenItems,
186             kScreenSize - NSMaxY([[[popups objectAtIndex:0] window] frame]));
187   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:0],
188                                   [popups objectAtIndex:1]));
191 TEST_F(PopupCollectionTest, TinyScreen) {
192   [collection_ setScreenFrame:NSMakeRect(0, 0, 800, 100)];
194   EXPECT_EQ(0u, [[collection_ popups] count]);
195   scoped_ptr<message_center::Notification> notification;
196   notification.reset(new message_center::Notification(
197       message_center::NOTIFICATION_TYPE_SIMPLE, "1", ASCIIToUTF16("One"),
198       ASCIIToUTF16("This is the first notification to"
199                    " be displayed"),
200       gfx::Image(), base::string16(), GURL(), DummyNotifierId(),
201       message_center::RichNotificationData(), NULL));
202   center_->AddNotification(notification.Pass());
203   WaitForAnimationEnded();
204   EXPECT_EQ(1u, [[collection_ popups] count]);
206   // Now give the notification a longer message so that it no longer fits.
207   notification.reset(new message_center::Notification(
208       message_center::NOTIFICATION_TYPE_SIMPLE, "1", ASCIIToUTF16("One"),
209       ASCIIToUTF16("This is now a very very very very "
210                    "very very very very very very very "
211                    "very very very very very very very "
212                    "very very very very very very very "
213                    "very very very very very very very "
214                    "very very very very very very very "
215                    "very very very very very very very "
216                    "long notification."),
217       gfx::Image(), base::string16(), GURL(), DummyNotifierId(),
218       message_center::RichNotificationData(), NULL));
219   center_->UpdateNotification("1", notification.Pass());
220   WaitForAnimationEnded();
221   EXPECT_EQ(0u, [[collection_ popups] count]);
224 TEST_F(PopupCollectionTest, UpdateIconAndBody) {
225   AddThreeNotifications();
226   NSArray* popups = [collection_ popups];
228   EXPECT_EQ(3u, [popups count]);
230   // Update "2" icon.
231   MCNotificationController* controller =
232       [[popups objectAtIndex:1] notificationController];
233   EXPECT_FALSE([[controller iconView] image]);
234   center_->SetNotificationIcon("2",
235       gfx::Image([[NSImage imageNamed:NSImageNameUser] retain]));
236   WaitForAnimationEnded();
237   EXPECT_TRUE([[controller iconView] image]);
239   EXPECT_EQ(3u, [popups count]);
240   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:0],
241                                   [popups objectAtIndex:1]));
242   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:1],
243                                   [popups objectAtIndex:2]));
245   // Replace "1".
246   controller = [[popups objectAtIndex:0] notificationController];
247   NSRect old_frame = [[controller view] frame];
248   scoped_ptr<message_center::Notification> notification;
249   notification.reset(new message_center::Notification(
250       message_center::NOTIFICATION_TYPE_SIMPLE, "1",
251       ASCIIToUTF16("One is going to get a much longer "
252                    "title than it previously had."),
253       ASCIIToUTF16("This is the first notification to "
254                    "be displayed, but it will also be "
255                    "updated to have a significantly "
256                    "longer body"),
257       gfx::Image(), base::string16(), GURL(), DummyNotifierId(),
258       message_center::RichNotificationData(), NULL));
259   center_->AddNotification(notification.Pass());
260   WaitForAnimationEnded();
261   EXPECT_GT(NSHeight([[controller view] frame]), NSHeight(old_frame));
263   // Test updated spacing.
264   EXPECT_EQ(3u, [popups count]);
265   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:0],
266                                   [popups objectAtIndex:1]));
267   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:1],
268                                   [popups objectAtIndex:2]));
269   EXPECT_EQ("1", [[popups objectAtIndex:0] notificationID]);
270   EXPECT_EQ("2", [[popups objectAtIndex:1] notificationID]);
271   EXPECT_EQ("3", [[popups objectAtIndex:2] notificationID]);
274 TEST_F(PopupCollectionTest, UpdatePriority) {
275   scoped_ptr<message_center::Notification> notification;
276   notification.reset(new message_center::Notification(
277       message_center::NOTIFICATION_TYPE_SIMPLE, "1", ASCIIToUTF16("One"),
278       ASCIIToUTF16("This notification should not yet toast."), gfx::Image(),
279       base::string16(), GURL(), DummyNotifierId(),
280       message_center::RichNotificationData(), NULL));
281   notification->set_priority(-1);
283   center_->AddNotification(notification.Pass());
284   WaitForAnimationEnded();
285   NSArray* popups = [collection_ popups];
286   EXPECT_EQ(0u, [popups count]);
288   // Raise priority -1 to 1. Notification should display.
289   notification.reset(new message_center::Notification(
290       message_center::NOTIFICATION_TYPE_SIMPLE, "1", ASCIIToUTF16("One"),
291       ASCIIToUTF16("This notification should now toast"), gfx::Image(),
292       base::string16(), GURL(), DummyNotifierId(),
293       message_center::RichNotificationData(), NULL));
294   notification->set_priority(1);
296   center_->UpdateNotification("1", notification.Pass());
297   WaitForAnimationEnded();
298   EXPECT_EQ(1u, [popups count]);
301 TEST_F(PopupCollectionTest, CloseCollectionBeforeNewPopupAnimationEnds) {
302   // Add a notification and don't wait for the animation to finish.
303   scoped_ptr<message_center::Notification> notification;
304   notification.reset(new message_center::Notification(
305       message_center::NOTIFICATION_TYPE_SIMPLE, "1", ASCIIToUTF16("One"),
306       ASCIIToUTF16("This is the first notification to"
307                    " be displayed"),
308       gfx::Image(), base::string16(), GURL(), DummyNotifierId(),
309       message_center::RichNotificationData(), NULL));
310   center_->AddNotification(notification.Pass());
312   // Release the popup collection before the animation ends. No crash should
313   // be expected.
314   collection_.reset();
317 TEST_F(PopupCollectionTest, CloseCollectionBeforeClosePopupAnimationEnds) {
318   AddThreeNotifications();
320   // Remove a notification and don't wait for the animation to finish.
321   center_->RemoveNotification("1", true);
323   // Release the popup collection before the animation ends. No crash should
324   // be expected.
325   collection_.reset();
328 TEST_F(PopupCollectionTest, CloseCollectionBeforeUpdatePopupAnimationEnds) {
329   AddThreeNotifications();
331   // Update a notification and don't wait for the animation to finish.
332   scoped_ptr<message_center::Notification> notification;
333   notification.reset(new message_center::Notification(
334       message_center::NOTIFICATION_TYPE_SIMPLE, "1", ASCIIToUTF16("One"),
335       ASCIIToUTF16("New message."), gfx::Image(), base::string16(), GURL(),
336       DummyNotifierId(), message_center::RichNotificationData(), NULL));
337   center_->UpdateNotification("1", notification.Pass());
339   // Release the popup collection before the animation ends. No crash should
340   // be expected.
341   collection_.reset();
344 }  // namespace message_center