// 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 #include #include #include "testing/gtest/include/gtest/gtest.h" #include "base/bind.h" #include "base/callback.h" #include "base/location.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/autofill/personal_data_manager.h" #include "chrome/browser/autofill/personal_data_manager_factory.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/glue/autofill_data_type_controller.h" #include "chrome/browser/sync/glue/autofill_profile_data_type_controller.h" #include "chrome/browser/sync/glue/data_type_controller.h" #include "chrome/browser/sync/glue/generic_change_processor.h" #include "chrome/browser/sync/glue/shared_change_processor.h" #include "chrome/browser/sync/glue/syncable_service_adapter.h" #include "chrome/browser/sync/internal_api/read_node.h" #include "chrome/browser/sync/internal_api/read_transaction.h" #include "chrome/browser/sync/internal_api/write_node.h" #include "chrome/browser/sync/internal_api/write_transaction.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/directory_manager.h" #include "chrome/browser/sync/syncable/model_type.h" #include "chrome/browser/sync/syncable/syncable.h" #include "chrome/browser/sync/test/engine/test_id_factory.h" #include "chrome/browser/sync/test_profile_sync_service.h" #include "chrome/browser/webdata/autocomplete_syncable_service.h" #include "chrome/browser/webdata/autofill_change.h" #include "chrome/browser/webdata/autofill_entry.h" #include "chrome/browser/webdata/autofill_profile_syncable_service.h" #include "chrome/browser/webdata/autofill_table.h" #include "chrome/browser/webdata/web_database.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/net/gaia/gaia_constants.h" #include "content/public/browser/notification_source.h" #include "content/test/test_browser_thread.h" #include "testing/gmock/include/gmock/gmock.h" using base::Time; using base::WaitableEvent; using browser_sync::AutofillDataTypeController; using browser_sync::AutofillProfileDataTypeController; using browser_sync::DataTypeController; using browser_sync::GenericChangeProcessor; using browser_sync::SharedChangeProcessor; using browser_sync::SyncableServiceAdapter; using browser_sync::GROUP_DB; using browser_sync::SyncBackendHostForProfileSyncTest; using browser_sync::UnrecoverableErrorHandler; using content::BrowserThread; 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::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* entries)); // NOLINT MOCK_METHOD3(GetAutofillTimestamps, bool(const string16& name, // NOLINT const string16& value, std::vector* timestamps)); MOCK_METHOD1(UpdateAutofillEntries, bool(const std::vector&)); // NOLINT MOCK_METHOD1(GetAutofillProfiles, bool(std::vector*)); // 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 syncable::ModelType GetModelType() { return syncable::UNSPECIFIED; } template<> syncable::ModelType GetModelType() { return syncable::AUTOFILL; } template<> syncable::ModelType GetModelType() { return syncable::AUTOFILL_PROFILE; } class WebDataServiceFake : public WebDataService { public: explicit WebDataServiceFake(WebDatabase* web_database) : web_database_(web_database), syncable_service_created_or_destroyed_(false, false) { } void StartSyncableService() { // The |autofill_profile_syncable_service_| must be constructed on the DB // thread. BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, base::Bind(&WebDataServiceFake::CreateSyncableService, base::Unretained(this))); syncable_service_created_or_destroyed_.Wait(); } void ShutdownSyncableService() { // The |autofill_profile_syncable_service_| must be destructed on the DB // thread. BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, base::Bind(&WebDataServiceFake::DestroySyncableService, base::Unretained(this))); syncable_service_created_or_destroyed_.Wait(); } virtual bool IsDatabaseLoaded() { return true; } virtual WebDatabase* GetDatabase() { return web_database_; } virtual AutocompleteSyncableService* GetAutocompleteSyncableService() const OVERRIDE { EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB)); EXPECT_TRUE(autocomplete_syncable_service_); return autocomplete_syncable_service_; } virtual AutofillProfileSyncableService* GetAutofillProfileSyncableService() const OVERRIDE { EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB)); EXPECT_TRUE(autofill_profile_syncable_service_); return autofill_profile_syncable_service_; } private: void CreateSyncableService() { ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB)); // These services are deleted in DestroySyncableService(). autocomplete_syncable_service_ = new AutocompleteSyncableService(this); autofill_profile_syncable_service_ = new AutofillProfileSyncableService(this); syncable_service_created_or_destroyed_.Signal(); } void DestroySyncableService() { ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB)); delete autofill_profile_syncable_service_; delete autocomplete_syncable_service_; syncable_service_created_or_destroyed_.Signal(); } WebDatabase* web_database_; // We own the syncable services, but don't use a |scoped_ptr| because the // lifetime must be managed on the DB thread. AutocompleteSyncableService* autocomplete_syncable_service_; AutofillProfileSyncableService* autofill_profile_syncable_service_; WaitableEvent syncable_service_created_or_destroyed_; }; ACTION_P(MakeAutocompleteSyncComponents, wds) { EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB)); if (!BrowserThread::CurrentlyOn(BrowserThread::DB)) return base::WeakPtr(); return wds->GetAutocompleteSyncableService()->AsWeakPtr(); } ACTION(MakeGenericChangeProcessor) { sync_api::UserShare* user_share = arg0->GetUserShare(); return new GenericChangeProcessor(arg1, arg2, user_share); } ACTION(MakeSharedChangeProcessor) { return new SharedChangeProcessor(); } ACTION_P(MakeAutofillProfileSyncComponents, wds) { EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB)); if (!BrowserThread::CurrentlyOn(BrowserThread::DB)) return base::WeakPtr();; return wds->GetAutofillProfileSyncableService()->AsWeakPtr(); } class AbstractAutofillFactory { public: virtual DataTypeController* CreateDataTypeController( ProfileSyncFactory* factory, ProfileMock* profile, ProfileSyncService* service) = 0; virtual void SetExpectation(ProfileSyncFactoryMock* factory, ProfileSyncService* service, WebDataService* wds, DataTypeController* dtc) = 0; virtual ~AbstractAutofillFactory() {} }; class AutofillEntryFactory : public AbstractAutofillFactory { public: browser_sync::DataTypeController* CreateDataTypeController( ProfileSyncFactory* factory, ProfileMock* profile, ProfileSyncService* service) { return new AutofillDataTypeController(factory, profile); } void SetExpectation(ProfileSyncFactoryMock* factory, ProfileSyncService* service, WebDataService* wds, DataTypeController* dtc) { EXPECT_CALL(*factory, CreateGenericChangeProcessor(_,_,_)). WillOnce(MakeGenericChangeProcessor()); EXPECT_CALL(*factory, CreateSharedChangeProcessor()). WillOnce(MakeSharedChangeProcessor()); EXPECT_CALL(*factory, GetAutocompleteSyncableService(_)). WillOnce(MakeAutocompleteSyncComponents(wds)); } }; class AutofillProfileFactory : public AbstractAutofillFactory { public: browser_sync::DataTypeController* CreateDataTypeController( ProfileSyncFactory* factory, ProfileMock* profile, ProfileSyncService* service) { return new AutofillProfileDataTypeController(factory, profile); } void SetExpectation(ProfileSyncFactoryMock* factory, ProfileSyncService* service, WebDataService* wds, DataTypeController* dtc) { EXPECT_CALL(*factory, CreateGenericChangeProcessor(_,_,_)). WillOnce(MakeGenericChangeProcessor()); EXPECT_CALL(*factory, CreateSharedChangeProcessor()). WillOnce(MakeSharedChangeProcessor()); EXPECT_CALL(*factory, GetAutofillProfileSyncableService(_)). WillOnce(MakeAutofillProfileSyncComponents(wds)); } }; class PersonalDataManagerMock: public PersonalDataManager { public: static ProfileKeyedService* Build(Profile* profile) { return new PersonalDataManagerMock; } MOCK_CONST_METHOD0(IsDataLoaded, bool()); MOCK_METHOD0(LoadProfiles, void()); MOCK_METHOD0(LoadCreditCards, void()); MOCK_METHOD0(Refresh, void()); }; template class AddAutofillTask; class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest { protected: ProfileSyncServiceAutofillTest() { } 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() { AbstractProfileSyncServiceTest::SetUp(); profile_.CreateRequestContext(); web_database_.reset(new WebDatabaseFake(&autofill_table_)); web_data_service_ = new WebDataServiceFake(web_database_.get()); personal_data_manager_ = static_cast( PersonalDataManagerFactory::GetInstance()->SetTestingFactoryAndUse( &profile_, PersonalDataManagerMock::Build)); EXPECT_CALL(*personal_data_manager_, LoadProfiles()).Times(1); EXPECT_CALL(*personal_data_manager_, LoadCreditCards()).Times(1); EXPECT_CALL(profile_, GetWebDataService(_)). // TokenService::Initialize // AutofillDataTypeController::StartModels() // In some tests: // AutofillProfileSyncableService::AutofillProfileSyncableService() WillRepeatedly(Return(web_data_service_.get())); personal_data_manager_->Init(&profile_); notification_service_ = new ThreadNotificationService( db_thread_.DeprecatedGetThreadObject()); notification_service_->Init(); // Note: This must be called *after* the notification service is created. web_data_service_->StartSyncableService(); } virtual void TearDown() { // Note: The tear down order is important. service_.reset(); web_data_service_->ShutdownSyncableService(); notification_service_->TearDown(); profile_.ResetRequestContext(); AbstractProfileSyncServiceTest::TearDown(); } 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)); EXPECT_CALL(profile_, GetProfileSyncService()).WillRepeatedly( Return(service_.get())); DataTypeController* data_type_controller = factory->CreateDataTypeController(&factory_, &profile_, service_.get()); SyncBackendHostForProfileSyncTest:: SetDefaultExpectationsForWorkerCreation(&profile_); factory->SetExpectation(&factory_, service_.get(), web_data_service_.get(), data_type_controller); EXPECT_CALL(factory_, CreateDataTypeManager(_, _)). WillOnce(ReturnNewDataTypeManager()); 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_.get())); service_->RegisterDataTypeController(data_type_controller); service_->Initialize(); MessageLoop::current()->Run(); } bool AddAutofillSyncNode(const AutofillEntry& entry) { sync_api::WriteTransaction trans(FROM_HERE, service_->GetUserShare()); sync_api::ReadNode autofill_root(&trans); if (!autofill_root.InitByTagLookup( syncable::ModelTypeToRootTag(syncable::AUTOFILL))) return false; sync_api::WriteNode node(&trans); std::string tag = AutocompleteSyncableService::KeyToTag( UTF16ToUTF8(entry.key().name()), UTF16ToUTF8(entry.key().value())); if (!node.InitUniqueByCreation(syncable::AUTOFILL, autofill_root, tag)) return false; sync_pb::EntitySpecifics specifics; AutocompleteSyncableService::WriteAutofillEntry(entry, &specifics); sync_pb::AutofillSpecifics* autofill_specifics = specifics.MutableExtension(sync_pb::autofill); node.SetAutofillSpecifics(*autofill_specifics); return true; } bool AddAutofillSyncNode(const AutofillProfile& profile) { sync_api::WriteTransaction trans(FROM_HERE, service_->GetUserShare()); sync_api::ReadNode autofill_root(&trans); if (!autofill_root.InitByTagLookup(kAutofillProfileTag)) return false; sync_api::WriteNode node(&trans); std::string tag = profile.guid(); if (!node.InitUniqueByCreation(syncable::AUTOFILL_PROFILE, autofill_root, tag)) return false; sync_pb::EntitySpecifics specifics; AutofillProfileSyncableService::WriteAutofillProfile(profile, &specifics); sync_pb::AutofillProfileSpecifics* profile_specifics = specifics.MutableExtension(sync_pb::autofill_profile); node.SetAutofillProfileSpecifics(*profile_specifics); return true; } bool GetAutofillEntriesFromSyncDB(std::vector* entries, std::vector* profiles) { sync_api::ReadTransaction trans(FROM_HERE, service_->GetUserShare()); sync_api::ReadNode autofill_root(&trans); if (!autofill_root.InitByTagLookup( syncable::ModelTypeToRootTag(syncable::AUTOFILL))) 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 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()); AutofillProfileSyncableService::OverwriteProfileWithServerData( autofill.profile(), &p); profiles->push_back(p); } child_id = child_node.GetSuccessorId(); } return true; } bool GetAutofillProfilesFromSyncDBUnderProfileNode( std::vector* profiles) { sync_api::ReadTransaction trans(FROM_HERE, service_->GetUserShare()); sync_api::ReadNode autofill_root(&trans); if (!autofill_root.InitByTagLookup(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()); AutofillProfileSyncableService::OverwriteProfileWithServerData( autofill, &p); 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