// 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 "base/bind.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop.h" #include "base/strings/string_number_conversions.h" #include "chrome/browser/extensions/app_notification.h" #include "chrome/browser/extensions/app_notification_manager.h" #include "chrome/test/base/testing_profile.h" #include "content/public/test/test_browser_thread.h" #include "sync/api/sync_error_factory.h" #include "sync/api/sync_error_factory_mock.h" #include "sync/protocol/app_notification_specifics.pb.h" #include "sync/protocol/sync.pb.h" #include "testing/gtest/include/gtest/gtest.h" using content::BrowserThread; using ::testing::_; using ::testing::Return; namespace { // Extract notification guid from syncer::SyncData. std::string GetGuid(const syncer::SyncData& sync_data) { return sync_data.GetSpecifics().app_notification().guid(); } // 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 syncer::SyncChangeList& change_list) OVERRIDE { // change_map_.erase(change_map_.begin(), change_map_.end()); for (syncer::SyncChangeList::const_iterator iter = change_list.begin(); iter != change_list.end(); ++iter) { change_map_[GetGuid(iter->sync_data())] = *iter; } return syncer::SyncError(); } bool ContainsGuid(const std::string& guid) { return change_map_.find(guid) != change_map_.end(); } syncer::SyncChange GetChangeByGuid(const std::string& guid) { DCHECK(ContainsGuid(guid)); return change_map_[guid]; } size_t change_list_size() { return change_map_.size(); } private: // Track the changes received in ProcessSyncChanges. std::map change_map_; DISALLOW_COPY_AND_ASSIGN(TestChangeProcessor); }; class SyncChangeProcessorDelegate : public syncer::SyncChangeProcessor { public: explicit SyncChangeProcessorDelegate(syncer::SyncChangeProcessor* recipient) : recipient_(recipient) { DCHECK(recipient_); } virtual ~SyncChangeProcessorDelegate() {} // syncer::SyncChangeProcessor implementation. virtual syncer::SyncError ProcessSyncChanges( const tracked_objects::Location& from_here, const syncer::SyncChangeList& change_list) OVERRIDE { return recipient_->ProcessSyncChanges(from_here, change_list); } private: // The recipient of all sync changes. syncer::SyncChangeProcessor* recipient_; DISALLOW_COPY_AND_ASSIGN(SyncChangeProcessorDelegate); }; } // namespace namespace extensions { class AppNotificationManagerSyncTest : public testing::Test { public: AppNotificationManagerSyncTest() : ui_thread_(BrowserThread::UI, &ui_loop_), file_thread_(BrowserThread::FILE), sync_processor_(new TestChangeProcessor), sync_processor_delegate_(new SyncChangeProcessorDelegate( sync_processor_.get())) {} virtual ~AppNotificationManagerSyncTest() { model_ = NULL; } virtual void SetUp() { ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI)); file_thread_.Start(); ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); profile_.reset(new TestingProfile(temp_dir_.path())); model_ = new AppNotificationManager(profile_.get()); model_->Init(); WaitForFileThread(); ASSERT_TRUE(model_->loaded()); } virtual void TearDown() { WaitForFileThread(); } static void PostQuitToUIThread() { BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, MessageLoop::QuitClosure()); } static void WaitForFileThread() { BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(&PostQuitToUIThread)); MessageLoop::current()->Run(); } AppNotificationManager* model() { return model_.get(); } TestChangeProcessor* processor() { return sync_processor_.get(); } scoped_ptr PassProcessor() { return sync_processor_delegate_.PassAs(); } // Creates a notification whose properties are set from the given integer. static AppNotification* CreateNotification(int suffix) { return CreateNotification(false, suffix); } static AppNotification* CreateNotification(bool is_local, int suffix) { std::string s = base::IntToString(suffix); return CreateNotification( is_local, suffix, "guid" + s, "ext" + s, "text" + s, "body" + s, "http://www.url" + s + ".com", "link text " + s); } static AppNotification* CreateNotification( bool is_local, int suffix, const std::string& extension_id) { std::string s = base::IntToString(suffix); return CreateNotification( is_local, suffix, "guid" + s, extension_id, "text" + s, "body" + s, "http://www.url" + s + ".com", "link text " + s); } // Creates a notification whose properties are set from the given integer // but does not set link url and link text. static AppNotification* CreateNotificationNoLink(int suffix) { return CreateNotificationNoLink(false, suffix); } static AppNotification* CreateNotificationNoLink(bool is_local, int suffix) { std::string s = base::IntToString(suffix); return CreateNotification( is_local, suffix, "guid" + s, "ext" + s, "text" + s, "body" + s, "", ""); } // link_url and link_text are only set if the passed in values are not empty. static AppNotification* CreateNotification(bool is_local, int64 time, const std::string& guid, const std::string& extension_id, const std::string& title, const std::string& body, const std::string& link_url, const std::string& link_text) { AppNotification* notif = new AppNotification( is_local, base::Time::FromInternalValue(time), guid, extension_id, title, body); if (!link_url.empty()) notif->set_link_url(GURL(link_url)); if (!link_text.empty()) notif->set_link_text(link_text); return notif; } static syncer::SyncData CreateSyncData(int suffix) { scoped_ptr notif(CreateNotification(suffix)); return AppNotificationManager::CreateSyncDataFromNotification(*notif); } static syncer::SyncData CreateSyncData( int suffix, const std::string& extension_id) { scoped_ptr notif( CreateNotification(false, suffix, extension_id)); return AppNotificationManager::CreateSyncDataFromNotification(*notif); } // Helper to create syncer::SyncChange. Takes ownership of |notif|. static syncer::SyncChange CreateSyncChange( syncer::SyncChange::SyncChangeType type, AppNotification* notif) { // Take control of notif to clean it up after we create data out of it. scoped_ptr scoped_notif(notif); return syncer::SyncChange( FROM_HERE, type, AppNotificationManager::CreateSyncDataFromNotification(*notif)); } void AssertSyncChange(const syncer::SyncChange& change, syncer::SyncChange::SyncChangeType type, const AppNotification& notif) { ASSERT_EQ(type, change.change_type()); scoped_ptr notif2( AppNotificationManager::CreateNotificationFromSyncData( change.sync_data())); ASSERT_TRUE(notif.Equals(*notif2)); } protected: MessageLoop ui_loop_; content::TestBrowserThread ui_thread_; content::TestBrowserThread file_thread_; // We keep two TemplateURLServices to test syncing between them. base::ScopedTempDir temp_dir_; scoped_ptr profile_; scoped_refptr model_; scoped_ptr sync_processor_; scoped_ptr sync_processor_delegate_; DISALLOW_COPY_AND_ASSIGN(AppNotificationManagerSyncTest); }; // Create an AppNotification, convert it to SyncData and convert it back. TEST_F(AppNotificationManagerSyncTest, NotificationToSyncDataToNotification) { { // Partial properties set. scoped_ptr notif1(CreateNotificationNoLink(1)); syncer::SyncData sync_data = AppNotificationManager::CreateSyncDataFromNotification(*notif1); scoped_ptr notif2( AppNotificationManager::CreateNotificationFromSyncData(sync_data)); EXPECT_TRUE(notif2.get()); EXPECT_TRUE(notif1->Equals(*notif2)); } { // All properties set. scoped_ptr notif1(CreateNotification(1)); syncer::SyncData sync_data = AppNotificationManager::CreateSyncDataFromNotification(*notif1); scoped_ptr notif2( AppNotificationManager::CreateNotificationFromSyncData(sync_data)); EXPECT_TRUE(notif2.get()); EXPECT_TRUE(notif1->Equals(*notif2)); } } // GetAllSyncData returns all notifications since none are marked local only. TEST_F(AppNotificationManagerSyncTest, GetAllSyncDataNoLocal) { model()->Add(CreateNotificationNoLink(1)); model()->Add(CreateNotification(2)); model()->Add(CreateNotification(3)); syncer::SyncDataList all_sync_data = model()->GetAllSyncData(syncer::APP_NOTIFICATIONS); EXPECT_EQ(3U, all_sync_data.size()); for (syncer::SyncDataList::const_iterator iter = all_sync_data.begin(); iter != all_sync_data.end(); ++iter) { scoped_ptr notif1( AppNotificationManager::CreateNotificationFromSyncData(*iter)); const std::string& guid = notif1->guid(); const std::string& ext_id = notif1->extension_id(); const AppNotification* notif2 = model()->GetNotification(ext_id, guid); ASSERT_TRUE(notif1->Equals(*notif2)); } } // GetAllSyncData should not return notifications marked as local only. TEST_F(AppNotificationManagerSyncTest, GetAllSyncDataSomeLocal) { model()->Add(CreateNotificationNoLink(1)); model()->Add(CreateNotification(true, 2)); model()->Add(CreateNotification(3)); model()->Add(CreateNotification(true, 4)); model()->Add(CreateNotification(5)); syncer::SyncDataList all_sync_data = model()->GetAllSyncData(syncer::APP_NOTIFICATIONS); EXPECT_EQ(3U, all_sync_data.size()); for (syncer::SyncDataList::const_iterator iter = all_sync_data.begin(); iter != all_sync_data.end(); ++iter) { scoped_ptr notif1( AppNotificationManager::CreateNotificationFromSyncData(*iter)); const std::string& guid = notif1->guid(); const std::string& ext_id = notif1->extension_id(); const AppNotification* notif2 = model()->GetNotification(ext_id, guid); ASSERT_TRUE(notif1->Equals(*notif2)); } } // Model assocation: both models are empty. TEST_F(AppNotificationManagerSyncTest, ModelAssocBothEmpty) { model()->MergeDataAndStartSyncing( syncer::APP_NOTIFICATIONS, syncer::SyncDataList(), // Empty. PassProcessor(), scoped_ptr(new syncer::SyncErrorFactoryMock())); EXPECT_EQ(0U, model()->GetAllSyncData(syncer::APP_NOTIFICATIONS).size()); EXPECT_EQ(0U, processor()->change_list_size()); } // Model assocation: empty sync model and non-empty local model. TEST_F(AppNotificationManagerSyncTest, ModelAssocModelEmpty) { syncer::SyncDataList initial_data; initial_data.push_back(CreateSyncData(1)); initial_data.push_back(CreateSyncData(2)); initial_data.push_back(CreateSyncData(3)); initial_data.push_back(CreateSyncData(4)); model()->MergeDataAndStartSyncing( syncer::APP_NOTIFICATIONS, initial_data, PassProcessor(), scoped_ptr(new syncer::SyncErrorFactoryMock())); EXPECT_EQ(4U, model()->GetAllSyncData(syncer::APP_NOTIFICATIONS).size()); // Model should all of the initial sync data. for (syncer::SyncDataList::const_iterator iter = initial_data.begin(); iter != initial_data.end(); ++iter) { scoped_ptr notif1( AppNotificationManager::CreateNotificationFromSyncData(*iter)); const std::string& ext_id = notif1->extension_id(); const std::string& guid = notif1->guid(); const AppNotification* notif2 = model()->GetNotification(ext_id, guid); EXPECT_TRUE(notif2); EXPECT_TRUE(notif1->Equals(*notif2)); } EXPECT_EQ(0U, processor()->change_list_size()); } // Model has some notifications, some of them are local only. Sync has some // notifications. No items match up. TEST_F(AppNotificationManagerSyncTest, ModelAssocBothNonEmptyNoOverlap) { AppNotification* n1 = CreateNotification(1); model()->Add(n1); AppNotification* n2 = CreateNotification(true, 2); model()->Add(n2); AppNotification* n3 = CreateNotification(3); model()->Add(n3); syncer::SyncDataList initial_data; initial_data.push_back(CreateSyncData(4)); initial_data.push_back(CreateSyncData(5)); initial_data.push_back(CreateSyncData(6)); initial_data.push_back(CreateSyncData(7)); model()->MergeDataAndStartSyncing( syncer::APP_NOTIFICATIONS, initial_data, PassProcessor(), scoped_ptr(new syncer::SyncErrorFactoryMock())); EXPECT_EQ(6U, model()->GetAllSyncData(syncer::APP_NOTIFICATIONS).size()); for (syncer::SyncDataList::const_iterator iter = initial_data.begin(); iter != initial_data.end(); ++iter) { scoped_ptr notif1( AppNotificationManager::CreateNotificationFromSyncData(*iter)); const std::string& ext_id = notif1->extension_id(); const std::string& guid = notif1->guid(); const AppNotification* notif2 = model()->GetNotification(ext_id, guid); EXPECT_TRUE(notif2); EXPECT_TRUE(notif1->Equals(*notif2)); } EXPECT_TRUE(model()->GetNotification(n1->extension_id(), n1->guid())); EXPECT_TRUE(model()->GetNotification(n2->extension_id(), n2->guid())); EXPECT_TRUE(model()->GetNotification(n3->extension_id(), n3->guid())); EXPECT_EQ(2U, processor()->change_list_size()); EXPECT_TRUE(processor()->ContainsGuid(n1->guid())); EXPECT_EQ(syncer::SyncChange::ACTION_ADD, processor()->GetChangeByGuid(n1->guid()).change_type()); EXPECT_FALSE(processor()->ContainsGuid(n2->guid())); EXPECT_TRUE(processor()->ContainsGuid(n3->guid())); EXPECT_EQ(syncer::SyncChange::ACTION_ADD, processor()->GetChangeByGuid(n3->guid()).change_type()); } // Model has some notifications, some of them are local only. Sync has some // notifications. Some items match up. TEST_F(AppNotificationManagerSyncTest, ModelAssocBothNonEmptySomeOverlap) { AppNotification* n1 = CreateNotification(1); model()->Add(n1); AppNotification* n2 = CreateNotification(true, 2); model()->Add(n2); AppNotification* n3 = CreateNotification(3); model()->Add(n3); AppNotification* n4 = CreateNotification(4); model()->Add(n4); syncer::SyncDataList initial_data; initial_data.push_back(CreateSyncData(5)); initial_data.push_back( AppNotificationManager::CreateSyncDataFromNotification(*n1)); initial_data.push_back(CreateSyncData(6)); initial_data.push_back( AppNotificationManager::CreateSyncDataFromNotification(*n4)); initial_data.push_back(CreateSyncData(7)); model()->MergeDataAndStartSyncing( syncer::APP_NOTIFICATIONS, initial_data, PassProcessor(), scoped_ptr(new syncer::SyncErrorFactoryMock())); EXPECT_EQ(6U, model()->GetAllSyncData(syncer::APP_NOTIFICATIONS).size()); for (syncer::SyncDataList::const_iterator iter = initial_data.begin(); iter != initial_data.end(); ++iter) { scoped_ptr notif1( AppNotificationManager::CreateNotificationFromSyncData(*iter)); const std::string& ext_id = notif1->extension_id(); const std::string& guid = notif1->guid(); const AppNotification* notif2 = model()->GetNotification(ext_id, guid); EXPECT_TRUE(notif2); EXPECT_TRUE(notif1->Equals(*notif2)); } EXPECT_TRUE(model()->GetNotification(n1->extension_id(), n1->guid())); EXPECT_TRUE(model()->GetNotification(n2->extension_id(), n2->guid())); EXPECT_TRUE(model()->GetNotification(n3->extension_id(), n3->guid())); EXPECT_TRUE(model()->GetNotification(n4->extension_id(), n4->guid())); EXPECT_EQ(1U, processor()->change_list_size()); EXPECT_FALSE(processor()->ContainsGuid(n1->guid())); EXPECT_FALSE(processor()->ContainsGuid(n2->guid())); EXPECT_TRUE(processor()->ContainsGuid(n3->guid())); EXPECT_EQ(syncer::SyncChange::ACTION_ADD, processor()->GetChangeByGuid(n3->guid()).change_type()); EXPECT_FALSE(processor()->ContainsGuid(n4->guid())); } // When an item that matches up in model and sync is different, an error // should be returned. TEST_F(AppNotificationManagerSyncTest, ModelAssocBothNonEmptyTitleMismatch) { AppNotification* n1 = CreateNotification(1); model()->Add(n1); AppNotification* n2 = CreateNotification(true, 2); model()->Add(n2); syncer::SyncDataList initial_data; initial_data.push_back(CreateSyncData(1)); scoped_ptr n1_a(CreateNotification( n1->is_local(), n1->creation_time().ToInternalValue(), n1->guid(), n1->extension_id(), n1->title() + "_changed", // different title n1->body(), n1->link_url().spec(), n1->link_text())); initial_data.push_back( AppNotificationManager::CreateSyncDataFromNotification(*n1_a)); scoped_ptr error_handler( new syncer::SyncErrorFactoryMock()); EXPECT_CALL(*error_handler, CreateAndUploadError(_, _)). WillOnce( Return( syncer::SyncError( FROM_HERE, "error", syncer::APP_NOTIFICATIONS))); syncer::SyncError sync_error = model()->MergeDataAndStartSyncing( syncer::APP_NOTIFICATIONS, initial_data, PassProcessor(), error_handler.PassAs()).error(); EXPECT_TRUE(sync_error.IsSet()); EXPECT_EQ(syncer::APP_NOTIFICATIONS, sync_error.type()); EXPECT_EQ(0U, processor()->change_list_size()); } // When an item in sync matches with a local-only item in model, an error // should be returned. TEST_F(AppNotificationManagerSyncTest, ModelAssocBothNonEmptyMatchesLocal) { AppNotification* n1 = CreateNotification(1); model()->Add(n1); AppNotification* n2 = CreateNotification(true, 2); model()->Add(n2); syncer::SyncDataList initial_data; initial_data.push_back(CreateSyncData(1)); scoped_ptr n2_a(CreateNotification(2)); initial_data.push_back( AppNotificationManager::CreateSyncDataFromNotification(*n2_a)); scoped_ptr error_handler( new syncer::SyncErrorFactoryMock()); EXPECT_CALL(*error_handler, CreateAndUploadError(_, _)). WillOnce( Return( syncer::SyncError( FROM_HERE, "error", syncer::APP_NOTIFICATIONS))); syncer::SyncError sync_error = model()->MergeDataAndStartSyncing( syncer::APP_NOTIFICATIONS, initial_data, PassProcessor(), error_handler.PassAs()).error(); EXPECT_TRUE(sync_error.IsSet()); EXPECT_EQ(syncer::APP_NOTIFICATIONS, sync_error.type()); EXPECT_EQ(0U, processor()->change_list_size()); } // Process sync changes when model is empty. TEST_F(AppNotificationManagerSyncTest, ProcessSyncChangesEmptyModel) { // We initially have no data. model()->MergeDataAndStartSyncing( syncer::APP_NOTIFICATIONS, syncer::SyncDataList(), PassProcessor(), scoped_ptr(new syncer::SyncErrorFactoryMock())); // Set up a bunch of ADDs. syncer::SyncChangeList changes; changes.push_back(CreateSyncChange( syncer::SyncChange::ACTION_ADD, CreateNotification(1))); changes.push_back(CreateSyncChange( syncer::SyncChange::ACTION_ADD, CreateNotification(2))); changes.push_back(CreateSyncChange( syncer::SyncChange::ACTION_ADD, CreateNotification(3))); model()->ProcessSyncChanges(FROM_HERE, changes); EXPECT_EQ(3U, model()->GetAllSyncData(syncer::APP_NOTIFICATIONS).size()); EXPECT_EQ(0U, processor()->change_list_size()); } // Process sync changes when model is not empty. TEST_F(AppNotificationManagerSyncTest, ProcessSyncChangesNonEmptyModel) { AppNotification* n1 = CreateNotification(1); model()->Add(n1); AppNotification* n2 = CreateNotification(2); model()->Add(n2); model()->MergeDataAndStartSyncing( syncer::APP_NOTIFICATIONS, syncer::SyncDataList(), PassProcessor(), scoped_ptr(new syncer::SyncErrorFactoryMock())); // Some adds and some deletes. syncer::SyncChangeList changes; changes.push_back(CreateSyncChange( syncer::SyncChange::ACTION_ADD, CreateNotification(3))); changes.push_back(CreateSyncChange( syncer::SyncChange::ACTION_DELETE, n1->Copy())); changes.push_back(CreateSyncChange( syncer::SyncChange::ACTION_ADD, CreateNotification(4))); model()->ProcessSyncChanges(FROM_HERE, changes); EXPECT_EQ(3U, model()->GetAllSyncData(syncer::APP_NOTIFICATIONS).size()); EXPECT_EQ(2U, processor()->change_list_size()); } // Process sync changes should ignore a bad ADD. // Hangs: http://crbug.com/149712 TEST_F(AppNotificationManagerSyncTest, DISABLED_ProcessSyncChangesIgnoreBadAdd) { AppNotification* n1 = CreateNotification(1); model()->Add(n1); AppNotification* n2 = CreateNotification(2); model()->Add(n2); model()->MergeDataAndStartSyncing( syncer::APP_NOTIFICATIONS, syncer::SyncDataList(), PassProcessor(), scoped_ptr(new syncer::SyncErrorFactoryMock())); // Some adds and some deletes. syncer::SyncChangeList changes; changes.push_back(CreateSyncChange( syncer::SyncChange::ACTION_ADD, CreateNotification(1))); syncer::SyncError error = model()->ProcessSyncChanges(FROM_HERE, changes); EXPECT_FALSE(error.IsSet()); EXPECT_EQ(2U, model()->GetAllSyncData(syncer::APP_NOTIFICATIONS).size()); EXPECT_EQ(2U, processor()->change_list_size()); } // Process sync changes should ignore a bad DELETE. TEST_F(AppNotificationManagerSyncTest, ProcessSyncChangesIgnoreBadDelete) { AppNotification* n1 = CreateNotification(1); model()->Add(n1); AppNotification* n2 = CreateNotification(2); model()->Add(n2); model()->MergeDataAndStartSyncing( syncer::APP_NOTIFICATIONS, syncer::SyncDataList(), PassProcessor(), scoped_ptr(new syncer::SyncErrorFactoryMock())); // Some adds and some deletes. syncer::SyncChangeList changes; changes.push_back(CreateSyncChange( syncer::SyncChange::ACTION_DELETE, CreateNotification(3))); syncer::SyncError error = model()->ProcessSyncChanges(FROM_HERE, changes); EXPECT_FALSE(error.IsSet()); EXPECT_EQ(2U, model()->GetAllSyncData(syncer::APP_NOTIFICATIONS).size()); EXPECT_EQ(2U, processor()->change_list_size()); } // Process sync changes should ignore bad UPDATEs. // Hangs: http://crbug.com/149712 TEST_F(AppNotificationManagerSyncTest, DISABLED_ProcessSyncChangesIgnoreBadUpdates) { AppNotification* n1 = CreateNotification(1); model()->Add(n1); AppNotification* n2 = CreateNotification(2); model()->Add(n2); model()->MergeDataAndStartSyncing( syncer::APP_NOTIFICATIONS, syncer::SyncDataList(), PassProcessor(), scoped_ptr(new syncer::SyncErrorFactoryMock())); // Some adds and some deletes. syncer::SyncChangeList changes; changes.push_back(CreateSyncChange( syncer::SyncChange::ACTION_UPDATE, CreateNotification(3))); AppNotification* n2_changed = n2->Copy(); n2_changed->set_link_text(n2_changed->link_text() + "-changed"); changes.push_back(CreateSyncChange( syncer::SyncChange::ACTION_UPDATE, n2_changed)); syncer::SyncError error = model()->ProcessSyncChanges(FROM_HERE, changes); EXPECT_FALSE(error.IsSet()); EXPECT_EQ(2U, model()->GetAllSyncData(syncer::APP_NOTIFICATIONS).size()); EXPECT_EQ(2U, processor()->change_list_size()); } // Process over 15 changes when model is not empty. TEST_F(AppNotificationManagerSyncTest, ProcessSyncChangesEmptyModelWithMax) { const std::string& ext_id = "e1"; model()->MergeDataAndStartSyncing( syncer::APP_NOTIFICATIONS, syncer::SyncDataList(), PassProcessor(), scoped_ptr(new syncer::SyncErrorFactoryMock())); for (unsigned int i = 0; i < AppNotificationManager::kMaxNotificationPerApp * 2; i++) { syncer::SyncChangeList changes; changes.push_back(CreateSyncChange( syncer::SyncChange::ACTION_ADD, CreateNotification(false, i, ext_id))); model()->ProcessSyncChanges(FROM_HERE, changes); if (i < AppNotificationManager::kMaxNotificationPerApp) { EXPECT_EQ(i + 1, model()->GetAllSyncData(syncer::APP_NOTIFICATIONS).size()); } else { EXPECT_EQ(AppNotificationManager::kMaxNotificationPerApp, model()->GetAllSyncData(syncer::APP_NOTIFICATIONS).size()); for (unsigned int j = i; j > i - 5; j--) { EXPECT_EQ( AppNotificationManager::kMaxNotificationPerApp, model()->GetAllSyncData(syncer::APP_NOTIFICATIONS).size()); } } } } // Stop syncing sets state correctly. TEST_F(AppNotificationManagerSyncTest, StopSyncing) { EXPECT_FALSE(model()->sync_processor_.get()); EXPECT_FALSE(model()->models_associated_); model()->MergeDataAndStartSyncing( syncer::APP_NOTIFICATIONS, syncer::SyncDataList(), PassProcessor(), scoped_ptr(new syncer::SyncErrorFactoryMock())); EXPECT_TRUE(model()->sync_processor_.get()); EXPECT_TRUE(model()->models_associated_); model()->StopSyncing(syncer::APP_NOTIFICATIONS); EXPECT_FALSE(model()->sync_processor_.get()); EXPECT_FALSE(model()->models_associated_); } // Adds get pushed to sync but local only are skipped. TEST_F(AppNotificationManagerSyncTest, AddsGetsSynced) { model()->MergeDataAndStartSyncing( syncer::APP_NOTIFICATIONS, syncer::SyncDataList(), PassProcessor(), scoped_ptr(new syncer::SyncErrorFactoryMock())); AppNotification* n1 = CreateNotification(1); model()->Add(n1); AppNotification* n2 = CreateNotification(2); model()->Add(n2); AppNotification* n3 = CreateNotification(true, 2); model()->Add(n3); EXPECT_EQ(2U, processor()->change_list_size()); EXPECT_TRUE(processor()->ContainsGuid(n1->guid())); syncer::SyncChange c1 = processor()->GetChangeByGuid(n1->guid()); AssertSyncChange(c1, syncer::SyncChange::ACTION_ADD, *n1); syncer::SyncChange c2 = processor()->GetChangeByGuid(n2->guid()); AssertSyncChange(c2, syncer::SyncChange::ACTION_ADD, *n2); } // Clear all gets pushed to sync. TEST_F(AppNotificationManagerSyncTest, ClearAllGetsSynced) { const std::string& ext_id = "e1"; scoped_ptr n1(CreateNotification(false, 1, ext_id)); scoped_ptr n2(CreateNotification(false, 2, ext_id)); scoped_ptr n3(CreateNotification(false, 3, ext_id)); scoped_ptr n4(CreateNotification(4)); syncer::SyncDataList initial_data; initial_data.push_back( AppNotificationManager::CreateSyncDataFromNotification(*n1)); initial_data.push_back( AppNotificationManager::CreateSyncDataFromNotification(*n2)); initial_data.push_back( AppNotificationManager::CreateSyncDataFromNotification(*n3)); initial_data.push_back( AppNotificationManager::CreateSyncDataFromNotification(*n4)); model()->MergeDataAndStartSyncing( syncer::APP_NOTIFICATIONS, initial_data, PassProcessor(), scoped_ptr(new syncer::SyncErrorFactoryMock())); model()->ClearAll(ext_id); EXPECT_EQ(3U, processor()->change_list_size()); EXPECT_TRUE(processor()->ContainsGuid(n1->guid())); syncer::SyncChange c1 = processor()->GetChangeByGuid(n1->guid()); AssertSyncChange(c1, syncer::SyncChange::ACTION_DELETE, *n1); syncer::SyncChange c2 = processor()->GetChangeByGuid(n2->guid()); AssertSyncChange(c2, syncer::SyncChange::ACTION_DELETE, *n2); syncer::SyncChange c3 = processor()->GetChangeByGuid(n3->guid()); AssertSyncChange(c3, syncer::SyncChange::ACTION_DELETE, *n3); } } // namespace extensions