Chromium Code Reviews| Index: chrome/browser/notifier/chrome_notifier_service_unittest.cc |
| diff --git a/chrome/browser/notifier/chrome_notifier_service_unittest.cc b/chrome/browser/notifier/chrome_notifier_service_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..a718ad10b71f5282ab9778091290b3360a721a80 |
| --- /dev/null |
| +++ b/chrome/browser/notifier/chrome_notifier_service_unittest.cc |
| @@ -0,0 +1,306 @@ |
| +// 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/memory/scoped_ptr.h" |
| +#include "chrome/browser/notifier/chrome_notifier_service.h" |
| +#include "chrome/browser/notifier/synced_notification.h" |
| +#include "sync/api/sync_change.h" |
| +#include "sync/api/sync_change_processor.h" |
| +#include "sync/api/sync_error_factory.h" |
| +#include "sync/api/sync_error_factory_mock.h" |
| +#include "sync/protocol/sync.pb.h" |
| +#include "sync/protocol/synced_notification_specifics.pb.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +using namespace sync_pb; |
| +using namespace syncer; |
| + |
| +namespace { |
| + |
| + |
|
dcheng
2013/01/07 20:42:10
Extra newline. Please remove.
Pete Williamson
2013/01/18 18:58:39
Done.
|
| +const char MY_APP_ID[] = "fboilmbenheemaomgaeehigklolhkhnf"; |
|
dcheng
2013/01/07 20:42:10
Wrong naming convention.
Pete Williamson
2013/01/18 18:58:39
Done.
|
| +const char COALESCING_KEY[] = "foo"; |
| +const unsigned long long FAKE_CREATION_TIME = 42; |
| + |
| +// Extract notification id from syncer::SyncData. |
| +std::string GetId(const SyncData& sync_data) { |
| + SyncedNotificationSpecifics specifics = sync_data.GetSpecifics(). |
| + synced_notification(); |
| + return specifics.coalesced_notification().notification(0).external_id(); |
| +} |
| + |
| +// Dummy SyncChangeProcessor used to help review what SyncChanges are pushed |
| +// back up to Sync. |
| +class TestChangeProcessor : public SyncChangeProcessor { |
| + public: |
| + TestChangeProcessor() { } |
| + virtual ~TestChangeProcessor() { } |
| + |
| + // Store a copy of all the changes passed in so we can examine them later. |
| + virtual SyncError ProcessSyncChanges( |
| + const tracked_objects::Location& from_here, |
| + const SyncChangeList& change_list) { |
| + change_map_.erase(change_map_.begin(), change_map_.end()); |
| + for (SyncChangeList::const_iterator iter = change_list.begin(); |
| + iter != change_list.end(); ++iter) { |
| + // Put the data into the change tracking map. |
| + change_map_[GetId(iter->sync_data())] = *iter; |
| + } |
| + |
| + return SyncError(); |
| + } |
| + |
| + 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) { |
| + DCHECK(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 SyncChangeProcessorDelegate : public SyncChangeProcessor { |
| + public: |
| + explicit SyncChangeProcessorDelegate(SyncChangeProcessor* recipient) |
| + : recipient_(recipient) { |
| + DCHECK(recipient_); |
| + } |
| + virtual ~SyncChangeProcessorDelegate() {} |
| + |
| + // syncer::SyncChangeProcessor implementation. |
| + virtual SyncError ProcessSyncChanges( |
| + const tracked_objects::Location& from_here, |
| + const SyncChangeList& change_list) OVERRIDE { |
| + return recipient_->ProcessSyncChanges(from_here, change_list); |
| + } |
| + |
| + private: |
| + // The recipient of all sync changes. |
| + SyncChangeProcessor* recipient_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(SyncChangeProcessorDelegate); |
| +}; |
| + |
| +} // namespace |
| + |
| +class ChromeNotifierServiceTest : public testing::Test { |
| + |
| + public: |
| + ChromeNotifierServiceTest() |
| + : sync_processor_(new TestChangeProcessor), |
| + sync_processor_delegate_(new SyncChangeProcessorDelegate( |
| + sync_processor_.get())) {} |
| + ~ChromeNotifierServiceTest() {} |
| + |
| + // Methods from testing::Test. |
| + |
| + virtual void SetUp() { |
| + } |
| + |
| + virtual void TearDown() { |
| + } |
| + |
| + TestChangeProcessor* processor() { return sync_processor_.get(); } |
| + |
| + scoped_ptr<SyncChangeProcessor> PassProcessor() { |
| + return sync_processor_delegate_.PassAs<SyncChangeProcessor>(); |
| + } |
| + |
| + SyncedNotification* CreateNotification(const std::string& message, |
| + const std::string& external_id) { |
| + SyncData sync_data = CreateSyncData(message, external_id); |
| + // Set enough fields in sync_data, including specifics, for our tests |
| + // to pass. |
| + return new SyncedNotification(sync_data); |
| + } |
| + |
| + // 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_notif(notification); |
| + return SyncChange( |
| + FROM_HERE, |
| + type, |
| + ChromeNotifierService::CreateSyncDataFromNotification(*notification)); |
| + } |
| + |
| + // Helper to create syncer::SyncData. |
| + static SyncData CreateSyncData(const std::string& message, |
| + const std::string& external_id) { |
| + |
| + // CreateLocalData makes a copy of this, so this can safely live |
| + // on the stack. |
| + EntitySpecifics entity_specifics; |
| + |
| + // Get a writeable pointer to the sync notifications specifics inside the |
| + // entity specifics. |
| + SyncedNotificationSpecifics* specifics = |
| + entity_specifics.mutable_synced_notification(); |
| + |
| + // Fill out as much of SyncedNotificationSpecifics as the tests need. |
| + |
| + // Set the layout type to Title and Subtext. |
| + specifics->mutable_coalesced_notification()-> |
| + mutable_render_info()-> |
| + mutable_layout()-> |
| + set_layout_type(RenderInfo_Layout_LayoutType_TITLE_AND_SUBTEXT); |
| + // Set the APP_ID field in the ID. |
| + specifics->mutable_coalesced_notification()-> |
| + mutable_id()-> |
| + set_app_id(MY_APP_ID); |
| + // Set the coalescing key in the ID. |
| + specifics->mutable_coalesced_notification()-> |
| + mutable_id()-> |
| + set_coalescing_key(COALESCING_KEY); |
| + // Set the title (of a title and subtext). |
| + specifics->mutable_coalesced_notification()-> |
| + mutable_render_info()-> |
| + mutable_layout()-> |
| + mutable_title_and_subtext_data()-> |
| + set_title(message); |
| + // Set the creation time. |
| + specifics->mutable_coalesced_notification()-> |
| + set_creation_time_msec(FAKE_CREATION_TIME); |
| + // Set the first notification, including external ID. |
| + specifics->mutable_coalesced_notification()-> |
| + add_notification(); |
| + specifics->mutable_coalesced_notification()-> |
| + mutable_notification(0)->set_external_id(external_id); |
| + |
| + |
| + SyncData sync_data = SyncData::CreateLocalData( |
| + "syncer::SYNCED_NOTIFICATIONS", |
| + "ChromeNotifierServiceUnitTest", |
| + entity_specifics); |
| + |
| + return sync_data; |
| + } |
| + |
| + private: |
| + scoped_ptr<TestChangeProcessor> sync_processor_; |
| + scoped_ptr<SyncChangeProcessorDelegate> sync_processor_delegate_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(ChromeNotifierServiceTest); |
| +}; |
| + |
| +// Create a Notification, convert it to SyncData and convert it back. |
| +TEST_F(ChromeNotifierServiceTest, NotificationToSyncDataToNotification) { |
| + // TODO(petewil): Make this more robust when I add more properties to |
| + // the notification. |
| + scoped_ptr<SyncedNotification> notif1(CreateNotification("1", "11")); |
| + SyncData sync_data = |
| + ChromeNotifierService::CreateSyncDataFromNotification(*notif1); |
| + scoped_ptr<SyncedNotification> notif2( |
| + ChromeNotifierService::CreateNotificationFromSyncData(sync_data)); |
| + EXPECT_TRUE(notif2.get()); |
| + EXPECT_TRUE(notif1->Equals(*notif2)); |
| +} |
| + |
| + |
| +// Model assocation: We have no local data, and no remote data. |
| +TEST_F(ChromeNotifierServiceTest, ModelAssocBothEmpty) { |
| + ChromeNotifierService notifier; |
| + |
| + notifier.MergeDataAndStartSyncing( |
| + SYNCED_NOTIFICATIONS, |
| + SyncDataList(), // Empty. |
| + PassProcessor(), |
| + scoped_ptr<SyncErrorFactory>(new 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; |
| + |
| + notifier.MergeDataAndStartSyncing( |
| + SYNCED_NOTIFICATIONS, |
| + SyncDataList(), |
| + PassProcessor(), |
| + scoped_ptr<SyncErrorFactory>(new SyncErrorFactoryMock())); |
| + |
| + // Set up a bunch of ADDs. |
| + SyncChangeList changes; |
| + changes.push_back(CreateSyncChange( |
| + SyncChange::ACTION_ADD, CreateNotification("1", "11"))); |
| + changes.push_back(CreateSyncChange( |
| + SyncChange::ACTION_ADD, CreateNotification("2", "22"))); |
| + changes.push_back(CreateSyncChange( |
| + SyncChange::ACTION_ADD, CreateNotification("3", "33"))); |
| + |
| + notifier.ProcessSyncChanges(FROM_HERE, changes); |
| + |
| + EXPECT_EQ(3U, notifier.GetAllSyncData(SYNCED_NOTIFICATIONS).size()); |
| +} |
| + |
| +// Model has some notifications, some of them are local only. Sync has some |
| +// notifications. No items match up. |
| +TEST_F(ChromeNotifierServiceTest, LocalRemoteBothNonEmptyNoOverlap) { |
| + ChromeNotifierService notifier; |
| + |
| + // Create some local fake data. |
| + SyncedNotification* n1 = CreateNotification("1", "11"); |
| + notifier.Add(n1); |
| + SyncedNotification* n2 = CreateNotification("2", "22"); |
| + notifier.Add(n2); |
| + SyncedNotification* n3 = CreateNotification("3", "33"); |
| + notifier.Add(n3); |
| + |
| + // Create some remote fake data. |
| + SyncDataList initial_data; |
| + SyncData s1 = CreateSyncData("4", "44"); |
| + initial_data.push_back(s1); |
| + initial_data.push_back(CreateSyncData("5", "55")); |
| + initial_data.push_back(CreateSyncData("6", "66")); |
| + initial_data.push_back(CreateSyncData("7", "77")); |
| + |
| + // Merge the local and remote data. |
| + notifier.MergeDataAndStartSyncing( |
| + SYNCED_NOTIFICATIONS, |
| + initial_data, |
| + PassProcessor(), |
| + scoped_ptr<SyncErrorFactory>(new SyncErrorFactoryMock())); |
| + |
| + // Ensure the array now has all local and remote notifications. |
| + EXPECT_EQ(7U, notifier.GetAllSyncData(SYNCED_NOTIFICATIONS).size()); |
| + for (SyncDataList::const_iterator iter = initial_data.begin(); |
| + iter != initial_data.end(); ++iter) { |
| + scoped_ptr<SyncedNotification> notif1( |
| + ChromeNotifierService::CreateNotificationFromSyncData(*iter)); |
| + // TODO(petewil): Revisit this when we expand the protobuf more. |
| + const std::string& value = notif1->title(); |
| + const SyncedNotification* notif2 = notifier.GetNotification(value); |
| + EXPECT_TRUE(NULL != notif2); |
| + EXPECT_TRUE(notif1->Equals(*notif2)); |
| + } |
| + EXPECT_TRUE(notifier.GetNotification(n1->title())); |
| + EXPECT_TRUE(notifier.GetNotification(n2->title())); |
| + EXPECT_TRUE(notifier.GetNotification(n3->title())); |
| + |
| + // Verify the changes made it up to the remote service. |
| + EXPECT_EQ(3U, processor()->change_list_size()); |
| + EXPECT_TRUE(processor()->ContainsId(n1->get_first_external_id())); |
| + EXPECT_EQ(SyncChange::ACTION_ADD, processor()->GetChangeById( |
| + n1->get_first_external_id()).change_type()); |
| + EXPECT_FALSE(processor()->ContainsId("44")); |
| + EXPECT_TRUE(processor()->ContainsId(n3->get_first_external_id())); |
| + EXPECT_EQ(SyncChange::ACTION_ADD, processor()->GetChangeById( |
| + n3->get_first_external_id()).change_type()); |
| +} |