// Copyright 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 #include #include #include "base/bind.h" #include "base/bind_helpers.h" #include "base/callback.h" #include "base/location.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/run_loop.h" #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" #include "base/synchronization/waitable_event.h" #include "base/thread_task_runner_handle.h" #include "base/threading/thread.h" #include "base/time/time.h" #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/country_names.h" #include "components/autofill/core/browser/personal_data_manager.h" #include "components/autofill/core/browser/webdata/autocomplete_syncable_service.h" #include "components/autofill/core/browser/webdata/autofill_change.h" #include "components/autofill/core/browser/webdata/autofill_data_type_controller.h" #include "components/autofill/core/browser/webdata/autofill_entry.h" #include "components/autofill/core/browser/webdata/autofill_profile_data_type_controller.h" #include "components/autofill/core/browser/webdata/autofill_profile_syncable_service.h" #include "components/autofill/core/browser/webdata/autofill_table.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" #include "components/autofill/core/common/autofill_pref_names.h" #include "components/browser_sync/browser/abstract_profile_sync_service_test.h" #include "components/browser_sync/browser/profile_sync_service.h" #include "components/browser_sync/browser/test_profile_sync_service.h" #include "components/sync_driver/data_type_controller.h" #include "components/sync_driver/data_type_manager_impl.h" #include "components/sync_driver/sync_api_component_factory_mock.h" #include "components/syncable_prefs/pref_service_syncable.h" #include "components/webdata/common/web_database.h" #include "components/webdata_services/web_data_service_test_util.h" #include "sync/internal_api/public/base/model_type.h" #include "sync/internal_api/public/data_type_debug_info_listener.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/syncable_write_transaction.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using autofill::AutocompleteSyncableService; using autofill::AutofillChange; using autofill::AutofillChangeList; using autofill::AutofillEntry; using autofill::AutofillKey; using autofill::AutofillProfile; using autofill::AutofillProfileChange; using autofill::AutofillProfileSyncableService; using autofill::AutofillTable; using autofill::AutofillWebDataService; using autofill::PersonalDataManager; using base::Time; using base::TimeDelta; using base::WaitableEvent; using browser_sync::AutofillDataTypeController; using browser_sync::AutofillProfileDataTypeController; using syncer::AUTOFILL; using syncer::AUTOFILL_PROFILE; using syncer::BaseNode; using syncer::syncable::CREATE; using syncer::syncable::GET_TYPE_ROOT; using syncer::syncable::MutableEntry; using syncer::syncable::UNITTEST; using syncer::syncable::WriterTag; using syncer::syncable::WriteTransaction; using testing::_; using testing::DoAll; using testing::ElementsAre; using testing::Not; using testing::SetArgumentPointee; using testing::Return; namespace { void RegisterAutofillPrefs(user_prefs::PrefRegistrySyncable* registry) { registry->RegisterBooleanPref(autofill::prefs::kAutofillEnabled, true); registry->RegisterBooleanPref(autofill::prefs::kAutofillWalletImportEnabled, true); } void RunAndSignal(const base::Closure& cb, WaitableEvent* event) { cb.Run(); event->Signal(); } 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(); Time date_created = base_time + TimeDelta::FromSeconds(time_shift0); Time date_last_used = date_created; if (time_shift1 >= 0) date_last_used = base_time + TimeDelta::FromSeconds(time_shift1); return AutofillEntry( AutofillKey(base::ASCIIToUTF16(name), base::ASCIIToUTF16(value)), date_created, date_last_used); } AutofillEntry MakeAutofillEntry(const char* name, const char* value, int time_shift) { return MakeAutofillEntry(name, value, time_shift, -1); } } // namespace class AutofillTableMock : public AutofillTable { public: AutofillTableMock() {} MOCK_METHOD2(RemoveFormElement, bool(const base::string16& name, const base::string16& value)); // NOLINT MOCK_METHOD1(GetAllAutofillEntries, bool(std::vector* entries)); // NOLINT MOCK_METHOD4(GetAutofillTimestamps, bool(const base::string16& name, // NOLINT const base::string16& value, Time* date_created, Time* date_last_used)); 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 }; MATCHER_P(MatchProfiles, profile, "") { return (profile.Compare(arg) == 0); } class WebDatabaseFake : public WebDatabase { public: explicit WebDatabaseFake(AutofillTable* autofill_table) { AddTable(autofill_table); } }; class MockAutofillBackend : public autofill::AutofillWebDataBackend { public: MockAutofillBackend(WebDatabase* web_database, const base::Closure& on_changed, const scoped_refptr& ui_thread) : web_database_(web_database), on_changed_(on_changed), ui_thread_(ui_thread) {} ~MockAutofillBackend() override {} WebDatabase* GetDatabase() override { return web_database_; } void AddObserver( autofill::AutofillWebDataServiceObserverOnDBThread* observer) override {} void RemoveObserver( autofill::AutofillWebDataServiceObserverOnDBThread* observer) override {} void RemoveExpiredFormElements() override {} void NotifyOfMultipleAutofillChanges() override { DCHECK(!ui_thread_->RunsTasksOnCurrentThread()); ui_thread_->PostTask(FROM_HERE, on_changed_); } private: WebDatabase* web_database_; base::Closure on_changed_; const scoped_refptr ui_thread_; }; class ProfileSyncServiceAutofillTest; template syncer::ModelType GetModelType() { return syncer::UNSPECIFIED; } template<> syncer::ModelType GetModelType() { return AUTOFILL; } template<> syncer::ModelType GetModelType() { return AUTOFILL_PROFILE; } class TokenWebDataServiceFake : public TokenWebData { public: TokenWebDataServiceFake( const scoped_refptr& ui_thread, const scoped_refptr& db_thread) : TokenWebData(ui_thread, db_thread) {} bool IsDatabaseLoaded() override { return true; } AutofillWebDataService::Handle GetAllTokens( WebDataServiceConsumer* consumer) override { // TODO(tim): It would be nice if WebDataService was injected on // construction of ProfileOAuth2TokenService 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; } private: ~TokenWebDataServiceFake() override {} DISALLOW_COPY_AND_ASSIGN(TokenWebDataServiceFake); }; class WebDataServiceFake : public AutofillWebDataService { public: WebDataServiceFake( const scoped_refptr& ui_thread, const scoped_refptr& db_thread) : AutofillWebDataService(ui_thread, db_thread), web_database_(NULL), autocomplete_syncable_service_(NULL), autofill_profile_syncable_service_(NULL), syncable_service_created_or_destroyed_(false, false), db_thread_(db_thread), ui_thread_(ui_thread) {} void SetDatabase(WebDatabase* web_database) { web_database_ = web_database; } void StartSyncableService() { // The |autofill_profile_syncable_service_| must be constructed on the DB // thread. const base::Closure& on_changed_callback = base::Bind( &WebDataServiceFake::NotifyAutofillMultipleChangedOnUIThread, AsWeakPtr()); db_thread_->PostTask( FROM_HERE, base::Bind(&WebDataServiceFake::CreateSyncableService, base::Unretained(this), on_changed_callback)); syncable_service_created_or_destroyed_.Wait(); } void ShutdownSyncableService() { // The |autofill_profile_syncable_service_| must be destructed on the DB // thread. db_thread_->PostTask(FROM_HERE, base::Bind(&WebDataServiceFake::DestroySyncableService, base::Unretained(this))); syncable_service_created_or_destroyed_.Wait(); } bool IsDatabaseLoaded() override { return true; } WebDatabase* GetDatabase() override { return web_database_; } void OnAutofillEntriesChanged(const AutofillChangeList& changes) { WaitableEvent event(true, false); base::Closure notify_cb = base::Bind(&AutocompleteSyncableService::AutofillEntriesChanged, base::Unretained(autocomplete_syncable_service_), changes); db_thread_->PostTask(FROM_HERE, base::Bind(&RunAndSignal, notify_cb, &event)); event.Wait(); } void OnAutofillProfileChanged(const AutofillProfileChange& changes) { WaitableEvent event(true, false); base::Closure notify_cb = base::Bind(&AutocompleteSyncableService::AutofillProfileChanged, base::Unretained(autofill_profile_syncable_service_), changes); db_thread_->PostTask(FROM_HERE, base::Bind(&RunAndSignal, notify_cb, &event)); event.Wait(); } private: ~WebDataServiceFake() override {} void CreateSyncableService(const base::Closure& on_changed_callback) { ASSERT_TRUE(db_thread_->RunsTasksOnCurrentThread()); // These services are deleted in DestroySyncableService(). backend_.reset(new MockAutofillBackend(GetDatabase(), on_changed_callback, ui_thread_.get())); AutocompleteSyncableService::CreateForWebDataServiceAndBackend( this, backend_.get()); AutofillProfileSyncableService::CreateForWebDataServiceAndBackend( this, backend_.get(), "en-US"); autocomplete_syncable_service_ = AutocompleteSyncableService::FromWebDataService(this); autofill_profile_syncable_service_ = AutofillProfileSyncableService::FromWebDataService(this); syncable_service_created_or_destroyed_.Signal(); } void DestroySyncableService() { ASSERT_TRUE(db_thread_->RunsTasksOnCurrentThread()); autocomplete_syncable_service_ = NULL; autofill_profile_syncable_service_ = NULL; backend_.reset(); syncable_service_created_or_destroyed_.Signal(); } WebDatabase* web_database_; AutocompleteSyncableService* autocomplete_syncable_service_; AutofillProfileSyncableService* autofill_profile_syncable_service_; scoped_ptr backend_; WaitableEvent syncable_service_created_or_destroyed_; const scoped_refptr db_thread_; const scoped_refptr ui_thread_; DISALLOW_COPY_AND_ASSIGN(WebDataServiceFake); }; ACTION_P(ReturnNewDataTypeManagerWithDebugListener, debug_listener) { return new sync_driver::DataTypeManagerImpl( debug_listener, arg1, arg2, arg3, arg4); } class MockPersonalDataManager : public PersonalDataManager { public: MockPersonalDataManager() : PersonalDataManager("en-US") {} MOCK_CONST_METHOD0(IsDataLoaded, bool()); MOCK_METHOD0(LoadProfiles, void()); MOCK_METHOD0(LoadCreditCards, void()); MOCK_METHOD0(Refresh, void()); }; template class AddAutofillHelper; class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest, public syncer::DataTypeDebugInfoListener { public: // DataTypeDebugInfoListener implementation. void OnDataTypeConfigureComplete(const std::vector< syncer::DataTypeConfigurationStats>& configuration_stats) override { ASSERT_EQ(1u, configuration_stats.size()); association_stats_ = configuration_stats[0].association_stats; } protected: ProfileSyncServiceAutofillTest() : debug_ptr_factory_(this) { autofill::CountryNames::SetLocaleString("en-US"); RegisterAutofillPrefs( profile_sync_service_bundle()->pref_service()->registry()); data_type_thread()->Start(); profile_sync_service_bundle()->set_db_thread( data_type_thread()->task_runner()); web_database_.reset(new WebDatabaseFake(&autofill_table_)); web_data_wrapper_ = make_scoped_ptr(new MockWebDataServiceWrapper( new WebDataServiceFake(base::ThreadTaskRunnerHandle::Get(), data_type_thread()->task_runner()), new TokenWebDataServiceFake(base::ThreadTaskRunnerHandle::Get(), data_type_thread()->task_runner()))); web_data_service_ = static_cast( web_data_wrapper_->GetAutofillWebData().get()); web_data_service_->SetDatabase(web_database_.get()); personal_data_manager_ = make_scoped_ptr(new MockPersonalDataManager()); EXPECT_CALL(personal_data_manager(), LoadProfiles()); EXPECT_CALL(personal_data_manager(), LoadCreditCards()); personal_data_manager_->Init( web_data_service_, profile_sync_service_bundle()->pref_service(), profile_sync_service_bundle()->account_tracker(), profile_sync_service_bundle()->signin_manager(), false); web_data_service_->StartSyncableService(); browser_sync::ProfileSyncServiceBundle::SyncClientBuilder builder( profile_sync_service_bundle()); builder.SetPersonalDataManager(personal_data_manager_.get()); builder.SetSyncServiceCallback(GetSyncServiceCallback()); builder.SetSyncableServiceCallback( base::Bind(&ProfileSyncServiceAutofillTest::GetSyncableServiceForType, base::Unretained(this))); builder.set_activate_model_creation(); sync_client_owned_ = builder.Build(); sync_client_ = sync_client_owned_.get(); // When UpdateAutofillEntries() is called with an empty list, the return // value should be |true|, rather than the default of |false|. std::vector empty; EXPECT_CALL(autofill_table_, UpdateAutofillEntries(empty)) .WillRepeatedly(Return(true)); } ~ProfileSyncServiceAutofillTest() override { web_data_service_->ShutdownOnUIThread(); web_data_service_->ShutdownSyncableService(); web_data_wrapper_->Shutdown(); web_data_service_ = nullptr; web_data_wrapper_.reset(); web_database_.reset(); // Shut down the service explicitly before some data members from this // test it needs will be deleted. sync_service()->Shutdown(); } int GetSyncCount(syncer::ModelType type) { syncer::ReadTransaction trans(FROM_HERE, sync_service()->GetUserShare()); syncer::ReadNode node(&trans); if (node.InitTypeRoot(type) != BaseNode::INIT_OK) return 0; return node.GetTotalNodeCount() - 1; } void StartSyncService(const base::Closure& callback, bool will_fail_association, syncer::ModelType type) { SigninManagerBase* signin = profile_sync_service_bundle()->signin_manager(); signin->SetAuthenticatedAccountInfo("12345", "test_user@gmail.com"); CreateSyncService(std::move(sync_client_owned_), callback); EXPECT_CALL(*profile_sync_service_bundle()->component_factory(), CreateDataTypeManager(_, _, _, _, _)) .WillOnce(ReturnNewDataTypeManagerWithDebugListener( syncer::MakeWeakHandle(debug_ptr_factory_.GetWeakPtr()))); EXPECT_CALL(personal_data_manager(), IsDataLoaded()) .WillRepeatedly(Return(true)); // We need tokens to get the tests going profile_sync_service_bundle()->auth_service()->UpdateCredentials( signin->GetAuthenticatedAccountId(), "oauth2_login_token"); sync_service()->RegisterDataTypeController(CreateDataTypeController(type)); sync_service()->Initialize(); base::RunLoop().Run(); // It's possible this test triggered an unrecoverable error, in which case // we can't get the sync count. if (sync_service()->IsSyncActive()) { EXPECT_EQ(GetSyncCount(type), association_stats_.num_sync_items_after_association); } EXPECT_EQ(association_stats_.num_sync_items_after_association, association_stats_.num_sync_items_before_association + association_stats_.num_sync_items_added - association_stats_.num_sync_items_deleted); } bool AddAutofillSyncNode(const AutofillEntry& entry) { syncer::WriteTransaction trans(FROM_HERE, sync_service()->GetUserShare()); syncer::WriteNode node(&trans); std::string tag = AutocompleteSyncableService::KeyToTag( base::UTF16ToUTF8(entry.key().name()), base::UTF16ToUTF8(entry.key().value())); syncer::WriteNode::InitUniqueByCreationResult result = node.InitUniqueByCreation(AUTOFILL, tag); if (result != syncer::WriteNode::INIT_SUCCESS) return false; sync_pb::EntitySpecifics specifics; AutocompleteSyncableService::WriteAutofillEntry(entry, &specifics); node.SetEntitySpecifics(specifics); return true; } bool AddAutofillSyncNode(const AutofillProfile& profile) { syncer::WriteTransaction trans(FROM_HERE, sync_service()->GetUserShare()); syncer::WriteNode node(&trans); std::string tag = profile.guid(); syncer::WriteNode::InitUniqueByCreationResult result = node.InitUniqueByCreation(AUTOFILL_PROFILE, tag); if (result != syncer::WriteNode::INIT_SUCCESS) return false; sync_pb::EntitySpecifics specifics; AutofillProfileSyncableService::WriteAutofillProfile(profile, &specifics); node.SetEntitySpecifics(specifics); return true; } bool GetAutofillEntriesFromSyncDB(std::vector* entries, std::vector* profiles) { syncer::ReadTransaction trans(FROM_HERE, sync_service()->GetUserShare()); syncer::ReadNode autofill_root(&trans); if (autofill_root.InitTypeRoot(AUTOFILL) != BaseNode::INIT_OK) { return false; } int64_t 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.GetEntitySpecifics().autofill()); if (autofill.has_value()) { AutofillKey key(base::UTF8ToUTF16(autofill.name()), base::UTF8ToUTF16(autofill.value())); std::vector