// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/location.h" #include "base/message_loop.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/webdata/autofill_profile_syncable_service.h" #include "components/autofill/core/browser/autofill_profile.h" #include "components/autofill/core/browser/webdata/autofill_change.h" #include "content/public/test/test_browser_thread.h" #include "sync/api/sync_error_factory.h" #include "sync/api/sync_error_factory_mock.h" #include "sync/protocol/sync.pb.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using ::testing::_; using ::testing::DoAll; using ::testing::Eq; using ::testing::Return; using ::testing::Property; using autofill::AutofillProfile; using autofill::AutofillProfileChange; using content::BrowserThread; // Some guids for testing. const char kGuid1[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44B"; const char kGuid2[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44C"; const char kGuid3[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44D"; const char kGuid4[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44E"; const char kHttpOrigin[] = "http://www.example.com/"; const char kHttpsOrigin[] = "https://www.example.com/"; const char kSettingsOrigin[] = "Chrome settings"; class MockAutofillProfileSyncableService : public AutofillProfileSyncableService { public: MockAutofillProfileSyncableService() {} virtual ~MockAutofillProfileSyncableService() {} using AutofillProfileSyncableService::DataBundle; using AutofillProfileSyncableService::set_sync_processor; using AutofillProfileSyncableService::CreateData; MOCK_METHOD1(LoadAutofillData, bool(std::vector*)); MOCK_METHOD1(SaveChangesToWebData, bool(const AutofillProfileSyncableService::DataBundle&)); }; ACTION_P(CopyData, data) { arg0->resize(data->size()); std::copy(data->begin(), data->end(), arg0->begin()); } MATCHER_P(CheckSyncChanges, n_sync_changes_list, "") { if (arg.size() != n_sync_changes_list.size()) return false; syncer::SyncChangeList::const_iterator passed, expected; for (passed = arg.begin(), expected = n_sync_changes_list.begin(); passed != arg.end() && expected != n_sync_changes_list.end(); ++passed, ++expected) { DCHECK(passed->IsValid()); if (passed->change_type() != expected->change_type()) return false; if (passed->sync_data().GetSpecifics().autofill_profile().guid() != expected->sync_data().GetSpecifics().autofill_profile().guid()) { return false; } } return true; } MATCHER_P(DataBundleCheck, n_bundle, "") { if ((arg.profiles_to_delete.size() != n_bundle.profiles_to_delete.size()) || (arg.profiles_to_update.size() != n_bundle.profiles_to_update.size()) || (arg.profiles_to_add.size() != n_bundle.profiles_to_add.size())) return false; for (size_t i = 0; i < arg.profiles_to_delete.size(); ++i) { if (arg.profiles_to_delete[i] != n_bundle.profiles_to_delete[i]) return false; } for (size_t i = 0; i < arg.profiles_to_update.size(); ++i) { if (*arg.profiles_to_update[i] != *n_bundle.profiles_to_update[i]) return false; } for (size_t i = 0; i < arg.profiles_to_add.size(); ++i) { if (*arg.profiles_to_add[i] != *n_bundle.profiles_to_add[i]) return false; } return true; } class MockSyncChangeProcessor : public syncer::SyncChangeProcessor { public: MockSyncChangeProcessor() {} virtual ~MockSyncChangeProcessor() {} MOCK_METHOD2(ProcessSyncChanges, syncer::SyncError(const tracked_objects::Location&, const syncer::SyncChangeList&)); }; class TestSyncChangeProcessor : public syncer::SyncChangeProcessor { public: TestSyncChangeProcessor() {} virtual ~TestSyncChangeProcessor() {} virtual syncer::SyncError ProcessSyncChanges( const tracked_objects::Location& location, const syncer::SyncChangeList& changes) OVERRIDE { changes_ = changes; return syncer::SyncError(); } const syncer::SyncChangeList& changes() { return changes_; } private: syncer::SyncChangeList changes_; }; class AutofillProfileSyncableServiceTest : public testing::Test { public: AutofillProfileSyncableServiceTest() : ui_thread_(BrowserThread::UI, &message_loop_), db_thread_(BrowserThread::DB, &message_loop_) {} virtual void SetUp() OVERRIDE { sync_processor_.reset(new MockSyncChangeProcessor); } // Wrapper around AutofillProfileSyncableService::MergeDataAndStartSyncing() // that also verifies expectations. void MergeDataAndStartSyncing( const std::vector& profiles_from_web_db, const syncer::SyncDataList& data_list, const MockAutofillProfileSyncableService::DataBundle& expected_bundle, const syncer::SyncChangeList& expected_change_list) { EXPECT_CALL(autofill_syncable_service_, LoadAutofillData(_)) .Times(1) .WillOnce(DoAll(CopyData(&profiles_from_web_db), Return(true))); EXPECT_CALL(autofill_syncable_service_, SaveChangesToWebData(DataBundleCheck(expected_bundle))) .Times(1) .WillOnce(Return(true)); ON_CALL(*sync_processor_, ProcessSyncChanges(_, _)) .WillByDefault(Return(syncer::SyncError())); EXPECT_CALL(*sync_processor_, ProcessSyncChanges(_, CheckSyncChanges(expected_change_list))) .Times(1) .WillOnce(Return(syncer::SyncError())); // Takes ownership of sync_processor_. autofill_syncable_service_.MergeDataAndStartSyncing( syncer::AUTOFILL_PROFILE, data_list, sync_processor_.PassAs(), scoped_ptr( new syncer::SyncErrorFactoryMock())); } protected: base::MessageLoop message_loop_; content::TestBrowserThread ui_thread_; content::TestBrowserThread db_thread_; MockAutofillProfileSyncableService autofill_syncable_service_; scoped_ptr sync_processor_; }; TEST_F(AutofillProfileSyncableServiceTest, MergeDataAndStartSyncing) { std::vector profiles_from_web_db; std::string guid_present1 = kGuid1; std::string guid_present2 = kGuid2; std::string guid_synced1 = kGuid3; std::string guid_synced2 = kGuid4; std::string origin_present1 = kHttpOrigin; std::string origin_present2 = std::string(); std::string origin_synced1 = kHttpsOrigin; std::string origin_synced2 = kSettingsOrigin; profiles_from_web_db.push_back( new AutofillProfile(guid_present1, origin_present1)); profiles_from_web_db.back()->SetRawInfo( autofill::NAME_FIRST, UTF8ToUTF16("John")); profiles_from_web_db.back()->SetRawInfo( autofill::ADDRESS_HOME_LINE1, UTF8ToUTF16("1 1st st")); profiles_from_web_db.push_back( new AutofillProfile(guid_present2, origin_present2)); profiles_from_web_db.back()->SetRawInfo( autofill::NAME_FIRST, UTF8ToUTF16("Tom")); profiles_from_web_db.back()->SetRawInfo( autofill::ADDRESS_HOME_LINE1, UTF8ToUTF16("2 2nd st")); syncer::SyncDataList data_list; AutofillProfile profile1(guid_synced1, origin_synced1); profile1.SetRawInfo(autofill::NAME_FIRST, UTF8ToUTF16("Jane")); data_list.push_back(autofill_syncable_service_.CreateData(profile1)); AutofillProfile profile2(guid_synced2, origin_synced2); profile2.SetRawInfo(autofill::NAME_FIRST, UTF8ToUTF16("Harry")); data_list.push_back(autofill_syncable_service_.CreateData(profile2)); // This one will have the name and origin updated. AutofillProfile profile3(guid_present2, origin_synced2); profile3.SetRawInfo(autofill::NAME_FIRST, UTF8ToUTF16("Tom Doe")); data_list.push_back(autofill_syncable_service_.CreateData(profile3)); syncer::SyncChangeList expected_change_list; expected_change_list.push_back( syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_ADD, MockAutofillProfileSyncableService::CreateData( *profiles_from_web_db.front()))); MockAutofillProfileSyncableService::DataBundle expected_bundle; expected_bundle.profiles_to_add.push_back(&profile1); expected_bundle.profiles_to_add.push_back(&profile2); expected_bundle.profiles_to_update.push_back(&profile3); MergeDataAndStartSyncing( profiles_from_web_db, data_list, expected_bundle, expected_change_list); autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE); } TEST_F(AutofillProfileSyncableServiceTest, MergeIdenticalProfiles) { std::vector profiles_from_web_db; std::string guid_present1 = kGuid1; std::string guid_present2 = kGuid2; std::string guid_synced1 = kGuid3; std::string guid_synced2 = kGuid4; std::string origin_present1 = kHttpOrigin; std::string origin_present2 = kSettingsOrigin; std::string origin_synced1 = kHttpsOrigin; std::string origin_synced2 = kHttpsOrigin; profiles_from_web_db.push_back( new AutofillProfile(guid_present1, origin_present1)); profiles_from_web_db.back()->SetRawInfo( autofill::NAME_FIRST, UTF8ToUTF16("John")); profiles_from_web_db.back()->SetRawInfo( autofill::ADDRESS_HOME_LINE1, UTF8ToUTF16("1 1st st")); profiles_from_web_db.push_back( new AutofillProfile(guid_present2, origin_present2)); profiles_from_web_db.back()->SetRawInfo( autofill::NAME_FIRST, UTF8ToUTF16("Tom")); profiles_from_web_db.back()->SetRawInfo( autofill::ADDRESS_HOME_LINE1, UTF8ToUTF16("2 2nd st")); // The synced profiles are identical to the local ones, except that the guids // are different. syncer::SyncDataList data_list; AutofillProfile profile1(guid_synced1, origin_synced1); profile1.SetRawInfo(autofill::NAME_FIRST, UTF8ToUTF16("John")); profile1.SetRawInfo(autofill::ADDRESS_HOME_LINE1, UTF8ToUTF16("1 1st st")); data_list.push_back(autofill_syncable_service_.CreateData(profile1)); AutofillProfile profile2(guid_synced2, origin_synced2); profile2.SetRawInfo(autofill::NAME_FIRST, UTF8ToUTF16("Tom")); profile2.SetRawInfo(autofill::ADDRESS_HOME_LINE1, UTF8ToUTF16("2 2nd st")); data_list.push_back(autofill_syncable_service_.CreateData(profile2)); AutofillProfile expected_profile(profile2); expected_profile.set_origin(kSettingsOrigin); syncer::SyncChangeList expected_change_list; expected_change_list.push_back( syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, MockAutofillProfileSyncableService::CreateData( expected_profile))); MockAutofillProfileSyncableService::DataBundle expected_bundle; expected_bundle.profiles_to_delete.push_back(guid_present1); expected_bundle.profiles_to_delete.push_back(guid_present2); expected_bundle.profiles_to_add.push_back(&profile1); expected_bundle.profiles_to_add.push_back(&expected_profile); MergeDataAndStartSyncing( profiles_from_web_db, data_list, expected_bundle, expected_change_list); autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE); } TEST_F(AutofillProfileSyncableServiceTest, MergeSimilarProfiles) { std::vector profiles_from_web_db; std::string guid_present1 = kGuid1; std::string guid_present2 = kGuid2; std::string guid_synced1 = kGuid3; std::string guid_synced2 = kGuid4; std::string origin_present1 = kHttpOrigin; std::string origin_present2 = kSettingsOrigin; std::string origin_synced1 = kHttpsOrigin; std::string origin_synced2 = kHttpsOrigin; profiles_from_web_db.push_back( new AutofillProfile(guid_present1, origin_present1)); profiles_from_web_db.back()->SetRawInfo( autofill::NAME_FIRST, UTF8ToUTF16("John")); profiles_from_web_db.back()->SetRawInfo( autofill::ADDRESS_HOME_LINE1, UTF8ToUTF16("1 1st st")); profiles_from_web_db.push_back( new AutofillProfile(guid_present2, origin_present2)); profiles_from_web_db.back()->SetRawInfo( autofill::NAME_FIRST, UTF8ToUTF16("Tom")); profiles_from_web_db.back()->SetRawInfo( autofill::ADDRESS_HOME_LINE1, UTF8ToUTF16("2 2nd st")); // The synced profiles are identical to the local ones, except that the guids // are different. syncer::SyncDataList data_list; AutofillProfile profile1(guid_synced1, origin_synced1); profile1.SetRawInfo(autofill::NAME_FIRST, UTF8ToUTF16("John")); profile1.SetRawInfo(autofill::ADDRESS_HOME_LINE1, UTF8ToUTF16("1 1st st")); profile1.SetRawInfo(autofill::COMPANY_NAME, UTF8ToUTF16("Frobbers, Inc.")); data_list.push_back(autofill_syncable_service_.CreateData(profile1)); AutofillProfile profile2(guid_synced2, origin_synced2); profile2.SetRawInfo(autofill::NAME_FIRST, UTF8ToUTF16("Tom")); profile2.SetRawInfo(autofill::ADDRESS_HOME_LINE1, UTF8ToUTF16("2 2nd st")); profile2.SetRawInfo(autofill::COMPANY_NAME, UTF8ToUTF16("Fizzbang, LLC.")); data_list.push_back(autofill_syncable_service_.CreateData(profile2)); // 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. AutofillProfile expected_profile(profile1); expected_profile.set_origin(origin_present1); syncer::SyncChangeList expected_change_list; expected_change_list.push_back( syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_ADD, MockAutofillProfileSyncableService::CreateData( *profiles_from_web_db.back()))); expected_change_list.push_back( syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, MockAutofillProfileSyncableService::CreateData( expected_profile))); MockAutofillProfileSyncableService::DataBundle expected_bundle; expected_bundle.profiles_to_delete.push_back(guid_present1); expected_bundle.profiles_to_add.push_back(&expected_profile); expected_bundle.profiles_to_add.push_back(&profile2); MergeDataAndStartSyncing( profiles_from_web_db, data_list, expected_bundle, expected_change_list); autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE); } // 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(AutofillProfileSyncableServiceTest, MergeDataEmptyOrigins) { std::vector profiles_from_web_db; // Create a profile with an empty origin. AutofillProfile profile(kGuid1, std::string()); profile.SetRawInfo(autofill::NAME_FIRST, UTF8ToUTF16("John")); profile.SetRawInfo(autofill::ADDRESS_HOME_LINE1, UTF8ToUTF16("1 1st st")); profiles_from_web_db.push_back(new AutofillProfile(profile)); // Create a Sync profile identical to |profile|, except with no origin set. sync_pb::EntitySpecifics specifics; sync_pb::AutofillProfileSpecifics* autofill_specifics = specifics.mutable_autofill_profile(); autofill_specifics->set_guid(profile.guid()); autofill_specifics->add_name_first("John"); autofill_specifics->add_name_middle(std::string()); autofill_specifics->add_name_last(std::string()); autofill_specifics->add_email_address(std::string()); autofill_specifics->add_phone_home_whole_number(std::string()); autofill_specifics->set_address_home_line1("1 1st st"); EXPECT_FALSE(autofill_specifics->has_origin()); syncer::SyncDataList data_list; data_list.push_back( syncer::SyncData::CreateLocalData( profile.guid(), profile.guid(), specifics)); MockAutofillProfileSyncableService::DataBundle expected_bundle; EXPECT_CALL(autofill_syncable_service_, LoadAutofillData(_)) .Times(1) .WillOnce(DoAll(CopyData(&profiles_from_web_db), Return(true))); EXPECT_CALL(autofill_syncable_service_, SaveChangesToWebData(DataBundleCheck(expected_bundle))) .Times(1) .WillOnce(Return(true)); EXPECT_CALL(*sync_processor_, ProcessSyncChanges(_, _)).Times(0); // Takes ownership of sync_processor_. autofill_syncable_service_.MergeDataAndStartSyncing( syncer::AUTOFILL_PROFILE, data_list, sync_processor_.PassAs(), scoped_ptr(new syncer::SyncErrorFactoryMock())); autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE); } TEST_F(AutofillProfileSyncableServiceTest, GetAllSyncData) { std::vector profiles_from_web_db; std::string guid_present1 = kGuid1; std::string guid_present2 = kGuid2; profiles_from_web_db.push_back( new AutofillProfile(guid_present1, kHttpOrigin)); profiles_from_web_db.back()->SetRawInfo( autofill::NAME_FIRST, UTF8ToUTF16("John")); profiles_from_web_db.push_back( new AutofillProfile(guid_present2, kHttpsOrigin)); profiles_from_web_db.back()->SetRawInfo( autofill::NAME_FIRST, UTF8ToUTF16("Jane")); syncer::SyncChangeList expected_change_list; expected_change_list.push_back( syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_ADD, MockAutofillProfileSyncableService::CreateData( *profiles_from_web_db.front()))); expected_change_list.push_back( syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_ADD, MockAutofillProfileSyncableService::CreateData( *profiles_from_web_db.back()))); MockAutofillProfileSyncableService::DataBundle expected_bundle; syncer::SyncDataList data_list; MergeDataAndStartSyncing( profiles_from_web_db, data_list, expected_bundle, expected_change_list); syncer::SyncDataList data = autofill_syncable_service_.GetAllSyncData(syncer::AUTOFILL_PROFILE); ASSERT_EQ(2U, data.size()); EXPECT_EQ(guid_present1, data[0].GetSpecifics().autofill_profile().guid()); EXPECT_EQ(guid_present2, data[1].GetSpecifics().autofill_profile().guid()); EXPECT_EQ(kHttpOrigin, data[0].GetSpecifics().autofill_profile().origin()); EXPECT_EQ(kHttpsOrigin, data[1].GetSpecifics().autofill_profile().origin()); autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE); } TEST_F(AutofillProfileSyncableServiceTest, ProcessSyncChanges) { std::vector profiles_from_web_db; std::string guid_present = kGuid1; std::string guid_synced = kGuid2; syncer::SyncChangeList change_list; AutofillProfile profile(guid_synced, kHttpOrigin); profile.SetRawInfo(autofill::NAME_FIRST, UTF8ToUTF16("Jane")); change_list.push_back( syncer::SyncChange( FROM_HERE, syncer::SyncChange::ACTION_ADD, MockAutofillProfileSyncableService::CreateData(profile))); AutofillProfile empty_profile(guid_present, kHttpsOrigin); change_list.push_back( syncer::SyncChange( FROM_HERE, syncer::SyncChange::ACTION_DELETE, MockAutofillProfileSyncableService::CreateData(empty_profile))); MockAutofillProfileSyncableService::DataBundle expected_bundle; expected_bundle.profiles_to_delete.push_back(guid_present); expected_bundle.profiles_to_add.push_back(&profile); EXPECT_CALL(autofill_syncable_service_, SaveChangesToWebData( DataBundleCheck(expected_bundle))) .Times(1) .WillOnce(Return(true)); autofill_syncable_service_.set_sync_processor(sync_processor_.release()); syncer::SyncError error = autofill_syncable_service_.ProcessSyncChanges( FROM_HERE, change_list); EXPECT_FALSE(error.IsSet()); } TEST_F(AutofillProfileSyncableServiceTest, AutofillProfileAdded) { // Will be owned by the syncable service. Keep a reference available here for // verifying test expectations. TestSyncChangeProcessor* sync_change_processor = new TestSyncChangeProcessor; autofill_syncable_service_.set_sync_processor(sync_change_processor); AutofillProfile profile(kGuid1, kHttpsOrigin); profile.SetRawInfo(autofill::NAME_FIRST, UTF8ToUTF16("Jane")); AutofillProfileChange change(AutofillProfileChange::ADD, kGuid1, &profile); autofill_syncable_service_.AutofillProfileChanged(change); ASSERT_EQ(1U, sync_change_processor->changes().size()); syncer::SyncChange result = sync_change_processor->changes()[0]; EXPECT_EQ(syncer::SyncChange::ACTION_ADD, result.change_type()); sync_pb::AutofillProfileSpecifics specifics = result.sync_data().GetSpecifics().autofill_profile(); EXPECT_EQ(kGuid1, specifics.guid()); EXPECT_EQ(kHttpsOrigin, specifics.origin()); EXPECT_THAT(specifics.name_first(), testing::ElementsAre("Jane")); } TEST_F(AutofillProfileSyncableServiceTest, AutofillProfileDeleted) { // Will be owned by the syncable service. Keep a reference available here for // verifying test expectations. TestSyncChangeProcessor* sync_change_processor = new TestSyncChangeProcessor; autofill_syncable_service_.set_sync_processor(sync_change_processor); AutofillProfileChange change(AutofillProfileChange::REMOVE, kGuid2, NULL); autofill_syncable_service_.AutofillProfileChanged(change); ASSERT_EQ(1U, sync_change_processor->changes().size()); syncer::SyncChange result = sync_change_processor->changes()[0]; EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, result.change_type()); sync_pb::AutofillProfileSpecifics specifics = result.sync_data().GetSpecifics().autofill_profile(); EXPECT_EQ(kGuid2, specifics.guid()); } TEST_F(AutofillProfileSyncableServiceTest, UpdateField) { AutofillProfile profile(kGuid1, kSettingsOrigin); std::string company1 = "A Company"; std::string company2 = "Another Company"; profile.SetRawInfo(autofill::COMPANY_NAME, UTF8ToUTF16(company1)); EXPECT_FALSE(AutofillProfileSyncableService::UpdateField( autofill::COMPANY_NAME, company1, &profile)); EXPECT_EQ(profile.GetRawInfo(autofill::COMPANY_NAME), UTF8ToUTF16(company1)); EXPECT_TRUE(AutofillProfileSyncableService::UpdateField( autofill::COMPANY_NAME, company2, &profile)); EXPECT_EQ(profile.GetRawInfo(autofill::COMPANY_NAME), UTF8ToUTF16(company2)); EXPECT_FALSE(AutofillProfileSyncableService::UpdateField( autofill::COMPANY_NAME, company2, &profile)); EXPECT_EQ(profile.GetRawInfo(autofill::COMPANY_NAME), UTF8ToUTF16(company2)); } TEST_F(AutofillProfileSyncableServiceTest, UpdateMultivaluedField) { AutofillProfile profile(kGuid1, kHttpsOrigin); std::vector values; values.push_back(UTF8ToUTF16("1@1.com")); values.push_back(UTF8ToUTF16("2@1.com")); profile.SetRawMultiInfo(autofill::EMAIL_ADDRESS, values); ::google::protobuf::RepeatedPtrField specifics_fields; specifics_fields.AddAllocated(new std::string("2@1.com")); specifics_fields.AddAllocated(new std::string("3@1.com")); EXPECT_TRUE(AutofillProfileSyncableService::UpdateMultivaluedField( autofill::EMAIL_ADDRESS, specifics_fields, &profile)); profile.GetRawMultiInfo(autofill::EMAIL_ADDRESS, &values); ASSERT_TRUE(values.size() == 2); EXPECT_EQ(values[0], UTF8ToUTF16("2@1.com")); EXPECT_EQ(values[1], UTF8ToUTF16("3@1.com")); EXPECT_FALSE(AutofillProfileSyncableService::UpdateMultivaluedField( autofill::EMAIL_ADDRESS, specifics_fields, &profile)); profile.GetRawMultiInfo(autofill::EMAIL_ADDRESS, &values); ASSERT_EQ(values.size(), 2U); EXPECT_EQ(values[0], UTF8ToUTF16("2@1.com")); EXPECT_EQ(values[1], UTF8ToUTF16("3@1.com")); EXPECT_TRUE(AutofillProfileSyncableService::UpdateMultivaluedField( autofill::EMAIL_ADDRESS, ::google::protobuf::RepeatedPtrField(), &profile)); profile.GetRawMultiInfo(autofill::EMAIL_ADDRESS, &values); ASSERT_EQ(values.size(), 1U); // Always have at least an empty string. EXPECT_EQ(values[0], UTF8ToUTF16("")); } TEST_F(AutofillProfileSyncableServiceTest, MergeProfile) { AutofillProfile profile1(kGuid1, kHttpOrigin); profile1.SetRawInfo( autofill::ADDRESS_HOME_LINE1, UTF8ToUTF16("111 First St.")); std::vector values; values.push_back(UTF8ToUTF16("1@1.com")); values.push_back(UTF8ToUTF16("2@1.com")); profile1.SetRawMultiInfo(autofill::EMAIL_ADDRESS, values); AutofillProfile profile2(kGuid2, kHttpsOrigin); profile2.SetRawInfo( autofill::ADDRESS_HOME_LINE1, UTF8ToUTF16("111 First St.")); // |values| now is [ "1@1.com", "2@1.com", "3@1.com" ]. values.push_back(UTF8ToUTF16("3@1.com")); profile2.SetRawMultiInfo(autofill::EMAIL_ADDRESS, values); values.clear(); values.push_back(UTF8ToUTF16("John")); profile1.SetRawMultiInfo(autofill::NAME_FIRST, values); values.push_back(UTF8ToUTF16("Jane")); profile2.SetRawMultiInfo(autofill::NAME_FIRST, values); values.clear(); values.push_back(UTF8ToUTF16("Doe")); profile1.SetRawMultiInfo(autofill::NAME_LAST, values); values.push_back(UTF8ToUTF16("Other")); profile2.SetRawMultiInfo(autofill::NAME_LAST, values); values.clear(); values.push_back(UTF8ToUTF16("650234567")); profile2.SetRawMultiInfo(autofill::PHONE_HOME_WHOLE_NUMBER, values); EXPECT_FALSE(AutofillProfileSyncableService::MergeProfile(profile2, &profile1, "en-US")); profile1.GetRawMultiInfo(autofill::NAME_FIRST, &values); ASSERT_EQ(values.size(), 2U); EXPECT_EQ(values[0], UTF8ToUTF16("John")); EXPECT_EQ(values[1], UTF8ToUTF16("Jane")); profile1.GetRawMultiInfo(autofill::NAME_LAST, &values); ASSERT_EQ(values.size(), 2U); EXPECT_EQ(values[0], UTF8ToUTF16("Doe")); EXPECT_EQ(values[1], UTF8ToUTF16("Other")); profile1.GetRawMultiInfo(autofill::EMAIL_ADDRESS, &values); ASSERT_EQ(values.size(), 3U); EXPECT_EQ(values[0], UTF8ToUTF16("1@1.com")); EXPECT_EQ(values[1], UTF8ToUTF16("2@1.com")); EXPECT_EQ(values[2], UTF8ToUTF16("3@1.com")); profile1.GetRawMultiInfo(autofill::PHONE_HOME_WHOLE_NUMBER, &values); ASSERT_EQ(values.size(), 1U); EXPECT_EQ(values[0], UTF8ToUTF16("650234567")); EXPECT_EQ(profile2.origin(), profile1.origin()); AutofillProfile profile3(kGuid3, kHttpOrigin); profile3.SetRawInfo( autofill::ADDRESS_HOME_LINE1, UTF8ToUTF16("111 First St.")); values.clear(); values.push_back(UTF8ToUTF16("Jane")); profile3.SetRawMultiInfo(autofill::NAME_FIRST, values); values.clear(); values.push_back(UTF8ToUTF16("Doe")); profile3.SetRawMultiInfo(autofill::NAME_LAST, values); EXPECT_TRUE(AutofillProfileSyncableService::MergeProfile(profile3, &profile1, "en-US")); profile1.GetRawMultiInfo(autofill::NAME_FIRST, &values); ASSERT_EQ(values.size(), 3U); EXPECT_EQ(values[0], UTF8ToUTF16("John")); EXPECT_EQ(values[1], UTF8ToUTF16("Jane")); EXPECT_EQ(values[2], UTF8ToUTF16("Jane")); profile1.GetRawMultiInfo(autofill::NAME_LAST, &values); ASSERT_EQ(values.size(), 3U); EXPECT_EQ(values[0], UTF8ToUTF16("Doe")); EXPECT_EQ(values[1], UTF8ToUTF16("Other")); EXPECT_EQ(values[2], UTF8ToUTF16("Doe")); // Middle name should have three entries as well. profile1.GetRawMultiInfo(autofill::NAME_MIDDLE, &values); ASSERT_EQ(values.size(), 3U); EXPECT_TRUE(values[0].empty()); EXPECT_TRUE(values[1].empty()); EXPECT_TRUE(values[2].empty()); profile1.GetRawMultiInfo(autofill::EMAIL_ADDRESS, &values); ASSERT_EQ(values.size(), 3U); EXPECT_EQ(values[0], UTF8ToUTF16("1@1.com")); EXPECT_EQ(values[1], UTF8ToUTF16("2@1.com")); EXPECT_EQ(values[2], UTF8ToUTF16("3@1.com")); profile1.GetRawMultiInfo(autofill::PHONE_HOME_WHOLE_NUMBER, &values); ASSERT_EQ(values.size(), 1U); EXPECT_EQ(values[0], UTF8ToUTF16("650234567")); }