| // Copyright 2018 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 "components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h" |
| |
| #include <stddef.h> |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/guid.h" |
| #include "base/location.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/run_loop.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/bind_test_util.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "base/time/time.h" |
| #include "components/autofill/core/browser/autofill_profile.h" |
| #include "components/autofill/core/browser/autofill_profile_sync_util.h" |
| #include "components/autofill/core/browser/country_names.h" |
| #include "components/autofill/core/browser/test_autofill_clock.h" |
| #include "components/autofill/core/browser/webdata/autofill_change.h" |
| #include "components/autofill/core/browser/webdata/autofill_table.h" |
| #include "components/autofill/core/browser/webdata/mock_autofill_webdata_backend.h" |
| #include "components/autofill/core/common/autofill_constants.h" |
| #include "components/sync/base/hash_util.h" |
| #include "components/sync/model/data_batch.h" |
| #include "components/sync/model/data_type_activation_request.h" |
| #include "components/sync/model/entity_data.h" |
| #include "components/sync/model/mock_model_type_change_processor.h" |
| #include "components/sync/model/sync_data.h" |
| #include "components/sync/model/sync_error_factory.h" |
| #include "components/sync/model/sync_error_factory_mock.h" |
| #include "components/sync/model_impl/client_tag_based_model_type_processor.h" |
| #include "components/sync/protocol/sync.pb.h" |
| #include "components/webdata/common/web_database.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace autofill { |
| |
| using base::ASCIIToUTF16; |
| using base::ScopedTempDir; |
| using base::UTF16ToUTF8; |
| using base::UTF8ToUTF16; |
| using sync_pb::AutofillProfileSpecifics; |
| using syncer::DataBatch; |
| using syncer::EntityChange; |
| using syncer::EntityChangeList; |
| using syncer::EntityData; |
| using syncer::EntityDataPtr; |
| using syncer::KeyAndData; |
| using syncer::MockModelTypeChangeProcessor; |
| using syncer::ModelType; |
| using testing::_; |
| using testing::DoAll; |
| using testing::ElementsAre; |
| using testing::Eq; |
| using testing::Property; |
| using testing::Return; |
| using testing::UnorderedElementsAre; |
| |
| namespace { |
| |
| // Some guids for testing. |
| const char kGuidA[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44A"; |
| const char kGuidB[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44B"; |
| const char kGuidC[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44C"; |
| const char kGuidD[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44D"; |
| const char kGuidInvalid[] = "EDC609ED-7EEE-4F27-B00C"; |
| const char kHttpOrigin[] = "http://www.example.com/"; |
| const char kHttpsOrigin[] = "https://www.example.com/"; |
| const int kValidityStateBitfield = 1984; |
| const char kLocaleString[] = "en-US"; |
| const base::Time kJune2017 = base::Time::FromDoubleT(1497552271); |
| |
| AutofillProfile CreateAutofillProfile( |
| const AutofillProfileSpecifics& specifics) { |
| // As more copying does not hurt in tests, we prefer to use AutofillProfile |
| // instead of std::unique_ptr<AutofillProfile> because of code brevity. |
| return *CreateAutofillProfileFromSpecifics(specifics); |
| } |
| |
| AutofillProfileSpecifics CreateAutofillProfileSpecifics( |
| const AutofillProfile& entry) { |
| // Reuse production code. We do not need EntityData, just take out the |
| // specifics. |
| std::unique_ptr<EntityData> entity_data = |
| CreateEntityDataFromAutofillProfile(entry); |
| return entity_data->specifics.autofill_profile(); |
| } |
| |
| AutofillProfileSpecifics CreateAutofillProfileSpecifics( |
| const std::string& guid, |
| const std::string& origin) { |
| AutofillProfileSpecifics specifics; |
| specifics.set_guid(guid); |
| specifics.set_origin(origin); |
| // Make it consistent with the constructor of AutofillProfile constructor (the |
| // clock value is overrided by TestAutofillClock in the test fixture). |
| specifics.set_use_count(1); |
| specifics.set_use_date(kJune2017.ToTimeT()); |
| return specifics; |
| } |
| |
| MATCHER_P(HasSpecifics, expected, "") { |
| AutofillProfile arg_profile = |
| CreateAutofillProfile(arg->specifics.autofill_profile()); |
| AutofillProfile expected_profile = CreateAutofillProfile(expected); |
| if (!arg_profile.EqualsIncludingUsageStatsForTesting(expected_profile)) { |
| *result_listener << "entry\n[" << arg_profile << "]\n" |
| << "did not match expected\n[" << expected_profile << "]"; |
| return false; |
| } |
| return true; |
| } |
| |
| MATCHER_P(WithUsageStats, expected, "") { |
| if (!arg.EqualsIncludingUsageStatsForTesting(expected)) { |
| *result_listener << "entry\n[" << arg << "]\n" |
| << "did not match expected\n[" << expected << "]"; |
| return false; |
| } |
| return true; |
| } |
| |
| void ExtractAutofillProfilesFromDataBatch( |
| std::unique_ptr<DataBatch> batch, |
| std::vector<AutofillProfile>* output) { |
| while (batch->HasNext()) { |
| const KeyAndData& data_pair = batch->Next(); |
| output->push_back( |
| CreateAutofillProfile(data_pair.second->specifics.autofill_profile())); |
| } |
| } |
| |
| // Returns a profile with all fields set. Contains identical data to the data |
| // returned from ConstructCompleteSpecifics(). |
| AutofillProfile ConstructCompleteProfile() { |
| AutofillProfile profile(kGuidA, kHttpsOrigin); |
| |
| profile.set_use_count(7); |
| profile.set_use_date(base::Time::FromTimeT(1423182152)); |
| |
| profile.SetRawInfo(NAME_FULL, ASCIIToUTF16("John K. Doe, Jr.")); |
| profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); |
| profile.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("K.")); |
| profile.SetRawInfo(NAME_LAST, ASCIIToUTF16("Doe")); |
| |
| profile.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("user@example.com")); |
| profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("1.800.555.1234")); |
| |
| profile.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("123 Fake St.\n" |
| "Apt. 42")); |
| EXPECT_EQ(ASCIIToUTF16("123 Fake St."), |
| profile.GetRawInfo(ADDRESS_HOME_LINE1)); |
| EXPECT_EQ(ASCIIToUTF16("Apt. 42"), profile.GetRawInfo(ADDRESS_HOME_LINE2)); |
| |
| profile.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Google, Inc.")); |
| profile.SetRawInfo(ADDRESS_HOME_CITY, ASCIIToUTF16("Mountain View")); |
| profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("California")); |
| profile.SetRawInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("94043")); |
| profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US")); |
| profile.SetRawInfo(ADDRESS_HOME_SORTING_CODE, ASCIIToUTF16("CEDEX")); |
| profile.SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY, |
| ASCIIToUTF16("Santa Clara")); |
| profile.set_language_code("en"); |
| profile.SetClientValidityFromBitfieldValue(kValidityStateBitfield); |
| return profile; |
| } |
| |
| // Returns AutofillProfileSpecifics with all Autofill profile fields set. |
| // Contains identical data to the data returned from ConstructCompleteProfile(). |
| AutofillProfileSpecifics ConstructCompleteSpecifics() { |
| AutofillProfileSpecifics specifics; |
| |
| specifics.set_guid(kGuidA); |
| specifics.set_origin(kHttpsOrigin); |
| specifics.set_use_count(7); |
| specifics.set_use_date(1423182152); |
| |
| specifics.add_name_first("John"); |
| specifics.add_name_middle("K."); |
| specifics.add_name_last("Doe"); |
| specifics.add_name_full("John K. Doe, Jr."); |
| |
| specifics.add_email_address("user@example.com"); |
| |
| specifics.add_phone_home_whole_number("1.800.555.1234"); |
| |
| specifics.set_address_home_line1("123 Fake St."); |
| specifics.set_address_home_line2("Apt. 42"); |
| specifics.set_address_home_street_address( |
| "123 Fake St.\n" |
| "Apt. 42"); |
| |
| specifics.set_company_name("Google, Inc."); |
| specifics.set_address_home_city("Mountain View"); |
| specifics.set_address_home_state("California"); |
| specifics.set_address_home_zip("94043"); |
| specifics.set_address_home_country("US"); |
| specifics.set_address_home_sorting_code("CEDEX"); |
| specifics.set_address_home_dependent_locality("Santa Clara"); |
| specifics.set_address_home_language_code("en"); |
| specifics.set_validity_state_bitfield(kValidityStateBitfield); |
| |
| return specifics; |
| } |
| |
| } // namespace |
| |
| class AutofillProfileSyncBridgeTest : public testing::Test { |
| public: |
| AutofillProfileSyncBridgeTest() {} |
| ~AutofillProfileSyncBridgeTest() override {} |
| |
| void SetUp() override { |
| // Fix a time for implicitly constructed use_dates in AutofillProfile. |
| test_clock_.SetNow(kJune2017); |
| CountryNames::SetLocaleString(kLocaleString); |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| db_.AddTable(&table_); |
| db_.Init(temp_dir_.GetPath().AppendASCII("SyncTestWebDatabase")); |
| ON_CALL(*backend(), GetDatabase()).WillByDefault(Return(&db_)); |
| ResetProcessor(); |
| ResetBridge(); |
| } |
| |
| void ResetProcessor() { |
| real_processor_ = |
| std::make_unique<syncer::ClientTagBasedModelTypeProcessor>( |
| syncer::AUTOFILL_PROFILE, /*dump_stack=*/base::DoNothing(), |
| /*commit_only=*/false); |
| mock_processor_.DelegateCallsByDefaultTo(real_processor_.get()); |
| } |
| |
| void ResetBridge() { |
| bridge_.reset(new AutofillProfileSyncBridge( |
| mock_processor_.CreateForwardingProcessor(), kLocaleString, &backend_)); |
| } |
| |
| void StartSyncing( |
| const std::vector<AutofillProfileSpecifics>& remote_data = {}) { |
| base::RunLoop loop; |
| syncer::DataTypeActivationRequest request; |
| request.error_handler = base::DoNothing(); |
| real_processor_->OnSyncStarting( |
| request, |
| base::BindLambdaForTesting( |
| [&loop](std::unique_ptr<syncer::DataTypeActivationResponse>) { |
| loop.Quit(); |
| })); |
| loop.Run(); |
| |
| // Initialize the processor with initial_sync_done. |
| sync_pb::ModelTypeState state; |
| state.set_initial_sync_done(true); |
| syncer::UpdateResponseDataList initial_updates; |
| for (const AutofillProfileSpecifics& specifics : remote_data) { |
| initial_updates.push_back(SpecificsToUpdateResponse(specifics)); |
| } |
| real_processor_->OnUpdateReceived(state, initial_updates); |
| } |
| |
| void ApplySyncChanges(const EntityChangeList& changes) { |
| const base::Optional<syncer::ModelError> error = bridge()->ApplySyncChanges( |
| bridge()->CreateMetadataChangeList(), changes); |
| EXPECT_FALSE(error) << error->ToString(); |
| } |
| |
| void AddAutofillProfilesToTable( |
| const std::vector<AutofillProfile>& profile_list) { |
| for (const auto& profile : profile_list) { |
| table_.AddAutofillProfile(profile); |
| } |
| } |
| |
| std::vector<AutofillProfile> GetAllLocalData() { |
| std::vector<AutofillProfile> data; |
| // Perform an async call synchronously for testing. |
| base::RunLoop loop; |
| bridge()->GetAllDataForDebugging(base::BindLambdaForTesting( |
| [&loop, &data](std::unique_ptr<DataBatch> batch) { |
| ExtractAutofillProfilesFromDataBatch(std::move(batch), &data); |
| loop.Quit(); |
| })); |
| loop.Run(); |
| return data; |
| } |
| |
| EntityDataPtr SpecificsToEntity(const AutofillProfileSpecifics& specifics) { |
| EntityData data; |
| *data.specifics.mutable_autofill_profile() = specifics; |
| data.client_tag_hash = syncer::GenerateSyncableHash( |
| syncer::AUTOFILL_PROFILE, bridge()->GetClientTag(data)); |
| return data.PassToPtr(); |
| } |
| |
| syncer::UpdateResponseData SpecificsToUpdateResponse( |
| const AutofillProfileSpecifics& specifics) { |
| syncer::UpdateResponseData data; |
| data.entity = SpecificsToEntity(specifics); |
| return data; |
| } |
| |
| AutofillProfileSyncBridge* bridge() { return bridge_.get(); } |
| |
| syncer::MockModelTypeChangeProcessor& mock_processor() { |
| return mock_processor_; |
| } |
| |
| AutofillTable* table() { return &table_; } |
| |
| MockAutofillWebDataBackend* backend() { return &backend_; } |
| |
| private: |
| autofill::TestAutofillClock test_clock_; |
| ScopedTempDir temp_dir_; |
| base::test::ScopedTaskEnvironment scoped_task_environment_; |
| testing::NiceMock<MockAutofillWebDataBackend> backend_; |
| AutofillTable table_; |
| WebDatabase db_; |
| testing::NiceMock<MockModelTypeChangeProcessor> mock_processor_; |
| std::unique_ptr<syncer::ClientTagBasedModelTypeProcessor> real_processor_; |
| std::unique_ptr<AutofillProfileSyncBridge> bridge_; |
| |
| DISALLOW_COPY_AND_ASSIGN(AutofillProfileSyncBridgeTest); |
| }; |
| |
| TEST_F(AutofillProfileSyncBridgeTest, AutofillProfileChanged_Added) { |
| StartSyncing({}); |
| |
| AutofillProfile local(kGuidA, kHttpsOrigin); |
| local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Jane")); |
| AutofillProfileChange change(AutofillProfileChange::ADD, kGuidA, &local); |
| |
| EXPECT_CALL( |
| mock_processor(), |
| Put(kGuidA, HasSpecifics(CreateAutofillProfileSpecifics(local)), _)); |
| |
| bridge()->AutofillProfileChanged(change); |
| } |
| |
| // Language code in autofill profiles should be synced to the server. |
| TEST_F(AutofillProfileSyncBridgeTest, |
| AutofillProfileChanged_Added_LanguageCodePropagates) { |
| StartSyncing({}); |
| |
| AutofillProfile local(kGuidA, kHttpsOrigin); |
| local.set_language_code("en"); |
| AutofillProfileChange change(AutofillProfileChange::ADD, kGuidA, &local); |
| |
| EXPECT_CALL( |
| mock_processor(), |
| Put(kGuidA, HasSpecifics(CreateAutofillProfileSpecifics(local)), _)); |
| |
| bridge()->AutofillProfileChanged(change); |
| } |
| |
| // Validity state bitfield in autofill profiles should be synced to the server. |
| TEST_F(AutofillProfileSyncBridgeTest, |
| AutofillProfileChanged_Added_LocalValidityBitfieldPropagates) { |
| StartSyncing({}); |
| |
| AutofillProfile local(kGuidA, kHttpsOrigin); |
| local.SetClientValidityFromBitfieldValue(kValidityStateBitfield); |
| AutofillProfileChange change(AutofillProfileChange::ADD, kGuidA, &local); |
| |
| EXPECT_CALL( |
| mock_processor(), |
| Put(kGuidA, HasSpecifics(CreateAutofillProfileSpecifics(local)), _)); |
| |
| bridge()->AutofillProfileChanged(change); |
| } |
| |
| // Local updates should be properly propagated to the server. |
| TEST_F(AutofillProfileSyncBridgeTest, AutofillProfileChanged_Updated) { |
| StartSyncing({}); |
| |
| AutofillProfile local(kGuidA, kHttpsOrigin); |
| local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Jane")); |
| AutofillProfileChange change(AutofillProfileChange::UPDATE, kGuidA, &local); |
| |
| EXPECT_CALL( |
| mock_processor(), |
| Put(kGuidA, HasSpecifics(CreateAutofillProfileSpecifics(local)), _)); |
| |
| bridge()->AutofillProfileChanged(change); |
| } |
| |
| // Usage stats should be updated by the client. |
| TEST_F(AutofillProfileSyncBridgeTest, |
| AutofillProfileChanged_Updated_UsageStatsOverwrittenByClient) { |
| // Remote data has a profile with usage stats. |
| AutofillProfileSpecifics remote = |
| CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); |
| remote.set_address_home_language_code("en"); |
| remote.set_use_count(9); |
| remote.set_use_date(25); |
| |
| StartSyncing({remote}); |
| EXPECT_THAT(GetAllLocalData(), |
| ElementsAre(WithUsageStats(CreateAutofillProfile(remote)))); |
| |
| // Update to the usage stats for that profile. |
| AutofillProfile local(kGuidA, kHttpsOrigin); |
| local.set_language_code("en"); |
| local.set_use_count(10U); |
| local.set_use_date(base::Time::FromTimeT(30)); |
| AutofillProfileChange change(AutofillProfileChange::UPDATE, kGuidA, &local); |
| |
| EXPECT_CALL( |
| mock_processor(), |
| Put(kGuidA, HasSpecifics(CreateAutofillProfileSpecifics(local)), _)); |
| |
| bridge()->AutofillProfileChanged(change); |
| } |
| |
| // Server profile updates should be ignored. |
| TEST_F(AutofillProfileSyncBridgeTest, |
| AutofillProfileChanged_Updated_IgnoreServerProfiles) { |
| StartSyncing({}); |
| |
| AutofillProfile server_profile(AutofillProfile::SERVER_PROFILE, "server-id"); |
| AutofillProfileChange change(AutofillProfileChange::UPDATE, |
| server_profile.guid(), &server_profile); |
| |
| EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); |
| // Should not crash. |
| bridge()->AutofillProfileChanged(change); |
| } |
| |
| TEST_F(AutofillProfileSyncBridgeTest, AutofillProfileChanged_Deleted) { |
| StartSyncing({}); |
| |
| AutofillProfileChange change(AutofillProfileChange::REMOVE, kGuidB, nullptr); |
| EXPECT_CALL(mock_processor(), Delete(kGuidB, _)); |
| |
| bridge()->AutofillProfileChanged(change); |
| } |
| |
| TEST_F(AutofillProfileSyncBridgeTest, GetAllDataForDebugging) { |
| AutofillProfile local1 = AutofillProfile(kGuidA, kHttpsOrigin); |
| local1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); |
| local1.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); |
| AutofillProfile local2 = AutofillProfile(kGuidB, kHttpsOrigin); |
| local2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom")); |
| local2.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2nd st")); |
| AddAutofillProfilesToTable({local1, local2}); |
| |
| EXPECT_THAT(GetAllLocalData(), UnorderedElementsAre(local1, local2)); |
| } |
| |
| TEST_F(AutofillProfileSyncBridgeTest, GetData) { |
| AutofillProfile local1 = AutofillProfile(kGuidA, kHttpsOrigin); |
| local1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); |
| local1.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); |
| AutofillProfile local2 = AutofillProfile(kGuidB, kHttpsOrigin); |
| local2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom")); |
| local2.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2nd st")); |
| AddAutofillProfilesToTable({local1, local2}); |
| |
| std::vector<AutofillProfile> data; |
| base::RunLoop loop; |
| bridge()->GetData({kGuidA}, |
| base::BindLambdaForTesting( |
| [&loop, &data](std::unique_ptr<DataBatch> batch) { |
| ExtractAutofillProfilesFromDataBatch(std::move(batch), |
| &data); |
| loop.Quit(); |
| })); |
| loop.Run(); |
| |
| EXPECT_THAT(data, ElementsAre(local1)); |
| } |
| |
| TEST_F(AutofillProfileSyncBridgeTest, MergeSyncData) { |
| AutofillProfile local1 = AutofillProfile(kGuidA, kHttpOrigin); |
| local1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); |
| local1.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); |
| AutofillProfile local2 = AutofillProfile(kGuidB, std::string()); |
| local2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom")); |
| local2.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2nd st")); |
| AddAutofillProfilesToTable({local1, local2}); |
| |
| AutofillProfileSpecifics remote1 = |
| CreateAutofillProfileSpecifics(kGuidC, kHttpsOrigin); |
| remote1.add_name_first("Jane"); |
| AutofillProfileSpecifics remote2 = |
| CreateAutofillProfileSpecifics(kGuidD, kSettingsOrigin); |
| remote2.add_name_first("Harry"); |
| // This one will have the name and origin updated. |
| AutofillProfileSpecifics remote3 = |
| CreateAutofillProfileSpecifics(kGuidB, kSettingsOrigin); |
| remote3.add_name_first("Tom Doe"); |
| |
| EXPECT_CALL( |
| mock_processor(), |
| Put(kGuidA, HasSpecifics(CreateAutofillProfileSpecifics(local1)), _)); |
| EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0); |
| |
| StartSyncing({remote1, remote2, remote3}); |
| |
| EXPECT_THAT(GetAllLocalData(), |
| UnorderedElementsAre(local1, CreateAutofillProfile(remote1), |
| CreateAutofillProfile(remote2), |
| CreateAutofillProfile(remote3))); |
| } |
| |
| // Ensure that all profile fields are able to be synced up from the client to |
| // the server. |
| TEST_F(AutofillProfileSyncBridgeTest, MergeSyncData_SyncAllFieldsToServer) { |
| AutofillProfile local = ConstructCompleteProfile(); |
| AddAutofillProfilesToTable({local}); |
| |
| // This complete profile is fully uploaded to sync. |
| EXPECT_CALL(mock_processor(), |
| Put(_, HasSpecifics(ConstructCompleteSpecifics()), _)); |
| StartSyncing({}); |
| |
| // No changes locally. |
| EXPECT_THAT(GetAllLocalData(), ElementsAre(WithUsageStats(local))); |
| } |
| |
| // Ensure that all profile fields are able to be synced down from the server to |
| // the client (and nothing gets uploaded back). |
| TEST_F(AutofillProfileSyncBridgeTest, MergeSyncData_SyncAllFieldsToClient) { |
| EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); |
| StartSyncing({ConstructCompleteSpecifics()}); |
| |
| EXPECT_THAT(GetAllLocalData(), |
| ElementsAre(WithUsageStats(ConstructCompleteProfile()))); |
| } |
| |
| TEST_F(AutofillProfileSyncBridgeTest, MergeSyncData_IdenticalProfiles) { |
| AutofillProfile local1 = AutofillProfile(kGuidA, kHttpOrigin); |
| local1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); |
| local1.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); |
| AutofillProfile local2 = AutofillProfile(kGuidB, kSettingsOrigin); |
| local2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom")); |
| local2.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2nd st")); |
| AddAutofillProfilesToTable({local1, local2}); |
| |
| // The synced profiles are identical to the local ones, except that the guids |
| // are different. |
| AutofillProfileSpecifics remote1 = |
| CreateAutofillProfileSpecifics(kGuidC, kHttpsOrigin); |
| remote1.add_name_first("John"); |
| remote1.set_address_home_street_address("1 1st st"); |
| AutofillProfileSpecifics remote2 = |
| CreateAutofillProfileSpecifics(kGuidD, kHttpsOrigin); |
| remote2.add_name_first("Tom"); |
| remote2.set_address_home_street_address("2 2nd st"); |
| |
| // Both remote profiles win, only the verified origin is taken over for the |
| // second profile. |
| AutofillProfileSpecifics merged2(remote2); |
| merged2.set_origin(kSettingsOrigin); |
| EXPECT_CALL(mock_processor(), Put(kGuidD, HasSpecifics(merged2), _)); |
| |
| StartSyncing({remote1, remote2}); |
| |
| EXPECT_THAT(GetAllLocalData(), |
| UnorderedElementsAre(CreateAutofillProfile(remote1), |
| CreateAutofillProfile(merged2))); |
| } |
| |
| TEST_F(AutofillProfileSyncBridgeTest, MergeSyncData_NonSimilarProfiles) { |
| AutofillProfile local = ConstructCompleteProfile(); |
| local.set_guid(kGuidA); |
| local.SetRawInfo(NAME_FULL, ASCIIToUTF16("John K. Doe, Jr.")); |
| local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); |
| local.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("K.")); |
| local.SetRawInfo(NAME_LAST, ASCIIToUTF16("Doe")); |
| AddAutofillProfilesToTable({local}); |
| |
| // The remote profile are not similar as the names are different (all other |
| // fields except for guids are identical). |
| AutofillProfileSpecifics remote = ConstructCompleteSpecifics(); |
| remote.set_guid(kGuidB); |
| remote.set_name_full(0, "Jane T. Roe, Sr."); |
| remote.set_name_first(0, "Jane"); |
| remote.set_name_middle(0, "T."); |
| remote.set_name_last(0, "Roe"); |
| |
| // The profiles are not similar enough and thus do not get merged. |
| // Expect the local one being synced up and the remote one being added to the |
| // local database. |
| EXPECT_CALL( |
| mock_processor(), |
| Put(kGuidA, HasSpecifics(CreateAutofillProfileSpecifics(local)), _)); |
| EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0); |
| |
| StartSyncing({remote}); |
| |
| EXPECT_THAT(GetAllLocalData(), |
| UnorderedElementsAre(local, CreateAutofillProfile(remote))); |
| } |
| |
| TEST_F(AutofillProfileSyncBridgeTest, MergeSyncData_SimilarProfiles) { |
| AutofillProfile local1 = AutofillProfile(kGuidA, kHttpOrigin); |
| local1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); |
| local1.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("1 1st st")); |
| local1.set_use_count(27); |
| AutofillProfile local2 = AutofillProfile(kGuidB, kSettingsOrigin); |
| local2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Tom")); |
| local2.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("2 2nd st")); |
| AddAutofillProfilesToTable({local1, local2}); |
| |
| // The synced profiles are identical to the local ones, except that the guids |
| // and use_count values are different. |
| AutofillProfileSpecifics remote1 = |
| CreateAutofillProfileSpecifics(kGuidC, kHttpsOrigin); |
| remote1.add_name_first("John"); |
| remote1.set_address_home_street_address("1 1st st"); |
| remote1.set_company_name("Frobbers, Inc."); |
| remote1.set_use_count(13); |
| AutofillProfileSpecifics remote2 = |
| CreateAutofillProfileSpecifics(kGuidD, kHttpsOrigin); |
| remote2.add_name_first("Tom"); |
| remote2.set_address_home_street_address("2 2nd st"); |
| remote2.set_company_name("Fizzbang, LLC."); |
| remote2.set_use_count(4); |
| |
| // The first profile should have its origin updated. |
| // The second profile should remain as-is, because an unverified profile |
| // should never overwrite a verified one. |
| AutofillProfileSpecifics merged1(remote1); |
| merged1.set_origin(kHttpOrigin); |
| // TODO(jkrcal): This is taken over from the previous test suite without any |
| // reasoning why this happens. This indeed happens, deep in |
| // AutofillProfileComparator when merging profiles both without NAME_FULL, we |
| // obtain a profile with NAME_FULL. Not sure if intended. |
| merged1.add_name_full("John"); |
| // Merging two profile takes their max use count. |
| merged1.set_use_count(27); |
| |
| // Expect updating the first (merged) profile and adding the second local one. |
| EXPECT_CALL(mock_processor(), Put(kGuidC, HasSpecifics(merged1), _)); |
| EXPECT_CALL( |
| mock_processor(), |
| Put(kGuidB, HasSpecifics(CreateAutofillProfileSpecifics(local2)), _)); |
| EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0); |
| |
| StartSyncing({remote1, remote2}); |
| |
| EXPECT_THAT(GetAllLocalData(), |
| UnorderedElementsAre( |
| WithUsageStats(CreateAutofillProfile(merged1)), local2, |
| WithUsageStats(CreateAutofillProfile(remote2)))); |
| } |
| |
| // TODO(jkrcal): All the MergeSimilarProfiles_* tests need some diff in Info to |
| // trigger the merge similar code path (we create the diff using phone number). |
| // Otherwise, we trigger the merge same code path and none of the tests pass. Is |
| // it desired? |
| |
| // Tests that MergeSimilarProfiles keeps the most recent use date of the two |
| // profiles being merged. |
| TEST_F(AutofillProfileSyncBridgeTest, |
| MergeSyncData_SimilarProfiles_OlderUseDate) { |
| // Different guids, same origin, difference in the phone number. |
| AutofillProfile local(kGuidA, kHttpOrigin); |
| local.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("650234567")); |
| local.set_use_date(base::Time::FromTimeT(30)); |
| AddAutofillProfilesToTable({local}); |
| |
| AutofillProfileSpecifics remote = |
| CreateAutofillProfileSpecifics(kGuidB, kHttpOrigin); |
| // |local| has a more recent use date. |
| remote.set_use_date(25); |
| |
| // The use date of |local| should replace the use date of |remote|. |
| AutofillProfileSpecifics merged(remote); |
| merged.set_use_date(30); |
| merged.add_phone_home_whole_number("650234567"); |
| EXPECT_CALL(mock_processor(), Put(kGuidB, HasSpecifics(merged), _)); |
| |
| StartSyncing({remote}); |
| } |
| |
| // Tests that MergeSimilarProfiles keeps the most recent use date of the two |
| // profiles being merged. |
| TEST_F(AutofillProfileSyncBridgeTest, |
| MergeSyncData_SimilarProfiles_NewerUseDate) { |
| // Different guids, same origin, difference in the phone number. |
| AutofillProfile local(kGuidA, kHttpOrigin); |
| local.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("650234567")); |
| local.set_use_date(base::Time::FromTimeT(30)); |
| AddAutofillProfilesToTable({local}); |
| |
| AutofillProfileSpecifics remote = |
| CreateAutofillProfileSpecifics(kGuidB, kHttpOrigin); |
| // |remote| has a more recent use date. |
| remote.set_use_date(35); |
| |
| // The use date of |local| should _not_ replace the use date of |remote|. |
| AutofillProfileSpecifics merged(remote); |
| merged.add_phone_home_whole_number("650234567"); |
| EXPECT_CALL(mock_processor(), Put(kGuidB, HasSpecifics(merged), _)); |
| |
| StartSyncing({remote}); |
| } |
| |
| // Tests that MergeSimilarProfiles saves the max of the use counts of the two |
| // profiles in |remote|. |
| TEST_F(AutofillProfileSyncBridgeTest, |
| MergeSyncData_SimilarProfiles_NonZeroUseCounts) { |
| // Different guids, same origin, difference in the phone number. |
| AutofillProfile local(kGuidA, kHttpOrigin); |
| local.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("650234567")); |
| local.set_use_count(12); |
| AddAutofillProfilesToTable({local}); |
| |
| AutofillProfileSpecifics remote = |
| CreateAutofillProfileSpecifics(kGuidB, kHttpOrigin); |
| remote.set_use_count(5); |
| |
| // The use count of |local| should replace the use count of |remote|. |
| AutofillProfileSpecifics merged(remote); |
| merged.set_use_count(12); |
| merged.add_phone_home_whole_number("650234567"); |
| EXPECT_CALL(mock_processor(), Put(kGuidB, HasSpecifics(merged), _)); |
| |
| StartSyncing({remote}); |
| } |
| |
| // Tests that when merging similar profiles for initial sync, we add the |
| // additional information of |local| into |remote|. |
| TEST_F(AutofillProfileSyncBridgeTest, |
| MergeSyncData_SimilarProfiles_LocalOriginPreserved) { |
| AutofillProfile local(kGuidA, kHttpsOrigin); |
| local.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("650234567")); |
| AddAutofillProfilesToTable({local}); |
| |
| AutofillProfileSpecifics remote = |
| CreateAutofillProfileSpecifics(kGuidB, kHttpOrigin); |
| remote.set_address_home_language_code("en"); |
| |
| // Expect that the resulting merged profile is written back to sync and that |
| // it has the phone number and origin from |local|. |
| AutofillProfileSpecifics merged(remote); |
| merged.set_origin(kHttpsOrigin); |
| merged.add_phone_home_whole_number("650234567"); |
| // TODO(jkrcal): Is this expected that language code gets deleted? Not |
| // explicitly covered by previous tests but happens. |
| merged.set_address_home_language_code(""); |
| EXPECT_CALL(mock_processor(), Put(kGuidB, HasSpecifics(merged), _)); |
| |
| StartSyncing({remote}); |
| } |
| |
| // Sync data without origin should not overwrite existing origin in local |
| // autofill profile. |
| TEST_F(AutofillProfileSyncBridgeTest, |
| MergeSyncData_SimilarProfiles_LocalExistingOriginPreserved) { |
| AutofillProfile local(kGuidA, kHttpsOrigin); |
| AddAutofillProfilesToTable({local}); |
| |
| // Remote data does not have an origin value. |
| AutofillProfileSpecifics remote = CreateAutofillProfileSpecifics(kGuidA, ""); |
| remote.clear_origin(); |
| remote.add_name_first("John"); |
| ASSERT_FALSE(remote.has_origin()); |
| |
| // Expect no sync events to add origin to the remote data. |
| EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); |
| StartSyncing({remote}); |
| |
| // Expect the local autofill profile to still have an origin after sync. |
| AutofillProfile merged(local); |
| merged.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); |
| |
| EXPECT_THAT(GetAllLocalData(), ElementsAre(merged)); |
| } |
| |
| // Ensure that no Sync events are generated to fill in missing origins from Sync |
| // with explicitly present empty ones. This ensures that the migration to add |
| // origins to profiles does not generate lots of needless Sync updates. |
| TEST_F(AutofillProfileSyncBridgeTest, |
| MergeSyncData_SimilarProfiles_LocalMissingOriginPreserved) { |
| AutofillProfile local = AutofillProfile(kGuidA, std::string()); |
| local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); |
| AddAutofillProfilesToTable({local}); |
| |
| // Create a Sync profile identical to |local|, except with no origin set. |
| AutofillProfileSpecifics remote = CreateAutofillProfileSpecifics(kGuidA, ""); |
| remote.clear_origin(); |
| remote.add_name_first("John"); |
| ASSERT_FALSE(remote.has_origin()); |
| |
| EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); |
| StartSyncing({remote}); |
| EXPECT_THAT(GetAllLocalData(), ElementsAre(local)); |
| } |
| |
| TEST_F(AutofillProfileSyncBridgeTest, ApplySyncChanges) { |
| AutofillProfile local = AutofillProfile(kGuidA, kHttpsOrigin); |
| AddAutofillProfilesToTable({local}); |
| |
| StartSyncing({}); |
| |
| AutofillProfileSpecifics remote = |
| CreateAutofillProfileSpecifics(kGuidB, kHttpOrigin); |
| remote.add_name_first("Jane"); |
| |
| EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); |
| EXPECT_CALL(mock_processor(), Delete(_, _)).Times(0); |
| |
| ApplySyncChanges( |
| {EntityChange::CreateDelete(kGuidA), |
| EntityChange::CreateAdd(kGuidB, SpecificsToEntity(remote))}); |
| |
| EXPECT_THAT(GetAllLocalData(), ElementsAre(CreateAutofillProfile(remote))); |
| } |
| |
| // Ensure that entries with invalid specifics are ignored. |
| TEST_F(AutofillProfileSyncBridgeTest, ApplySyncChanges_OmitsInvalidSpecifics) { |
| StartSyncing({}); |
| |
| AutofillProfileSpecifics remote_valid = |
| CreateAutofillProfileSpecifics(kGuidA, std::string()); |
| AutofillProfileSpecifics remote_invalid = |
| CreateAutofillProfileSpecifics(kGuidInvalid, std::string()); |
| |
| EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); |
| ApplySyncChanges( |
| {EntityChange::CreateAdd(kGuidA, SpecificsToEntity(remote_valid)), |
| EntityChange::CreateAdd(kGuidInvalid, |
| SpecificsToEntity(remote_invalid))}); |
| |
| EXPECT_THAT(GetAllLocalData(), |
| ElementsAre(CreateAutofillProfile(remote_valid))); |
| } |
| |
| // Verifies that setting the street address field also sets the (deprecated) |
| // address line 1 and line 2 fields. |
| TEST_F(AutofillProfileSyncBridgeTest, StreetAddress_SplitAutomatically) { |
| AutofillProfile local; |
| local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("123 Example St.\n" |
| "Apt. 42")); |
| EXPECT_EQ(ASCIIToUTF16("123 Example St."), |
| local.GetRawInfo(ADDRESS_HOME_LINE1)); |
| EXPECT_EQ(ASCIIToUTF16("Apt. 42"), local.GetRawInfo(ADDRESS_HOME_LINE2)); |
| |
| // The same does _not_ work for profile specifics. |
| AutofillProfileSpecifics remote; |
| remote.set_address_home_street_address( |
| "123 Example St.\n" |
| "Apt. 42"); |
| EXPECT_FALSE(remote.has_address_home_line1()); |
| EXPECT_FALSE(remote.has_address_home_line2()); |
| } |
| |
| // Verifies that setting the (deprecated) address line 1 and line 2 fields also |
| // sets the street address. |
| TEST_F(AutofillProfileSyncBridgeTest, StreetAddress_JointAutomatically) { |
| AutofillProfile local; |
| local.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("123 Example St.")); |
| local.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("Apt. 42")); |
| EXPECT_EQ(ASCIIToUTF16("123 Example St.\n" |
| "Apt. 42"), |
| local.GetRawInfo(ADDRESS_HOME_STREET_ADDRESS)); |
| |
| // The same does _not_ work for profile specifics. |
| AutofillProfileSpecifics remote; |
| remote.set_address_home_line1("123 Example St."); |
| remote.set_address_home_line2("Apt. 42"); |
| EXPECT_FALSE(remote.has_address_home_street_address()); |
| } |
| |
| // Ensure that the street address field takes precedence over the (deprecated) |
| // address line 1 and line 2 fields, even though these are expected to always be |
| // in sync in practice. |
| TEST_F(AutofillProfileSyncBridgeTest, |
| RemoteWithSameGuid_StreetAddress_TakesPrecedenceOverAddressLines) { |
| // Create remote entry with conflicting address data in the street address |
| // field vs. the address line 1 and address line 2 fields. |
| AutofillProfileSpecifics remote = |
| CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); |
| remote.set_address_home_line1("123 Example St."); |
| remote.set_address_home_line2("Apt. 42"); |
| remote.set_address_home_street_address( |
| "456 El Camino Real\n" |
| "Suite #1337"); |
| |
| StartSyncing({remote}); |
| |
| // Verify that full street address takes precedence over address lines. |
| AutofillProfile local(kGuidA, kHttpsOrigin); |
| local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, |
| ASCIIToUTF16("456 El Camino Real\n" |
| "Suite #1337")); |
| local.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("456 El Camino Real")); |
| local.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("Suite #1337")); |
| EXPECT_THAT(GetAllLocalData(), ElementsAre(local)); |
| } |
| |
| // Ensure that no Sync events are generated to fill in missing street address |
| // fields from Sync with explicitly present ones identical to the data stored in |
| // the line1 and line2 fields. This ensures that the migration to add the |
| // street address field to profiles does not generate lots of needless Sync |
| // updates. |
| TEST_F(AutofillProfileSyncBridgeTest, |
| RemoteWithSameGuid_StreetAddress_NoUpdateToEmptyStreetAddressSyncedUp) { |
| AutofillProfile local(kGuidA, kHttpsOrigin); |
| local.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, ASCIIToUTF16("123 Example St.\n" |
| "Apt. 42")); |
| AddAutofillProfilesToTable({local}); |
| |
| // Create a Sync profile identical to |profile|, except without street address |
| // explicitly set. |
| AutofillProfileSpecifics remote = |
| CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); |
| remote.set_address_home_line1("123 Example St."); |
| remote.set_address_home_line2("Apt. 42"); |
| |
| // No update to sync, no change in local data. |
| EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); |
| StartSyncing({remote}); |
| EXPECT_THAT(GetAllLocalData(), ElementsAre(local)); |
| } |
| |
| // Missing language code field should not generate sync events. |
| TEST_F(AutofillProfileSyncBridgeTest, |
| RemoteWithSameGuid_LanguageCode_MissingCodesNoSync) { |
| AutofillProfile local(kGuidA, kHttpsOrigin); |
| ASSERT_TRUE(local.language_code().empty()); |
| AddAutofillProfilesToTable({local}); |
| |
| // Remote data does not have a language code value. |
| AutofillProfileSpecifics remote = |
| CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); |
| ASSERT_FALSE(remote.has_address_home_language_code()); |
| |
| // No update to sync, no change in local data. |
| EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); |
| StartSyncing({remote}); |
| EXPECT_THAT(GetAllLocalData(), ElementsAre(local)); |
| } |
| |
| // Empty language code should be overwritten by sync. |
| TEST_F(AutofillProfileSyncBridgeTest, |
| RemoteWithSameGuid_LanguageCode_ExistingRemoteWinsOverMissingLocal) { |
| AutofillProfile local(kGuidA, kHttpsOrigin); |
| ASSERT_TRUE(local.language_code().empty()); |
| AddAutofillProfilesToTable({local}); |
| |
| // Remote data has "en" language code. |
| AutofillProfileSpecifics remote = |
| CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); |
| remote.set_address_home_language_code("en"); |
| |
| // No update to sync, remote language code overwrites the empty local one. |
| EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); |
| StartSyncing({remote}); |
| EXPECT_THAT(GetAllLocalData(), ElementsAre(CreateAutofillProfile(remote))); |
| } |
| |
| // Local language code should be overwritten by remote one. |
| TEST_F(AutofillProfileSyncBridgeTest, |
| RemoteWithSameGuid_LanguageCode_ExistingRemoteWinsOverExistingLocal) { |
| AutofillProfile local(kGuidA, kHttpsOrigin); |
| local.set_language_code("de"); |
| AddAutofillProfilesToTable({local}); |
| |
| // Remote data has "en" language code. |
| AutofillProfileSpecifics remote = |
| CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); |
| remote.set_address_home_language_code("en"); |
| |
| // No update to sync, remote language code overwrites the local one. |
| EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); |
| StartSyncing({remote}); |
| EXPECT_THAT(GetAllLocalData(), ElementsAre(CreateAutofillProfile(remote))); |
| } |
| |
| // Sync data without language code should not overwrite existing language code |
| // in local autofill profile. |
| TEST_F(AutofillProfileSyncBridgeTest, |
| RemoteWithSameGuid_LanguageCode_ExistingLocalWinsOverMissingRemote) { |
| // Local autofill profile has "en" language code. |
| AutofillProfile local(kGuidA, kHttpsOrigin); |
| local.set_language_code("en"); |
| AddAutofillProfilesToTable({local}); |
| |
| // Remote data does not have a language code value. |
| AutofillProfileSpecifics remote = |
| CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); |
| remote.add_name_first("John"); |
| ASSERT_FALSE(remote.has_address_home_language_code()); |
| |
| // Expect local autofill profile to still have "en" language code after |
| AutofillProfile merged(local); |
| merged.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); |
| |
| // No update to sync, remote language code overwrites the local one. |
| EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); |
| StartSyncing({remote}); |
| EXPECT_THAT(GetAllLocalData(), ElementsAre(merged)); |
| } |
| |
| // Missing validity state bitifield should not generate sync events. |
| TEST_F(AutofillProfileSyncBridgeTest, |
| RemoteWithSameGuid_ValidityState_DefaultValueNoSync) { |
| AutofillProfile local(kGuidA, kHttpsOrigin); |
| ASSERT_EQ(0, local.GetClientValidityBitfieldValue()); |
| ASSERT_FALSE(local.is_client_validity_states_updated()); |
| AddAutofillProfilesToTable({local}); |
| |
| // Remote data does not have a validity state bitfield value. |
| AutofillProfileSpecifics remote = |
| CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); |
| ASSERT_FALSE(remote.has_validity_state_bitfield()); |
| ASSERT_FALSE(remote.is_client_validity_states_updated()); |
| |
| // No update to sync, no change in local data. |
| EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); |
| StartSyncing({remote}); |
| EXPECT_THAT(GetAllLocalData(), ElementsAre(local)); |
| } |
| |
| // Default validity state bitfield should be overwritten by sync. |
| TEST_F(AutofillProfileSyncBridgeTest, |
| RemoteWithSameGuid_ValidityState_ExistingRemoteWinsOverMissingLocal) { |
| AutofillProfile local(kGuidA, kHttpsOrigin); |
| ASSERT_EQ(0, local.GetClientValidityBitfieldValue()); |
| AddAutofillProfilesToTable({local}); |
| |
| // Remote data has a non default validity state bitfield value. |
| AutofillProfileSpecifics remote = |
| CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); |
| remote.set_validity_state_bitfield(kValidityStateBitfield); |
| ASSERT_TRUE(remote.has_validity_state_bitfield()); |
| |
| // No update to sync, the validity bitfield should be stored to local. |
| EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); |
| StartSyncing({remote}); |
| EXPECT_THAT(GetAllLocalData(), ElementsAre(CreateAutofillProfile(remote))); |
| } |
| |
| // Local validity state bitfield should be overwritten by sync. |
| TEST_F(AutofillProfileSyncBridgeTest, |
| RemoteWithSameGuid_ValidityState_ExistingRemoteWinsOverExistingLocal) { |
| AutofillProfile local(kGuidA, kHttpsOrigin); |
| local.SetClientValidityFromBitfieldValue(kValidityStateBitfield + 1); |
| AddAutofillProfilesToTable({local}); |
| |
| // Remote data has a non default validity state bitfield value. |
| AutofillProfileSpecifics remote = |
| CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); |
| remote.set_validity_state_bitfield(kValidityStateBitfield); |
| ASSERT_TRUE(remote.has_validity_state_bitfield()); |
| |
| // No update to sync, the remote validity bitfield should overwrite local. |
| EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); |
| StartSyncing({remote}); |
| EXPECT_THAT(GetAllLocalData(), ElementsAre(CreateAutofillProfile(remote))); |
| } |
| |
| // Sync data without a default validity state bitfield should not overwrite |
| // an existing validity state bitfield in local autofill profile. |
| TEST_F(AutofillProfileSyncBridgeTest, |
| RemoteWithSameGuid_ValidityState_ExistingLocalWinsOverMissingRemote) { |
| AutofillProfile local(kGuidA, kHttpsOrigin); |
| local.SetClientValidityFromBitfieldValue(kValidityStateBitfield); |
| AddAutofillProfilesToTable({local}); |
| |
| // Remote data has a non default validity state bitfield value. |
| AutofillProfileSpecifics remote = |
| CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); |
| remote.add_name_first("John"); |
| ASSERT_FALSE(remote.has_validity_state_bitfield()); |
| |
| // Expect local autofill profile to still have the validity state after. |
| AutofillProfile merged(local); |
| merged.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); |
| |
| // No update to sync, the local validity bitfield should stay untouched. |
| EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); |
| StartSyncing({remote}); |
| EXPECT_THAT(GetAllLocalData(), ElementsAre(merged)); |
| } |
| |
| // Missing full name field should not generate sync events. |
| TEST_F(AutofillProfileSyncBridgeTest, |
| RemoteWithSameGuid_FullName_MissingValueNoSync) { |
| // Local autofill profile has an empty full name. |
| AutofillProfile local(kGuidA, kHttpsOrigin); |
| local.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); |
| AddAutofillProfilesToTable({local}); |
| |
| // Remote data does not have a full name value. |
| AutofillProfileSpecifics remote = |
| CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); |
| remote.add_name_first(std::string("John")); |
| |
| // No update to sync, no change in local data. |
| EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); |
| StartSyncing({remote}); |
| EXPECT_THAT(GetAllLocalData(), ElementsAre(local)); |
| } |
| |
| TEST_F(AutofillProfileSyncBridgeTest, |
| RemoteWithSameGuid_FullName_ExistingLocalWinsOverMissingRemote) { |
| // Local autofill profile has a full name. |
| AutofillProfile local(kGuidA, kHttpsOrigin); |
| local.SetRawInfo(NAME_FULL, ASCIIToUTF16("John Jacob Smith, Jr")); |
| AddAutofillProfilesToTable({local}); |
| |
| // Remote data does not have a full name value. |
| AutofillProfileSpecifics remote = |
| CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); |
| remote.add_name_first(std::string("John")); |
| remote.add_name_middle(std::string("Jacob")); |
| remote.add_name_last(std::string("Smith")); |
| |
| // Expect local autofill profile to still have the same full name after. |
| AutofillProfile merged(local); |
| merged.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John")); |
| merged.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("Jacob")); |
| merged.SetRawInfo(NAME_LAST, ASCIIToUTF16("Smith")); |
| |
| // No update to sync, no change in local data. |
| EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); |
| StartSyncing({remote}); |
| EXPECT_THAT(GetAllLocalData(), ElementsAre(merged)); |
| } |
| |
| // Missing use_count/use_date fields should not generate sync events. |
| TEST_F(AutofillProfileSyncBridgeTest, |
| RemoteWithSameGuid_UsageStats_MissingValueNoSync) { |
| // Local autofill profile has 0 for use_count/use_date. |
| AutofillProfile local(kGuidA, kHttpsOrigin); |
| local.set_language_code("en"); |
| local.set_use_count(0); |
| local.set_use_date(base::Time()); |
| AddAutofillProfilesToTable({local}); |
| |
| // Remote data does not have use_count/use_date. |
| AutofillProfileSpecifics remote = |
| CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); |
| remote.clear_use_count(); |
| remote.clear_use_date(); |
| remote.set_address_home_language_code("en"); |
| |
| // No update to sync, no change in local data. |
| EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); |
| StartSyncing({remote}); |
| EXPECT_THAT(GetAllLocalData(), ElementsAre(WithUsageStats(local))); |
| } |
| |
| struct UpdatesUsageStatsTestCase { |
| size_t local_use_count; |
| base::Time local_use_date; |
| size_t remote_use_count; |
| int remote_use_date; |
| size_t merged_use_count; |
| base::Time merged_use_date; |
| }; |
| |
| class AutofillProfileSyncBridgeUpdatesUsageStatsTest |
| : public AutofillProfileSyncBridgeTest, |
| public testing::WithParamInterface<UpdatesUsageStatsTestCase> { |
| public: |
| AutofillProfileSyncBridgeUpdatesUsageStatsTest() {} |
| ~AutofillProfileSyncBridgeUpdatesUsageStatsTest() override {} |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(AutofillProfileSyncBridgeUpdatesUsageStatsTest); |
| }; |
| |
| TEST_P(AutofillProfileSyncBridgeUpdatesUsageStatsTest, UpdatesUsageStats) { |
| auto test_case = GetParam(); |
| |
| // Local data has usage stats. |
| AutofillProfile local(kGuidA, kHttpsOrigin); |
| local.set_language_code("en"); |
| local.set_use_count(test_case.local_use_count); |
| local.set_use_date(test_case.local_use_date); |
| ASSERT_EQ(test_case.local_use_count, local.use_count()); |
| ASSERT_EQ(test_case.local_use_date, local.use_date()); |
| AddAutofillProfilesToTable({local}); |
| |
| // Remote data has usage stats. |
| AutofillProfileSpecifics remote = |
| CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin); |
| remote.set_address_home_language_code("en"); |
| remote.set_use_count(test_case.remote_use_count); |
| remote.set_use_date(test_case.remote_use_date); |
| ASSERT_TRUE(remote.has_use_count()); |
| ASSERT_TRUE(remote.has_use_date()); |
| |
| // Expect the local autofill profile to have usage stats after sync. |
| AutofillProfile merged(local); |
| merged.set_use_count(test_case.merged_use_count); |
| merged.set_use_date(test_case.merged_use_date); |
| |
| // Expect no changes to remote data. |
| EXPECT_CALL(mock_processor(), Put(_, _, _)).Times(0); |
| |
| StartSyncing({remote}); |
| EXPECT_THAT(GetAllLocalData(), ElementsAre(WithUsageStats(merged))); |
| } |
| |
| INSTANTIATE_TEST_CASE_P( |
| AutofillProfileSyncBridgeTest, |
| AutofillProfileSyncBridgeUpdatesUsageStatsTest, |
| testing::Values( |
| // Local profile with default stats. |
| UpdatesUsageStatsTestCase{ |
| /*local_use_count=*/0U, |
| /*local_use_date=*/base::Time(), |
| /*remote_use_count=*/9U, |
| /*remote_use_date=*/4321, |
| /*merged_use_count=*/9U, |
| /*merged_use_date=*/base::Time::FromTimeT(4321)}, |
| // Local profile has older stats than the server. |
| UpdatesUsageStatsTestCase{ |
| /*local_use_count=*/3U, |
| /*local_use_date=*/base::Time::FromTimeT(1234), |
| /*remote_use_count=*/9U, /*remote_use_date=*/4321, |
| /*merged_use_count=*/9U, |
| /*merged_use_date=*/base::Time::FromTimeT(4321)}, |
| // Local profile has newer stats than the server |
| UpdatesUsageStatsTestCase{ |
| /*local_use_count=*/10U, |
| /*local_use_date=*/base::Time::FromTimeT(9999), |
| /*remote_use_count=*/9U, /*remote_use_date=*/4321, |
| /*merged_use_count=*/9U, |
| /*merged_use_date=*/base::Time::FromTimeT(4321)})); |
| |
| } // namespace autofill |