1 // Copyright (c) 2012 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 <map> 6 7 #include "base/memory/scoped_ptr.h" 8 #include "base/strings/utf_string_conversions.h" 9 #include "base/values.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/chrome_notifier_service.h" 14 #include "chrome/browser/notifications/sync_notifier/sync_notifier_test_utils.h" 15 #include "chrome/browser/notifications/sync_notifier/synced_notification.h" 16 #include "chrome/browser/profiles/profile.h" 17 #include "content/public/test/test_browser_thread_bundle.h" 18 #include "sync/api/sync_change.h" 19 #include "sync/api/sync_change_processor.h" 20 #include "sync/api/sync_error_factory.h" 21 #include "sync/api/sync_error_factory_mock.h" 22 #include "testing/gtest/include/gtest/gtest.h" 23 #include "ui/message_center/message_center_util.h" 24 25 using sync_pb::SyncedNotificationSpecifics; 26 using sync_pb::EntitySpecifics; 27 using syncer::SyncData; 28 using syncer::SyncChange; 29 using syncer::SyncChangeList; 30 using syncer::SyncDataList; 31 using syncer::SYNCED_NOTIFICATIONS; 32 using notifier::SyncedNotification; 33 using notifier::ChromeNotifierService; 34 35 namespace { 36 37 const int kNotificationPriority = static_cast<int>( 38 message_center::LOW_PRIORITY); 39 40 // Extract notification id from syncer::SyncData. 41 std::string GetNotificationId(const SyncData& sync_data) { 42 SyncedNotificationSpecifics specifics = sync_data.GetSpecifics(). 43 synced_notification(); 44 45 return specifics.coalesced_notification().key(); 46 } 47 48 // Stub out the NotificationUIManager for unit testing. 49 class StubNotificationUIManager : public NotificationUIManager { 50 public: 51 StubNotificationUIManager() 52 : notification_(GURL(), 53 GURL(), 54 string16(), 55 string16(), 56 new MockNotificationDelegate("stub")) {} 57 virtual ~StubNotificationUIManager() {} 58 59 // Adds a notification to be displayed. Virtual for unit test override. 60 virtual void Add(const Notification& notification, Profile* profile) 61 OVERRIDE { 62 // Make a deep copy of the notification that we can inspect. 63 notification_ = notification; 64 profile_ = profile; 65 } 66 67 virtual bool Update(const Notification& notification, Profile* profile) 68 OVERRIDE { 69 // Make a deep copy of the notification that we can inspect. 70 notification_ = notification; 71 profile_ = profile; 72 return true; 73 } 74 75 // Returns true if any notifications match the supplied ID, either currently 76 // displayed or in the queue. 77 virtual const Notification* FindById(const std::string& id) const OVERRIDE { 78 return (notification_.id() == id) ? ¬ification_ : NULL; 79 } 80 81 // Removes any notifications matching the supplied ID, either currently 82 // displayed or in the queue. Returns true if anything was removed. 83 virtual bool CancelById(const std::string& notification_id) OVERRIDE { 84 dismissed_id_ = notification_id; 85 return true; 86 } 87 88 // Adds the notification_id for each outstanding notification to the set 89 // |notification_ids| (must not be NULL). 90 virtual std::set<std::string> GetAllIdsByProfileAndSourceOrigin( 91 Profile* profile, 92 const GURL& source) OVERRIDE { 93 std::set<std::string> notification_ids; 94 if (source == notification_.origin_url() && 95 profile->IsSameProfile(profile_)) 96 notification_ids.insert(notification_.notification_id()); 97 return notification_ids; 98 } 99 100 // Removes notifications matching the |source_origin| (which could be an 101 // extension ID). Returns true if anything was removed. 102 virtual bool CancelAllBySourceOrigin(const GURL& source_origin) OVERRIDE { 103 return false; 104 } 105 106 // Removes notifications matching |profile|. Returns true if any were removed. 107 virtual bool CancelAllByProfile(Profile* profile) OVERRIDE { 108 return false; 109 } 110 111 // Cancels all pending notifications and closes anything currently showing. 112 // Used when the app is terminating. 113 virtual void CancelAll() OVERRIDE {} 114 115 // Test hook to get the notification so we can check it 116 const Notification& notification() const { return notification_; } 117 118 // Test hook to check the ID of the last notification cancelled. 119 std::string& dismissed_id() { return dismissed_id_; } 120 121 private: 122 DISALLOW_COPY_AND_ASSIGN(StubNotificationUIManager); 123 Notification notification_; 124 Profile* profile_; 125 std::string dismissed_id_; 126 }; 127 128 // Dummy SyncChangeProcessor used to help review what SyncChanges are pushed 129 // back up to Sync. 130 class TestChangeProcessor : public syncer::SyncChangeProcessor { 131 public: 132 TestChangeProcessor() { } 133 virtual ~TestChangeProcessor() { } 134 135 // Store a copy of all the changes passed in so we can examine them later. 136 virtual syncer::SyncError ProcessSyncChanges( 137 const tracked_objects::Location& from_here, 138 const SyncChangeList& change_list) OVERRIDE { 139 change_map_.clear(); 140 for (SyncChangeList::const_iterator iter = change_list.begin(); 141 iter != change_list.end(); ++iter) { 142 // Put the data into the change tracking map. 143 change_map_[GetNotificationId(iter->sync_data())] = *iter; 144 } 145 146 return syncer::SyncError(); 147 } 148 149 size_t change_list_size() { return change_map_.size(); } 150 151 bool ContainsId(const std::string& id) { 152 return change_map_.find(id) != change_map_.end(); 153 } 154 155 SyncChange GetChangeById(const std::string& id) { 156 EXPECT_TRUE(ContainsId(id)); 157 return change_map_[id]; 158 } 159 160 private: 161 // Track the changes received in ProcessSyncChanges. 162 std::map<std::string, SyncChange> change_map_; 163 164 DISALLOW_COPY_AND_ASSIGN(TestChangeProcessor); 165 }; 166 167 class SyncChangeProcessorDelegate : public syncer::SyncChangeProcessor { 168 public: 169 explicit SyncChangeProcessorDelegate(SyncChangeProcessor* recipient) 170 : recipient_(recipient) { 171 EXPECT_TRUE(recipient_); 172 } 173 virtual ~SyncChangeProcessorDelegate() {} 174 175 // syncer::SyncChangeProcessor implementation. 176 virtual syncer::SyncError ProcessSyncChanges( 177 const tracked_objects::Location& from_here, 178 const SyncChangeList& change_list) OVERRIDE { 179 return recipient_->ProcessSyncChanges(from_here, change_list); 180 } 181 182 private: 183 // The recipient of all sync changes. 184 SyncChangeProcessor* recipient_; 185 186 DISALLOW_COPY_AND_ASSIGN(SyncChangeProcessorDelegate); 187 }; 188 189 } // namespace 190 191 class ChromeNotifierServiceTest : public testing::Test { 192 public: 193 ChromeNotifierServiceTest() 194 : sync_processor_(new TestChangeProcessor), 195 sync_processor_delegate_(new SyncChangeProcessorDelegate( 196 sync_processor_.get())) {} 197 virtual ~ChromeNotifierServiceTest() {} 198 199 // Methods from testing::Test. 200 virtual void SetUp() { 201 ChromeNotifierService::set_avoid_bitmap_fetching_for_test(true); 202 } 203 virtual void TearDown() {} 204 205 TestChangeProcessor* processor() { 206 return static_cast<TestChangeProcessor*>(sync_processor_.get()); 207 } 208 209 scoped_ptr<syncer::SyncChangeProcessor> PassProcessor() { 210 return sync_processor_delegate_.Pass(); 211 } 212 213 SyncedNotification* CreateNotification( 214 const std::string& title, 215 const std::string& text, 216 const std::string& app_icon_url, 217 const std::string& image_url, 218 const std::string& app_id, 219 const std::string& key, 220 sync_pb::CoalescedSyncedNotification_ReadState read_state) { 221 SyncData sync_data = CreateSyncData(title, text, app_icon_url, image_url, 222 app_id, key, read_state); 223 // Set enough fields in sync_data, including specifics, for our tests 224 // to pass. 225 return new SyncedNotification(sync_data); 226 } 227 228 // Helper to create syncer::SyncChange. 229 static SyncChange CreateSyncChange( 230 SyncChange::SyncChangeType type, 231 SyncedNotification* notification) { 232 // Take control of the notification to clean it up after we create data 233 // out of it. 234 scoped_ptr<SyncedNotification> scoped_notification(notification); 235 return SyncChange( 236 FROM_HERE, 237 type, 238 ChromeNotifierService::CreateSyncDataFromNotification(*notification)); 239 } 240 241 private: 242 scoped_ptr<syncer::SyncChangeProcessor> sync_processor_; 243 scoped_ptr<syncer::SyncChangeProcessor> sync_processor_delegate_; 244 content::TestBrowserThreadBundle thread_bundle_; 245 246 DISALLOW_COPY_AND_ASSIGN(ChromeNotifierServiceTest); 247 }; 248 249 // TODO(petewil): Add more tests as I add functionalty. Tests are based on 250 // chrome/browser/extensions/app_notification_manager_sync_unittest.cc 251 252 // Create a Notification, convert it to SyncData and convert it back. 253 TEST_F(ChromeNotifierServiceTest, NotificationToSyncDataToNotification) { 254 scoped_ptr<SyncedNotification> notification1( 255 CreateNotification(kTitle1, kText1, kIconUrl1, kImageUrl1, kAppId1, 256 kKey1, kUnread)); 257 SyncData sync_data = 258 ChromeNotifierService::CreateSyncDataFromNotification(*notification1); 259 scoped_ptr<SyncedNotification> notification2( 260 ChromeNotifierService::CreateNotificationFromSyncData(sync_data)); 261 EXPECT_TRUE(notification2.get()); 262 EXPECT_TRUE(notification1->EqualsIgnoringReadState(*notification2)); 263 EXPECT_EQ(notification1->GetReadState(), notification2->GetReadState()); 264 } 265 266 // Model assocation: We have no local data, and no remote data. 267 TEST_F(ChromeNotifierServiceTest, ModelAssocBothEmpty) { 268 StubNotificationUIManager notification_manager; 269 ChromeNotifierService notifier(NULL, ¬ification_manager); 270 271 notifier.MergeDataAndStartSyncing( 272 SYNCED_NOTIFICATIONS, 273 SyncDataList(), // Empty. 274 PassProcessor(), 275 scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); 276 277 EXPECT_EQ(0U, notifier.GetAllSyncData(SYNCED_NOTIFICATIONS).size()); 278 EXPECT_EQ(0U, processor()->change_list_size()); 279 } 280 281 // Process sync changes when there is no local data. 282 TEST_F(ChromeNotifierServiceTest, ProcessSyncChangesEmptyModel) { 283 // We initially have no data. 284 StubNotificationUIManager notification_manager; 285 ChromeNotifierService notifier(NULL, ¬ification_manager); 286 notifier.set_avoid_bitmap_fetching_for_test(true); 287 288 notifier.MergeDataAndStartSyncing( 289 SYNCED_NOTIFICATIONS, 290 SyncDataList(), 291 PassProcessor(), 292 scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); 293 294 // Set up a bunch of ADDs. 295 SyncChangeList changes; 296 changes.push_back(CreateSyncChange( 297 SyncChange::ACTION_ADD, CreateNotification( 298 kTitle1, kText1, kIconUrl1, kImageUrl1, kAppId1, kKey1, kUnread))); 299 changes.push_back(CreateSyncChange( 300 SyncChange::ACTION_ADD, CreateNotification( 301 kTitle2, kText2, kIconUrl2, kImageUrl2, kAppId2, kKey2, kUnread))); 302 changes.push_back(CreateSyncChange( 303 SyncChange::ACTION_ADD, CreateNotification( 304 kTitle3, kText3, kIconUrl3, kImageUrl3, kAppId3, kKey3, kUnread))); 305 306 notifier.ProcessSyncChanges(FROM_HERE, changes); 307 308 EXPECT_EQ(3U, notifier.GetAllSyncData(SYNCED_NOTIFICATIONS).size()); 309 // TODO(petewil): verify that the list entries have expected values to make 310 // this test more robust. 311 } 312 313 // Process sync changes when there is no local data. 314 TEST_F(ChromeNotifierServiceTest, ProcessSyncChangesNonEmptyModel) { 315 StubNotificationUIManager notification_manager; 316 ChromeNotifierService notifier(NULL, ¬ification_manager); 317 notifier.set_avoid_bitmap_fetching_for_test(true); 318 319 // Create some local fake data. 320 scoped_ptr<SyncedNotification> n1(CreateNotification( 321 kTitle1, kText1, kIconUrl1, kImageUrl1, kAppId1, kKey1, kUnread)); 322 notifier.AddForTest(n1.Pass()); 323 scoped_ptr<SyncedNotification> n2(CreateNotification( 324 kTitle2, kText2, kIconUrl2, kImageUrl2, kAppId2, kKey2, kUnread)); 325 notifier.AddForTest(n2.Pass()); 326 scoped_ptr<SyncedNotification> n3(CreateNotification( 327 kTitle3, kText3, kIconUrl3, kImageUrl3, kAppId3, kKey3, kUnread)); 328 notifier.AddForTest(n3.Pass()); 329 330 notifier.MergeDataAndStartSyncing( 331 SYNCED_NOTIFICATIONS, 332 SyncDataList(), 333 PassProcessor(), 334 scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); 335 336 // Set up some ADDs, some UPDATES, and some DELETEs 337 SyncChangeList changes; 338 changes.push_back(CreateSyncChange( 339 SyncChange::ACTION_ADD, CreateNotification( 340 kTitle4, kText4, kIconUrl4, kImageUrl4, kAppId4, kKey4, kUnread))); 341 changes.push_back(CreateSyncChange( 342 SyncChange::ACTION_UPDATE, CreateNotification( 343 kTitle2, kText2, kIconUrl2, kImageUrl2, kAppId2, kKey2, kRead))); 344 changes.push_back(CreateSyncChange( 345 SyncChange::ACTION_DELETE, CreateNotification( 346 kTitle3, kText3, kIconUrl3, kImageUrl3, kAppId3, kKey3, kDismissed))); 347 348 // Simulate incoming new notifications at runtime. 349 notifier.ProcessSyncChanges(FROM_HERE, changes); 350 351 // We should find notifications 1, 2, and 4, but not 3. 352 EXPECT_EQ(3U, notifier.GetAllSyncData(SYNCED_NOTIFICATIONS).size()); 353 EXPECT_TRUE(notifier.FindNotificationById(kKey1)); 354 EXPECT_TRUE(notifier.FindNotificationById(kKey2)); 355 EXPECT_FALSE(notifier.FindNotificationById(kKey3)); 356 EXPECT_TRUE(notifier.FindNotificationById(kKey4)); 357 } 358 359 // Process sync changes that arrive before the change they are supposed to 360 // modify. 361 TEST_F(ChromeNotifierServiceTest, ProcessSyncChangesOutOfOrder) { 362 StubNotificationUIManager notification_manager; 363 ChromeNotifierService notifier(NULL, ¬ification_manager); 364 notifier.set_avoid_bitmap_fetching_for_test(true); 365 366 // Create some local fake data. 367 scoped_ptr<SyncedNotification> n1(CreateNotification( 368 kTitle1, kText1, kIconUrl1, kImageUrl1, kAppId1, kKey1, kUnread)); 369 notifier.AddForTest(n1.Pass()); 370 scoped_ptr<SyncedNotification> n2(CreateNotification( 371 kTitle2, kText2, kIconUrl2, kImageUrl2, kAppId2, kKey2, kUnread)); 372 notifier.AddForTest(n2.Pass()); 373 scoped_ptr<SyncedNotification> n3(CreateNotification( 374 kTitle3, kText3, kIconUrl3, kImageUrl3, kAppId3, kKey3, kUnread)); 375 notifier.AddForTest(n3.Pass()); 376 377 notifier.MergeDataAndStartSyncing( 378 SYNCED_NOTIFICATIONS, 379 SyncDataList(), 380 PassProcessor(), 381 scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); 382 383 SyncChangeList changes; 384 // UPDATE a notification we have not seen an add for. 385 changes.push_back(CreateSyncChange( 386 SyncChange::ACTION_UPDATE, CreateNotification( 387 kTitle4, kText4, kIconUrl4, kImageUrl4, kAppId4, kKey4, kUnread))); 388 // ADD a notification that we already have. 389 changes.push_back(CreateSyncChange( 390 SyncChange::ACTION_ADD, CreateNotification( 391 kTitle2, kText2, kIconUrl2, kImageUrl2, kAppId2, kKey2, kRead))); 392 // DELETE a notification we have not seen yet. 393 changes.push_back(CreateSyncChange( 394 SyncChange::ACTION_DELETE, CreateNotification( 395 kTitle5, kText5, kIconUrl5, kImageUrl5, kAppId5, kKey5, kDismissed))); 396 397 // Simulate incoming new notifications at runtime. 398 notifier.ProcessSyncChanges(FROM_HERE, changes); 399 400 // We should find notifications 1, 2, 3, and 4, but not 5. 401 EXPECT_EQ(4U, notifier.GetAllSyncData(SYNCED_NOTIFICATIONS).size()); 402 EXPECT_TRUE(notifier.FindNotificationById(kKey1)); 403 EXPECT_TRUE(notifier.FindNotificationById(kKey2)); 404 EXPECT_TRUE(notifier.FindNotificationById(kKey3)); 405 EXPECT_TRUE(notifier.FindNotificationById(kKey4)); 406 EXPECT_FALSE(notifier.FindNotificationById(kKey5)); 407 } 408 409 410 // Model has some notifications, some of them are local only. Sync has some 411 // notifications. No items match up. 412 TEST_F(ChromeNotifierServiceTest, LocalRemoteBothNonEmptyNoOverlap) { 413 StubNotificationUIManager notification_manager; 414 ChromeNotifierService notifier(NULL, ¬ification_manager); 415 notifier.set_avoid_bitmap_fetching_for_test(true); 416 417 // Create some local fake data. 418 scoped_ptr<SyncedNotification> n1(CreateNotification( 419 kTitle1, kText1, kIconUrl1, kImageUrl1, kAppId1, kKey1, kUnread)); 420 notifier.AddForTest(n1.Pass()); 421 scoped_ptr<SyncedNotification> n2(CreateNotification( 422 kTitle2, kText2, kIconUrl2, kImageUrl2, kAppId2, kKey2, kUnread)); 423 notifier.AddForTest(n2.Pass()); 424 scoped_ptr<SyncedNotification> n3(CreateNotification( 425 kTitle3, kText3, kIconUrl3, kImageUrl3, kAppId3, kKey3, kUnread)); 426 notifier.AddForTest(n3.Pass()); 427 428 // Create some remote fake data. 429 SyncDataList initial_data; 430 initial_data.push_back(CreateSyncData(kTitle4, kText4, kIconUrl4, kImageUrl4, 431 kAppId4, kKey4, kUnread)); 432 initial_data.push_back(CreateSyncData(kTitle5, kText5, kIconUrl5, kImageUrl5, 433 kAppId5, kKey5, kUnread)); 434 initial_data.push_back(CreateSyncData(kTitle6, kText6, kIconUrl6, kImageUrl6, 435 kAppId6, kKey6, kUnread)); 436 initial_data.push_back(CreateSyncData(kTitle7, kText7, kIconUrl7, kImageUrl7, 437 kAppId7, kKey7, kUnread)); 438 439 // Merge the local and remote data. 440 notifier.MergeDataAndStartSyncing( 441 SYNCED_NOTIFICATIONS, 442 initial_data, 443 PassProcessor(), 444 scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); 445 446 // Ensure the local store now has all local and remote notifications. 447 EXPECT_EQ(7U, notifier.GetAllSyncData(SYNCED_NOTIFICATIONS).size()); 448 EXPECT_TRUE(notifier.FindNotificationById(kKey1)); 449 EXPECT_TRUE(notifier.FindNotificationById(kKey2)); 450 EXPECT_TRUE(notifier.FindNotificationById(kKey3)); 451 EXPECT_TRUE(notifier.FindNotificationById(kKey4)); 452 EXPECT_TRUE(notifier.FindNotificationById(kKey5)); 453 EXPECT_TRUE(notifier.FindNotificationById(kKey6)); 454 EXPECT_TRUE(notifier.FindNotificationById(kKey7)); 455 456 // Test the type conversion and construction functions. 457 for (SyncDataList::const_iterator iter = initial_data.begin(); 458 iter != initial_data.end(); ++iter) { 459 scoped_ptr<SyncedNotification> notification1( 460 ChromeNotifierService::CreateNotificationFromSyncData(*iter)); 461 // TODO(petewil): Revisit this when we add version info to notifications. 462 const std::string& key = notification1->GetKey(); 463 const SyncedNotification* notification2 = 464 notifier.FindNotificationById(key); 465 EXPECT_TRUE(NULL != notification2); 466 EXPECT_TRUE(notification1->EqualsIgnoringReadState(*notification2)); 467 EXPECT_EQ(notification1->GetReadState(), notification2->GetReadState()); 468 } 469 EXPECT_TRUE(notifier.FindNotificationById(kKey1)); 470 EXPECT_TRUE(notifier.FindNotificationById(kKey2)); 471 EXPECT_TRUE(notifier.FindNotificationById(kKey3)); 472 } 473 474 // Test the local store having the read bit unset, the remote store having 475 // it set. 476 TEST_F(ChromeNotifierServiceTest, ModelAssocBothNonEmptyReadMismatch1) { 477 StubNotificationUIManager notification_manager; 478 ChromeNotifierService notifier(NULL, ¬ification_manager); 479 notifier.set_avoid_bitmap_fetching_for_test(true); 480 481 // Create some local fake data. 482 scoped_ptr<SyncedNotification> n1(CreateNotification( 483 kTitle1, kText1, kIconUrl1, kImageUrl1, kAppId1, kKey1, kUnread)); 484 notifier.AddForTest(n1.Pass()); 485 scoped_ptr<SyncedNotification> n2(CreateNotification( 486 kTitle2, kText2, kIconUrl2, kImageUrl2, kAppId2, kKey2, kUnread)); 487 notifier.AddForTest(n2.Pass()); 488 489 // Create some remote fake data, item 1 matches except for the read state. 490 syncer::SyncDataList initial_data; 491 initial_data.push_back(CreateSyncData(kTitle1, kText1, kIconUrl1, kImageUrl1, 492 kAppId1, kKey1, kDismissed)); 493 // Merge the local and remote data. 494 notifier.MergeDataAndStartSyncing( 495 syncer::SYNCED_NOTIFICATIONS, 496 initial_data, 497 PassProcessor(), 498 scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); 499 500 // Ensure the local store still has only two notifications, and the read 501 // state of the first is now read. 502 EXPECT_EQ(2U, notifier.GetAllSyncData(syncer::SYNCED_NOTIFICATIONS).size()); 503 SyncedNotification* notification1 = 504 notifier.FindNotificationById(kKey1); 505 EXPECT_FALSE(NULL == notification1); 506 EXPECT_EQ(SyncedNotification::kDismissed, notification1->GetReadState()); 507 EXPECT_TRUE(notifier.FindNotificationById(kKey2)); 508 EXPECT_FALSE(notifier.FindNotificationById(kKey3)); 509 510 // Make sure that the notification manager was told to dismiss the 511 // notification. 512 EXPECT_EQ(std::string(kKey1), notification_manager.dismissed_id()); 513 514 // Ensure no new data will be sent to the remote store for notification1. 515 EXPECT_EQ(0U, processor()->change_list_size()); 516 EXPECT_FALSE(processor()->ContainsId(kKey1)); 517 } 518 519 // Test when the local store has the read bit set, and the remote store has 520 // it unset. 521 TEST_F(ChromeNotifierServiceTest, ModelAssocBothNonEmptyReadMismatch2) { 522 StubNotificationUIManager notification_manager; 523 ChromeNotifierService notifier(NULL, ¬ification_manager); 524 notifier.set_avoid_bitmap_fetching_for_test(true); 525 526 // Create some local fake data. 527 scoped_ptr<SyncedNotification> n1(CreateNotification( 528 kTitle1, kText1, kIconUrl1, kImageUrl1, kAppId1, kKey1, kDismissed)); 529 notifier.AddForTest(n1.Pass()); 530 scoped_ptr<SyncedNotification> n2(CreateNotification( 531 kTitle2, kText2, kIconUrl2, kImageUrl2, kAppId2, kKey2, kUnread)); 532 notifier.AddForTest(n2.Pass()); 533 534 // Create some remote fake data, item 1 matches except for the read state. 535 syncer::SyncDataList initial_data; 536 initial_data.push_back(CreateSyncData(kTitle1, kText1, kIconUrl1, kImageUrl1, 537 kAppId1, kKey1, kUnread)); 538 // Merge the local and remote data. 539 notifier.MergeDataAndStartSyncing( 540 syncer::SYNCED_NOTIFICATIONS, 541 initial_data, 542 PassProcessor(), 543 scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); 544 545 // Ensure the local store still has only two notifications, and the read 546 // state of the first is now read. 547 EXPECT_EQ(2U, notifier.GetAllSyncData(syncer::SYNCED_NOTIFICATIONS).size()); 548 SyncedNotification* notification1 = 549 notifier.FindNotificationById(kKey1); 550 EXPECT_FALSE(NULL == notification1); 551 EXPECT_EQ(SyncedNotification::kDismissed, notification1->GetReadState()); 552 EXPECT_TRUE(notifier.FindNotificationById(kKey2)); 553 EXPECT_FALSE(notifier.FindNotificationById(kKey3)); 554 555 // Ensure the new data will be sent to the remote store for notification1. 556 EXPECT_EQ(1U, processor()->change_list_size()); 557 EXPECT_TRUE(processor()->ContainsId(kKey1)); 558 EXPECT_EQ(SyncChange::ACTION_UPDATE, processor()->GetChangeById( 559 kKey1).change_type()); 560 } 561 562 // We have a notification in the local store, we get an updated version 563 // of the same notification remotely, it should take precedence. 564 TEST_F(ChromeNotifierServiceTest, ModelAssocBothNonEmptyWithUpdate) { 565 StubNotificationUIManager notification_manager; 566 ChromeNotifierService notifier(NULL, ¬ification_manager); 567 568 // Create some local fake data. 569 scoped_ptr<SyncedNotification> n1(CreateNotification( 570 kTitle1, kText1, kIconUrl1, kImageUrl1, kAppId1, kKey1, kDismissed)); 571 notifier.AddForTest(n1.Pass()); 572 573 // Create some remote fake data, item 1 matches the ID, but has different data 574 syncer::SyncDataList initial_data; 575 initial_data.push_back(CreateSyncData(kTitle2, kText2, kIconUrl2, kImageUrl2, 576 kAppId1, kKey1, kUnread)); 577 // Merge the local and remote data. 578 notifier.MergeDataAndStartSyncing( 579 syncer::SYNCED_NOTIFICATIONS, 580 initial_data, 581 PassProcessor(), 582 scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock())); 583 584 // Ensure the local store still has only one notification 585 EXPECT_EQ(1U, notifier.GetAllSyncData(syncer::SYNCED_NOTIFICATIONS).size()); 586 SyncedNotification* notification1 = 587 notifier.FindNotificationById(kKey1); 588 589 EXPECT_FALSE(NULL == notification1); 590 EXPECT_EQ(SyncedNotification::kUnread, notification1->GetReadState()); 591 EXPECT_EQ(std::string(kTitle2), notification1->GetTitle()); 592 593 // Ensure no new data will be sent to the remote store for notification1. 594 EXPECT_EQ(0U, processor()->change_list_size()); 595 EXPECT_FALSE(processor()->ContainsId(kKey1)); 596 } 597 598 // TODO(petewil): There are more tests to add, such as when we add an API 599 // to allow data entry from the client, we might have a more up to date 600 // item on the client than the server, or we might have a merge conflict. 601