Home | History | Annotate | Download | only in sync_notifier
      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) ? &notification_ : 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, &notification_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, &notification_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, &notification_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, &notification_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, &notification_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, &notification_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, &notification_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, &notification_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