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/base/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 class PopupCollectionTest : public ui::CocoaTest {
23 : message_loop_(base::MessageLoop::TYPE_UI) {
24 message_center::MessageCenter::Initialize();
25 center_ = message_center::MessageCenter::Get();
27 [[MCPopupCollection alloc] initWithMessageCenter:center_]);
28 [collection_ setAnimationDuration:0.001];
29 [collection_ setAnimationEndedCallback:^{
30 if (nested_run_loop_.get())
31 nested_run_loop_->Quit();
35 virtual void TearDown() OVERRIDE {
36 collection_.reset(); // Close all popups.
37 ui::CocoaTest::TearDown();
40 virtual ~PopupCollectionTest() {
41 message_center::MessageCenter::Shutdown();
44 void AddThreeNotifications() {
45 scoped_ptr<message_center::Notification> notification;
46 notification.reset(new message_center::Notification(
47 message_center::NOTIFICATION_TYPE_SIMPLE,
50 ASCIIToUTF16("This is the first notification to"
54 message_center::NotifierId(),
55 message_center::RichNotificationData(),
57 center_->AddNotification(notification.Pass());
59 notification.reset(new message_center::Notification(
60 message_center::NOTIFICATION_TYPE_SIMPLE,
63 ASCIIToUTF16("This is the second notification."),
66 message_center::NotifierId(),
67 message_center::RichNotificationData(),
69 center_->AddNotification(notification.Pass());
71 notification.reset(new message_center::Notification(
72 message_center::NOTIFICATION_TYPE_SIMPLE,
74 ASCIIToUTF16("Three"),
75 ASCIIToUTF16("This is the third notification "
76 "that has a much longer body "
77 "than the other notifications. It "
78 "may not fit on the screen if we "
79 "set the screen size too small or "
80 "if the notification is way too big"),
83 message_center::NotifierId(),
84 message_center::RichNotificationData(),
86 center_->AddNotification(notification.Pass());
87 WaitForAnimationEnded();
90 bool CheckSpacingBetween(MCPopupController* upper, MCPopupController* lower) {
91 CGFloat minY = NSMinY([[upper window] frame]);
92 CGFloat maxY = NSMaxY([[lower window] frame]);
93 CGFloat delta = minY - maxY;
94 EXPECT_EQ(message_center::kMarginBetweenItems, delta);
95 return delta == message_center::kMarginBetweenItems;
98 void WaitForAnimationEnded() {
99 if (![collection_ isAnimating])
101 nested_run_loop_.reset(new base::RunLoop());
102 nested_run_loop_->Run();
103 nested_run_loop_.reset();
106 base::MessageLoop message_loop_;
107 scoped_ptr<base::RunLoop> nested_run_loop_;
108 message_center::MessageCenter* center_;
109 base::scoped_nsobject<MCPopupCollection> collection_;
112 TEST_F(PopupCollectionTest, AddThreeCloseOne) {
113 EXPECT_EQ(0u, [[collection_ popups] count]);
114 AddThreeNotifications();
115 EXPECT_EQ(3u, [[collection_ popups] count]);
117 center_->RemoveNotification("2", true);
118 WaitForAnimationEnded();
119 EXPECT_EQ(2u, [[collection_ popups] count]);
122 TEST_F(PopupCollectionTest, AttemptFourOneOffscreen) {
123 [collection_ setScreenFrame:NSMakeRect(0, 0, 800, 300)];
125 EXPECT_EQ(0u, [[collection_ popups] count]);
126 AddThreeNotifications();
127 EXPECT_EQ(2u, [[collection_ popups] count]); // "3" does not fit on screen.
129 scoped_ptr<message_center::Notification> notification;
131 notification.reset(new message_center::Notification(
132 message_center::NOTIFICATION_TYPE_SIMPLE,
134 ASCIIToUTF16("Four"),
135 ASCIIToUTF16("This is the fourth notification."),
138 message_center::NotifierId(),
139 message_center::RichNotificationData(),
141 center_->AddNotification(notification.Pass());
142 WaitForAnimationEnded();
144 // Remove "1" and "3" should fit on screen.
145 center_->RemoveNotification("1", true);
146 WaitForAnimationEnded();
147 ASSERT_EQ(2u, [[collection_ popups] count]);
149 EXPECT_EQ("2", [[[collection_ popups] objectAtIndex:0] notificationID]);
150 EXPECT_EQ("3", [[[collection_ popups] objectAtIndex:1] notificationID]);
152 // Remove "2" and "4" should fit on screen.
153 center_->RemoveNotification("2", true);
154 WaitForAnimationEnded();
155 ASSERT_EQ(2u, [[collection_ popups] count]);
157 EXPECT_EQ("3", [[[collection_ popups] objectAtIndex:0] notificationID]);
158 EXPECT_EQ("4", [[[collection_ popups] objectAtIndex:1] notificationID]);
161 TEST_F(PopupCollectionTest, LayoutSpacing) {
162 const CGFloat kScreenSize = 500;
163 [collection_ setScreenFrame:NSMakeRect(0, 0, kScreenSize, kScreenSize)];
165 AddThreeNotifications();
166 NSArray* popups = [collection_ popups];
168 EXPECT_EQ(message_center::kMarginBetweenItems,
169 kScreenSize - NSMaxY([[[popups objectAtIndex:0] window] frame]));
171 EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:0],
172 [popups objectAtIndex:1]));
173 EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:1],
174 [popups objectAtIndex:2]));
176 // Set priority so that kMaxVisiblePopupNotifications does not hide it.
177 message_center::RichNotificationData optional;
178 optional.priority = message_center::HIGH_PRIORITY;
179 scoped_ptr<message_center::Notification> notification;
180 notification.reset(new message_center::Notification(
181 message_center::NOTIFICATION_TYPE_SIMPLE,
183 ASCIIToUTF16("Four"),
184 ASCIIToUTF16("This is the fourth notification."),
187 message_center::NotifierId(),
190 center_->AddNotification(notification.Pass());
191 WaitForAnimationEnded();
192 EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:2],
193 [popups objectAtIndex:3]));
196 center_->RemoveNotification("2", true);
197 WaitForAnimationEnded();
198 EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:0],
199 [popups objectAtIndex:1]));
200 EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:1],
201 [popups objectAtIndex:2]));
204 center_->RemoveNotification("2", true);
205 WaitForAnimationEnded();
206 EXPECT_EQ(message_center::kMarginBetweenItems,
207 kScreenSize - NSMaxY([[[popups objectAtIndex:0] window] frame]));
208 EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:0],
209 [popups objectAtIndex:1]));
212 TEST_F(PopupCollectionTest, TinyScreen) {
213 [collection_ setScreenFrame:NSMakeRect(0, 0, 800, 100)];
215 EXPECT_EQ(0u, [[collection_ popups] count]);
216 scoped_ptr<message_center::Notification> notification;
217 notification.reset(new message_center::Notification(
218 message_center::NOTIFICATION_TYPE_SIMPLE,
221 ASCIIToUTF16("This is the first notification to"
225 message_center::NotifierId(),
226 message_center::RichNotificationData(),
228 center_->AddNotification(notification.Pass());
229 WaitForAnimationEnded();
230 EXPECT_EQ(1u, [[collection_ popups] count]);
232 // Now give the notification a longer message so that it no longer fits.
233 notification.reset(new message_center::Notification(
234 message_center::NOTIFICATION_TYPE_SIMPLE,
237 ASCIIToUTF16("This is now a very very very very "
238 "very very very very very very very "
239 "very very very very very very very "
240 "very very very very very very very "
241 "very very very very very very very "
242 "very very very very very very very "
243 "very very very very very very very "
244 "long notification."),
247 message_center::NotifierId(),
248 message_center::RichNotificationData(),
250 center_->UpdateNotification("1", notification.Pass());
251 WaitForAnimationEnded();
252 EXPECT_EQ(0u, [[collection_ popups] count]);
255 TEST_F(PopupCollectionTest, UpdateIconAndBody) {
256 AddThreeNotifications();
257 NSArray* popups = [collection_ popups];
259 EXPECT_EQ(3u, [popups count]);
262 MCNotificationController* controller =
263 [[popups objectAtIndex:1] notificationController];
264 EXPECT_FALSE([[controller iconView] image]);
265 center_->SetNotificationIcon("2",
266 gfx::Image([[NSImage imageNamed:NSImageNameUser] retain]));
267 WaitForAnimationEnded();
268 EXPECT_TRUE([[controller iconView] image]);
270 EXPECT_EQ(3u, [popups count]);
271 EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:0],
272 [popups objectAtIndex:1]));
273 EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:1],
274 [popups objectAtIndex:2]));
277 controller = [[popups objectAtIndex:0] notificationController];
278 NSRect old_frame = [[controller view] frame];
279 scoped_ptr<message_center::Notification> notification;
280 notification.reset(new message_center::Notification(
281 message_center::NOTIFICATION_TYPE_SIMPLE,
283 ASCIIToUTF16("One is going to get a much longer "
284 "title than it previously had."),
285 ASCIIToUTF16("This is the first notification to "
286 "be displayed, but it will also be "
287 "updated to have a significantly "
291 message_center::NotifierId(),
292 message_center::RichNotificationData(),
294 center_->AddNotification(notification.Pass());
295 WaitForAnimationEnded();
296 EXPECT_GT(NSHeight([[controller view] frame]), NSHeight(old_frame));
298 // Test updated spacing.
299 EXPECT_EQ(3u, [popups count]);
300 EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:0],
301 [popups objectAtIndex:1]));
302 EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:1],
303 [popups objectAtIndex:2]));
304 EXPECT_EQ("1", [[popups objectAtIndex:0] notificationID]);
305 EXPECT_EQ("2", [[popups objectAtIndex:1] notificationID]);
306 EXPECT_EQ("3", [[popups objectAtIndex:2] notificationID]);
309 TEST_F(PopupCollectionTest, CloseCollectionBeforeNewPopupAnimationEnds) {
310 // Add a notification and don't wait for the animation to finish.
311 scoped_ptr<message_center::Notification> notification;
312 notification.reset(new message_center::Notification(
313 message_center::NOTIFICATION_TYPE_SIMPLE,
316 ASCIIToUTF16("This is the first notification to"
320 message_center::NotifierId(),
321 message_center::RichNotificationData(),
323 center_->AddNotification(notification.Pass());
325 // Release the popup collection before the animation ends. No crash should
330 TEST_F(PopupCollectionTest, CloseCollectionBeforeClosePopupAnimationEnds) {
331 AddThreeNotifications();
333 // Remove a notification and don't wait for the animation to finish.
334 center_->RemoveNotification("1", true);
336 // Release the popup collection before the animation ends. No crash should
341 TEST_F(PopupCollectionTest, CloseCollectionBeforeUpdatePopupAnimationEnds) {
342 AddThreeNotifications();
344 // Update a notification and don't wait for the animation to finish.
345 scoped_ptr<message_center::Notification> notification;
346 notification.reset(new message_center::Notification(
347 message_center::NOTIFICATION_TYPE_SIMPLE,
350 ASCIIToUTF16("New message."),
353 message_center::NotifierId(),
354 message_center::RichNotificationData(),
356 center_->UpdateNotification("1", notification.Pass());
358 // Release the popup collection before the animation ends. No crash should