// 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 "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/message_loop.h" #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" #include "base/synchronization/waitable_event.h" #include "base/time/time.h" #include "chrome/browser/autofill/personal_data_manager_factory.h" #include "chrome/browser/prefs/pref_service_syncable.h" #include "chrome/browser/signin/fake_profile_oauth2_token_service.h" #include "chrome/browser/signin/fake_profile_oauth2_token_service_builder.h" #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" #include "chrome/browser/signin/signin_manager_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/profile_sync_components_factory.h" #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/sync/profile_sync_service_factory.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/web_data_service_factory.h" #include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile_manager.h" #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/personal_data_manager.h" #include "components/autofill/core/browser/webdata/autofill_change.h" #include "components/autofill/core/browser/webdata/autofill_entry.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/signin/core/browser/signin_manager.h" #include "components/sync_driver/data_type_controller.h" #include "components/webdata/common/web_data_service_test_util.h" #include "components/webdata/common/web_database.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/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 "sync/test/engine/test_id_factory.h" #include "testing/gmock/include/gmock/gmock.h" using autofill::AutofillChange; using autofill::AutofillChangeList; using autofill::AutofillEntry; using autofill::ServerFieldType; 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 content::BrowserThread; using syncer::AUTOFILL; using syncer::AUTOFILL_PROFILE; using syncer::BaseNode; using syncer::syncable::BASE_VERSION; using syncer::syncable::CREATE; using syncer::syncable::GET_TYPE_ROOT; 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 sync_driver::DataTypeController; using testing::_; using testing::DoAll; using testing::ElementsAre; using testing::Not; using testing::SetArgumentPointee; using testing::Return; class HistoryService; namespace syncable { class Id; } namespace { const char kTestProfileName[] = "test-profile"; void RunAndSignal(const base::Closure& cb, WaitableEvent* event) { cb.Run(); event->Signal(); } } // namespace class AutofillTableMock : public AutofillTable { public: AutofillTableMock() : AutofillTable("en-US") {} 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, base::Time* date_created, base::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) : web_database_(web_database), on_changed_(on_changed) { } virtual ~MockAutofillBackend() {} virtual WebDatabase* GetDatabase() OVERRIDE { return web_database_; } virtual void AddObserver( autofill::AutofillWebDataServiceObserverOnDBThread* observer) OVERRIDE {} virtual void RemoveObserver( autofill::AutofillWebDataServiceObserverOnDBThread* observer) OVERRIDE {} virtual void RemoveExpiredFormElements() OVERRIDE {} virtual void NotifyOfMultipleAutofillChanges() OVERRIDE { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, on_changed_); } private: WebDatabase* web_database_; base::Closure on_changed_; }; 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 TokenWebDataServiceFake : public TokenWebData { public: TokenWebDataServiceFake() : TokenWebData( BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB)) { } virtual bool IsDatabaseLoaded() OVERRIDE { return true; } virtual 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: virtual ~TokenWebDataServiceFake() {} DISALLOW_COPY_AND_ASSIGN(TokenWebDataServiceFake); }; class WebDataServiceFake : public AutofillWebDataService { public: WebDataServiceFake() : AutofillWebDataService( BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB)), web_database_(NULL), autocomplete_syncable_service_(NULL), autofill_profile_syncable_service_(NULL), syncable_service_created_or_destroyed_(false, false) { } 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()); BrowserThread::PostTask(BrowserThread::DB, 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. 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_; } void OnAutofillEntriesChanged(const AutofillChangeList& changes) { WaitableEvent event(true, false); base::Closure notify_cb = base::Bind(&AutocompleteSyncableService::AutofillEntriesChanged, base::Unretained(autocomplete_syncable_service_), changes); BrowserThread::PostTask( BrowserThread::DB, 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); BrowserThread::PostTask( BrowserThread::DB, FROM_HERE, base::Bind(&RunAndSignal, notify_cb, &event)); event.Wait(); } private: virtual ~WebDataServiceFake() {} void CreateSyncableService(const base::Closure& on_changed_callback) { ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB)); // These services are deleted in DestroySyncableService(). backend_.reset(new MockAutofillBackend( GetDatabase(), on_changed_callback)); 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(BrowserThread::CurrentlyOn(BrowserThread::DB)); 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_; DISALLOW_COPY_AND_ASSIGN(WebDataServiceFake); }; KeyedService* BuildMockWebDataServiceWrapper(content::BrowserContext* profile) { return new MockWebDataServiceWrapper( new WebDataServiceFake(), new TokenWebDataServiceFake()); } ACTION_P(MakeAutocompleteSyncComponents, wds) { EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB)); if (!BrowserThread::CurrentlyOn(BrowserThread::DB)) return base::WeakPtr(); return AutocompleteSyncableService::FromWebDataService(wds)->AsWeakPtr(); } ACTION_P(ReturnNewDataTypeManagerWithDebugListener, debug_listener) { return new sync_driver::DataTypeManagerImpl( base::Closure(), debug_listener, arg1, arg2, arg3, arg4); } ACTION_P(MakeAutofillProfileSyncComponents, wds) { EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB)); if (!BrowserThread::CurrentlyOn(BrowserThread::DB)) return base::WeakPtr(); return AutofillProfileSyncableService::FromWebDataService(wds)->AsWeakPtr(); } class AbstractAutofillFactory { public: virtual DataTypeController* CreateDataTypeController( ProfileSyncComponentsFactory* factory, TestingProfile* profile, ProfileSyncService* service) = 0; virtual void SetExpectation(ProfileSyncComponentsFactoryMock* factory, ProfileSyncService* service, AutofillWebDataService* wds, DataTypeController* dtc) = 0; virtual ~AbstractAutofillFactory() {} }; class AutofillEntryFactory : public AbstractAutofillFactory { public: virtual DataTypeController* CreateDataTypeController( ProfileSyncComponentsFactory* factory, TestingProfile* profile, ProfileSyncService* service) OVERRIDE { return new AutofillDataTypeController(factory, profile); } virtual void SetExpectation(ProfileSyncComponentsFactoryMock* factory, ProfileSyncService* service, AutofillWebDataService* wds, DataTypeController* dtc) OVERRIDE { EXPECT_CALL(*factory, GetSyncableServiceForType(syncer::AUTOFILL)). WillOnce(MakeAutocompleteSyncComponents(wds)); } }; class AutofillProfileFactory : public AbstractAutofillFactory { public: virtual DataTypeController* CreateDataTypeController( ProfileSyncComponentsFactory* factory, TestingProfile* profile, ProfileSyncService* service) OVERRIDE { return new AutofillProfileDataTypeController(factory, profile); } virtual void SetExpectation(ProfileSyncComponentsFactoryMock* factory, ProfileSyncService* service, AutofillWebDataService* wds, DataTypeController* dtc) OVERRIDE { EXPECT_CALL(*factory, GetSyncableServiceForType(syncer::AUTOFILL_PROFILE)). WillOnce(MakeAutofillProfileSyncComponents(wds)); } }; 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()); static KeyedService* Build(content::BrowserContext* profile) { return new MockPersonalDataManager(); } }; template class AddAutofillHelper; class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest, public syncer::DataTypeDebugInfoListener { public: // DataTypeDebugInfoListener implementation. virtual void OnDataTypeConfigureComplete( const std::vector& configuration_stats) OVERRIDE { ASSERT_EQ(1u, configuration_stats.size()); association_stats_ = configuration_stats[0].association_stats; } protected: ProfileSyncServiceAutofillTest() : profile_manager_(TestingBrowserProcess::GetGlobal()), debug_ptr_factory_(this) { } 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(); ASSERT_TRUE(profile_manager_.SetUp()); TestingProfile::TestingFactories testing_factories; testing_factories.push_back(std::make_pair( ProfileOAuth2TokenServiceFactory::GetInstance(), BuildAutoIssuingFakeProfileOAuth2TokenService)); profile_ = profile_manager_.CreateTestingProfile( kTestProfileName, scoped_ptr(), base::UTF8ToUTF16(kTestProfileName), 0, std::string(), testing_factories); web_database_.reset(new WebDatabaseFake(&autofill_table_)); MockWebDataServiceWrapper* wrapper = static_cast( WebDataServiceFactory::GetInstance()->SetTestingFactoryAndUse( profile_, BuildMockWebDataServiceWrapper)); web_data_service_ = static_cast(wrapper->GetAutofillWebData().get()); web_data_service_->SetDatabase(web_database_.get()); personal_data_manager_ = static_cast( autofill::PersonalDataManagerFactory::GetInstance() ->SetTestingFactoryAndUse(profile_, MockPersonalDataManager::Build)); EXPECT_CALL(*personal_data_manager_, LoadProfiles()).Times(1); EXPECT_CALL(*personal_data_manager_, LoadCreditCards()).Times(1); personal_data_manager_->Init( WebDataServiceFactory::GetAutofillWebDataForProfile( profile_, Profile::EXPLICIT_ACCESS), profile_->GetPrefs(), profile_->IsOffTheRecord()); web_data_service_->StartSyncableService(); // 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)); } virtual void TearDown() OVERRIDE { // Note: The tear down order is important. ProfileSyncServiceFactory::GetInstance()->SetTestingFactory(profile_, NULL); web_data_service_->ShutdownOnUIThread(); web_data_service_->ShutdownSyncableService(); web_data_service_ = NULL; // To prevent a leak, fully release TestURLRequestContext to ensure its // destruction on the IO message loop. profile_ = NULL; profile_manager_.DeleteTestingProfile(kTestProfileName); AbstractProfileSyncServiceTest::TearDown(); } int GetSyncCount(syncer::ModelType type) { syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare()); syncer::ReadNode node(&trans); if (node.InitTypeRoot(type) != syncer::BaseNode::INIT_OK) return 0; return node.GetTotalNodeCount() - 1; } void StartSyncService(const base::Closure& callback, bool will_fail_association, syncer::ModelType type) { AbstractAutofillFactory* factory = GetFactory(type); SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile_); signin->SetAuthenticatedUsername("test_user@gmail.com"); sync_service_ = TestProfileSyncService::BuildAutoStartAsyncInit(profile_, callback); ProfileSyncComponentsFactoryMock* components = sync_service_->components_factory_mock(); DataTypeController* data_type_controller = factory->CreateDataTypeController(components, profile_, sync_service_); factory->SetExpectation(components, sync_service_, web_data_service_.get(), data_type_controller); EXPECT_CALL(*components, 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 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_) ->UpdateCredentials("test_user@gmail.com", "oauth2_login_token"); sync_service_->RegisterDataTypeController(data_type_controller); sync_service_->Initialize(); base::MessageLoop::current()->Run(); // It's possible this test triggered an unrecoverable error, in which case // we can't get the sync count. if (sync_service_->ShouldPushChanges()) { 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::ReadNode autofill_root(&trans); if (autofill_root.InitTypeRoot(syncer::AUTOFILL) != BaseNode::INIT_OK) { return false; } 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(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, sync_service_->GetUserShare()); syncer::ReadNode autofill_root(&trans); if (autofill_root.InitTypeRoot(AUTOFILL_PROFILE) != 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, sync_service_->GetUserShare()); syncer::ReadNode autofill_root(&trans); if (autofill_root.InitTypeRoot(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(base::UTF8ToUTF16(autofill.name()), base::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.front(), timestamps.back())); } else if (autofill.has_profile()) { AutofillProfile p; p.set_guid(autofill.profile().guid()); AutofillProfileSyncableService::OverwriteProfileWithServerData( autofill.profile(), &p, "en-US"); profiles->push_back(p); } child_id = child_node.GetSuccessorId(); } return true; } bool GetAutofillProfilesFromSyncDBUnderProfileNode( std::vector* profiles) { syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare()); syncer::ReadNode autofill_root(&trans); if (autofill_root.InitTypeRoot(AUTOFILL_PROFILE) != 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, "en-US"); 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); // Only permit UpdateAutofillEntries() to be called with an empty list. std::vector empty; EXPECT_CALL(autofill_table_, UpdateAutofillEntries(Not(empty))).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(); base::Time date_created = base_time + TimeDelta::FromSeconds(time_shift0); base::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); } static AutofillEntry MakeAutofillEntry(const char* name, const char* value, int time_shift) { return MakeAutofillEntry(name, value, time_shift, -1); } friend class AddAutofillHelper; friend class AddAutofillHelper; friend class FakeServerUpdater; TestingProfileManager profile_manager_; TestingProfile* profile_; AutofillTableMock autofill_table_; scoped_ptr web_database_; scoped_refptr web_data_service_; MockPersonalDataManager* personal_data_manager_; syncer::DataTypeAssociationStats association_stats_; base::WeakPtrFactory debug_ptr_factory_; }; template class AddAutofillHelper { public: AddAutofillHelper(ProfileSyncServiceAutofillTest* test, const std::vector& entries) : callback_(base::Bind(&AddAutofillHelper::AddAutofillCallback, base::Unretained(this), test, entries)), success_(false) { } const base::Closure& callback() const { return callback_; } bool success() { return success_; } private: void AddAutofillCallback(ProfileSyncServiceAutofillTest* test, const std::vector& entries) { if (!test->CreateRoot(GetModelType())) return; for (size_t i = 0; i < entries.size(); ++i) { if (!test->AddAutofillSyncNode(entries[i])) return; } success_ = true; } base::Closure callback_; bool success_; }; // Overload write transaction to use custom NotifyTransactionComplete class WriteTransactionTest: public WriteTransaction { public: WriteTransactionTest(const tracked_objects::Location& from_here, WriterTag writer, syncer::syncable::Directory* directory, scoped_ptr* wait_for_syncapi) : WriteTransaction(from_here, writer, directory), wait_for_syncapi_(wait_for_syncapi) { } virtual void NotifyTransactionComplete( syncer::ModelTypeSet types) OVERRIDE { // 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* wait_for_syncapi_; }; // Our fake server updater. Needs the RefCountedThreadSafe inheritance so we can // post tasks with it. class FakeServerUpdater : public base::RefCountedThreadSafe { public: FakeServerUpdater(TestProfileSyncService* service, scoped_ptr* wait_for_start, scoped_ptr* 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)); syncer::UserShare* user_share = service_->GetUserShare(); syncer::syncable::Directory* directory = user_share->directory.get(); // Create autofill protobuf. std::string tag = AutocompleteSyncableService::KeyToTag( base::UTF16ToUTF8(entry_.key().name()), base::UTF16ToUTF8(entry_.key().value())); sync_pb::AutofillSpecifics new_autofill; new_autofill.set_name(base::UTF16ToUTF8(entry_.key().name())); new_autofill.set_value(base::UTF16ToUTF8(entry_.key().value())); new_autofill.add_usage_timestamp(entry_.date_created().ToInternalValue()); if (entry_.date_created() != entry_.date_last_used()) { new_autofill.add_usage_timestamp( entry_.date_last_used().ToInternalValue()); } sync_pb::EntitySpecifics entity_specifics; entity_specifics.mutable_autofill()->CopyFrom(new_autofill); { // Tell main thread we've started (*wait_for_start_)->Signal(); // Create write transaction. WriteTransactionTest trans(FROM_HERE, UNITTEST, directory, wait_for_syncapi_); // Create actual entry based on autofill protobuf information. // Simulates effects of UpdateLocalDataFromServerData MutableEntry parent(&trans, GET_TYPE_ROOT, syncer::AUTOFILL); MutableEntry item(&trans, CREATE, syncer::AUTOFILL, parent.GetId(), tag); ASSERT_TRUE(item.good()); item.PutSpecifics(entity_specifics); item.PutServerSpecifics(entity_specifics); item.PutBaseVersion(1); syncer::syncable::Id server_item_id = service_->id_factory()->NewServerId(); item.PutId(server_item_id); syncer::syncable::Id new_predecessor; ASSERT_TRUE(item.PutPredecessor(new_predecessor)); } DVLOG(1) << "FakeServerUpdater finishing."; is_finished_.Signal(); } void CreateNewEntry(const AutofillEntry& entry) { entry_ = entry; ASSERT_FALSE(BrowserThread::CurrentlyOn(BrowserThread::DB)); if (!BrowserThread::PostTask( BrowserThread::DB, FROM_HERE, base::Bind(&FakeServerUpdater::Update, this))) { NOTREACHED() << "Failed to post task to the db thread."; return; } } void CreateNewEntryAndWait(const AutofillEntry& entry) { entry_ = entry; ASSERT_FALSE(BrowserThread::CurrentlyOn(BrowserThread::DB)); is_finished_.Reset(); if (!BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, base::Bind(&FakeServerUpdater::Update, this))) { NOTREACHED() << "Failed to post task to the db thread."; return; } is_finished_.Wait(); } private: friend class base::RefCountedThreadSafe; ~FakeServerUpdater() { } AutofillEntry entry_; TestProfileSyncService* service_; scoped_ptr* wait_for_start_; scoped_ptr* wait_for_syncapi_; WaitableEvent is_finished_; syncer::syncable::Id parent_id_; }; namespace { // Checks if the field of type |field_type| in |profile1| includes all values // of the field in |profile2|. bool IncludesField(const AutofillProfile& profile1, const AutofillProfile& profile2, ServerFieldType field_type) { std::vector values1; profile1.GetRawMultiInfo(field_type, &values1); std::vector values2; profile2.GetRawMultiInfo(field_type, &values2); std::set values_set; for (size_t i = 0; i < values1.size(); ++i) values_set.insert(values1[i]); for (size_t i = 0; i < values2.size(); ++i) if (values_set.find(values2[i]) == values_set.end()) return false; return true; } } // namespace // 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(base::Closure(), true, syncer::AUTOFILL); EXPECT_TRUE(sync_service_->HasUnrecoverableError()); } TEST_F(ProfileSyncServiceAutofillTest, EmptyNativeEmptySync) { EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).WillOnce(Return(true)); SetIdleChangeProcessorExpectations(); CreateRootHelper create_root(this, syncer::AUTOFILL); EXPECT_CALL(*personal_data_manager_, Refresh()); StartSyncService(create_root.callback(), false, syncer::AUTOFILL); EXPECT_TRUE(create_root.success()); std::vector sync_entries; std::vector 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 entries; entries.push_back(MakeAutofillEntry("foo", "bar", 1)); EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)). WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true))); SetIdleChangeProcessorExpectations(); CreateRootHelper create_root(this, syncer::AUTOFILL); EXPECT_CALL(*personal_data_manager_, Refresh()); StartSyncService(create_root.callback(), false, syncer::AUTOFILL); ASSERT_TRUE(create_root.success()); std::vector sync_entries; std::vector 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 profiles; std::vector 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"); 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(); CreateRootHelper create_root(this, syncer::AUTOFILL_PROFILE); StartSyncService(create_root.callback(), false, syncer::AUTOFILL_PROFILE); ASSERT_TRUE(create_root.success()); std::vector 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 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))); SetIdleChangeProcessorExpectations(); CreateRootHelper create_root(this, syncer::AUTOFILL); EXPECT_CALL(*personal_data_manager_, Refresh()); StartSyncService(create_root.callback(), false, syncer::AUTOFILL); ASSERT_TRUE(create_root.success()); std::vector sync_entries; std::vector 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 native_entries; native_entries.push_back(native_entry); EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)). WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); std::vector sync_entries; sync_entries.push_back(sync_entry); AddAutofillHelper add_autofill(this, sync_entries); EXPECT_CALL(autofill_table_, UpdateAutofillEntries(ElementsAre(sync_entry))). WillOnce(Return(true)); EXPECT_CALL(*personal_data_manager_, Refresh()); StartSyncService(add_autofill.callback(), false, syncer::AUTOFILL); ASSERT_TRUE(add_autofill.success()); std::set expected_entries; expected_entries.insert(native_entry); expected_entries.insert(sync_entry); std::vector new_sync_entries; std::vector new_sync_profiles; ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, &new_sync_profiles)); std::set 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 native_entries; native_entries.push_back(native_entry); EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)). WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true))); std::vector sync_entries; sync_entries.push_back(sync_entry); AddAutofillHelper add_autofill(this, sync_entries); EXPECT_CALL(autofill_table_, UpdateAutofillEntries(ElementsAre(merged_entry))).WillOnce(Return(true)); EXPECT_CALL(*personal_data_manager_, Refresh()); StartSyncService(add_autofill.callback(), false, syncer::AUTOFILL); ASSERT_TRUE(add_autofill.success()); std::vector new_sync_entries; std::vector 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"); 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"); std::vector native_profiles; native_profiles.push_back(native_profile); EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)). WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true))); std::vector sync_profiles; sync_profiles.push_back(sync_profile); AddAutofillHelper add_autofill(this, sync_profiles); EXPECT_CALL(autofill_table_, UpdateAutofillProfile(MatchProfiles(sync_profile))). WillOnce(Return(true)); EXPECT_CALL(*personal_data_manager_, Refresh()); StartSyncService(add_autofill.callback(), false, syncer::AUTOFILL_PROFILE); ASSERT_TRUE(add_autofill.success()); std::vector 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, HasNativeHasSyncMergeProfileCombine) { 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"); AutofillProfile* native_profile = new AutofillProfile; // Same address, but different names, phones and e-mails. autofill::test::SetProfileInfoWithGuid(native_profile, "23355099-1170-4B71-8ED4-144470CC9EBF", "Billing", "Alicia", "Saenz", "joewayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA", "91601", "US", "19482937549"); AutofillProfile expected_profile(sync_profile); expected_profile.OverwriteWithOrAddTo(*native_profile, "en-US"); std::vector native_profiles; native_profiles.push_back(native_profile); EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)). WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true))); EXPECT_CALL(autofill_table_, AddAutofillProfile(MatchProfiles(expected_profile))). WillOnce(Return(true)); EXPECT_CALL(autofill_table_, RemoveAutofillProfile("23355099-1170-4B71-8ED4-144470CC9EBF")). WillOnce(Return(true)); std::vector sync_profiles; sync_profiles.push_back(sync_profile); AddAutofillHelper add_autofill(this, sync_profiles); EXPECT_CALL(*personal_data_manager_, Refresh()); StartSyncService(add_autofill.callback(), false, syncer::AUTOFILL_PROFILE); ASSERT_TRUE(add_autofill.success()); std::vector new_sync_profiles; ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode( &new_sync_profiles)); ASSERT_EQ(1U, new_sync_profiles.size()); // Check that key fields are the same. EXPECT_TRUE(new_sync_profiles[0].IsSubsetOf(sync_profile, "en-US")); // Check that multivalued fields of the synced back data include original // data. EXPECT_TRUE( IncludesField(new_sync_profiles[0], sync_profile, autofill::NAME_FULL)); EXPECT_TRUE(IncludesField( new_sync_profiles[0], sync_profile, autofill::EMAIL_ADDRESS)); EXPECT_TRUE(IncludesField( new_sync_profiles[0], sync_profile, autofill::PHONE_HOME_WHOLE_NUMBER)); } 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"); 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"); std::vector native_profiles; native_profiles.push_back(native_profile); EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)). WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true))); std::vector sync_profiles; sync_profiles.push_back(sync_profile); AddAutofillHelper add_autofill(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(add_autofill.callback(), false, syncer::AUTOFILL_PROFILE); ASSERT_TRUE(add_autofill.success()); std::vector 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(*personal_data_manager_, Refresh()); SetIdleChangeProcessorExpectations(); CreateRootHelper create_root(this, syncer::AUTOFILL); StartSyncService(create_root.callback(), false, syncer::AUTOFILL); ASSERT_TRUE(create_root.success()); AutofillEntry added_entry(MakeAutofillEntry("added", "entry", 1)); EXPECT_CALL(autofill_table_, GetAutofillTimestamps(_, _, _, _)). WillOnce(DoAll(SetArgumentPointee<2>(added_entry.date_created()), SetArgumentPointee<3>(added_entry.date_last_used()), Return(true))); AutofillChangeList changes; changes.push_back(AutofillChange(AutofillChange::ADD, added_entry.key())); web_data_service_->OnAutofillEntriesChanged(changes); std::vector new_sync_entries; std::vector 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(); CreateRootHelper create_root(this, syncer::AUTOFILL_PROFILE); StartSyncService(create_root.callback(), false, syncer::AUTOFILL_PROFILE); ASSERT_TRUE(create_root.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"); AutofillProfileChange change( AutofillProfileChange::ADD, added_profile.guid(), &added_profile); web_data_service_->OnAutofillProfileChanged(change); std::vector 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 original_entries; original_entries.push_back(original_entry); EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)). WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); EXPECT_CALL(*personal_data_manager_, Refresh()); CreateRootHelper create_root(this, syncer::AUTOFILL); StartSyncService(create_root.callback(), false, syncer::AUTOFILL); ASSERT_TRUE(create_root.success()); AutofillEntry updated_entry(MakeAutofillEntry("my", "entry", 1, 2)); EXPECT_CALL(autofill_table_, GetAutofillTimestamps(_, _, _, _)). WillOnce(DoAll(SetArgumentPointee<2>(updated_entry.date_created()), SetArgumentPointee<3>(updated_entry.date_last_used()), Return(true))); AutofillChangeList changes; changes.push_back(AutofillChange(AutofillChange::UPDATE, updated_entry.key())); web_data_service_->OnAutofillEntriesChanged(changes); std::vector new_sync_entries; std::vector 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 original_entries; original_entries.push_back(original_entry); EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)). WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); EXPECT_CALL(*personal_data_manager_, Refresh()); CreateRootHelper create_root(this, syncer::AUTOFILL); StartSyncService(create_root.callback(), false, syncer::AUTOFILL); ASSERT_TRUE(create_root.success()); AutofillChangeList changes; changes.push_back(AutofillChange(AutofillChange::REMOVE, original_entry.key())); web_data_service_->OnAutofillEntriesChanged(changes); std::vector new_sync_entries; std::vector 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"); 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"); std::vector native_profiles; native_profiles.push_back(native_profile); EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)). WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true))); std::vector sync_profiles; sync_profiles.push_back(sync_profile); AddAutofillHelper add_autofill(this, sync_profiles); EXPECT_CALL(*personal_data_manager_, Refresh()); StartSyncService(add_autofill.callback(), false, syncer::AUTOFILL_PROFILE); ASSERT_TRUE(add_autofill.success()); AutofillProfileChange change( AutofillProfileChange::REMOVE, sync_profile.guid(), NULL); web_data_service_->OnAutofillProfileChanged(change); std::vector new_sync_profiles; ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode( &new_sync_profiles)); ASSERT_EQ(0U, new_sync_profiles.size()); } // http://crbug.com/57884 TEST_F(ProfileSyncServiceAutofillTest, DISABLED_ServerChangeRace) { // Once for MergeDataAndStartSyncing() and twice for ProcessSyncChanges(), via // LoadAutofillData(). EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)). Times(3).WillRepeatedly(Return(true)); // On the other hand Autofill and Autocomplete are separated now, so // GetAutofillProfiles() should not be called. EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).Times(0); EXPECT_CALL(autofill_table_, UpdateAutofillEntries(_)). WillRepeatedly(Return(true)); EXPECT_CALL(*personal_data_manager_, Refresh()).Times(3); CreateRootHelper create_root(this, syncer::AUTOFILL); StartSyncService(create_root.callback(), false, syncer::AUTOFILL); ASSERT_TRUE(create_root.success()); // (true, false) means we have to reset after |Signal|, init to unsignaled. scoped_ptr wait_for_start(new WaitableEvent(true, false)); scoped_ptr wait_for_syncapi(new WaitableEvent(true, false)); scoped_refptr updater(new FakeServerUpdater( sync_service_, &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)); DVLOG(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 sync_entries; std::vector 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++) { DVLOG(1) << "Entry " << i << ": " << sync_entries[i].key().name() << ", " << sync_entries[i].key().value(); } }