// 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 #include #include #include "testing/gtest/include/gtest/gtest.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/callback.h" #include "base/compiler_specific.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/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/signin/signin_manager.h" #include "chrome/browser/signin/signin_manager_factory.h" #include "chrome/browser/signin/token_service_factory.h" #include "chrome/browser/sync/abstract_profile_sync_service_test.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/profile_sync_components_factory.h" #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/sync/profile_sync_test_util.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_data_service.h" #include "chrome/browser/webdata/web_data_service_factory.h" #include "chrome/browser/webdata/web_database.h" #include "chrome/common/chrome_notification_types.h" #include "content/public/browser/notification_source.h" #include "content/public/test/test_browser_thread.h" #include "google_apis/gaia/gaia_constants.h" #include "sync/internal_api/public/base/model_type.h" #include "sync/internal_api/public/read_node.h" #include "sync/internal_api/public/read_transaction.h" #include "sync/internal_api/public/write_node.h" #include "sync/internal_api/public/write_transaction.h" #include "sync/protocol/autofill_specifics.pb.h" #include "sync/syncable/mutable_entry.h" #include "sync/syncable/write_transaction.h" #include "sync/test/engine/test_id_factory.h" #include "testing/gmock/include/gmock/gmock.h" using base::Time; using base::TimeDelta; using base::WaitableEvent; using browser_sync::AutofillDataTypeController; using browser_sync::AutofillProfileDataTypeController; using browser_sync::DataTypeController; using browser_sync::GenericChangeProcessor; using browser_sync::SharedChangeProcessor; using content::BrowserThread; using syncer::AUTOFILL; using syncer::BaseNode; using syncer::syncable::BASE_VERSION; using syncer::syncable::CREATE; using syncer::syncable::GET_BY_SERVER_TAG; using syncer::syncable::MutableEntry; using syncer::syncable::SERVER_SPECIFICS; using syncer::syncable::SPECIFICS; using syncer::syncable::UNITTEST; using syncer::syncable::WriterTag; using syncer::syncable::WriteTransaction; using testing::_; using testing::DoAll; using testing::ElementsAre; using testing::SetArgumentPointee; using testing::Return; namespace syncable { class Id; } class HistoryService; 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(UpdateAutofillProfileMulti, bool(const AutofillProfile&)); // NOLINT MOCK_METHOD1(AddAutofillProfile, bool(const AutofillProfile&)); // NOLINT MOCK_METHOD1(RemoveAutofillProfile, bool(const std::string&)); // NOLINT }; MATCHER_P(MatchProfiles, profile, "") { return (profile.Compare(arg) == 0); } class WebDatabaseFake : public WebDatabase { public: explicit WebDatabaseFake(AutofillTable* autofill_table) : autofill_table_(autofill_table) {} virtual AutofillTable* GetAutofillTable() OVERRIDE { return autofill_table_; } private: AutofillTable* autofill_table_; }; class ProfileSyncServiceAutofillTest; template syncer::ModelType GetModelType() { return syncer::UNSPECIFIED; } template<> syncer::ModelType GetModelType() { return syncer::AUTOFILL; } template<> syncer::ModelType GetModelType() { return syncer::AUTOFILL_PROFILE; } class WebDataServiceFake : public WebDataService { public: WebDataServiceFake() : web_database_(NULL), syncable_service_created_or_destroyed_(false, false) { } static scoped_refptr Build(Profile* profile) { return new WebDataServiceFake; } void SetDatabase(WebDatabase* web_database) { web_database_ = web_database; } 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() OVERRIDE { return true; } virtual WebDatabase* GetDatabase() OVERRIDE { return web_database_; } virtual WebDataService::Handle GetAllTokens( WebDataServiceConsumer* consumer) OVERRIDE { // TODO(tim): It would be nice if WebDataService was injected on // construction of TokenService rather than fetched by Initialize so that // this isn't necessary (we could pass a NULL service). We currently do // return it via EXPECT_CALLs, but without depending on order-of- // initialization (which seems way more fragile) we can't tell which // component is asking at what time, and some components in these Autofill // tests require a WebDataService. return 0; } 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_; } virtual void ShutdownOnUIThread() OVERRIDE {} private: virtual ~WebDataServiceFake() {} 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) { syncer::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( ProfileSyncComponentsFactory* factory, ProfileMock* profile, ProfileSyncService* service) = 0; virtual void SetExpectation(ProfileSyncComponentsFactoryMock* factory, ProfileSyncService* service, WebDataService* wds, DataTypeController* dtc) = 0; virtual ~AbstractAutofillFactory() {} }; class AutofillEntryFactory : public AbstractAutofillFactory { public: virtual browser_sync::DataTypeController* CreateDataTypeController( ProfileSyncComponentsFactory* factory, ProfileMock* profile, ProfileSyncService* service) OVERRIDE { return new AutofillDataTypeController(factory, profile, service); } virtual void SetExpectation(ProfileSyncComponentsFactoryMock* factory, ProfileSyncService* service, WebDataService* wds, DataTypeController* dtc) OVERRIDE { EXPECT_CALL(*factory, CreateGenericChangeProcessor(_,_,_)). WillOnce(MakeGenericChangeProcessor()); EXPECT_CALL(*factory, CreateSharedChangeProcessor()). WillOnce(MakeSharedChangeProcessor()); EXPECT_CALL(*factory, GetSyncableServiceForType(syncer::AUTOFILL)). WillOnce(MakeAutocompleteSyncComponents(wds)); } }; class AutofillProfileFactory : public AbstractAutofillFactory { public: virtual browser_sync::DataTypeController* CreateDataTypeController( ProfileSyncComponentsFactory* factory, ProfileMock* profile, ProfileSyncService* service) OVERRIDE { return new AutofillProfileDataTypeController(factory, profile, service); } virtual void SetExpectation(ProfileSyncComponentsFactoryMock* factory, ProfileSyncService* service, WebDataService* wds, DataTypeController* dtc) OVERRIDE { EXPECT_CALL(*factory, CreateGenericChangeProcessor(_,_,_)). WillOnce(MakeGenericChangeProcessor()); EXPECT_CALL(*factory, CreateSharedChangeProcessor()). WillOnce(MakeSharedChangeProcessor()); EXPECT_CALL(*factory, GetSyncableServiceForType(syncer::AUTOFILL_PROFILE)). 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 AddAutofillHelper; class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest { protected: ProfileSyncServiceAutofillTest() { } virtual ~ProfileSyncServiceAutofillTest() { } AutofillProfileFactory profile_factory_; AutofillEntryFactory entry_factory_; AbstractAutofillFactory* GetFactory(syncer::ModelType type) { if (type == syncer::AUTOFILL) { return &entry_factory_; } else if (type == syncer::AUTOFILL_PROFILE) { return &profile_factory_; } else { NOTREACHED(); return NULL; } } virtual void SetUp() OVERRIDE { AbstractProfileSyncServiceTest::SetUp(); profile_.reset(new ProfileMock()); profile_->CreateRequestContext(); web_database_.reset(new WebDatabaseFake(&autofill_table_)); web_data_service_ = static_cast( WebDataServiceFactory::GetInstance()->SetTestingFactoryAndUse( profile_.get(), WebDataServiceFake::Build).get()); web_data_service_->SetDatabase(web_database_.get()); personal_data_manager_ = static_cast( PersonalDataManagerFactory::GetInstance()->SetTestingFactoryAndUse( profile_.get(), PersonalDataManagerMock::Build)); token_service_ = static_cast( TokenServiceFactory::GetInstance()->SetTestingFactoryAndUse( profile_.get(), BuildTokenService)); EXPECT_CALL(*personal_data_manager_, LoadProfiles()).Times(1); EXPECT_CALL(*personal_data_manager_, LoadCreditCards()).Times(1); personal_data_manager_->Init(profile_.get()); // Note: This must be called *after* the notification service is created. web_data_service_->StartSyncableService(); } virtual void TearDown() OVERRIDE { // Note: The tear down order is important. service_->Shutdown(); service_.reset(); web_data_service_->ShutdownSyncableService(); profile_->ResetRequestContext(); // To prevent a leak, fully release TestURLRequestContext to ensure its // destruction on the IO message loop. profile_.reset(); AbstractProfileSyncServiceTest::TearDown(); } void StartSyncService(const base::Closure& callback, bool will_fail_association, syncer::ModelType type) { AbstractAutofillFactory* factory = GetFactory(type); SigninManager* signin = SigninManagerFactory::GetForProfile(profile_.get()); signin->SetAuthenticatedUsername("test_user"); ProfileSyncComponentsFactoryMock* components_factory = new ProfileSyncComponentsFactoryMock(); service_.reset( new TestProfileSyncService(components_factory, profile_.get(), signin, ProfileSyncService::AUTO_START, false, callback)); DataTypeController* data_type_controller = factory->CreateDataTypeController(components_factory, profile_.get(), service_.get()); factory->SetExpectation(components_factory, service_.get(), web_data_service_.get(), data_type_controller); EXPECT_CALL(*components_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"); service_->RegisterDataTypeController(data_type_controller); service_->Initialize(); MessageLoop::current()->Run(); } bool AddAutofillSyncNode(const AutofillEntry& entry) { syncer::WriteTransaction trans(FROM_HERE, service_->GetUserShare()); syncer::ReadNode autofill_root(&trans); if (autofill_root.InitByTagLookup( syncer::ModelTypeToRootTag(syncer::AUTOFILL)) != BaseNode::INIT_OK) { return false; } syncer::WriteNode node(&trans); std::string tag = AutocompleteSyncableService::KeyToTag( UTF16ToUTF8(entry.key().name()), UTF16ToUTF8(entry.key().value())); syncer::WriteNode::InitUniqueByCreationResult result = node.InitUniqueByCreation(syncer::AUTOFILL, autofill_root, tag); if (result != syncer::WriteNode::INIT_SUCCESS) return false; sync_pb::EntitySpecifics specifics; AutocompleteSyncableService::WriteAutofillEntry(entry, &specifics); sync_pb::AutofillSpecifics* autofill_specifics = specifics.mutable_autofill(); node.SetAutofillSpecifics(*autofill_specifics); return true; } bool AddAutofillSyncNode(const AutofillProfile& profile) { syncer::WriteTransaction trans(FROM_HERE, service_->GetUserShare()); syncer::ReadNode autofill_root(&trans); if (autofill_root.InitByTagLookup(kAutofillProfileTag) != BaseNode::INIT_OK) { return false; } syncer::WriteNode node(&trans); std::string tag = profile.guid(); syncer::WriteNode::InitUniqueByCreationResult result = node.InitUniqueByCreation(syncer::AUTOFILL_PROFILE, autofill_root, tag); if (result != syncer::WriteNode::INIT_SUCCESS) return false; sync_pb::EntitySpecifics specifics; AutofillProfileSyncableService::WriteAutofillProfile(profile, &specifics); sync_pb::AutofillProfileSpecifics* profile_specifics = specifics.mutable_autofill_profile(); node.SetAutofillProfileSpecifics(*profile_specifics); return true; } bool GetAutofillEntriesFromSyncDB(std::vector* entries, std::vector* profiles) { syncer::ReadTransaction trans(FROM_HERE, service_->GetUserShare()); syncer::ReadNode autofill_root(&trans); if (autofill_root.InitByTagLookup( syncer::ModelTypeToRootTag(syncer::AUTOFILL)) != BaseNode::INIT_OK) { return false; } int64 child_id = autofill_root.GetFirstChildId(); while (child_id != syncer::kInvalidId) { syncer::ReadNode child_node(&trans); if (child_node.InitByIdLookup(child_id) != BaseNode::INIT_OK) 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) { syncer::ReadTransaction trans(FROM_HERE, service_->GetUserShare()); syncer::ReadNode autofill_root(&trans); if (autofill_root.InitByTagLookup(kAutofillProfileTag) != BaseNode::INIT_OK) { return false; } int64 child_id = autofill_root.GetFirstChildId(); while (child_id != syncer::kInvalidId) { syncer::ReadNode child_node(&trans); if (child_node.InitByIdLookup(child_id) != BaseNode::INIT_OK) 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, int time_shift0, int time_shift1) { // Time deep in the past would cause Autocomplete sync to discard the // entries. static Time base_time = Time::Now().LocalMidnight(); std::vector