blob: 8428fca3a3d2e23b2280160ec93657afaf779a0e [file] [log] [blame]
// 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/message_loop/message_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/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);
class FakeAutofillBackend : public AutofillWebDataBackend {
public:
FakeAutofillBackend() {}
~FakeAutofillBackend() override {}
WebDatabase* GetDatabase() override { return db_; }
void AddObserver(
autofill::AutofillWebDataServiceObserverOnDBSequence* observer) override {
}
void RemoveObserver(
autofill::AutofillWebDataServiceObserverOnDBSequence* observer) override {
}
void RemoveExpiredFormElements() override {}
void NotifyOfMultipleAutofillChanges() override {}
void NotifyThatSyncHasStarted(ModelType model_type) override {}
void SetWebDatabase(WebDatabase* db) { db_ = db; }
private:
WebDatabase* db_;
};
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"));
backend_.SetWebDatabase(&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_; }
FakeAutofillBackend* backend() { return &backend_; }
private:
autofill::TestAutofillClock test_clock_;
ScopedTempDir temp_dir_;
base::test::ScopedTaskEnvironment scoped_task_environment_;
FakeAutofillBackend 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());
AddAutofillProfilesToTable({local});
// Remote data does not have a validity state bitfield value.
AutofillProfileSpecifics remote =
CreateAutofillProfileSpecifics(kGuidA, kHttpsOrigin);
ASSERT_FALSE(remote.has_validity_state_bitfield());
// 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