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()); |
+} |