root/chrome/browser/notifications/sync_notifier/chrome_notifier_service_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. GetNotificationId
  2. ProcessSyncChanges
  3. GetAllSyncData
  4. change_list_size
  5. ContainsId
  6. GetChangeById
  7. sync_processor_wrapper_
  8. SetUp
  9. TearDown
  10. notification_manager
  11. processor
  12. PassProcessor
  13. CreateNotification
  14. CreateSyncChange
  15. AddTestingAppInfos
  16. TEST_F
  17. TEST_F
  18. TEST_F
  19. TEST_F
  20. TEST_F
  21. TEST_F
  22. TEST_F
  23. TEST_F
  24. TEST_F
  25. TEST_F
  26. TEST_F
  27. TEST_F
  28. TEST_F
  29. TEST_F

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <map>

#include "base/command_line.h"
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_ptr.h"
#include "base/prefs/pref_service.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/notifications/notification.h"
#include "chrome/browser/notifications/notification_test_util.h"
#include "chrome/browser/notifications/notification_ui_manager.h"
#include "chrome/browser/notifications/sync_notifier/chrome_notifier_service.h"
#include "chrome/browser/notifications/sync_notifier/sync_notifier_test_utils.h"
#include "chrome/browser/notifications/sync_notifier/synced_notification.h"
#include "chrome/browser/notifications/sync_notifier/synced_notification_app_info.h"
#include "chrome/browser/notifications/sync_notifier/synced_notification_app_info_service.h"
#include "chrome/browser/notifications/sync_notifier/synced_notification_app_info_service_factory.h"
#include "chrome/browser/prefs/browser_prefs.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_pref_service_syncable.h"
#include "chrome/test/base/testing_profile.h"
#include "components/user_prefs/pref_registry_syncable.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "sync/api/sync_change.h"
#include "sync/api/sync_change_processor.h"
#include "sync/api/sync_change_processor_wrapper_for_test.h"
#include "sync/api/sync_error_factory.h"
#include "sync/api/sync_error_factory_mock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/message_center/message_center_util.h"

using sync_pb::SyncedNotificationSpecifics;
using sync_pb::EntitySpecifics;
using syncer::SyncData;
using syncer::SyncChange;
using syncer::SyncChangeList;
using syncer::SyncChangeProcessorWrapperForTest;
using syncer::SyncDataList;
using syncer::SYNCED_NOTIFICATIONS;
using notifier::SyncedNotification;
using notifier::ChromeNotifierService;

namespace {

// Extract notification id from syncer::SyncData.
std::string GetNotificationId(const SyncData& sync_data) {
  SyncedNotificationSpecifics specifics = sync_data.GetSpecifics().
      synced_notification();

  return specifics.coalesced_notification().key();
}

}  // namespace

namespace notifier {

// Dummy SyncChangeProcessor used to help review what SyncChanges are pushed
// back up to Sync.
class TestChangeProcessor : public syncer::SyncChangeProcessor {
 public:
  TestChangeProcessor() { }
  virtual ~TestChangeProcessor() { }

  // Store a copy of all the changes passed in so we can examine them later.
  virtual syncer::SyncError ProcessSyncChanges(
      const tracked_objects::Location& from_here,
      const SyncChangeList& change_list) OVERRIDE {
    change_map_.clear();
    for (SyncChangeList::const_iterator iter = change_list.begin();
        iter != change_list.end(); ++iter) {
      // Put the data into the change tracking map.
      change_map_[GetNotificationId(iter->sync_data())] = *iter;
    }

    return syncer::SyncError();
  }

  virtual syncer::SyncDataList GetAllSyncData(syncer::ModelType type) const
      OVERRIDE {
    return syncer::SyncDataList();
  }

  size_t change_list_size() { return change_map_.size(); }

  bool ContainsId(const std::string& id) {
    return change_map_.find(id) != change_map_.end();
  }

  SyncChange GetChangeById(const std::string& id) {
    EXPECT_TRUE(ContainsId(id));
    return change_map_[id];
  }

 private:
  // Track the changes received in ProcessSyncChanges.
  std::map<std::string, SyncChange> change_map_;

  DISALLOW_COPY_AND_ASSIGN(TestChangeProcessor);
};

class ChromeNotifierServiceTest : public testing::Test {
 public:
  ChromeNotifierServiceTest()
      : sync_processor_(new TestChangeProcessor),
        sync_processor_wrapper_(
            new SyncChangeProcessorWrapperForTest(sync_processor_.get())) {}
  virtual ~ChromeNotifierServiceTest() {}

  // Methods from testing::Test.
  virtual void SetUp() {
    // These tests rely on synced notifications being active.  Some testers
    // report the channel as STABLE so we need to manually enable it.
    // See crbug.com/338426 for details.
    CommandLine::ForCurrentProcess()->AppendSwitch(
        switches::kEnableSyncSyncedNotifications);

    // Prevent test code from trying to go to the network.
    ChromeNotifierService::set_avoid_bitmap_fetching_for_test(true);
    notification_manager_.reset(new StubNotificationUIManager(GURL(
        kSyncedNotificationsWelcomeOrigin)));

    // Set up a profile for the unit tests to use.
    profile_.reset(new TestingProfile());

    // Set up the testing SyncedNotificationAppInfoService with some test data.
    AddTestingAppInfos();
  }

  virtual void TearDown() {
    notification_manager_.reset();
  }

  StubNotificationUIManager* notification_manager() {
    return notification_manager_.get();
  }

  TestChangeProcessor* processor() {
    return static_cast<TestChangeProcessor*>(sync_processor_.get());
  }

  scoped_ptr<syncer::SyncChangeProcessor> PassProcessor() {
    return sync_processor_wrapper_.Pass();
  }

  SyncedNotification* CreateNotification(
      const std::string& title,
      const std::string& text,
      const std::string& app_icon_url,
      const std::string& image_url,
      const std::string& app_id,
      const std::string& key,
      sync_pb::CoalescedSyncedNotification_ReadState read_state) {
    SyncData sync_data = CreateSyncData(title, text, app_icon_url, image_url,
                                        app_id, key, read_state);
    // Set enough fields in sync_data, including specifics, for our tests
    // to pass.
    return new SyncedNotification(sync_data, NULL, notification_manager_.get());
  }

  // Helper to create syncer::SyncChange.
  static SyncChange CreateSyncChange(
      SyncChange::SyncChangeType type,
      SyncedNotification* notification) {
    // Take control of the notification to clean it up after we create data
    // out of it.
    scoped_ptr<SyncedNotification> scoped_notification(notification);
    return SyncChange(
        FROM_HERE,
        type,
        ChromeNotifierService::CreateSyncDataFromNotification(*notification));
  }

  void AddTestingAppInfos() {
    // Get the SyncedNotificationAppInfoService from the browser object.
    SyncedNotificationAppInfoService* synced_notification_app_info_service =
        SyncedNotificationAppInfoServiceFactory::GetForProfile(
            profile_.get(), Profile::EXPLICIT_ACCESS);

    // Create a notification to add.
    // The sending_service_infos_ list will take ownership of this pointer.
    scoped_ptr<SyncedNotificationAppInfo> test_service1(
        new SyncedNotificationAppInfo(profile_.get(),
                                      std::string(kSendingService1Name),
                                      synced_notification_app_info_service));

    // Add some App IDs.
    test_service1->AddAppId(kAppId1);
    test_service1->AddAppId(kAppId2);

    // Set this icon's GURLs.
    test_service1->SetSettingsURLs(GURL(kTestIconUrl), GURL());

    // Call AddForTest.
    synced_notification_app_info_service->AddForTest(test_service1.Pass());
  }

 protected:
  scoped_ptr<TestingProfile> profile_;

 private:
  scoped_ptr<syncer::SyncChangeProcessor> sync_processor_;
  scoped_ptr<syncer::SyncChangeProcessor> sync_processor_wrapper_;
  scoped_ptr<StubNotificationUIManager> notification_manager_;
  content::TestBrowserThreadBundle thread_bundle_;

  DISALLOW_COPY_AND_ASSIGN(ChromeNotifierServiceTest);
};

// Create a Notification, convert it to SyncData and convert it back.
TEST_F(ChromeNotifierServiceTest, NotificationToSyncDataToNotification) {
  ChromeNotifierService notifier(profile_.get(), notification_manager());

  scoped_ptr<SyncedNotification> notification1(
      CreateNotification(kTitle1, kText1, kIconUrl1, kImageUrl1, kAppId1,
                         kKey1, kUnread));
  SyncData sync_data =
      ChromeNotifierService::CreateSyncDataFromNotification(*notification1);
  scoped_ptr<SyncedNotification> notification2(
      notifier.CreateNotificationFromSyncData(sync_data));
  EXPECT_TRUE(notification2.get());
  EXPECT_TRUE(notification1->EqualsIgnoringReadState(*notification2));
  EXPECT_EQ(notification1->GetReadState(), notification2->GetReadState());
}

// Model assocation:  We have no local data, and no remote data.
TEST_F(ChromeNotifierServiceTest, ModelAssocBothEmpty) {

  ChromeNotifierService notifier(profile_.get(), notification_manager());

  notifier.MergeDataAndStartSyncing(
      SYNCED_NOTIFICATIONS,
      SyncDataList(),  // Empty.
      PassProcessor(),
      scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock()));

  EXPECT_EQ(0U, notifier.GetAllSyncData(SYNCED_NOTIFICATIONS).size());
  EXPECT_EQ(0U, processor()->change_list_size());
}

// Process sync changes when there is no local data.
TEST_F(ChromeNotifierServiceTest, ProcessSyncChangesEmptyModel) {
  // We initially have no data.
  ChromeNotifierService notifier(profile_.get(), notification_manager());
  notifier.set_avoid_bitmap_fetching_for_test(true);

  notifier.MergeDataAndStartSyncing(
      SYNCED_NOTIFICATIONS,
      SyncDataList(),
      PassProcessor(),
      scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock()));

  // Set up a bunch of ADDs.
  SyncChangeList changes;
  changes.push_back(CreateSyncChange(
      SyncChange::ACTION_ADD, CreateNotification(
          kTitle1, kText1, kIconUrl1, kImageUrl1, kAppId1, kKey1, kUnread)));
  changes.push_back(CreateSyncChange(
      SyncChange::ACTION_ADD, CreateNotification(
          kTitle2, kText2, kIconUrl2, kImageUrl2, kAppId2, kKey2, kUnread)));
  changes.push_back(CreateSyncChange(
      SyncChange::ACTION_ADD, CreateNotification(
          kTitle3, kText3, kIconUrl3, kImageUrl3, kAppId3, kKey3, kUnread)));

  notifier.ProcessSyncChanges(FROM_HERE, changes);

  EXPECT_EQ(3U, notifier.GetAllSyncData(SYNCED_NOTIFICATIONS).size());
  // TODO(petewil): verify that the list entries have expected values to make
  // this test more robust.
}

// Process sync changes when there is local data.
TEST_F(ChromeNotifierServiceTest, ProcessSyncChangesNonEmptyModel) {
  ChromeNotifierService notifier(profile_.get(), notification_manager());
  notifier.set_avoid_bitmap_fetching_for_test(true);

  // Create some local fake data.
  scoped_ptr<SyncedNotification> n1(CreateNotification(
      kTitle1, kText1, kIconUrl1, kImageUrl1, kAppId1, kKey1, kUnread));
  notifier.AddForTest(n1.Pass());
  scoped_ptr<SyncedNotification> n2(CreateNotification(
      kTitle2, kText2, kIconUrl2, kImageUrl2, kAppId2, kKey2, kUnread));
  notifier.AddForTest(n2.Pass());
  scoped_ptr<SyncedNotification> n3(CreateNotification(
      kTitle3, kText3, kIconUrl3, kImageUrl3, kAppId3, kKey3, kUnread));
  notifier.AddForTest(n3.Pass());

  notifier.MergeDataAndStartSyncing(
      SYNCED_NOTIFICATIONS,
      SyncDataList(),
      PassProcessor(),
      scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock()));

  // Set up some ADDs, some UPDATES, and some DELETEs
  SyncChangeList changes;
  changes.push_back(CreateSyncChange(
      SyncChange::ACTION_ADD, CreateNotification(
          kTitle4, kText4, kIconUrl4, kImageUrl4, kAppId4, kKey4, kUnread)));
  changes.push_back(CreateSyncChange(
      SyncChange::ACTION_UPDATE, CreateNotification(
          kTitle2, kText2, kIconUrl2, kImageUrl2, kAppId2, kKey2, kRead)));
  changes.push_back(CreateSyncChange(
      SyncChange::ACTION_DELETE, CreateNotification(
          kTitle3, kText3, kIconUrl3, kImageUrl3, kAppId3, kKey3, kDismissed)));

  // Simulate incoming new notifications at runtime.
  notifier.ProcessSyncChanges(FROM_HERE, changes);

  // We should find notifications 1, 2, and 4, but not 3.
  EXPECT_EQ(3U, notifier.GetAllSyncData(SYNCED_NOTIFICATIONS).size());
  EXPECT_TRUE(notifier.FindNotificationById(kKey1));
  EXPECT_TRUE(notifier.FindNotificationById(kKey2));
  EXPECT_FALSE(notifier.FindNotificationById(kKey3));
  EXPECT_TRUE(notifier.FindNotificationById(kKey4));

  // Verify that the first run preference is now set to false.
  bool first_run = notifier.profile()->GetPrefs()->GetBoolean(
      prefs::kSyncedNotificationFirstRun);
  EXPECT_NE(true, first_run);
}

// Process sync changes that arrive before the change they are supposed to
// modify.
TEST_F(ChromeNotifierServiceTest, ProcessSyncChangesOutOfOrder) {
  ChromeNotifierService notifier(profile_.get(), notification_manager());
  notifier.set_avoid_bitmap_fetching_for_test(true);

  // Create some local fake data.
  scoped_ptr<SyncedNotification> n1(CreateNotification(
      kTitle1, kText1, kIconUrl1, kImageUrl1, kAppId1, kKey1, kUnread));
  notifier.AddForTest(n1.Pass());
  scoped_ptr<SyncedNotification> n2(CreateNotification(
      kTitle2, kText2, kIconUrl2, kImageUrl2, kAppId2, kKey2, kUnread));
  notifier.AddForTest(n2.Pass());
  scoped_ptr<SyncedNotification> n3(CreateNotification(
      kTitle3, kText3, kIconUrl3, kImageUrl3, kAppId3, kKey3, kUnread));
  notifier.AddForTest(n3.Pass());

  notifier.MergeDataAndStartSyncing(
      SYNCED_NOTIFICATIONS,
      SyncDataList(),
      PassProcessor(),
      scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock()));

  SyncChangeList changes;
  // UPDATE a notification we have not seen an add for.
  changes.push_back(CreateSyncChange(
      SyncChange::ACTION_UPDATE, CreateNotification(
          kTitle4, kText4, kIconUrl4, kImageUrl4, kAppId4, kKey4, kUnread)));
  // ADD a notification that we already have.
  changes.push_back(CreateSyncChange(
      SyncChange::ACTION_ADD, CreateNotification(
          kTitle2, kText2, kIconUrl2, kImageUrl2, kAppId2, kKey2, kRead)));
  // DELETE a notification we have not seen yet.
  changes.push_back(CreateSyncChange(
      SyncChange::ACTION_DELETE, CreateNotification(
          kTitle5, kText5, kIconUrl5, kImageUrl5, kAppId5, kKey5, kDismissed)));

  // Simulate incoming new notifications at runtime.
  notifier.ProcessSyncChanges(FROM_HERE, changes);

  // We should find notifications 1, 2, 3, and 4, but not 5.
  EXPECT_EQ(4U, notifier.GetAllSyncData(SYNCED_NOTIFICATIONS).size());
  EXPECT_TRUE(notifier.FindNotificationById(kKey1));
  EXPECT_TRUE(notifier.FindNotificationById(kKey2));
  EXPECT_TRUE(notifier.FindNotificationById(kKey3));
  EXPECT_TRUE(notifier.FindNotificationById(kKey4));
  EXPECT_FALSE(notifier.FindNotificationById(kKey5));
}


// Model has some notifications, some of them are local only. Sync has some
// notifications. No items match up.
TEST_F(ChromeNotifierServiceTest, LocalRemoteBothNonEmptyNoOverlap) {
  ChromeNotifierService notifier(profile_.get(), notification_manager());
  notifier.set_avoid_bitmap_fetching_for_test(true);

  // Create some local fake data.
  scoped_ptr<SyncedNotification> n1(CreateNotification(
      kTitle1, kText1, kIconUrl1, kImageUrl1, kAppId1, kKey1, kUnread));
  notifier.AddForTest(n1.Pass());
  scoped_ptr<SyncedNotification> n2(CreateNotification(
      kTitle2, kText2, kIconUrl2, kImageUrl2, kAppId2, kKey2, kUnread));
  notifier.AddForTest(n2.Pass());
  scoped_ptr<SyncedNotification> n3(CreateNotification(
      kTitle3, kText3, kIconUrl3, kImageUrl3, kAppId3, kKey3, kUnread));
  notifier.AddForTest(n3.Pass());

  // Create some remote fake data.
  SyncDataList initial_data;
  initial_data.push_back(CreateSyncData(kTitle4, kText4, kIconUrl4, kImageUrl4,
                                        kAppId4, kKey4, kUnread));
  initial_data.push_back(CreateSyncData(kTitle5, kText5, kIconUrl5, kImageUrl5,
                                        kAppId5, kKey5, kUnread));
  initial_data.push_back(CreateSyncData(kTitle6, kText6, kIconUrl6, kImageUrl6,
                                        kAppId6, kKey6, kUnread));
  initial_data.push_back(CreateSyncData(kTitle7, kText7, kIconUrl7, kImageUrl7,
                                        kAppId7, kKey7, kUnread));

  // Merge the local and remote data.
  notifier.MergeDataAndStartSyncing(
      SYNCED_NOTIFICATIONS,
      initial_data,
      PassProcessor(),
      scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock()));

  // Ensure the local store now has all local and remote notifications.
  EXPECT_EQ(7U, notifier.GetAllSyncData(SYNCED_NOTIFICATIONS).size());
  EXPECT_TRUE(notifier.FindNotificationById(kKey1));
  EXPECT_TRUE(notifier.FindNotificationById(kKey2));
  EXPECT_TRUE(notifier.FindNotificationById(kKey3));
  EXPECT_TRUE(notifier.FindNotificationById(kKey4));
  EXPECT_TRUE(notifier.FindNotificationById(kKey5));
  EXPECT_TRUE(notifier.FindNotificationById(kKey6));
  EXPECT_TRUE(notifier.FindNotificationById(kKey7));

  // Test the type conversion and construction functions.
  for (SyncDataList::const_iterator iter = initial_data.begin();
      iter != initial_data.end(); ++iter) {
    scoped_ptr<SyncedNotification> notification1(
        notifier.CreateNotificationFromSyncData(*iter));
    // TODO(petewil): Revisit this when we add version info to notifications.
    const std::string& key = notification1->GetKey();
    const SyncedNotification* notification2 =
        notifier.FindNotificationById(key);
    EXPECT_TRUE(NULL != notification2);
    EXPECT_TRUE(notification1->EqualsIgnoringReadState(*notification2));
    EXPECT_EQ(notification1->GetReadState(), notification2->GetReadState());
  }
  EXPECT_TRUE(notifier.FindNotificationById(kKey1));
  EXPECT_TRUE(notifier.FindNotificationById(kKey2));
  EXPECT_TRUE(notifier.FindNotificationById(kKey3));
}

// Test the local store having the read bit unset, the remote store having
// it set.
TEST_F(ChromeNotifierServiceTest, ModelAssocBothNonEmptyReadMismatch1) {
  ChromeNotifierService notifier(profile_.get(), notification_manager());
  notifier.set_avoid_bitmap_fetching_for_test(true);

  // Create some local fake data.
  scoped_ptr<SyncedNotification> n1(CreateNotification(
      kTitle1, kText1, kIconUrl1, kImageUrl1, kAppId1, kKey1, kUnread));
  notifier.AddForTest(n1.Pass());
  scoped_ptr<SyncedNotification> n2(CreateNotification(
      kTitle2, kText2, kIconUrl2, kImageUrl2, kAppId2, kKey2, kUnread));
  notifier.AddForTest(n2.Pass());

  // Create some remote fake data, item 1 matches except for the read state.
  syncer::SyncDataList initial_data;
  initial_data.push_back(CreateSyncData(kTitle1, kText1, kIconUrl1, kImageUrl1,
                                        kAppId1, kKey1, kDismissed));
  // Merge the local and remote data.
  notifier.MergeDataAndStartSyncing(
      syncer::SYNCED_NOTIFICATIONS,
      initial_data,
      PassProcessor(),
      scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock()));

  // Ensure the local store still has only two notifications, and the read
  // state of the first is now read.
  EXPECT_EQ(2U, notifier.GetAllSyncData(syncer::SYNCED_NOTIFICATIONS).size());
  SyncedNotification* notification1 =
      notifier.FindNotificationById(kKey1);
  EXPECT_FALSE(NULL == notification1);
  EXPECT_EQ(SyncedNotification::kDismissed, notification1->GetReadState());
  EXPECT_TRUE(notifier.FindNotificationById(kKey2));
  EXPECT_FALSE(notifier.FindNotificationById(kKey3));

  // Make sure that the notification manager was told to dismiss the
  // notification.
  EXPECT_EQ(std::string(kKey1), notification_manager()->dismissed_id());

  // Ensure no new data will be sent to the remote store for notification1.
  EXPECT_EQ(0U, processor()->change_list_size());
  EXPECT_FALSE(processor()->ContainsId(kKey1));
}

// Test when the local store has the read bit set, and the remote store has
// it unset.
TEST_F(ChromeNotifierServiceTest, ModelAssocBothNonEmptyReadMismatch2) {
  ChromeNotifierService notifier(profile_.get(), notification_manager());
  notifier.set_avoid_bitmap_fetching_for_test(true);

  // Create some local fake data.
  scoped_ptr<SyncedNotification> n1(CreateNotification(
      kTitle1, kText1, kIconUrl1, kImageUrl1, kAppId1, kKey1, kDismissed));
  notifier.AddForTest(n1.Pass());
  scoped_ptr<SyncedNotification> n2(CreateNotification(
      kTitle2, kText2, kIconUrl2, kImageUrl2, kAppId2, kKey2, kUnread));
  notifier.AddForTest(n2.Pass());

  // Create some remote fake data, item 1 matches except for the read state.
  syncer::SyncDataList initial_data;
  initial_data.push_back(CreateSyncData(kTitle1, kText1, kIconUrl1, kImageUrl1,
                                        kAppId1, kKey1, kUnread));
  // Merge the local and remote data.
  notifier.MergeDataAndStartSyncing(
      syncer::SYNCED_NOTIFICATIONS,
      initial_data,
      PassProcessor(),
      scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock()));

  // Ensure the local store still has only two notifications, and the read
  // state of the first is now read.
  EXPECT_EQ(2U, notifier.GetAllSyncData(syncer::SYNCED_NOTIFICATIONS).size());
  SyncedNotification* notification1 =
      notifier.FindNotificationById(kKey1);
  EXPECT_FALSE(NULL == notification1);
  EXPECT_EQ(SyncedNotification::kDismissed, notification1->GetReadState());
  EXPECT_TRUE(notifier.FindNotificationById(kKey2));
  EXPECT_FALSE(notifier.FindNotificationById(kKey3));

  // Ensure the new data will be sent to the remote store for notification1.
  EXPECT_EQ(1U, processor()->change_list_size());
  EXPECT_TRUE(processor()->ContainsId(kKey1));
  EXPECT_EQ(SyncChange::ACTION_UPDATE, processor()->GetChangeById(
      kKey1).change_type());
}

// We have a notification in the local store, we get an updated version
// of the same notification remotely, it should take precedence.
TEST_F(ChromeNotifierServiceTest, ModelAssocBothNonEmptyWithUpdate) {
  ChromeNotifierService notifier(profile_.get(), notification_manager());

  // Create some local fake data.
  scoped_ptr<SyncedNotification> n1(CreateNotification(
      kTitle1, kText1, kIconUrl1, kImageUrl1, kAppId1, kKey1, kDismissed));
  notifier.AddForTest(n1.Pass());

  // Create some remote fake data, item 1 matches the ID, but has different data
  syncer::SyncDataList initial_data;
  initial_data.push_back(CreateSyncData(kTitle2, kText2, kIconUrl2, kImageUrl2,
                                        kAppId1, kKey1, kUnread));
  // Merge the local and remote data.
  notifier.MergeDataAndStartSyncing(
      syncer::SYNCED_NOTIFICATIONS,
      initial_data,
      PassProcessor(),
      scoped_ptr<syncer::SyncErrorFactory>(new syncer::SyncErrorFactoryMock()));

  // Ensure the local store still has only one notification
  EXPECT_EQ(1U, notifier.GetAllSyncData(syncer::SYNCED_NOTIFICATIONS).size());
  SyncedNotification* notification1 =
      notifier.FindNotificationById(kKey1);

  EXPECT_FALSE(NULL == notification1);
  EXPECT_EQ(SyncedNotification::kUnread, notification1->GetReadState());
  EXPECT_EQ(std::string(kTitle2), notification1->GetTitle());

  // Ensure no new data will be sent to the remote store for notification1.
  EXPECT_EQ(0U, processor()->change_list_size());
  EXPECT_FALSE(processor()->ContainsId(kKey1));
}

TEST_F(ChromeNotifierServiceTest, ServiceEnabledTest) {
  ChromeNotifierService notifier(profile_.get(), notification_manager());
  std::set<std::string>::iterator iter;
  std::string first_synced_notification_service_id(kSendingService1Name);

  // Create some local fake data.
  scoped_ptr<SyncedNotification> n1(CreateNotification(
      kTitle1, kText1, kIconUrl1, kImageUrl1, kAppId1, kKey1, kUnread));
  notifier.AddForTest(n1.Pass());

  // Enable the service and ensure the service is in the list.
  // Initially the service starts in the disabled state.
  notifier.OnSyncedNotificationServiceEnabled(kSendingService1Name, true);
  iter = find(notifier.enabled_sending_services_.begin(),
              notifier.enabled_sending_services_.end(),
              first_synced_notification_service_id);

  EXPECT_NE(notifier.enabled_sending_services_.end(), iter);

  // TODO(petewil): Verify Display gets called too.
  // Disable the service and ensure it is gone from the list and the
  // notification_manager.
  notifier.OnSyncedNotificationServiceEnabled(kSendingService1Name, false);
  iter = find(notifier.enabled_sending_services_.begin(),
              notifier.enabled_sending_services_.end(),
              first_synced_notification_service_id);

  EXPECT_EQ(notifier.enabled_sending_services_.end(), iter);
  EXPECT_EQ(notification_manager()->dismissed_id(), std::string(kKey1));
}

// http://crbug.com/341326
#if defined(TOOLKIT_GTK)
#define MAYBE_AddNewSendingServicesTest DISABLED_AddNewSendingServicesTest
#else
#define MAYBE_AddNewSendingServicesTest AddNewSendingServicesTest
#endif
TEST_F(ChromeNotifierServiceTest, MAYBE_AddNewSendingServicesTest) {
  // This test will see if we get a new sending service after the first
  // notification for that service.
  ChromeNotifierService notifier(profile_.get(), notification_manager());
  notifier.set_avoid_bitmap_fetching_for_test(true);

  // We initially have no data.
  EXPECT_EQ(0U, notifier.enabled_sending_services_.size());
  EXPECT_EQ(0U, notifier.GetAllSyncData(SYNCED_NOTIFICATIONS).size());

  // Set up an ADD.
  SyncChangeList changes;
  changes.push_back(
      CreateSyncChange(SyncChange::ACTION_ADD,
                       CreateNotification(kTitle1,
                                          kText1,
                                          kIconUrl1,
                                          kImageUrl1,
                                          kAppId1,
                                          kKey1,
                                          kUnread)));

  notifier.ProcessSyncChanges(FROM_HERE, changes);

  EXPECT_EQ(1U, notifier.GetAllSyncData(SYNCED_NOTIFICATIONS).size());

  // Verify that the first synced notification service is enabled in memory.
  std::set<std::string>::iterator iter;
  std::string first_notification_service_id(kSendingService1Name);
  iter = find(notifier.enabled_sending_services_.begin(),
              notifier.enabled_sending_services_.end(),
              first_notification_service_id);

  EXPECT_NE(notifier.enabled_sending_services_.end(), iter);

  // We should have gotten the synced notification and a welcome notification.
  EXPECT_EQ(2U, notification_manager()->added_notifications());
  EXPECT_TRUE(notification_manager()->welcomed());

  changes.clear();
  changes.push_back(
      CreateSyncChange(SyncChange::ACTION_ADD,
                       CreateNotification(kTitle2,
                                          kText2,
                                          kIconUrl2,
                                          kImageUrl2,
                                          kAppId1,
                                          kKey2,
                                          kUnread)));
  notifier.ProcessSyncChanges(FROM_HERE, changes);

  // But adding another notification should not cause another welcome.
  EXPECT_EQ(3U, notification_manager()->added_notifications());
}

TEST_F(ChromeNotifierServiceTest, CheckInitializedServicesTest) {
  // This test will see if we get a new sending service after the first
  // notification for that service.
  ChromeNotifierService notifier(profile_.get(), notification_manager());
  notifier.set_avoid_bitmap_fetching_for_test(true);

  // Initialize but do not enable the sending service.
  notifier.initialized_sending_services_.insert(kSendingService1Name);
  ASSERT_EQ(0U, notifier.enabled_sending_services_.size());
  ASSERT_EQ(1U, notifier.initialized_sending_services_.size());

  // We initially have no data.
  EXPECT_EQ(0U, notifier.enabled_sending_services_.size());
  EXPECT_EQ(0U, notifier.GetAllSyncData(SYNCED_NOTIFICATIONS).size());

  // Set up an ADD.
  std::string first_synced_notification_service_id(kSendingService1Name);

  SyncChangeList changes;
  changes.push_back(
      CreateSyncChange(SyncChange::ACTION_ADD,
                       CreateNotification(kTitle1,
                                          kText1,
                                          kIconUrl1,
                                          kImageUrl1,
                                          kAppId1,
                                          kKey1,
                                          kUnread)));

  notifier.ProcessSyncChanges(FROM_HERE, changes);

  // Since we added to |initialized_sending_services_| before receiving the
  // synced notification, we should not have enabled this service while
  // processing the sync change.
  EXPECT_EQ(0U, notifier.enabled_sending_services_.size());
  EXPECT_EQ(0U, notification_manager()->added_notifications());
}

#if defined(TOOLKIT_GTK)
// TODO(petewil): crbug.com/358031
#define MAYBE_SetAddedAppIdsTest DISABLED_SetAddedAppIdsTest
#else
#define MAYBE_SetAddedAppIdsTest SetAddedAppIdsTest
#endif
TEST_F(ChromeNotifierServiceTest, MAYBE_SetAddedAppIdsTest) {
  ChromeNotifierService notifier(profile_.get(), notification_manager());
  notifier.set_avoid_bitmap_fetching_for_test(true);

  // Add some notifications to our notification list.
  scoped_ptr<SyncedNotification> n1(CreateNotification(
      kTitle1, kText1, kIconUrl1, kImageUrl1, kAppId1, kKey1, kUnread));
  n1->SetNotifierServiceForTest(&notifier);
  notifier.AddForTest(n1.Pass());

  EXPECT_EQ(static_cast<size_t>(0),
            notification_manager()->added_notifications());

  // Call SetAddedAppIds.
  std::vector<std::string> added_app_ids;
  added_app_ids.push_back(std::string(kAppId1));
  notifier.OnAddedAppIds(added_app_ids);

  // Verify the notification was added by the notification_manager.
  // We see one welcome notification and one new notification.
  EXPECT_EQ(static_cast<size_t>(2),
            notification_manager()->added_notifications());
}

TEST_F(ChromeNotifierServiceTest, SetRemovedAppIdsTest) {
  ChromeNotifierService notifier(profile_.get(), notification_manager());
  notifier.set_avoid_bitmap_fetching_for_test(true);

  // Add some notifications to our notification list.
  scoped_ptr<SyncedNotification> n1(CreateNotification(
      kTitle1, kText1, kIconUrl1, kImageUrl1, kAppId1, kKey1, kUnread));
  notifier.AddForTest(n1.Pass());

  // Call SetRemovedAppIds.
  std::vector<std::string> removed_app_ids;
  removed_app_ids.push_back(std::string(kAppId1));
  notifier.OnRemovedAppIds(removed_app_ids);

  // Verify the notification was "removed" in the notification manager.
  EXPECT_EQ(std::string(kKey1), notification_manager()->dismissed_id());
}

// TODO(petewil): Add a test that we do *not* get a welcome dialog unless we
// have a valid app info for the notification.

}  // namespace notifier

/* [<][>][^][v][top][bottom][index][help] */