Commit ec0501c5 authored by Siyu An's avatar Siyu An Committed by Commit Bot

[Autofill Offer] Implement MergeSyncData function

This will set the synced data to the Autofill Table.

Added tests also

Bug: 1112095
Change-Id: Ib1b05101a4db43797b8923fd215a550d5f03b6c8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2412816
Commit-Queue: Siyu An <siyua@chromium.org>
Reviewed-by: default avatarDominic Battré <battre@chromium.org>
Reviewed-by: default avatarMarc Treib <treib@chromium.org>
Reviewed-by: default avatarJared Saul <jsaul@google.com>
Cr-Commit-Position: refs/heads/master@{#808062}
parent c31055f0
...@@ -581,20 +581,34 @@ CreditCardCloudTokenData GetCreditCardCloudTokenData2() { ...@@ -581,20 +581,34 @@ CreditCardCloudTokenData GetCreditCardCloudTokenData2() {
return data; return data;
} }
AutofillOfferData GetCardLinkedOfferData() { AutofillOfferData GetCardLinkedOfferData1() {
AutofillOfferData data; AutofillOfferData data;
data.offer_id = 111; data.offer_id = 111;
data.offer_reward_amount = "5%"; data.offer_reward_amount = "5%";
// Sets the expiry to be 45 days later. // Sets the expiry to be 45 days later.
data.expiry = AutofillClock::Now() + base::TimeDelta::FromDays(45); data.expiry = AutofillClock::Now() + base::TimeDelta::FromDays(45);
data.offer_details_url = GURL("http://www.example.com"); data.offer_details_url = GURL("http://www.example1.com");
data.merchant_domain = std::vector<GURL>(); data.merchant_domain = std::vector<GURL>();
data.merchant_domain.emplace_back(GURL("http://www.example.com")); data.merchant_domain.emplace_back(GURL("http://www.example1.com"));
data.eligible_instrument_id = std::vector<int64_t>(); data.eligible_instrument_id = std::vector<int64_t>();
data.eligible_instrument_id.emplace_back(111111); data.eligible_instrument_id.emplace_back(111111);
return data; return data;
} }
AutofillOfferData GetCardLinkedOfferData2() {
AutofillOfferData data;
data.offer_id = 222;
data.offer_reward_amount = "$10";
// Sets the expiry to be 40 days later.
data.expiry = AutofillClock::Now() + base::TimeDelta::FromDays(40);
data.offer_details_url = GURL("http://www.example2.com");
data.merchant_domain = std::vector<GURL>();
data.merchant_domain.emplace_back(GURL("http://www.example2.com"));
data.eligible_instrument_id = std::vector<int64_t>();
data.eligible_instrument_id.emplace_back(222222);
return data;
}
void SetProfileInfo(AutofillProfile* profile, void SetProfileInfo(AutofillProfile* profile,
const char* first_name, const char* first_name,
const char* middle_name, const char* middle_name,
......
...@@ -184,7 +184,11 @@ CreditCardCloudTokenData GetCreditCardCloudTokenData1(); ...@@ -184,7 +184,11 @@ CreditCardCloudTokenData GetCreditCardCloudTokenData1();
CreditCardCloudTokenData GetCreditCardCloudTokenData2(); CreditCardCloudTokenData GetCreditCardCloudTokenData2();
// Returns an autofill card linked offer data full of dummy info. // Returns an autofill card linked offer data full of dummy info.
AutofillOfferData GetCardLinkedOfferData(); AutofillOfferData GetCardLinkedOfferData1();
// Returns an autofill card linked offer data full of dummy info, different from
// the one above.
AutofillOfferData GetCardLinkedOfferData2();
// A unit testing utility that is common to a number of the Autofill unit // A unit testing utility that is common to a number of the Autofill unit
// tests. |SetProfileInfo| provides a quick way to populate a profile with // tests. |SetProfileInfo| provides a quick way to populate a profile with
......
...@@ -15,4 +15,54 @@ AutofillOfferData::AutofillOfferData(const AutofillOfferData&) = default; ...@@ -15,4 +15,54 @@ AutofillOfferData::AutofillOfferData(const AutofillOfferData&) = default;
AutofillOfferData& AutofillOfferData::operator=(const AutofillOfferData&) = AutofillOfferData& AutofillOfferData::operator=(const AutofillOfferData&) =
default; default;
bool AutofillOfferData::operator==(
const AutofillOfferData& other_offer_data) const {
return Compare(other_offer_data) == 0;
}
bool AutofillOfferData::operator!=(
const AutofillOfferData& other_offer_data) const {
return Compare(other_offer_data) != 0;
}
int AutofillOfferData::Compare(
const AutofillOfferData& other_offer_data) const {
int comparison = offer_id - other_offer_data.offer_id;
if (comparison != 0)
return comparison;
comparison =
offer_reward_amount.compare(other_offer_data.offer_reward_amount);
if (comparison != 0)
return comparison;
if (expiry < other_offer_data.expiry)
return -1;
if (expiry > other_offer_data.expiry)
return 1;
comparison = offer_details_url.spec().compare(
other_offer_data.offer_details_url.spec());
if (comparison != 0)
return comparison;
// TODO(crbug.com/1112095): Change merchant domain in AutofillOfferData to
// string (now it is GURL) and handle its comparison in a separate CL.
std::vector<int64_t> eligible_instrument_id_copy = eligible_instrument_id;
std::vector<int64_t> other_eligible_instrument_id_copy =
other_offer_data.eligible_instrument_id;
std::sort(eligible_instrument_id_copy.begin(),
eligible_instrument_id_copy.end());
std::sort(other_eligible_instrument_id_copy.begin(),
other_eligible_instrument_id_copy.end());
if (eligible_instrument_id_copy < other_eligible_instrument_id_copy)
return -1;
if (eligible_instrument_id_copy > other_eligible_instrument_id_copy)
return 1;
return 0;
}
} // namespace autofill } // namespace autofill
\ No newline at end of file
...@@ -22,6 +22,13 @@ struct AutofillOfferData { ...@@ -22,6 +22,13 @@ struct AutofillOfferData {
~AutofillOfferData(); ~AutofillOfferData();
AutofillOfferData(const AutofillOfferData&); AutofillOfferData(const AutofillOfferData&);
AutofillOfferData& operator=(const AutofillOfferData&); AutofillOfferData& operator=(const AutofillOfferData&);
bool operator==(const AutofillOfferData& other_offer_data) const;
bool operator!=(const AutofillOfferData& other_offer_data) const;
// Compares two AutofillOfferData based on their member fields. Returns 0 if
// the two offer data are exactly same. Otherwise returns the comparison
// result of first found difference.
int Compare(const AutofillOfferData& other_offer_data) const;
// The unique server ID for this offer data. // The unique server ID for this offer data.
int64_t offer_id; int64_t offer_id;
......
...@@ -311,6 +311,31 @@ void SetAutofillOfferSpecificsFromOfferData( ...@@ -311,6 +311,31 @@ void SetAutofillOfferSpecificsFromOfferData(
} }
} }
AutofillOfferData AutofillOfferDataFromOfferSpecifics(
const sync_pb::AutofillOfferSpecifics& offer_specifics) {
AutofillOfferData offer_data;
offer_data.offer_id = offer_specifics.id();
if (offer_specifics.has_percentage_reward()) {
offer_data.offer_reward_amount =
offer_specifics.percentage_reward().percentage();
} else {
offer_data.offer_reward_amount =
offer_specifics.fixed_amount_reward().amount();
}
offer_data.expiry =
base::Time::UnixEpoch() +
base::TimeDelta::FromSeconds(offer_specifics.offer_expiry_date());
offer_data.offer_details_url = GURL(offer_specifics.offer_details_url());
for (const std::string& domain : offer_specifics.merchant_domain()) {
offer_data.merchant_domain.emplace_back(domain);
}
for (int64_t instrument_id :
offer_specifics.card_linked_offer_data().instrument_id()) {
offer_data.eligible_instrument_id.push_back(instrument_id);
}
return offer_data;
}
AutofillProfile ProfileFromSpecifics( AutofillProfile ProfileFromSpecifics(
const sync_pb::WalletPostalAddress& address) { const sync_pb::WalletPostalAddress& address) {
AutofillProfile profile(AutofillProfile::SERVER_PROFILE, std::string()); AutofillProfile profile(AutofillProfile::SERVER_PROFILE, std::string());
...@@ -431,4 +456,41 @@ void PopulateWalletTypesFromSyncData( ...@@ -431,4 +456,41 @@ void PopulateWalletTypesFromSyncData(
} }
} }
template <class Item>
bool AreAnyItemsDifferent(const std::vector<std::unique_ptr<Item>>& old_data,
const std::vector<Item>& new_data) {
if (old_data.size() != new_data.size())
return true;
std::vector<const Item*> old_ptrs;
old_ptrs.reserve(old_data.size());
for (const std::unique_ptr<Item>& old_item : old_data)
old_ptrs.push_back(old_item.get());
std::vector<const Item*> new_ptrs;
new_ptrs.reserve(new_data.size());
for (const Item& new_item : new_data)
new_ptrs.push_back(&new_item);
// Sort our vectors.
auto compare_less = [](const Item* lhs, const Item* rhs) {
return lhs->Compare(*rhs) < 0;
};
std::sort(old_ptrs.begin(), old_ptrs.end(), compare_less);
std::sort(new_ptrs.begin(), new_ptrs.end(), compare_less);
auto compare_equal = [](const Item* lhs, const Item* rhs) {
return lhs->Compare(*rhs) == 0;
};
return !std::equal(old_ptrs.begin(), old_ptrs.end(), new_ptrs.begin(),
compare_equal);
}
template bool AreAnyItemsDifferent<>(
const std::vector<std::unique_ptr<AutofillOfferData>>&,
const std::vector<AutofillOfferData>&);
template bool AreAnyItemsDifferent<>(
const std::vector<std::unique_ptr<CreditCardCloudTokenData>>&,
const std::vector<CreditCardCloudTokenData>&);
} // namespace autofill } // namespace autofill
...@@ -62,6 +62,10 @@ void SetAutofillOfferSpecificsFromOfferData( ...@@ -62,6 +62,10 @@ void SetAutofillOfferSpecificsFromOfferData(
const AutofillOfferData& offer_data, const AutofillOfferData& offer_data,
sync_pb::AutofillOfferSpecifics* offer_specifics); sync_pb::AutofillOfferSpecifics* offer_specifics);
// Creates an AutofillOfferData from the specified |offer_specifics|.
AutofillOfferData AutofillOfferDataFromOfferSpecifics(
const sync_pb::AutofillOfferSpecifics& offer_specifics);
// Creates an AutofillProfile from the specified |address| specifics. // Creates an AutofillProfile from the specified |address| specifics.
AutofillProfile ProfileFromSpecifics( AutofillProfile ProfileFromSpecifics(
const sync_pb::WalletPostalAddress& address); const sync_pb::WalletPostalAddress& address);
...@@ -86,6 +90,13 @@ void PopulateWalletTypesFromSyncData( ...@@ -86,6 +90,13 @@ void PopulateWalletTypesFromSyncData(
std::vector<PaymentsCustomerData>* customer_data, std::vector<PaymentsCustomerData>* customer_data,
std::vector<CreditCardCloudTokenData>* cloud_token_data); std::vector<CreditCardCloudTokenData>* cloud_token_data);
// A helper function to compare two sets of data. Returns true if there is
// any difference. It uses the Compare() of the Item class instead of comparison
// operators and does not care about the order of items in the dataset.
template <class Item>
bool AreAnyItemsDifferent(const std::vector<std::unique_ptr<Item>>& old_data,
const std::vector<Item>& new_data);
} // namespace autofill } // namespace autofill
#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_SYNC_BRIDGE_UTIL_H_ #endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_SYNC_BRIDGE_UTIL_H_
...@@ -243,7 +243,7 @@ TEST_F(AutofillSyncBridgeUtilTest, ...@@ -243,7 +243,7 @@ TEST_F(AutofillSyncBridgeUtilTest,
// AutofillOfferSpecifics. // AutofillOfferSpecifics.
TEST_F(AutofillSyncBridgeUtilTest, OfferSpecificsFromOfferData) { TEST_F(AutofillSyncBridgeUtilTest, OfferSpecificsFromOfferData) {
sync_pb::AutofillOfferSpecifics offer_specifics; sync_pb::AutofillOfferSpecifics offer_specifics;
AutofillOfferData offer_data = test::GetCardLinkedOfferData(); AutofillOfferData offer_data = test::GetCardLinkedOfferData1();
SetAutofillOfferSpecificsFromOfferData(offer_data, &offer_specifics); SetAutofillOfferSpecificsFromOfferData(offer_data, &offer_specifics);
EXPECT_EQ(offer_specifics.id(), offer_data.offer_id); EXPECT_EQ(offer_specifics.id(), offer_data.offer_id);
...@@ -270,5 +270,39 @@ TEST_F(AutofillSyncBridgeUtilTest, OfferSpecificsFromOfferData) { ...@@ -270,5 +270,39 @@ TEST_F(AutofillSyncBridgeUtilTest, OfferSpecificsFromOfferData) {
} }
} }
// Ensures that the ShouldResetAutofillWalletData function works correctly, if
// the two given data sets have the same size.
TEST_F(AutofillSyncBridgeUtilTest,
ShouldResetAutofillWalletData_SameDataSetSize) {
std::vector<std::unique_ptr<AutofillOfferData>> old_offer_data;
std::vector<AutofillOfferData> new_offer_data;
AutofillOfferData data1 = test::GetCardLinkedOfferData1();
AutofillOfferData data2 = test::GetCardLinkedOfferData2();
old_offer_data.push_back(std::make_unique<AutofillOfferData>(data1));
new_offer_data.push_back(data2);
old_offer_data.push_back(std::make_unique<AutofillOfferData>(data2));
new_offer_data.push_back(data1);
EXPECT_FALSE(AreAnyItemsDifferent(old_offer_data, new_offer_data));
new_offer_data.at(0).offer_id += 456;
EXPECT_TRUE(AreAnyItemsDifferent(old_offer_data, new_offer_data));
}
// Ensures that the ShouldResetAutofillWalletData function works correctly, if
// the two given data sets have different size.
TEST_F(AutofillSyncBridgeUtilTest,
ShouldResetAutofillWalletData_DifferentDataSetSize) {
std::vector<std::unique_ptr<AutofillOfferData>> old_offer_data;
std::vector<AutofillOfferData> new_offer_data;
AutofillOfferData data1 = test::GetCardLinkedOfferData1();
AutofillOfferData data2 = test::GetCardLinkedOfferData2();
old_offer_data.push_back(std::make_unique<AutofillOfferData>(data1));
new_offer_data.push_back(data2);
new_offer_data.push_back(data1);
EXPECT_TRUE(AreAnyItemsDifferent(old_offer_data, new_offer_data));
}
} // namespace } // namespace
} // namespace autofill } // namespace autofill
...@@ -9,9 +9,11 @@ ...@@ -9,9 +9,11 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "components/autofill/core/browser/data_model/autofill_offer_data.h" #include "components/autofill/core/browser/data_model/autofill_offer_data.h"
#include "components/autofill/core/browser/webdata/autofill_sync_bridge_util.h"
#include "components/autofill/core/browser/webdata/autofill_table.h" #include "components/autofill/core/browser/webdata/autofill_table.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_backend.h" #include "components/autofill/core/browser/webdata/autofill_webdata_backend.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
#include "components/sync/model/mutable_data_batch.h"
#include "components/sync/model_impl/client_tag_based_model_type_processor.h" #include "components/sync/model_impl/client_tag_based_model_type_processor.h"
#include "components/sync/model_impl/sync_metadata_store_change_list.h" #include "components/sync/model_impl/sync_metadata_store_change_list.h"
...@@ -80,7 +82,17 @@ AutofillWalletOfferSyncBridge::CreateMetadataChangeList() { ...@@ -80,7 +82,17 @@ AutofillWalletOfferSyncBridge::CreateMetadataChangeList() {
base::Optional<syncer::ModelError> AutofillWalletOfferSyncBridge::MergeSyncData( base::Optional<syncer::ModelError> AutofillWalletOfferSyncBridge::MergeSyncData(
std::unique_ptr<syncer::MetadataChangeList> metadata_change_list, std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
syncer::EntityChangeList entity_data) { syncer::EntityChangeList entity_data) {
NOTIMPLEMENTED(); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// All metadata changes have been already written, return early for an error.
base::Optional<syncer::ModelError> error =
static_cast<syncer::SyncMetadataStoreChangeList*>(
metadata_change_list.get())
->TakeError();
if (error) {
return error;
}
MergeRemoteData(std::move(entity_data));
return base::nullopt; return base::nullopt;
} }
...@@ -88,18 +100,18 @@ base::Optional<syncer::ModelError> ...@@ -88,18 +100,18 @@ base::Optional<syncer::ModelError>
AutofillWalletOfferSyncBridge::ApplySyncChanges( AutofillWalletOfferSyncBridge::ApplySyncChanges(
std::unique_ptr<syncer::MetadataChangeList> metadata_change_list, std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
syncer::EntityChangeList entity_data) { syncer::EntityChangeList entity_data) {
NOTIMPLEMENTED(); // This bridge does not support incremental updates, so whenever this is
// called, the change list should be empty.
DCHECK(entity_data.empty()) << "Received an unsupported incremental update.";
return base::nullopt; return base::nullopt;
} }
void AutofillWalletOfferSyncBridge::GetData(StorageKeyList storage_keys, void AutofillWalletOfferSyncBridge::GetData(StorageKeyList storage_keys,
DataCallback callback) { DataCallback callback) {}
NOTIMPLEMENTED();
}
void AutofillWalletOfferSyncBridge::GetAllDataForDebugging( void AutofillWalletOfferSyncBridge::GetAllDataForDebugging(
DataCallback callback) { DataCallback callback) {
NOTIMPLEMENTED(); GetAllDataImpl(std::move(callback));
} }
std::string AutofillWalletOfferSyncBridge::GetClientTag( std::string AutofillWalletOfferSyncBridge::GetClientTag(
...@@ -122,7 +134,66 @@ bool AutofillWalletOfferSyncBridge::SupportsIncrementalUpdates() const { ...@@ -122,7 +134,66 @@ bool AutofillWalletOfferSyncBridge::SupportsIncrementalUpdates() const {
void AutofillWalletOfferSyncBridge::ApplyStopSyncChanges( void AutofillWalletOfferSyncBridge::ApplyStopSyncChanges(
std::unique_ptr<syncer::MetadataChangeList> delete_metadata_change_list) { std::unique_ptr<syncer::MetadataChangeList> delete_metadata_change_list) {
NOTIMPLEMENTED(); // If a metadata change list gets passed in, that means sync is actually
// disabled, so we want to delete the payments data.
if (delete_metadata_change_list) {
MergeRemoteData(syncer::EntityChangeList());
}
}
void AutofillWalletOfferSyncBridge::GetAllDataImpl(DataCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::vector<std::unique_ptr<AutofillOfferData>> offers;
if (!GetAutofillTable()->GetCreditCardOffers(&offers)) {
change_processor()->ReportError(
{FROM_HERE, "Failed to load offer data from table."});
return;
}
auto batch = std::make_unique<syncer::MutableDataBatch>();
for (const std::unique_ptr<AutofillOfferData>& offer : offers) {
auto entity_data = std::make_unique<syncer::EntityData>();
sync_pb::AutofillOfferSpecifics* offer_specifics =
entity_data->specifics.mutable_autofill_offer();
SetAutofillOfferSpecificsFromOfferData(*offer, offer_specifics);
entity_data->name =
"Offer " +
GetBase64EncodedId(GetClientTagFromSpecifics(*offer_specifics));
batch->Put(GetStorageKeyFromSpecifics(*offer_specifics),
std::move(entity_data));
}
std::move(callback).Run(std::move(batch));
}
void AutofillWalletOfferSyncBridge::MergeRemoteData(
const syncer::EntityChangeList& entity_data) {
std::vector<AutofillOfferData> offer_data;
for (const std::unique_ptr<syncer::EntityChange>& change : entity_data) {
DCHECK(change->data().specifics.has_autofill_offer());
// TODO(crbug.com/1112095): Add offer data validation.
offer_data.push_back(AutofillOfferDataFromOfferSpecifics(
change->data().specifics.autofill_offer()));
}
AutofillTable* table = GetAutofillTable();
// Only do a write operation if there is any difference between server data
// and local data.
std::vector<std::unique_ptr<AutofillOfferData>> existing_offers;
table->GetCreditCardOffers(&existing_offers);
if (AreAnyItemsDifferent(existing_offers, offer_data))
table->SetCreditCardOffers(offer_data);
// Commit the transaction to make sure the data and the metadata with the
// new progress marker is written down (especially on Android where we
// cannot rely on committing transactions on shutdown). We need to commit
// even if the wallet data has not changed because the model type state incl.
// the progress marker always changes.
web_data_backend_->CommitChanges();
} }
AutofillTable* AutofillWalletOfferSyncBridge::GetAutofillTable() { AutofillTable* AutofillWalletOfferSyncBridge::GetAutofillTable() {
......
...@@ -35,7 +35,7 @@ class AutofillWalletOfferSyncBridge : public base::SupportsUserData::Data, ...@@ -35,7 +35,7 @@ class AutofillWalletOfferSyncBridge : public base::SupportsUserData::Data,
static syncer::ModelTypeSyncBridge* FromWebDataService( static syncer::ModelTypeSyncBridge* FromWebDataService(
AutofillWebDataService* web_data_service); AutofillWebDataService* web_data_service);
explicit AutofillWalletOfferSyncBridge( AutofillWalletOfferSyncBridge(
std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor, std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor,
AutofillWebDataBackend* web_data_backend); AutofillWebDataBackend* web_data_backend);
~AutofillWalletOfferSyncBridge() override; ~AutofillWalletOfferSyncBridge() override;
...@@ -62,6 +62,12 @@ class AutofillWalletOfferSyncBridge : public base::SupportsUserData::Data, ...@@ -62,6 +62,12 @@ class AutofillWalletOfferSyncBridge : public base::SupportsUserData::Data,
delete_metadata_change_list) override; delete_metadata_change_list) override;
private: private:
// Helper function to send all offer data to the callback.
void GetAllDataImpl(DataCallback callback);
// Merges synced remote offer data.
void MergeRemoteData(const syncer::EntityChangeList& entity_data);
// Returns the table associated with the |web_data_backend_|. // Returns the table associated with the |web_data_backend_|.
AutofillTable* GetAutofillTable(); AutofillTable* GetAutofillTable();
......
...@@ -10,7 +10,9 @@ ...@@ -10,7 +10,9 @@
#include <utility> #include <utility>
#include "base/files/scoped_temp_dir.h" #include "base/files/scoped_temp_dir.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/test/bind_test_util.h"
#include "base/test/task_environment.h" #include "base/test/task_environment.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/autofill_test_utils.h"
...@@ -26,6 +28,7 @@ ...@@ -26,6 +28,7 @@
#include "components/sync/model/mock_model_type_change_processor.h" #include "components/sync/model/mock_model_type_change_processor.h"
#include "components/sync/model/sync_data.h" #include "components/sync/model/sync_data.h"
#include "components/sync/model_impl/client_tag_based_model_type_processor.h" #include "components/sync/model_impl/client_tag_based_model_type_processor.h"
#include "components/sync/model_impl/in_memory_metadata_change_list.h"
#include "components/sync/protocol/autofill_specifics.pb.h" #include "components/sync/protocol/autofill_specifics.pb.h"
#include "components/sync/protocol/sync.pb.h" #include "components/sync/protocol/sync.pb.h"
#include "components/webdata/common/web_database.h" #include "components/webdata/common/web_database.h"
...@@ -45,6 +48,57 @@ using testing::Return; ...@@ -45,6 +48,57 @@ using testing::Return;
const char kLocaleString[] = "en-US"; const char kLocaleString[] = "en-US";
const char kDefaultCacheGuid[] = "CacheGuid"; const char kDefaultCacheGuid[] = "CacheGuid";
void ExtractAutofillOfferSpecificsFromDataBatch(
std::unique_ptr<syncer::DataBatch> batch,
std::vector<AutofillOfferSpecifics>* output) {
while (batch->HasNext()) {
const syncer::KeyAndData& data_pair = batch->Next();
output->push_back(data_pair.second->specifics.autofill_offer());
}
}
std::string AutofillOfferSpecificsAsDebugString(
const AutofillOfferSpecifics& specifics) {
std::ostringstream output;
std::string offer_reward_amount_string =
specifics.has_percentage_reward()
? specifics.percentage_reward().percentage()
: specifics.fixed_amount_reward().amount();
std::string domain_string;
for (std::string merchant_domain : specifics.merchant_domain()) {
base::StrAppend(&domain_string, {merchant_domain, ", "});
}
std::string instrument_id_string;
for (int64_t eligible_instrument_id :
specifics.card_linked_offer_data().instrument_id()) {
base::StrAppend(&instrument_id_string,
{base::NumberToString(eligible_instrument_id), ", "});
}
output << "[id: " << specifics.id()
<< ", offer_reward_amount: " << offer_reward_amount_string
<< ", offer_expiry_date: " << specifics.offer_expiry_date()
<< ", offer_details_url: " << specifics.offer_details_url()
<< ", merchant_domain: " << domain_string
<< ", eligible_instrument_id: " << instrument_id_string << "]";
return output.str();
}
MATCHER_P(EqualsSpecifics, expected, "") {
if (arg.SerializeAsString() != expected.SerializeAsString()) {
*result_listener << "entry\n"
<< AutofillOfferSpecificsAsDebugString(arg) << "\n"
<< "did not match expected\n"
<< AutofillOfferSpecificsAsDebugString(expected);
return false;
}
return true;
}
} // namespace } // namespace
class AutofillWalletOfferSyncBridgeTest : public testing::Test { class AutofillWalletOfferSyncBridgeTest : public testing::Test {
...@@ -88,6 +142,53 @@ class AutofillWalletOfferSyncBridgeTest : public testing::Test { ...@@ -88,6 +142,53 @@ class AutofillWalletOfferSyncBridgeTest : public testing::Test {
mock_processor_.CreateForwardingProcessor(), &backend_); mock_processor_.CreateForwardingProcessor(), &backend_);
} }
void StartSyncing(
const std::vector<AutofillOfferSpecifics>& remote_data = {}) {
base::RunLoop loop;
syncer::DataTypeActivationRequest request;
request.error_handler = base::DoNothing();
request.cache_guid = kDefaultCacheGuid;
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);
state.mutable_progress_marker()
->mutable_gc_directive()
->set_version_watermark(1);
syncer::UpdateResponseDataList initial_updates;
for (const AutofillOfferSpecifics& specifics : remote_data) {
initial_updates.push_back(SpecificsToUpdateResponse(specifics));
}
real_processor_->OnUpdateReceived(state, std::move(initial_updates));
}
std::vector<AutofillOfferSpecifics> GetAllLocalData() {
std::vector<AutofillOfferSpecifics> data;
// Perform an async call synchronously for testing.
base::RunLoop loop;
bridge()->GetAllDataForDebugging(base::BindLambdaForTesting(
[&loop, &data](std::unique_ptr<syncer::DataBatch> batch) {
ExtractAutofillOfferSpecificsFromDataBatch(std::move(batch), &data);
loop.Quit();
}));
loop.Run();
return data;
}
syncer::UpdateResponseData SpecificsToUpdateResponse(
const AutofillOfferSpecifics& specifics) {
syncer::UpdateResponseData data;
data.entity = SpecificsToEntity(specifics);
return data;
}
EntityData SpecificsToEntity(const AutofillOfferSpecifics& specifics) { EntityData SpecificsToEntity(const AutofillOfferSpecifics& specifics) {
EntityData data; EntityData data;
*data.specifics.mutable_autofill_offer() = specifics; *data.specifics.mutable_autofill_offer() = specifics;
...@@ -100,6 +201,8 @@ class AutofillWalletOfferSyncBridgeTest : public testing::Test { ...@@ -100,6 +201,8 @@ class AutofillWalletOfferSyncBridgeTest : public testing::Test {
AutofillWalletOfferSyncBridge* bridge() { return bridge_.get(); } AutofillWalletOfferSyncBridge* bridge() { return bridge_.get(); }
MockAutofillWebDataBackend* backend() { return &backend_; }
private: private:
ScopedTempDir temp_dir_; ScopedTempDir temp_dir_;
base::test::SingleThreadTaskEnvironment task_environment_; base::test::SingleThreadTaskEnvironment task_environment_;
...@@ -113,7 +216,7 @@ class AutofillWalletOfferSyncBridgeTest : public testing::Test { ...@@ -113,7 +216,7 @@ class AutofillWalletOfferSyncBridgeTest : public testing::Test {
TEST_F(AutofillWalletOfferSyncBridgeTest, VerifyGetClientTag) { TEST_F(AutofillWalletOfferSyncBridgeTest, VerifyGetClientTag) {
AutofillOfferSpecifics specifics; AutofillOfferSpecifics specifics;
AutofillOfferData data = test::GetCardLinkedOfferData(); AutofillOfferData data = test::GetCardLinkedOfferData1();
SetAutofillOfferSpecificsFromOfferData(data, &specifics); SetAutofillOfferSpecificsFromOfferData(data, &specifics);
EXPECT_EQ(bridge()->GetClientTag(SpecificsToEntity(specifics)), EXPECT_EQ(bridge()->GetClientTag(SpecificsToEntity(specifics)),
base::NumberToString(data.offer_id)); base::NumberToString(data.offer_id));
...@@ -121,10 +224,78 @@ TEST_F(AutofillWalletOfferSyncBridgeTest, VerifyGetClientTag) { ...@@ -121,10 +224,78 @@ TEST_F(AutofillWalletOfferSyncBridgeTest, VerifyGetClientTag) {
TEST_F(AutofillWalletOfferSyncBridgeTest, VerifyGetStorageKey) { TEST_F(AutofillWalletOfferSyncBridgeTest, VerifyGetStorageKey) {
AutofillOfferSpecifics specifics; AutofillOfferSpecifics specifics;
AutofillOfferData data = test::GetCardLinkedOfferData(); AutofillOfferData data = test::GetCardLinkedOfferData1();
SetAutofillOfferSpecificsFromOfferData(data, &specifics); SetAutofillOfferSpecificsFromOfferData(data, &specifics);
EXPECT_EQ(bridge()->GetStorageKey(SpecificsToEntity(specifics)), EXPECT_EQ(bridge()->GetStorageKey(SpecificsToEntity(specifics)),
base::NumberToString(data.offer_id)); base::NumberToString(data.offer_id));
} }
// Tests that when a new offer data is sent by the server, the client only keeps
// the new data.
TEST_F(AutofillWalletOfferSyncBridgeTest, MergeSyncData_NewData) {
// Create one offer data in the client table.
AutofillOfferData old_data = test::GetCardLinkedOfferData1();
table()->SetCreditCardOffers({old_data});
// Create a different one on the server.
AutofillOfferSpecifics offer_specifics;
SetAutofillOfferSpecificsFromOfferData(test::GetCardLinkedOfferData2(),
&offer_specifics);
EXPECT_CALL(*backend(), CommitChanges());
StartSyncing({offer_specifics});
// Only the server offer should be present on the client.
EXPECT_THAT(GetAllLocalData(),
testing::UnorderedElementsAre(EqualsSpecifics(offer_specifics)));
}
// Tests that when no data is sent by the server, all local data should be
// deleted.
TEST_F(AutofillWalletOfferSyncBridgeTest, MergeSyncData_NoData) {
// Create one offer data in the client table.
AutofillOfferData client_data = test::GetCardLinkedOfferData1();
table()->SetCreditCardOffers({client_data});
EXPECT_CALL(*backend(), CommitChanges());
StartSyncing({});
EXPECT_TRUE(GetAllLocalData().empty());
}
// Tests that when sync is stopped and the data type is disabled, client should
// remove all client data.
TEST_F(AutofillWalletOfferSyncBridgeTest, ApplyStopSyncChanges_ClearAllData) {
// Create one offer data in the client table.
AutofillOfferData client_data = test::GetCardLinkedOfferData1();
table()->SetCreditCardOffers({client_data});
EXPECT_CALL(*backend(), CommitChanges());
// Passing in a non-null metadata change list indicates to the bridge that
// sync is stopping but the data type is not disabled.
bridge()->ApplyStopSyncChanges(/*delete_metadata_change_list=*/
std::make_unique<
syncer::InMemoryMetadataChangeList>());
EXPECT_TRUE(GetAllLocalData().empty());
}
// Tests that when sync is stopped but the data type is not disabled, client
// should keep all the data.
TEST_F(AutofillWalletOfferSyncBridgeTest, ApplyStopSyncChanges_KeepAllData) {
// Create one offer data in the client table.
AutofillOfferData client_data = test::GetCardLinkedOfferData1();
table()->SetCreditCardOffers({client_data});
// We do not write to DB at all, so we should not commit any changes.
EXPECT_CALL(*backend(), CommitChanges()).Times(0);
// Passing in a null metadata change list indicates to the bridge that
// sync is stopping and the data type is disabled.
bridge()->ApplyStopSyncChanges(/*delete_metadata_change_list=*/nullptr);
EXPECT_FALSE(GetAllLocalData().empty());
}
} // namespace autofill } // namespace autofill
...@@ -435,7 +435,7 @@ bool AutofillWalletSyncBridge::SetCreditCardCloudTokenData( ...@@ -435,7 +435,7 @@ bool AutofillWalletSyncBridge::SetCreditCardCloudTokenData(
std::vector<std::unique_ptr<CreditCardCloudTokenData>> existing_data; std::vector<std::unique_ptr<CreditCardCloudTokenData>> existing_data;
table->GetCreditCardCloudTokenData(&existing_data); table->GetCreditCardCloudTokenData(&existing_data);
if (ShouldResetAutofillWalletData(existing_data, cloud_token_data)) { if (AreAnyItemsDifferent(existing_data, cloud_token_data)) {
table->SetCreditCardCloudTokenData(cloud_token_data); table->SetCreditCardCloudTokenData(cloud_token_data);
return true; return true;
} }
...@@ -512,36 +512,6 @@ AutofillWalletSyncBridge::ComputeAutofillWalletDiff( ...@@ -512,36 +512,6 @@ AutofillWalletSyncBridge::ComputeAutofillWalletDiff(
return result; return result;
} }
template <class Item>
bool AutofillWalletSyncBridge::ShouldResetAutofillWalletData(
const std::vector<std::unique_ptr<Item>>& old_data,
const std::vector<Item>& new_data) {
std::vector<const Item*> old_ptrs;
old_ptrs.reserve(old_data.size());
for (const std::unique_ptr<Item>& old_item : old_data)
old_ptrs.push_back(old_item.get());
std::vector<const Item*> new_ptrs;
new_ptrs.reserve(new_data.size());
for (const Item& new_item : new_data)
new_ptrs.push_back(&new_item);
if (old_ptrs.size() != new_ptrs.size())
return true;
// Sort our vectors.
auto compare_less = [](const Item* lhs, const Item* rhs) {
return lhs->Compare(*rhs) < 0;
};
std::sort(old_ptrs.begin(), old_ptrs.end(), compare_less);
std::sort(new_ptrs.begin(), new_ptrs.end(), compare_less);
auto compare_equal = [](const Item* lhs, const Item* rhs) {
return lhs->Compare(*rhs) == 0;
};
return !std::equal(old_ptrs.begin(), old_ptrs.end(), new_ptrs.begin(),
compare_equal);
}
AutofillTable* AutofillWalletSyncBridge::GetAutofillTable() { AutofillTable* AutofillWalletSyncBridge::GetAutofillTable() {
return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase()); return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase());
} }
......
...@@ -127,13 +127,6 @@ class AutofillWalletSyncBridge : public base::SupportsUserData::Data, ...@@ -127,13 +127,6 @@ class AutofillWalletSyncBridge : public base::SupportsUserData::Data,
const std::vector<std::unique_ptr<Item>>& old_data, const std::vector<std::unique_ptr<Item>>& old_data,
const std::vector<Item>& new_data); const std::vector<Item>& new_data);
// A simpler version of ComputeAutofillWalletDiff that only returns true if
// there is any difference.
template <class Item>
bool ShouldResetAutofillWalletData(
const std::vector<std::unique_ptr<Item>>& old_data,
const std::vector<Item>& new_data);
// Returns the table associated with the |web_data_backend_|. // Returns the table associated with the |web_data_backend_|.
AutofillTable* GetAutofillTable(); AutofillTable* GetAutofillTable();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment