| // Copyright (c) 2011 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 <set> |
| #include <string> |
| #include <vector> |
| |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| #include "base/callback.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/message_loop.h" |
| #include "base/string16.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/task.h" |
| #include "base/time.h" |
| #include "base/utf_string_conversions.h" |
| #include "chrome/browser/autofill/autofill_common_test.h" |
| #include "chrome/browser/sync/abstract_profile_sync_service_test.h" |
| #include "chrome/browser/sync/engine/model_changing_syncer_command.h" |
| #include "chrome/browser/sync/engine/syncapi.h" |
| #include "chrome/browser/sync/glue/autofill_change_processor.h" |
| #include "chrome/browser/sync/glue/autofill_data_type_controller.h" |
| #include "chrome/browser/sync/glue/autofill_model_associator.h" |
| #include "chrome/browser/sync/glue/autofill_profile_change_processor.h" |
| #include "chrome/browser/sync/glue/autofill_profile_data_type_controller.h" |
| #include "chrome/browser/sync/glue/autofill_profile_model_associator.h" |
| #include "chrome/browser/sync/glue/data_type_controller.h" |
| #include "chrome/browser/sync/profile_sync_factory.h" |
| #include "chrome/browser/sync/profile_sync_service.h" |
| #include "chrome/browser/sync/profile_sync_test_util.h" |
| #include "chrome/browser/sync/protocol/autofill_specifics.pb.h" |
| #include "chrome/browser/sync/syncable/autofill_migration.h" |
| #include "chrome/browser/sync/syncable/directory_manager.h" |
| #include "chrome/browser/sync/syncable/model_type.h" |
| #include "chrome/browser/sync/syncable/syncable.h" |
| #include "chrome/browser/sync/test_profile_sync_service.h" |
| #include "chrome/browser/webdata/autofill_change.h" |
| #include "chrome/browser/webdata/autofill_entry.h" |
| #include "chrome/browser/webdata/autofill_table.h" |
| #include "chrome/browser/webdata/web_database.h" |
| #include "chrome/common/net/gaia/gaia_constants.h" |
| #include "chrome/test/profile_mock.h" |
| #include "chrome/test/sync/engine/test_id_factory.h" |
| #include "content/browser/browser_thread.h" |
| #include "content/common/notification_source.h" |
| #include "content/common/notification_type.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| |
| using base::Time; |
| using base::WaitableEvent; |
| using browser_sync::AutofillChangeProcessor; |
| using browser_sync::AutofillDataTypeController; |
| using browser_sync::AutofillModelAssociator; |
| using browser_sync::AutofillProfileChangeProcessor; |
| using browser_sync::AutofillProfileDataTypeController; |
| using browser_sync::AutofillProfileModelAssociator; |
| using browser_sync::DataTypeController; |
| using browser_sync::GROUP_DB; |
| using browser_sync::kAutofillTag; |
| using browser_sync::SyncBackendHostForProfileSyncTest; |
| using browser_sync::UnrecoverableErrorHandler; |
| using syncable::CREATE_NEW_UPDATE_ITEM; |
| using syncable::AUTOFILL; |
| using syncable::BASE_VERSION; |
| using syncable::CREATE; |
| using syncable::GET_BY_SERVER_TAG; |
| using syncable::INVALID; |
| using syncable::MutableEntry; |
| using syncable::OriginalEntries; |
| using syncable::SERVER_PARENT_ID; |
| using syncable::SERVER_SPECIFICS; |
| using syncable::SPECIFICS; |
| using syncable::UNITTEST; |
| using syncable::WriterTag; |
| using syncable::WriteTransaction; |
| using testing::_; |
| using testing::DoAll; |
| using testing::DoDefault; |
| using testing::ElementsAre; |
| using testing::Eq; |
| using testing::Invoke; |
| using testing::Mock; |
| using testing::Return; |
| using testing::SaveArg; |
| using testing::SetArgumentPointee; |
| |
| namespace syncable { |
| class Id; |
| } |
| |
| class AutofillTableMock : public AutofillTable { |
| public: |
| AutofillTableMock() : AutofillTable(NULL, NULL) {} |
| MOCK_METHOD2(RemoveFormElement, |
| bool(const string16& name, const string16& value)); // NOLINT |
| MOCK_METHOD1(GetAllAutofillEntries, |
| bool(std::vector<AutofillEntry>* entries)); // NOLINT |
| MOCK_METHOD3(GetAutofillTimestamps, |
| bool(const string16& name, // NOLINT |
| const string16& value, |
| std::vector<base::Time>* timestamps)); |
| MOCK_METHOD1(UpdateAutofillEntries, |
| bool(const std::vector<AutofillEntry>&)); // NOLINT |
| MOCK_METHOD1(GetAutofillProfiles, |
| bool(std::vector<AutofillProfile*>*)); // NOLINT |
| MOCK_METHOD1(UpdateAutofillProfile, |
| bool(const AutofillProfile&)); // NOLINT |
| MOCK_METHOD1(AddAutofillProfile, |
| bool(const AutofillProfile&)); // NOLINT |
| MOCK_METHOD1(RemoveAutofillProfile, |
| bool(const std::string&)); // NOLINT |
| }; |
| |
| class WebDatabaseFake : public WebDatabase { |
| public: |
| explicit WebDatabaseFake(AutofillTable* autofill_table) |
| : autofill_table_(autofill_table) {} |
| |
| virtual AutofillTable* GetAutofillTable() { |
| return autofill_table_; |
| } |
| |
| private: |
| AutofillTable* autofill_table_; |
| }; |
| |
| |
| class ProfileSyncServiceAutofillTest; |
| |
| template<class AutofillProfile> |
| syncable::ModelType GetModelType() { |
| return syncable::UNSPECIFIED; |
| } |
| |
| template<> |
| syncable::ModelType GetModelType<AutofillEntry>() { |
| return syncable::AUTOFILL; |
| } |
| |
| template<> |
| syncable::ModelType GetModelType<AutofillProfile>() { |
| return syncable::AUTOFILL_PROFILE; |
| } |
| |
| class WebDataServiceFake : public WebDataService { |
| public: |
| explicit WebDataServiceFake(WebDatabase* web_database) |
| : web_database_(web_database) {} |
| virtual bool IsDatabaseLoaded() { |
| return true; |
| } |
| |
| virtual WebDatabase* GetDatabase() { |
| return web_database_; |
| } |
| |
| private: |
| WebDatabase* web_database_; |
| }; |
| |
| ACTION_P4(MakeAutofillSyncComponents, service, wd, pdm, dtc) { |
| EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| if (!BrowserThread::CurrentlyOn(BrowserThread::DB)) |
| return ProfileSyncFactory::SyncComponents(NULL, NULL); |
| AutofillModelAssociator* model_associator = |
| new AutofillModelAssociator(service, wd, pdm); |
| AutofillChangeProcessor* change_processor = |
| new AutofillChangeProcessor(model_associator, wd, pdm, dtc); |
| return ProfileSyncFactory::SyncComponents(model_associator, |
| change_processor); |
| } |
| |
| ACTION_P4(MakeAutofillProfileSyncComponents, service, wd, pdm, dtc) { |
| EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| if (!BrowserThread::CurrentlyOn(BrowserThread::DB)) |
| return ProfileSyncFactory::SyncComponents(NULL, NULL); |
| AutofillProfileModelAssociator* model_associator = |
| new AutofillProfileModelAssociator(service, wd, pdm); |
| AutofillProfileChangeProcessor* change_processor = |
| new AutofillProfileChangeProcessor(model_associator, wd, pdm, dtc); |
| return ProfileSyncFactory::SyncComponents(model_associator, |
| change_processor); |
| } |
| |
| class AbstractAutofillFactory { |
| public: |
| virtual AutofillDataTypeController* CreateDataTypeController( |
| ProfileSyncFactory *factory, |
| ProfileMock* profile, |
| ProfileSyncService* service) = 0; |
| virtual void SetExpectation(ProfileSyncFactoryMock* factory, |
| ProfileSyncService* service, |
| WebDatabase* wd, |
| PersonalDataManager* pdm, |
| DataTypeController* dtc) = 0; |
| virtual ~AbstractAutofillFactory() {} |
| }; |
| |
| class AutofillEntryFactory : public AbstractAutofillFactory { |
| public: |
| browser_sync::AutofillDataTypeController* CreateDataTypeController( |
| ProfileSyncFactory *factory, |
| ProfileMock* profile, |
| ProfileSyncService* service) { |
| return new AutofillDataTypeController(factory, |
| profile, |
| service); |
| } |
| |
| void SetExpectation(ProfileSyncFactoryMock* factory, |
| ProfileSyncService* service, |
| WebDatabase* wd, |
| PersonalDataManager* pdm, |
| DataTypeController* dtc) { |
| EXPECT_CALL(*factory, CreateAutofillSyncComponents(_,_,_,_)). |
| WillOnce(MakeAutofillSyncComponents(service, wd, pdm, dtc)); |
| } |
| }; |
| |
| class AutofillProfileFactory : public AbstractAutofillFactory { |
| public: |
| browser_sync::AutofillDataTypeController* CreateDataTypeController( |
| ProfileSyncFactory *factory, |
| ProfileMock* profile, |
| ProfileSyncService* service) { |
| return new AutofillProfileDataTypeController(factory, |
| profile, |
| service); |
| } |
| |
| void SetExpectation(ProfileSyncFactoryMock* factory, |
| ProfileSyncService* service, |
| WebDatabase* wd, |
| PersonalDataManager* pdm, |
| DataTypeController* dtc) { |
| EXPECT_CALL(*factory, CreateAutofillProfileSyncComponents(_,_,_,_)). |
| WillOnce(MakeAutofillProfileSyncComponents(service, wd, pdm, dtc)); |
| } |
| }; |
| |
| class PersonalDataManagerMock: public PersonalDataManager { |
| public: |
| MOCK_CONST_METHOD0(IsDataLoaded, bool()); |
| MOCK_METHOD0(LoadProfiles, void()); |
| MOCK_METHOD0(LoadCreditCards, void()); |
| MOCK_METHOD0(Refresh, void()); |
| }; |
| template <class T> class AddAutofillTask; |
| |
| class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest { |
| protected: |
| ProfileSyncServiceAutofillTest() : db_thread_(BrowserThread::DB) {} |
| |
| AutofillProfileFactory profile_factory_; |
| AutofillEntryFactory entry_factory_; |
| |
| AbstractAutofillFactory* GetFactory(syncable::ModelType type) { |
| if (type == syncable::AUTOFILL) { |
| return &entry_factory_; |
| } else if (type == syncable::AUTOFILL_PROFILE) { |
| return &profile_factory_; |
| } else { |
| NOTREACHED(); |
| return NULL; |
| } |
| } |
| virtual void SetUp() { |
| profile_.CreateRequestContext(); |
| web_database_.reset(new WebDatabaseFake(&autofill_table_)); |
| web_data_service_ = new WebDataServiceFake(web_database_.get()); |
| personal_data_manager_ = new PersonalDataManagerMock(); |
| EXPECT_CALL(*personal_data_manager_, LoadProfiles()).Times(1); |
| EXPECT_CALL(*personal_data_manager_, LoadCreditCards()).Times(1); |
| personal_data_manager_->Init(&profile_); |
| db_thread_.Start(); |
| |
| notification_service_ = new ThreadNotificationService(&db_thread_); |
| notification_service_->Init(); |
| } |
| |
| virtual void TearDown() { |
| service_.reset(); |
| notification_service_->TearDown(); |
| db_thread_.Stop(); |
| { |
| // The request context gets deleted on the I/O thread. To prevent a leak |
| // supply one here. |
| BrowserThread io_thread(BrowserThread::IO, MessageLoop::current()); |
| profile_.ResetRequestContext(); |
| } |
| MessageLoop::current()->RunAllPending(); |
| } |
| |
| void StartSyncService(Task* task, |
| bool will_fail_association, |
| syncable::ModelType type) { |
| AbstractAutofillFactory* factory = GetFactory(type); |
| service_.reset( |
| new TestProfileSyncService(&factory_, &profile_, "test_user", false, |
| task)); |
| AutofillDataTypeController* data_type_controller = |
| factory->CreateDataTypeController(&factory_, |
| &profile_, |
| service_.get()); |
| SyncBackendHostForProfileSyncTest:: |
| SetDefaultExpectationsForWorkerCreation(&profile_); |
| |
| factory->SetExpectation(&factory_, |
| service_.get(), |
| web_database_.get(), |
| personal_data_manager_.get(), |
| data_type_controller); |
| |
| EXPECT_CALL(factory_, CreateDataTypeManager(_, _)). |
| WillOnce(ReturnNewDataTypeManager()); |
| |
| EXPECT_CALL(profile_, GetWebDataService(_)). |
| WillOnce(Return(web_data_service_.get())); |
| |
| EXPECT_CALL(profile_, GetPersonalDataManager()). |
| WillRepeatedly(Return(personal_data_manager_.get())); |
| |
| EXPECT_CALL(*personal_data_manager_, IsDataLoaded()). |
| WillRepeatedly(Return(true)); |
| |
| // We need tokens to get the tests going |
| token_service_.IssueAuthTokenForTest( |
| GaiaConstants::kSyncService, "token"); |
| |
| EXPECT_CALL(profile_, GetTokenService()). |
| WillRepeatedly(Return(&token_service_)); |
| |
| service_->RegisterDataTypeController(data_type_controller); |
| service_->Initialize(); |
| MessageLoop::current()->Run(); |
| } |
| |
| bool AddAutofillSyncNode(const AutofillEntry& entry) { |
| sync_api::WriteTransaction trans(service_->GetUserShare()); |
| sync_api::ReadNode autofill_root(&trans); |
| if (!autofill_root.InitByTagLookup(browser_sync::kAutofillTag)) |
| return false; |
| |
| sync_api::WriteNode node(&trans); |
| std::string tag = AutofillModelAssociator::KeyToTag(entry.key().name(), |
| entry.key().value()); |
| if (!node.InitUniqueByCreation(syncable::AUTOFILL, autofill_root, tag)) |
| return false; |
| |
| AutofillChangeProcessor::WriteAutofillEntry(entry, &node); |
| return true; |
| } |
| |
| bool AddAutofillSyncNode(const AutofillProfile& profile) { |
| sync_api::WriteTransaction trans(service_->GetUserShare()); |
| sync_api::ReadNode autofill_root(&trans); |
| if (!autofill_root.InitByTagLookup(browser_sync::kAutofillProfileTag)) |
| return false; |
| sync_api::WriteNode node(&trans); |
| std::string tag = profile.guid(); |
| if (!node.InitUniqueByCreation(syncable::AUTOFILL_PROFILE, |
| autofill_root, tag)) |
| return false; |
| AutofillProfileChangeProcessor::WriteAutofillProfile(profile, &node); |
| return true; |
| } |
| |
| bool GetAutofillEntriesFromSyncDB(std::vector<AutofillEntry>* entries, |
| std::vector<AutofillProfile>* profiles) { |
| sync_api::ReadTransaction trans(service_->GetUserShare()); |
| sync_api::ReadNode autofill_root(&trans); |
| if (!autofill_root.InitByTagLookup(browser_sync::kAutofillTag)) |
| return false; |
| |
| int64 child_id = autofill_root.GetFirstChildId(); |
| while (child_id != sync_api::kInvalidId) { |
| sync_api::ReadNode child_node(&trans); |
| if (!child_node.InitByIdLookup(child_id)) |
| return false; |
| |
| const sync_pb::AutofillSpecifics& autofill( |
| child_node.GetAutofillSpecifics()); |
| if (autofill.has_value()) { |
| AutofillKey key(UTF8ToUTF16(autofill.name()), |
| UTF8ToUTF16(autofill.value())); |
| std::vector<base::Time> timestamps; |
| int timestamps_count = autofill.usage_timestamp_size(); |
| for (int i = 0; i < timestamps_count; ++i) { |
| timestamps.push_back(Time::FromInternalValue( |
| autofill.usage_timestamp(i))); |
| } |
| entries->push_back(AutofillEntry(key, timestamps)); |
| } else if (autofill.has_profile()) { |
| AutofillProfile p; |
| p.set_guid(autofill.profile().guid()); |
| AutofillProfileModelAssociator::OverwriteProfileWithServerData(&p, |
| autofill.profile()); |
| profiles->push_back(p); |
| } |
| child_id = child_node.GetSuccessorId(); |
| } |
| return true; |
| } |
| |
| bool GetAutofillProfilesFromSyncDBUnderProfileNode( |
| std::vector<AutofillProfile>* profiles) { |
| sync_api::ReadTransaction trans(service_->GetUserShare()); |
| sync_api::ReadNode autofill_root(&trans); |
| if (!autofill_root.InitByTagLookup(browser_sync::kAutofillProfileTag)) |
| return false; |
| |
| int64 child_id = autofill_root.GetFirstChildId(); |
| while (child_id != sync_api::kInvalidId) { |
| sync_api::ReadNode child_node(&trans); |
| if (!child_node.InitByIdLookup(child_id)) |
| return false; |
| |
| const sync_pb::AutofillProfileSpecifics& autofill( |
| child_node.GetAutofillProfileSpecifics()); |
| AutofillProfile p; |
| p.set_guid(autofill.guid()); |
| AutofillProfileModelAssociator::OverwriteProfileWithServerData(&p, |
| autofill); |
| profiles->push_back(p); |
| child_id = child_node.GetSuccessorId(); |
| } |
| return true; |
| } |
| |
| void SetIdleChangeProcessorExpectations() { |
| EXPECT_CALL(autofill_table_, RemoveFormElement(_, _)).Times(0); |
| EXPECT_CALL(autofill_table_, GetAutofillTimestamps(_, _, _)).Times(0); |
| EXPECT_CALL(autofill_table_, UpdateAutofillEntries(_)).Times(0); |
| } |
| |
| static AutofillEntry MakeAutofillEntry(const char* name, |
| const char* value, |
| time_t timestamp0, |
| time_t timestamp1) { |
| std::vector<Time> timestamps; |
| if (timestamp0 > 0) |
| timestamps.push_back(Time::FromTimeT(timestamp0)); |
| if (timestamp1 > 0) |
| timestamps.push_back(Time::FromTimeT(timestamp1)); |
| return AutofillEntry( |
| AutofillKey(ASCIIToUTF16(name), ASCIIToUTF16(value)), timestamps); |
| } |
| |
| static AutofillEntry MakeAutofillEntry(const char* name, |
| const char* value, |
| time_t timestamp) { |
| return MakeAutofillEntry(name, value, timestamp, -1); |
| } |
| |
| friend class AddAutofillTask<AutofillEntry>; |
| friend class AddAutofillTask<AutofillProfile>; |
| friend class FakeServerUpdater; |
| |
| BrowserThread db_thread_; |
| scoped_refptr<ThreadNotificationService> notification_service_; |
| |
| ProfileMock profile_; |
| AutofillTableMock autofill_table_; |
| scoped_ptr<WebDatabaseFake> web_database_; |
| scoped_refptr<WebDataService> web_data_service_; |
| scoped_refptr<PersonalDataManagerMock> personal_data_manager_; |
| }; |
| |
| template <class T> |
| class AddAutofillTask : public Task { |
| public: |
| AddAutofillTask(ProfileSyncServiceAutofillTest* test, |
| const std::vector<T>& entries) |
| : test_(test), entries_(entries), success_(false) { |
| } |
| |
| virtual void Run() { |
| if (!test_->CreateRoot(GetModelType<T>())) |
| return; |
| for (size_t i = 0; i < entries_.size(); ++i) { |
| if (!test_->AddAutofillSyncNode(entries_[i])) |
| return; |
| } |
| success_ = true; |
| } |
| bool success() { return success_; } |
| |
| private: |
| ProfileSyncServiceAutofillTest* test_; |
| const std::vector<T>& entries_; |
| bool success_; |
| }; |
| |
| // Overload write transaction to use custom NotifyTransactionComplete |
| static const bool kLoggingInfo = true; |
| class WriteTransactionTest: public WriteTransaction { |
| public: |
| WriteTransactionTest(const syncable::ScopedDirLookup& directory, |
| WriterTag writer, const char* source_file, |
| int line, |
| scoped_ptr<WaitableEvent> *wait_for_syncapi) |
| : WriteTransaction(directory, writer, source_file, line), |
| wait_for_syncapi_(wait_for_syncapi) { } |
| |
| virtual void NotifyTransactionComplete(syncable::ModelTypeBitSet types) { |
| // This is where we differ. Force a thread change here, giving another |
| // thread a chance to create a WriteTransaction |
| (*wait_for_syncapi_)->Wait(); |
| |
| WriteTransaction::NotifyTransactionComplete(types); |
| } |
| |
| private: |
| scoped_ptr<WaitableEvent> *wait_for_syncapi_; |
| }; |
| |
| // Our fake server updater. Needs the RefCountedThreadSafe inheritance so we can |
| // post tasks with it. |
| class FakeServerUpdater: public base::RefCountedThreadSafe<FakeServerUpdater> { |
| public: |
| FakeServerUpdater(TestProfileSyncService *service, |
| scoped_ptr<WaitableEvent> *wait_for_start, |
| scoped_ptr<WaitableEvent> *wait_for_syncapi) |
| : entry_(ProfileSyncServiceAutofillTest::MakeAutofillEntry("0", "0", 0)), |
| service_(service), |
| wait_for_start_(wait_for_start), |
| wait_for_syncapi_(wait_for_syncapi), |
| is_finished_(false, false) { } |
| |
| void Update() { |
| // This gets called in a modelsafeworker thread. |
| ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| |
| sync_api::UserShare* user_share = service_->GetUserShare(); |
| syncable::DirectoryManager* dir_manager = user_share->dir_manager.get(); |
| syncable::ScopedDirLookup dir(dir_manager, user_share->name); |
| ASSERT_TRUE(dir.good()); |
| |
| // Create autofill protobuf |
| std::string tag = AutofillModelAssociator::KeyToTag(entry_.key().name(), |
| entry_.key().value()); |
| sync_pb::AutofillSpecifics new_autofill; |
| new_autofill.set_name(UTF16ToUTF8(entry_.key().name())); |
| new_autofill.set_value(UTF16ToUTF8(entry_.key().value())); |
| const std::vector<base::Time>& ts(entry_.timestamps()); |
| for (std::vector<base::Time>::const_iterator timestamp = ts.begin(); |
| timestamp != ts.end(); ++timestamp) { |
| new_autofill.add_usage_timestamp(timestamp->ToInternalValue()); |
| } |
| |
| sync_pb::EntitySpecifics entity_specifics; |
| entity_specifics.MutableExtension(sync_pb::autofill)-> |
| CopyFrom(new_autofill); |
| |
| { |
| // Tell main thread we've started |
| (*wait_for_start_)->Signal(); |
| |
| // Create write transaction. |
| WriteTransactionTest trans(dir, UNITTEST, __FILE__, __LINE__, |
| wait_for_syncapi_); |
| |
| // Create actual entry based on autofill protobuf information. |
| // Simulates effects of SyncerUtil::UpdateLocalDataFromServerData |
| MutableEntry parent(&trans, GET_BY_SERVER_TAG, kAutofillTag); |
| MutableEntry item(&trans, CREATE, parent.Get(syncable::ID), tag); |
| ASSERT_TRUE(item.good()); |
| item.Put(SPECIFICS, entity_specifics); |
| item.Put(SERVER_SPECIFICS, entity_specifics); |
| item.Put(BASE_VERSION, 1); |
| syncable::Id server_item_id = service_->id_factory()->NewServerId(); |
| item.Put(syncable::ID, server_item_id); |
| syncable::Id new_predecessor; |
| ASSERT_TRUE(item.PutPredecessor(new_predecessor)); |
| } |
| VLOG(1) << "FakeServerUpdater finishing."; |
| is_finished_.Signal(); |
| } |
| |
| void CreateNewEntry(const AutofillEntry& entry) { |
| entry_ = entry; |
| scoped_ptr<Callback0::Type> c(NewCallback((FakeServerUpdater *)this, |
| &FakeServerUpdater::Update)); |
| std::vector<browser_sync::ModelSafeWorker*> workers; |
| service_->GetBackendForTest()->GetWorkers(&workers); |
| |
| ASSERT_FALSE(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| if (!BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| NewRunnableMethod(this, &FakeServerUpdater::Update))) { |
| NOTREACHED() << "Failed to post task to the db thread."; |
| return; |
| } |
| } |
| |
| void CreateNewEntryAndWait(const AutofillEntry& entry) { |
| entry_ = entry; |
| scoped_ptr<Callback0::Type> c(NewCallback((FakeServerUpdater *)this, |
| &FakeServerUpdater::Update)); |
| std::vector<browser_sync::ModelSafeWorker*> workers; |
| service_->GetBackendForTest()->GetWorkers(&workers); |
| |
| ASSERT_FALSE(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| is_finished_.Reset(); |
| if (!BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| NewRunnableMethod(this, &FakeServerUpdater::Update))) { |
| NOTREACHED() << "Failed to post task to the db thread."; |
| return; |
| } |
| is_finished_.Wait(); |
| } |
| |
| private: |
| friend class base::RefCountedThreadSafe<FakeServerUpdater>; |
| ~FakeServerUpdater() { } |
| |
| AutofillEntry entry_; |
| TestProfileSyncService *service_; |
| scoped_ptr<WaitableEvent> *wait_for_start_; |
| scoped_ptr<WaitableEvent> *wait_for_syncapi_; |
| WaitableEvent is_finished_; |
| syncable::Id parent_id_; |
| }; |
| |
| // TODO(skrul): Test abort startup. |
| // TODO(skrul): Test processing of cloud changes. |
| // TODO(tim): Add autofill data type controller test, and a case to cover |
| // waiting for the PersonalDataManager. |
| TEST_F(ProfileSyncServiceAutofillTest, FailModelAssociation) { |
| // Don't create the root autofill node so startup fails. |
| StartSyncService(NULL, true, syncable::AUTOFILL); |
| EXPECT_TRUE(service_->unrecoverable_error_detected()); |
| } |
| |
| TEST_F(ProfileSyncServiceAutofillTest, EmptyNativeEmptySync) { |
| EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).WillOnce(Return(true)); |
| EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true)); |
| SetIdleChangeProcessorExpectations(); |
| CreateRootTask task(this, syncable::AUTOFILL); |
| EXPECT_CALL(*personal_data_manager_, Refresh()); |
| StartSyncService(&task, false, syncable::AUTOFILL); |
| ASSERT_TRUE(task.success()); |
| std::vector<AutofillEntry> sync_entries; |
| std::vector<AutofillProfile> sync_profiles; |
| ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles)); |
| EXPECT_EQ(0U, sync_entries.size()); |
| EXPECT_EQ(0U, sync_profiles.size()); |
| } |
| |
| TEST_F(ProfileSyncServiceAutofillTest, HasNativeEntriesEmptySync) { |
| std::vector<AutofillEntry> entries; |
| entries.push_back(MakeAutofillEntry("foo", "bar", 1)); |
| EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)). |
| WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true))); |
| EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true)); |
| SetIdleChangeProcessorExpectations(); |
| CreateRootTask task(this, syncable::AUTOFILL); |
| EXPECT_CALL(*personal_data_manager_, Refresh()); |
| StartSyncService(&task, false, syncable::AUTOFILL); |
| ASSERT_TRUE(task.success()); |
| std::vector<AutofillEntry> sync_entries; |
| std::vector<AutofillProfile> sync_profiles; |
| ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles)); |
| ASSERT_EQ(1U, entries.size()); |
| EXPECT_TRUE(entries[0] == sync_entries[0]); |
| EXPECT_EQ(0U, sync_profiles.size()); |
| } |
| |
| TEST_F(ProfileSyncServiceAutofillTest, HasProfileEmptySync) { |
| |
| std::vector<AutofillProfile*> profiles; |
| std::vector<AutofillProfile> expected_profiles; |
| // Owned by GetAutofillProfiles caller. |
| AutofillProfile* profile0 = new AutofillProfile; |
| autofill_test::SetProfileInfoWithGuid(profile0, |
| "54B3F9AA-335E-4F71-A27D-719C41564230", "Billing", |
| "Mitchell", "Morrison", |
| "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", |
| "91601", "US", "12345678910", "01987654321"); |
| profiles.push_back(profile0); |
| expected_profiles.push_back(*profile0); |
| EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)). |
| WillOnce(DoAll(SetArgumentPointee<0>(profiles), Return(true))); |
| EXPECT_CALL(*personal_data_manager_, Refresh()); |
| SetIdleChangeProcessorExpectations(); |
| CreateRootTask task(this, syncable::AUTOFILL_PROFILE); |
| StartSyncService(&task, false, syncable::AUTOFILL_PROFILE); |
| ASSERT_TRUE(task.success()); |
| std::vector<AutofillProfile> sync_profiles; |
| ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode(&sync_profiles)); |
| EXPECT_EQ(1U, sync_profiles.size()); |
| EXPECT_EQ(0, expected_profiles[0].Compare(sync_profiles[0])); |
| } |
| |
| TEST_F(ProfileSyncServiceAutofillTest, HasNativeWithDuplicatesEmptySync) { |
| // There is buggy autofill code that allows duplicate name/value |
| // pairs to exist in the database with separate pair_ids. |
| std::vector<AutofillEntry> entries; |
| entries.push_back(MakeAutofillEntry("foo", "bar", 1)); |
| entries.push_back(MakeAutofillEntry("dup", "", 2)); |
| entries.push_back(MakeAutofillEntry("dup", "", 3)); |
| EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)). |
| WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true))); |
| EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true)); |
| SetIdleChangeProcessorExpectations(); |
| CreateRootTask task(this, syncable::AUTOFILL); |
| EXPECT_CALL(*personal_data_manager_, Refresh()); |
| StartSyncService(&task, false, syncable::AUTOFILL); |
| ASSERT_TRUE(task.success()); |
| std::vector<AutofillEntry> sync_entries; |
| std::vector<AutofillProfile> sync_profiles; |
| ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles)); |
| EXPECT_EQ(2U, sync_entries.size()); |
| } |
| |
| TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncNoMerge) { |
| AutofillEntry native_entry(MakeAutofillEntry("native", "entry", 1)); |
| AutofillEntry sync_entry(MakeAutofillEntry("sync", "entry", 2)); |
| |
| std::vector<AutofillEntry> native_entries; |
| native_entries.push_back(native_entry); |
| |
| EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)). |
| WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); |
| |
| EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true)); |
| |
| std::vector<AutofillEntry> sync_entries; |
| sync_entries.push_back(sync_entry); |
| |
| AddAutofillTask<AutofillEntry> task(this, sync_entries); |
| |
| EXPECT_CALL(autofill_table_, UpdateAutofillEntries(ElementsAre(sync_entry))). |
| WillOnce(Return(true)); |
| |
| EXPECT_CALL(*personal_data_manager_, Refresh()); |
| StartSyncService(&task, false, syncable::AUTOFILL); |
| ASSERT_TRUE(task.success()); |
| |
| std::set<AutofillEntry> expected_entries; |
| expected_entries.insert(native_entry); |
| expected_entries.insert(sync_entry); |
| |
| std::vector<AutofillEntry> new_sync_entries; |
| std::vector<AutofillProfile> new_sync_profiles; |
| ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, |
| &new_sync_profiles)); |
| std::set<AutofillEntry> new_sync_entries_set(new_sync_entries.begin(), |
| new_sync_entries.end()); |
| |
| EXPECT_TRUE(expected_entries == new_sync_entries_set); |
| } |
| |
| TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeEntry) { |
| AutofillEntry native_entry(MakeAutofillEntry("merge", "entry", 1)); |
| AutofillEntry sync_entry(MakeAutofillEntry("merge", "entry", 2)); |
| AutofillEntry merged_entry(MakeAutofillEntry("merge", "entry", 1, 2)); |
| |
| std::vector<AutofillEntry> native_entries; |
| native_entries.push_back(native_entry); |
| EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)). |
| WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); |
| EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true)); |
| |
| std::vector<AutofillEntry> sync_entries; |
| sync_entries.push_back(sync_entry); |
| AddAutofillTask<AutofillEntry> task(this, sync_entries); |
| |
| EXPECT_CALL(autofill_table_, |
| UpdateAutofillEntries(ElementsAre(merged_entry))).WillOnce(Return(true)); |
| EXPECT_CALL(*personal_data_manager_, Refresh()); |
| StartSyncService(&task, false, syncable::AUTOFILL); |
| ASSERT_TRUE(task.success()); |
| |
| std::vector<AutofillEntry> new_sync_entries; |
| std::vector<AutofillProfile> new_sync_profiles; |
| ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, |
| &new_sync_profiles)); |
| ASSERT_EQ(1U, new_sync_entries.size()); |
| EXPECT_TRUE(merged_entry == new_sync_entries[0]); |
| } |
| |
| TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeProfile) { |
| AutofillProfile sync_profile; |
| autofill_test::SetProfileInfoWithGuid(&sync_profile, |
| "23355099-1170-4B71-8ED4-144470CC9EBE", "Billing", |
| "Mitchell", "Morrison", |
| "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", |
| "91601", "US", "12345678910", "01987654321"); |
| |
| AutofillProfile* native_profile = new AutofillProfile; |
| autofill_test::SetProfileInfoWithGuid(native_profile, |
| "23355099-1170-4B71-8ED4-144470CC9EBE", "Billing", "Alicia", "Saenz", |
| "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", |
| "32801", "US", "19482937549", "13502849239"); |
| |
| std::vector<AutofillProfile*> native_profiles; |
| native_profiles.push_back(native_profile); |
| EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)). |
| WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true))); |
| |
| std::vector<AutofillProfile> sync_profiles; |
| sync_profiles.push_back(sync_profile); |
| AddAutofillTask<AutofillProfile> task(this, sync_profiles); |
| |
| EXPECT_CALL(autofill_table_, UpdateAutofillProfile(_)). |
| WillOnce(Return(true)); |
| EXPECT_CALL(*personal_data_manager_, Refresh()); |
| StartSyncService(&task, false, syncable::AUTOFILL_PROFILE); |
| ASSERT_TRUE(task.success()); |
| |
| std::vector<AutofillProfile> new_sync_profiles; |
| ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode( |
| &new_sync_profiles)); |
| ASSERT_EQ(1U, new_sync_profiles.size()); |
| EXPECT_EQ(0, sync_profile.Compare(new_sync_profiles[0])); |
| } |
| |
| TEST_F(ProfileSyncServiceAutofillTest, MergeProfileWithDifferentGuid) { |
| AutofillProfile sync_profile; |
| |
| autofill_test::SetProfileInfoWithGuid(&sync_profile, |
| "23355099-1170-4B71-8ED4-144470CC9EBE", "Billing", |
| "Mitchell", "Morrison", |
| "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", |
| "91601", "US", "12345678910", "01987654321"); |
| |
| std::string native_guid = "EDC609ED-7EEE-4F27-B00C-423242A9C44B"; |
| AutofillProfile* native_profile = new AutofillProfile; |
| autofill_test::SetProfileInfoWithGuid(native_profile, |
| native_guid.c_str(), "Billing", |
| "Mitchell", "Morrison", |
| "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", |
| "91601", "US", "12345678910", "01987654321"); |
| |
| std::vector<AutofillProfile*> native_profiles; |
| native_profiles.push_back(native_profile); |
| EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)). |
| WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true))); |
| |
| std::vector<AutofillProfile> sync_profiles; |
| sync_profiles.push_back(sync_profile); |
| AddAutofillTask<AutofillProfile> task(this, sync_profiles); |
| |
| EXPECT_CALL(autofill_table_, AddAutofillProfile(_)). |
| WillOnce(Return(true)); |
| EXPECT_CALL(autofill_table_, RemoveAutofillProfile(native_guid)). |
| WillOnce(Return(true)); |
| EXPECT_CALL(*personal_data_manager_, Refresh()); |
| StartSyncService(&task, false, syncable::AUTOFILL_PROFILE); |
| ASSERT_TRUE(task.success()); |
| |
| std::vector<AutofillProfile> new_sync_profiles; |
| ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode( |
| &new_sync_profiles)); |
| ASSERT_EQ(1U, new_sync_profiles.size()); |
| EXPECT_EQ(0, sync_profile.Compare(new_sync_profiles[0])); |
| EXPECT_EQ(sync_profile.guid(), new_sync_profiles[0].guid()); |
| } |
| |
| TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddEntry) { |
| EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).WillOnce(Return(true)); |
| EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true)); |
| EXPECT_CALL(*personal_data_manager_, Refresh()); |
| SetIdleChangeProcessorExpectations(); |
| CreateRootTask task(this, syncable::AUTOFILL); |
| StartSyncService(&task, false, syncable::AUTOFILL); |
| ASSERT_TRUE(task.success()); |
| |
| AutofillEntry added_entry(MakeAutofillEntry("added", "entry", 1)); |
| std::vector<base::Time> timestamps(added_entry.timestamps()); |
| |
| EXPECT_CALL(autofill_table_, GetAutofillTimestamps(_, _, _)). |
| WillOnce(DoAll(SetArgumentPointee<2>(timestamps), Return(true))); |
| |
| AutofillChangeList changes; |
| changes.push_back(AutofillChange(AutofillChange::ADD, added_entry.key())); |
| scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_)); |
| notifier->Notify(NotificationType::AUTOFILL_ENTRIES_CHANGED, |
| Source<WebDataService>(web_data_service_.get()), |
| Details<AutofillChangeList>(&changes)); |
| |
| std::vector<AutofillEntry> new_sync_entries; |
| std::vector<AutofillProfile> new_sync_profiles; |
| ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, |
| &new_sync_profiles)); |
| ASSERT_EQ(1U, new_sync_entries.size()); |
| EXPECT_TRUE(added_entry == new_sync_entries[0]); |
| } |
| |
| TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddProfile) { |
| EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true)); |
| EXPECT_CALL(*personal_data_manager_, Refresh()); |
| SetIdleChangeProcessorExpectations(); |
| CreateRootTask task(this, syncable::AUTOFILL_PROFILE); |
| StartSyncService(&task, false, syncable::AUTOFILL_PROFILE); |
| ASSERT_TRUE(task.success()); |
| |
| AutofillProfile added_profile; |
| autofill_test::SetProfileInfoWithGuid(&added_profile, |
| "D6ADA912-D374-4C0A-917D-F5C8EBE43011", "Josephine", "Alicia", "Saenz", |
| "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", |
| "32801", "US", "19482937549", "13502849239"); |
| |
| AutofillProfileChange change(AutofillProfileChange::ADD, |
| added_profile.guid(), &added_profile); |
| scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_)); |
| notifier->Notify(NotificationType::AUTOFILL_PROFILE_CHANGED, |
| Source<WebDataService>(web_data_service_.get()), |
| Details<AutofillProfileChange>(&change)); |
| |
| std::vector<AutofillProfile> new_sync_profiles; |
| ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode( |
| &new_sync_profiles)); |
| ASSERT_EQ(1U, new_sync_profiles.size()); |
| EXPECT_EQ(0, added_profile.Compare(new_sync_profiles[0])); |
| } |
| |
| TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateEntry) { |
| AutofillEntry original_entry(MakeAutofillEntry("my", "entry", 1)); |
| std::vector<AutofillEntry> original_entries; |
| original_entries.push_back(original_entry); |
| |
| EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)). |
| WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); |
| EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true)); |
| EXPECT_CALL(*personal_data_manager_, Refresh()); |
| CreateRootTask task(this, syncable::AUTOFILL); |
| StartSyncService(&task, false, syncable::AUTOFILL); |
| ASSERT_TRUE(task.success()); |
| |
| AutofillEntry updated_entry(MakeAutofillEntry("my", "entry", 1, 2)); |
| std::vector<base::Time> timestamps(updated_entry.timestamps()); |
| |
| EXPECT_CALL(autofill_table_, GetAutofillTimestamps(_, _, _)). |
| WillOnce(DoAll(SetArgumentPointee<2>(timestamps), Return(true))); |
| |
| AutofillChangeList changes; |
| changes.push_back(AutofillChange(AutofillChange::UPDATE, |
| updated_entry.key())); |
| scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_)); |
| notifier->Notify(NotificationType::AUTOFILL_ENTRIES_CHANGED, |
| Source<WebDataService>(web_data_service_.get()), |
| Details<AutofillChangeList>(&changes)); |
| |
| std::vector<AutofillEntry> new_sync_entries; |
| std::vector<AutofillProfile> new_sync_profiles; |
| ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, |
| &new_sync_profiles)); |
| ASSERT_EQ(1U, new_sync_entries.size()); |
| EXPECT_TRUE(updated_entry == new_sync_entries[0]); |
| } |
| |
| |
| TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveEntry) { |
| AutofillEntry original_entry(MakeAutofillEntry("my", "entry", 1)); |
| std::vector<AutofillEntry> original_entries; |
| original_entries.push_back(original_entry); |
| |
| EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)). |
| WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); |
| EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true)); |
| EXPECT_CALL(*personal_data_manager_, Refresh()); |
| CreateRootTask task(this, syncable::AUTOFILL); |
| StartSyncService(&task, false, syncable::AUTOFILL); |
| ASSERT_TRUE(task.success()); |
| |
| AutofillChangeList changes; |
| changes.push_back(AutofillChange(AutofillChange::REMOVE, |
| original_entry.key())); |
| scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_)); |
| notifier->Notify(NotificationType::AUTOFILL_ENTRIES_CHANGED, |
| Source<WebDataService>(web_data_service_.get()), |
| Details<AutofillChangeList>(&changes)); |
| |
| std::vector<AutofillEntry> new_sync_entries; |
| std::vector<AutofillProfile> new_sync_profiles; |
| ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, |
| &new_sync_profiles)); |
| ASSERT_EQ(0U, new_sync_entries.size()); |
| } |
| |
| TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveProfile) { |
| AutofillProfile sync_profile; |
| autofill_test::SetProfileInfoWithGuid(&sync_profile, |
| "3BA5FA1B-1EC4-4BB3-9B57-EC92BE3C1A09", "Josephine", "Alicia", "Saenz", |
| "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", |
| "32801", "US", "19482937549", "13502849239"); |
| AutofillProfile* native_profile = new AutofillProfile; |
| autofill_test::SetProfileInfoWithGuid(native_profile, |
| "3BA5FA1B-1EC4-4BB3-9B57-EC92BE3C1A09", "Josephine", "Alicia", "Saenz", |
| "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL", |
| "32801", "US", "19482937549", "13502849239"); |
| |
| std::vector<AutofillProfile*> native_profiles; |
| native_profiles.push_back(native_profile); |
| EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)). |
| WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true))); |
| |
| std::vector<AutofillProfile> sync_profiles; |
| sync_profiles.push_back(sync_profile); |
| AddAutofillTask<AutofillProfile> task(this, sync_profiles); |
| EXPECT_CALL(*personal_data_manager_, Refresh()); |
| StartSyncService(&task, false, syncable::AUTOFILL_PROFILE); |
| ASSERT_TRUE(task.success()); |
| |
| AutofillProfileChange change(AutofillProfileChange::REMOVE, |
| sync_profile.guid(), NULL); |
| scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_)); |
| notifier->Notify(NotificationType::AUTOFILL_PROFILE_CHANGED, |
| Source<WebDataService>(web_data_service_.get()), |
| Details<AutofillProfileChange>(&change)); |
| |
| std::vector<AutofillProfile> new_sync_profiles; |
| ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode( |
| &new_sync_profiles)); |
| ASSERT_EQ(0U, new_sync_profiles.size()); |
| } |
| |
| TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeError) { |
| EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).WillOnce(Return(true)); |
| EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true)); |
| EXPECT_CALL(*personal_data_manager_, Refresh()); |
| CreateRootTask task(this, syncable::AUTOFILL); |
| StartSyncService(&task, false, syncable::AUTOFILL); |
| ASSERT_TRUE(task.success()); |
| |
| // Inject an evil entry into the sync db to conflict with the same |
| // entry added by the user. |
| AutofillEntry evil_entry(MakeAutofillEntry("evil", "entry", 1)); |
| ASSERT_TRUE(AddAutofillSyncNode(evil_entry)); |
| |
| AutofillChangeList changes; |
| changes.push_back(AutofillChange(AutofillChange::ADD, |
| evil_entry.key())); |
| scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_)); |
| notifier->Notify(NotificationType::AUTOFILL_ENTRIES_CHANGED, |
| Source<WebDataService>(web_data_service_.get()), |
| Details<AutofillChangeList>(&changes)); |
| |
| // Wait for the PPS to shut everything down and signal us. |
| ProfileSyncServiceObserverMock observer; |
| service_->AddObserver(&observer); |
| EXPECT_CALL(observer, OnStateChanged()).WillOnce(QuitUIMessageLoop()); |
| MessageLoop::current()->Run(); |
| EXPECT_TRUE(service_->unrecoverable_error_detected()); |
| |
| // Ensure future autofill notifications don't crash. |
| notifier->Notify(NotificationType::AUTOFILL_ENTRIES_CHANGED, |
| Source<WebDataService>(web_data_service_.get()), |
| Details<AutofillChangeList>(&changes)); |
| } |
| |
| // Crashy, http://crbug.com/57884 |
| TEST_F(ProfileSyncServiceAutofillTest, DISABLED_ServerChangeRace) { |
| EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).WillOnce(Return(true)); |
| EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true)); |
| EXPECT_CALL(autofill_table_, UpdateAutofillEntries(_)). |
| WillRepeatedly(Return(true)); |
| EXPECT_CALL(*personal_data_manager_, Refresh()).Times(3); |
| CreateRootTask task(this, syncable::AUTOFILL); |
| StartSyncService(&task, false, syncable::AUTOFILL); |
| ASSERT_TRUE(task.success()); |
| |
| // (true, false) means we have to reset after |Signal|, init to unsignaled. |
| scoped_ptr<WaitableEvent> wait_for_start(new WaitableEvent(true, false)); |
| scoped_ptr<WaitableEvent> wait_for_syncapi(new WaitableEvent(true, false)); |
| scoped_refptr<FakeServerUpdater> updater(new FakeServerUpdater( |
| service_.get(), &wait_for_start, &wait_for_syncapi)); |
| |
| // This server side update will stall waiting for CommitWaiter. |
| updater->CreateNewEntry(MakeAutofillEntry("server", "entry", 1)); |
| wait_for_start->Wait(); |
| |
| AutofillEntry syncapi_entry(MakeAutofillEntry("syncapi", "entry", 2)); |
| ASSERT_TRUE(AddAutofillSyncNode(syncapi_entry)); |
| VLOG(1) << "Syncapi update finished."; |
| |
| // If we reach here, it means syncapi succeeded and we didn't deadlock. Yay! |
| // Signal FakeServerUpdater that it can complete. |
| wait_for_syncapi->Signal(); |
| |
| // Make another entry to ensure nothing broke afterwards and wait for finish |
| // to clean up. |
| updater->CreateNewEntryAndWait(MakeAutofillEntry("server2", "entry2", 3)); |
| |
| std::vector<AutofillEntry> sync_entries; |
| std::vector<AutofillProfile> sync_profiles; |
| ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles)); |
| EXPECT_EQ(3U, sync_entries.size()); |
| EXPECT_EQ(0U, sync_profiles.size()); |
| for (size_t i = 0; i < sync_entries.size(); i++) { |
| VLOG(1) << "Entry " << i << ": " << sync_entries[i].key().name() |
| << ", " << sync_entries[i].key().value(); |
| } |
| } |