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. 4 5#include <string> 6 7#include "base/memory/scoped_ptr.h" 8#include "base/message_loop/message_loop.h" 9#include "base/strings/utf_string_conversions.h" 10#include "chrome/browser/notifications/notification.h" 11#include "chrome/browser/notifications/notification_test_util.h" 12#include "chrome/browser/notifications/notification_ui_manager.h" 13#include "chrome/browser/notifications/sync_notifier/sync_notifier_test_utils.h" 14#include "chrome/browser/notifications/sync_notifier/synced_notification.h" 15#include "chrome/browser/profiles/profile.h" 16#include "content/public/browser/browser_thread.h" 17#include "content/public/test/test_browser_thread.h" 18#include "testing/gtest/include/gtest/gtest.h" 19#include "third_party/skia/include/core/SkBitmap.h" 20#include "ui/message_center/message_center_util.h" 21#include "ui/message_center/notification_types.h" 22 23using syncer::SyncData; 24using notifier::SyncedNotification; 25using sync_pb::EntitySpecifics; 26using sync_pb::SyncedNotificationSpecifics; 27 28namespace { 29const int kNotificationPriority = static_cast<int>( 30 message_center::LOW_PRIORITY); 31 32bool UseRichNotifications() { 33 return message_center::IsRichNotificationEnabled(); 34} 35 36} // namespace 37 38namespace notifier { 39 40// Stub out the NotificationUIManager for unit testing. 41class StubNotificationUIManager : public NotificationUIManager { 42 public: 43 StubNotificationUIManager() 44 : notification_(GURL(), 45 GURL(), 46 base::string16(), 47 base::string16(), 48 new MockNotificationDelegate("stub")) {} 49 virtual ~StubNotificationUIManager() {} 50 51 // Adds a notification to be displayed. Virtual for unit test override. 52 virtual void Add(const Notification& notification, Profile* profile) 53 OVERRIDE { 54 // Make a deep copy of the notification that we can inspect. 55 notification_ = notification; 56 profile_ = profile; 57 } 58 59 virtual bool Update(const Notification& notification, Profile* profile) 60 OVERRIDE { 61 // Make a deep copy of the notification that we can inspect. 62 notification_ = notification; 63 profile_ = profile; 64 return true; 65 } 66 67 // Returns true if any notifications match the supplied ID, either currently 68 // displayed or in the queue. 69 virtual const Notification* FindById(const std::string& id) const OVERRIDE { 70 return (notification_.id() == id) ? ¬ification_ : NULL; 71 } 72 73 // Removes any notifications matching the supplied ID, either currently 74 // displayed or in the queue. Returns true if anything was removed. 75 virtual bool CancelById(const std::string& notification_id) OVERRIDE { 76 dismissed_id_ = notification_id; 77 return true; 78 } 79 80 virtual std::set<std::string> GetAllIdsByProfileAndSourceOrigin( 81 Profile* profile, 82 const GURL& source) OVERRIDE { 83 std::set<std::string> notification_ids; 84 if (source == notification_.origin_url() && 85 profile->IsSameProfile(profile_)) 86 notification_ids.insert(notification_.notification_id()); 87 return notification_ids; 88 } 89 90 // Removes notifications matching the |source_origin| (which could be an 91 // extension ID). Returns true if anything was removed. 92 virtual bool CancelAllBySourceOrigin(const GURL& source_origin) OVERRIDE { 93 return false; 94 } 95 96 // Removes notifications matching |profile|. Returns true if any were removed. 97 virtual bool CancelAllByProfile(Profile* profile) OVERRIDE { 98 return false; 99 } 100 101 // Cancels all pending notifications and closes anything currently showing. 102 // Used when the app is terminating. 103 virtual void CancelAll() OVERRIDE {} 104 105 // Test hook to get the notification so we can check it 106 const Notification& notification() const { return notification_; } 107 108 // Test hook to check the ID of the last notification cancelled. 109 std::string& dismissed_id() { return dismissed_id_; } 110 111 private: 112 DISALLOW_COPY_AND_ASSIGN(StubNotificationUIManager); 113 Notification notification_; 114 Profile* profile_; 115 std::string dismissed_id_; 116}; 117 118class SyncedNotificationTest : public testing::Test { 119 public: 120 SyncedNotificationTest() 121 : ui_thread_(content::BrowserThread::UI, &message_loop_) {} 122 virtual ~SyncedNotificationTest() {} 123 124 // Methods from testing::Test. 125 126 virtual void SetUp() OVERRIDE { 127 sync_data1_ = CreateSyncData(kTitle1, kText1, kIconUrl1, kImageUrl1, 128 kAppId1, kKey1, kUnread); 129 sync_data2_ = CreateSyncData(kTitle2, kText2, kIconUrl2, kImageUrl2, 130 kAppId2, kKey2, kUnread); 131 // Notification 3 will have the same ID as notification1, but different 132 // data inside. 133 sync_data3_ = CreateSyncData(kTitle3, kText3, kIconUrl3, kImageUrl3, 134 kAppId1, kKey1, kUnread); 135 // Notification 4 will be the same as 1, but the read state will be 'read'. 136 sync_data4_ = CreateSyncData(kTitle1, kText1, kIconUrl1, kImageUrl1, 137 kAppId1, kKey1, kDismissed); 138 139 notification1_.reset(new SyncedNotification(sync_data1_)); 140 notification2_.reset(new SyncedNotification(sync_data2_)); 141 notification3_.reset(new SyncedNotification(sync_data3_)); 142 notification4_.reset(new SyncedNotification(sync_data4_)); 143 } 144 145 virtual void TearDown() OVERRIDE { 146 } 147 148 virtual void AddButtonBitmaps(SyncedNotification* notification, 149 unsigned int how_many) { 150 for (unsigned int i = 0; i < how_many; ++i) { 151 notification->button_bitmaps_.push_back(gfx::Image()); 152 } 153 } 154 155 scoped_ptr<SyncedNotification> notification1_; 156 scoped_ptr<SyncedNotification> notification2_; 157 scoped_ptr<SyncedNotification> notification3_; 158 scoped_ptr<SyncedNotification> notification4_; 159 syncer::SyncData sync_data1_; 160 syncer::SyncData sync_data2_; 161 syncer::SyncData sync_data3_; 162 syncer::SyncData sync_data4_; 163 164 private: 165 base::MessageLoopForIO message_loop_; 166 content::TestBrowserThread ui_thread_; 167 168 DISALLOW_COPY_AND_ASSIGN(SyncedNotificationTest); 169}; 170 171// test simple accessors 172 173TEST_F(SyncedNotificationTest, GetAppIdTest) { 174 std::string found_app_id = notification1_->GetAppId(); 175 std::string expected_app_id(kAppId1); 176 177 EXPECT_EQ(found_app_id, expected_app_id); 178} 179 180TEST_F(SyncedNotificationTest, GetKeyTest) { 181 std::string found_key = notification1_->GetKey(); 182 std::string expected_key(kKey1); 183 184 EXPECT_EQ(expected_key, found_key); 185} 186 187TEST_F(SyncedNotificationTest, GetTitleTest) { 188 std::string found_title = notification1_->GetTitle(); 189 std::string expected_title(kTitle1); 190 191 EXPECT_EQ(expected_title, found_title); 192} 193 194TEST_F(SyncedNotificationTest, GetIconURLTest) { 195 std::string found_icon_url = notification1_->GetAppIconUrl().spec(); 196 std::string expected_icon_url(kIconUrl1); 197 198 EXPECT_EQ(expected_icon_url, found_icon_url); 199} 200 201TEST_F(SyncedNotificationTest, GetReadStateTest) { 202 SyncedNotification::ReadState found_state1 = 203 notification1_->GetReadState(); 204 SyncedNotification::ReadState expected_state1(SyncedNotification::kUnread); 205 206 EXPECT_EQ(expected_state1, found_state1); 207 208 SyncedNotification::ReadState found_state2 = 209 notification4_->GetReadState(); 210 SyncedNotification::ReadState expected_state2(SyncedNotification::kDismissed); 211 212 EXPECT_EQ(expected_state2, found_state2); 213} 214 215// TODO(petewil): Improve ctor to pass in an image and type so this test can 216// pass on actual data. 217TEST_F(SyncedNotificationTest, GetImageURLTest) { 218 GURL found_image_url = notification1_->GetImageUrl(); 219 GURL expected_image_url = GURL(kImageUrl1); 220 221 EXPECT_EQ(expected_image_url, found_image_url); 222} 223 224// TODO(petewil): test with a multi-line message 225TEST_F(SyncedNotificationTest, GetTextTest) { 226 std::string found_text = notification1_->GetText(); 227 std::string expected_text(kText1); 228 229 EXPECT_EQ(expected_text, found_text); 230} 231 232TEST_F(SyncedNotificationTest, GetCreationTimeTest) { 233 uint64 found_time = notification1_->GetCreationTime(); 234 EXPECT_EQ(kFakeCreationTime, found_time); 235} 236 237TEST_F(SyncedNotificationTest, GetPriorityTest) { 238 double found_priority = notification1_->GetPriority(); 239 EXPECT_EQ(static_cast<double>(kNotificationPriority), found_priority); 240} 241 242TEST_F(SyncedNotificationTest, GetButtonCountTest) { 243 int found_button_count = notification1_->GetButtonCount(); 244 EXPECT_EQ(2, found_button_count); 245} 246 247TEST_F(SyncedNotificationTest, GetNotificationCountTest) { 248 int found_notification_count = notification1_->GetNotificationCount(); 249 EXPECT_EQ(3, found_notification_count); 250} 251 252TEST_F(SyncedNotificationTest, GetDefaultDestinationDataTest) { 253 std::string default_destination_title = 254 notification1_->GetDefaultDestinationTitle(); 255 GURL default_destination_icon_url = 256 notification1_->GetDefaultDestinationIconUrl(); 257 GURL default_destination_url = 258 notification1_->GetDefaultDestinationUrl(); 259 EXPECT_EQ(std::string(kDefaultDestinationTitle), default_destination_title); 260 EXPECT_EQ(GURL(kDefaultDestinationIconUrl), 261 default_destination_icon_url); 262 EXPECT_EQ(GURL(kDefaultDestinationUrl), default_destination_url); 263} 264 265TEST_F(SyncedNotificationTest, GetButtonDataTest) { 266 std::string button_one_title = notification1_->GetButtonTitle(0); 267 GURL button_one_icon_url = notification1_->GetButtonIconUrl(0); 268 GURL button_one_url = notification1_->GetButtonUrl(0); 269 std::string button_two_title = notification1_->GetButtonTitle(1); 270 GURL button_two_icon_url = notification1_->GetButtonIconUrl(1); 271 GURL button_two_url = notification1_->GetButtonUrl(1); 272 EXPECT_EQ(std::string(kButtonOneTitle), button_one_title); 273 EXPECT_EQ(GURL(kButtonOneIconUrl), button_one_icon_url); 274 EXPECT_EQ(GURL(kButtonOneUrl), button_one_url); 275 EXPECT_EQ(std::string(kButtonTwoTitle), button_two_title); 276 EXPECT_EQ(GURL(kButtonTwoIconUrl), button_two_icon_url); 277 EXPECT_EQ(GURL(kButtonTwoUrl), button_two_url); 278} 279 280TEST_F(SyncedNotificationTest, ContainedNotificationTest) { 281 std::string notification_title1 = 282 notification1_->GetContainedNotificationTitle(0); 283 std::string notification_title2 = 284 notification1_->GetContainedNotificationTitle(1); 285 std::string notification_title3 = 286 notification1_->GetContainedNotificationTitle(2); 287 std::string notification_message1 = 288 notification1_->GetContainedNotificationMessage(0); 289 std::string notification_message2 = 290 notification1_->GetContainedNotificationMessage(1); 291 std::string notification_message3 = 292 notification1_->GetContainedNotificationMessage(2); 293 294 EXPECT_EQ(std::string(kContainedTitle1), notification_title1); 295 EXPECT_EQ(std::string(kContainedTitle2), notification_title2); 296 EXPECT_EQ(std::string(kContainedTitle3), notification_title3); 297 EXPECT_EQ(std::string(kContainedMessage1), notification_message1); 298 EXPECT_EQ(std::string(kContainedMessage2), notification_message2); 299 EXPECT_EQ(std::string(kContainedMessage3), notification_message3); 300} 301 302// test that EqualsIgnoringReadState works as we expect 303TEST_F(SyncedNotificationTest, EqualsIgnoringReadStateTest) { 304 EXPECT_TRUE(notification1_->EqualsIgnoringReadState(*notification1_)); 305 EXPECT_TRUE(notification2_->EqualsIgnoringReadState(*notification2_)); 306 EXPECT_FALSE(notification1_->EqualsIgnoringReadState(*notification2_)); 307 EXPECT_FALSE(notification1_->EqualsIgnoringReadState(*notification3_)); 308 EXPECT_TRUE(notification1_->EqualsIgnoringReadState(*notification4_)); 309} 310 311TEST_F(SyncedNotificationTest, UpdateTest) { 312 scoped_ptr<SyncedNotification> notification5; 313 notification5.reset(new SyncedNotification(sync_data1_)); 314 315 // update with the sync data from notification2, and ensure they are equal. 316 notification5->Update(sync_data2_); 317 EXPECT_TRUE(notification5->EqualsIgnoringReadState(*notification2_)); 318 EXPECT_EQ(notification5->GetReadState(), notification2_->GetReadState()); 319 EXPECT_FALSE(notification5->EqualsIgnoringReadState(*notification1_)); 320} 321 322TEST_F(SyncedNotificationTest, ShowTest) { 323 324 if (!UseRichNotifications()) 325 return; 326 327 StubNotificationUIManager notification_manager; 328 329 // Call the method under test using the pre-populated data. 330 notification1_->Show(¬ification_manager, NULL, NULL); 331 332 const Notification notification = notification_manager.notification(); 333 334 // Check the base fields of the notification. 335 EXPECT_EQ(message_center::NOTIFICATION_TYPE_IMAGE, notification.type()); 336 EXPECT_EQ(std::string(kTitle1), UTF16ToUTF8(notification.title())); 337 EXPECT_EQ(std::string(kText1), UTF16ToUTF8(notification.message())); 338 EXPECT_EQ(std::string(kExpectedOriginUrl), notification.origin_url().spec()); 339 EXPECT_EQ(std::string(kKey1), UTF16ToUTF8(notification.replace_id())); 340 341 EXPECT_EQ(kFakeCreationTime, notification.timestamp().ToDoubleT()); 342 EXPECT_EQ(kNotificationPriority, notification.priority()); 343} 344 345TEST_F(SyncedNotificationTest, DismissTest) { 346 347 if (!UseRichNotifications()) 348 return; 349 350 StubNotificationUIManager notification_manager; 351 352 // Call the method under test using a dismissed notification. 353 notification4_->Show(¬ification_manager, NULL, NULL); 354 355 EXPECT_EQ(std::string(kKey1), notification_manager.dismissed_id()); 356} 357 358TEST_F(SyncedNotificationTest, AddBitmapToFetchQueueTest) { 359 scoped_ptr<SyncedNotification> notification6; 360 notification6.reset(new SyncedNotification(sync_data1_)); 361 362 // Add two bitmaps to the queue. 363 notification6->AddBitmapToFetchQueue(GURL(kIconUrl1)); 364 notification6->AddBitmapToFetchQueue(GURL(kIconUrl2)); 365 366 EXPECT_EQ(2, notification6->active_fetcher_count_); 367 EXPECT_EQ(GURL(kIconUrl1), notification6->fetchers_[0]->url()); 368 EXPECT_EQ(GURL(kIconUrl2), notification6->fetchers_[1]->url()); 369 370 notification6->AddBitmapToFetchQueue(GURL(kIconUrl2)); 371 EXPECT_EQ(2, notification6->active_fetcher_count_); 372} 373 374TEST_F(SyncedNotificationTest, OnFetchCompleteTest) { 375 if (!UseRichNotifications()) 376 return; 377 378 StubNotificationUIManager notification_manager; 379 380 // Set up the internal state that FetchBitmaps() would have set. 381 notification1_->notification_manager_ = ¬ification_manager; 382 383 // Add the bitmaps to the queue for us to match up. 384 notification1_->AddBitmapToFetchQueue(GURL(kIconUrl1)); 385 notification1_->AddBitmapToFetchQueue(GURL(kImageUrl1)); 386 notification1_->AddBitmapToFetchQueue(GURL(kButtonOneIconUrl)); 387 notification1_->AddBitmapToFetchQueue(GURL(kButtonTwoIconUrl)); 388 389 EXPECT_EQ(4, notification1_->active_fetcher_count_); 390 391 // Put some realistic looking bitmap data into the url_fetcher. 392 SkBitmap bitmap; 393 394 // Put a real bitmap into "bitmap". 2x2 bitmap of green 32 bit pixels. 395 bitmap.setConfig(SkBitmap::kARGB_8888_Config, 2, 2); 396 bitmap.allocPixels(); 397 bitmap.eraseColor(SK_ColorGREEN); 398 399 // Allocate the button_bitmaps_ array as the calling function normally would. 400 AddButtonBitmaps(notification1_.get(), 2); 401 402 notification1_->OnFetchComplete(GURL(kIconUrl1), &bitmap); 403 EXPECT_EQ(3, notification1_->active_fetcher_count_); 404 405 // When we call OnFetchComplete on the last bitmap, show should be called. 406 notification1_->OnFetchComplete(GURL(kImageUrl1), &bitmap); 407 EXPECT_EQ(2, notification1_->active_fetcher_count_); 408 409 notification1_->OnFetchComplete(GURL(kButtonOneIconUrl), &bitmap); 410 EXPECT_EQ(1, notification1_->active_fetcher_count_); 411 412 notification1_->OnFetchComplete(GURL(kButtonTwoIconUrl), &bitmap); 413 EXPECT_EQ(0, notification1_->active_fetcher_count_); 414 415 // Since we check Show() thoroughly in its own test, we only check cursorily. 416 EXPECT_EQ(message_center::NOTIFICATION_TYPE_IMAGE, 417 notification_manager.notification().type()); 418 EXPECT_EQ(std::string(kTitle1), 419 UTF16ToUTF8(notification_manager.notification().title())); 420 EXPECT_EQ(std::string(kText1), 421 UTF16ToUTF8(notification_manager.notification().message())); 422 423 // TODO(petewil): Check that the bitmap in the notification is what we expect. 424 // This fails today, the type info is different. 425 // EXPECT_TRUE(gfx::BitmapsAreEqual( 426 // image, notification1_->GetAppIconBitmap())); 427} 428 429TEST_F(SyncedNotificationTest, QueueBitmapFetchJobsTest) { 430 if (!UseRichNotifications()) 431 return; 432 433 StubNotificationUIManager notification_manager; 434 435 notification1_->QueueBitmapFetchJobs(¬ification_manager, NULL, NULL); 436 437 // There should be 4 urls in the queue, icon, image, and two buttons. 438 EXPECT_EQ(4, notification1_->active_fetcher_count_); 439} 440 441// TODO(petewil): Add a test for a notification being read and or deleted. 442 443} // namespace notifier 444