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

[Autofill Offer] Add sync integration tests for the offer type

Offer data will be server-only data, so I figure we don't need two
clients integration tests.

Bug: 1093057
Change-Id: I8afb57ed49b7ab6d57071c331ea679fdf1928260
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2453351
Commit-Queue: Siyu An <siyua@chromium.org>
Reviewed-by: default avatarJan Krcal <jkrcal@chromium.org>
Reviewed-by: default avatarMarc Treib <treib@chromium.org>
Cr-Commit-Position: refs/heads/master@{#816337}
parent 7770990a
// Copyright 2020 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 "chrome/browser/sync/test/integration/offer_helper.h"
#include "base/strings/string_number_conversions.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/data_model/autofill_offer_data.h"
#include "components/autofill/core/browser/webdata/autofill_sync_bridge_util.h"
using autofill::AutofillOfferData;
using autofill::SetAutofillOfferSpecificsFromOfferData;
using autofill::test::GetCardLinkedOfferData1;
using sync_pb::AutofillOfferSpecifics;
using sync_pb::SyncEntity;
namespace offer_helper {
SyncEntity CreateDefaultSyncCardLinkedOffer() {
return CreateSyncCardLinkedOffer(GetCardLinkedOfferData1());
}
SyncEntity CreateSyncCardLinkedOffer(const AutofillOfferData& offer_data) {
SyncEntity entity;
entity.set_name(base::NumberToString(offer_data.offer_id));
entity.set_id_string(base::NumberToString(offer_data.offer_id));
entity.set_version(0); // Will be overridden by the fake server.
entity.set_ctime(12345);
entity.set_mtime(12345);
AutofillOfferSpecifics* offer_specifics =
entity.mutable_specifics()->mutable_autofill_offer();
SetAutofillOfferSpecificsFromOfferData(offer_data, offer_specifics);
return entity;
}
} // namespace offer_helper
// Copyright 2020 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.
#ifndef CHROME_BROWSER_SYNC_TEST_INTEGRATION_OFFER_HELPER_H_
#define CHROME_BROWSER_SYNC_TEST_INTEGRATION_OFFER_HELPER_H_
#include <string>
namespace autofill {
struct AutofillOfferData;
} // namespace autofill
namespace sync_pb {
class SyncEntity;
} // namespace sync_pb
namespace offer_helper {
sync_pb::SyncEntity CreateDefaultSyncCardLinkedOffer();
sync_pb::SyncEntity CreateSyncCardLinkedOffer(
const autofill::AutofillOfferData& offer_data);
} // namespace offer_helper
#endif // CHROME_BROWSER_SYNC_TEST_INTEGRATION_OFFER_HELPER_H_
......@@ -32,8 +32,10 @@ bool AreProgressMarkersEquivalent(const std::string& serialized1,
DCHECK(marker1.data_type_id() == marker2.data_type_id());
if (syncer::GetModelTypeFromSpecificsFieldNumber(marker1.data_type_id()) ==
syncer::AUTOFILL_WALLET_DATA) {
return fake_server::AreWalletDataProgressMarkersEquivalent(marker1,
syncer::AUTOFILL_WALLET_DATA ||
syncer::GetModelTypeFromSpecificsFieldNumber(marker1.data_type_id()) ==
syncer::AUTOFILL_WALLET_OFFER) {
return fake_server::AreFullUpdateTypeDataProgressMarkersEquivalent(marker1,
marker2);
}
return marker1.SerializeAsString() == marker2.SerializeAsString();
......
// Copyright 2020 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 "build/build_config.h"
#include "chrome/browser/sync/test/integration/autofill_helper.h"
#include "chrome/browser/sync/test/integration/offer_helper.h"
#include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
#include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
#include "chrome/browser/sync/test/integration/sync_test.h"
#include "chrome/browser/sync/test/integration/wallet_helper.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/data_model/autofill_offer_data.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/personal_data_manager_observer.h"
#include "components/autofill/core/common/autofill_payments_features.h"
#include "components/sync/base/model_type.h"
#include "components/sync/driver/sync_driver_switches.h"
#include "components/sync/driver/sync_service.h"
#include "components/sync/protocol/model_type_state.pb.h"
#include "components/sync/test/fake_server/fake_server.h"
#include "content/public/test/browser_test.h"
#include "testing/gmock/include/gmock/gmock.h"
using autofill::AutofillOfferData;
using autofill::features::kAutofillEnableOffersInDownstream;
using autofill::test::GetCardLinkedOfferData1;
using autofill::test::GetCardLinkedOfferData2;
using offer_helper::CreateDefaultSyncCardLinkedOffer;
using offer_helper::CreateSyncCardLinkedOffer;
using switches::kSyncAutofillWalletOfferData;
using wallet_helper::GetPersonalDataManager;
using wallet_helper::GetWalletModelTypeState;
namespace {
ACTION_P(QuitMessageLoop, loop) {
loop->Quit();
}
const syncer::SyncFirstSetupCompleteSource kSetSourceFromTest =
syncer::SyncFirstSetupCompleteSource::BASIC_FLOW;
} // namespace
class SingleClientOfferSyncTest : public SyncTest {
public:
SingleClientOfferSyncTest() : SyncTest(SINGLE_CLIENT) {
features_.InitWithFeatures(
/*enabled_features=*/{kSyncAutofillWalletOfferData,
kAutofillEnableOffersInDownstream},
/*disabled_features=*/{});
}
~SingleClientOfferSyncTest() override = default;
SingleClientOfferSyncTest(const SingleClientOfferSyncTest&) = delete;
SingleClientOfferSyncTest& operator=(const SingleClientOfferSyncTest&) =
delete;
protected:
void WaitForOnPersonalDataChanged(autofill::PersonalDataManager* pdm) {
testing::NiceMock<PersonalDataLoadedObserverMock> personal_data_observer;
pdm->AddObserver(&personal_data_observer);
base::RunLoop run_loop;
EXPECT_CALL(personal_data_observer, OnPersonalDataChanged())
.WillOnce(QuitMessageLoop(&run_loop));
run_loop.Run();
pdm->RemoveObserver(&personal_data_observer);
}
void WaitForNumberOfOffers(size_t expected_count,
autofill::PersonalDataManager* pdm) {
while (pdm->GetCreditCardOffers().size() != expected_count ||
pdm->HasPendingQueriesForTesting()) {
WaitForOnPersonalDataChanged(pdm);
}
}
bool TriggerGetUpdatesAndWait() {
const base::Time now = base::Time::Now();
// Trigger a sync and wait for the new data to arrive.
TriggerSyncForModelTypes(
0, syncer::ModelTypeSet(syncer::AUTOFILL_WALLET_OFFER));
return FullUpdateTypeProgressMarkerChecker(now, GetSyncService(0),
syncer::AUTOFILL_WALLET_OFFER)
.Wait();
}
private:
base::test::ScopedFeatureList features_;
};
// Ensures that the offer sync type is enabled by default.
IN_PROC_BROWSER_TEST_F(SingleClientOfferSyncTest, EnabledByDefault) {
ASSERT_TRUE(SetupSync());
ASSERT_TRUE(GetClient(0)->service()->GetActiveDataTypes().Has(
syncer::AUTOFILL_WALLET_OFFER));
}
// Ensures that offer data should get cleared from the database when sync is
// disabled.
IN_PROC_BROWSER_TEST_F(SingleClientOfferSyncTest, ClearOnDisableSync) {
GetFakeServer()->SetOfferData({CreateDefaultSyncCardLinkedOffer()});
ASSERT_TRUE(SetupSync());
autofill::PersonalDataManager* pdm = GetPersonalDataManager(0);
ASSERT_NE(nullptr, pdm);
// Make sure the offer data is in the DB.
ASSERT_EQ(1uL, pdm->GetCreditCardOffers().size());
// Disable sync, the offer data should be gone.
GetSyncService(0)->StopAndClear();
WaitForNumberOfOffers(0, pdm);
EXPECT_EQ(0uL, pdm->GetCreditCardOffers().size());
// Turn sync on again, the data should come back.
GetSyncService(0)->GetUserSettings()->SetSyncRequested(true);
// StopAndClear() also clears the "first setup complete" flag, so set it
// again.
GetSyncService(0)->GetUserSettings()->SetFirstSetupComplete(
kSetSourceFromTest);
// Wait until Sync restores the card and it arrives at PDM.
WaitForNumberOfOffers(1, pdm);
EXPECT_EQ(1uL, pdm->GetCreditCardOffers().size());
}
// Ensures that offer data should get cleared from the database when sync is
// (temporarily) stopped, e.g. due to the Sync feature toggle in Android
// settings.
IN_PROC_BROWSER_TEST_F(SingleClientOfferSyncTest, ClearOnStopSync) {
GetFakeServer()->SetOfferData({CreateDefaultSyncCardLinkedOffer()});
ASSERT_TRUE(SetupSync());
autofill::PersonalDataManager* pdm = GetPersonalDataManager(0);
ASSERT_NE(nullptr, pdm);
// Make sure the offer data is in the DB.
ASSERT_EQ(1uL, pdm->GetCreditCardOffers().size());
// Stop sync, the offer data should be gone.
GetSyncService(0)->GetUserSettings()->SetSyncRequested(false);
WaitForNumberOfOffers(0, pdm);
EXPECT_EQ(0uL, pdm->GetCreditCardOffers().size());
// Turn sync on again, the data should come back.
GetSyncService(0)->GetUserSettings()->SetSyncRequested(true);
// Wait until Sync restores the card and it arrives at PDM.
WaitForNumberOfOffers(1, pdm);
EXPECT_EQ(1uL, pdm->GetCreditCardOffers().size());
}
// ChromeOS does not sign out, so the test below does not apply.
#if !defined(OS_CHROMEOS)
// Offer data should get cleared from the database when the user signs out.
IN_PROC_BROWSER_TEST_F(SingleClientOfferSyncTest, ClearOnSignOut) {
GetFakeServer()->SetOfferData({CreateDefaultSyncCardLinkedOffer()});
ASSERT_TRUE(SetupSync());
autofill::PersonalDataManager* pdm = GetPersonalDataManager(0);
ASSERT_NE(nullptr, pdm);
// Make sure the data & metadata is in the DB.
ASSERT_EQ(1uL, pdm->GetCreditCardOffers().size());
// Signout, the data & metadata should be gone.
GetClient(0)->SignOutPrimaryAccount();
WaitForNumberOfOffers(0, pdm);
EXPECT_EQ(0uL, pdm->GetCreditCardOffers().size());
}
#endif // !defined(OS_CHROMEOS)
// Offer is not using incremental updates. Make sure existing data gets
// replaced when synced down.
IN_PROC_BROWSER_TEST_F(SingleClientOfferSyncTest,
NewSyncDataShouldReplaceExistingData) {
AutofillOfferData offer1 = GetCardLinkedOfferData1();
offer1.offer_id = 999;
GetFakeServer()->SetOfferData({CreateSyncCardLinkedOffer(offer1)});
ASSERT_TRUE(SetupSync());
// Make sure the data is in the DB.
autofill::PersonalDataManager* pdm = GetPersonalDataManager(0);
ASSERT_NE(nullptr, pdm);
std::vector<AutofillOfferData*> offers = pdm->GetCreditCardOffers();
ASSERT_EQ(1uL, offers.size());
EXPECT_EQ(999, offers[0]->offer_id);
// Put some completely new data in the sync server.
AutofillOfferData offer2 = GetCardLinkedOfferData2();
offer2.offer_id = 888;
GetFakeServer()->SetOfferData({CreateSyncCardLinkedOffer(offer2)});
WaitForOnPersonalDataChanged(pdm);
// Make sure only the new data is present.
offers = pdm->GetCreditCardOffers();
ASSERT_EQ(1uL, offers.size());
EXPECT_EQ(888, offers[0]->offer_id);
}
// Offer is not using incremental updates. The server either sends a non-empty
// update with deletion gc directives and with the (possibly empty) full data
// set, or (more often) an empty update.
IN_PROC_BROWSER_TEST_F(SingleClientOfferSyncTest, EmptyUpdatesAreIgnored) {
AutofillOfferData offer1 = GetCardLinkedOfferData1();
offer1.offer_id = 999;
GetFakeServer()->SetOfferData({CreateSyncCardLinkedOffer(offer1)});
ASSERT_TRUE(SetupSync());
// Make sure the card is in the DB.
autofill::PersonalDataManager* pdm = GetPersonalDataManager(0);
ASSERT_NE(nullptr, pdm);
std::vector<AutofillOfferData*> offers = pdm->GetCreditCardOffers();
ASSERT_EQ(1uL, offers.size());
EXPECT_EQ(999, offers[0]->offer_id);
// Trigger a sync and wait for the new data to arrive.
sync_pb::ModelTypeState state_before =
GetWalletModelTypeState(syncer::AUTOFILL_WALLET_OFFER, 0);
ASSERT_TRUE(TriggerGetUpdatesAndWait());
// Check that the new progress marker is stored for empty updates. This is a
// regression check for crbug.com/924447.
sync_pb::ModelTypeState state_after =
GetWalletModelTypeState(syncer::AUTOFILL_WALLET_OFFER, 0);
EXPECT_NE(state_before.progress_marker().token(),
state_after.progress_marker().token());
// Refresh the pdm to make sure we are checking its state after any potential
// changes from sync in the DB propagate into pdm. As we don't expect anything
// to change, we have no better specific condition to wait for.
pdm->Refresh();
while (pdm->HasPendingQueriesForTesting()) {
WaitForOnPersonalDataChanged(pdm);
}
// Make sure the same data is present on the client.
offers = pdm->GetCreditCardOffers();
ASSERT_EQ(1uL, offers.size());
EXPECT_EQ(999, offers[0]->offer_id);
}
// If the server sends the same offers with changed data, they should change on
// the client.
IN_PROC_BROWSER_TEST_F(SingleClientOfferSyncTest, ChangedEntityGetsUpdated) {
AutofillOfferData offer = GetCardLinkedOfferData1();
offer.offer_id = 999;
offer.eligible_instrument_id.clear();
offer.eligible_instrument_id.push_back(111111);
GetFakeServer()->SetOfferData({CreateSyncCardLinkedOffer(offer)});
ASSERT_TRUE(SetupSync());
// Make sure the card is in the DB.
autofill::PersonalDataManager* pdm = GetPersonalDataManager(0);
ASSERT_NE(nullptr, pdm);
std::vector<AutofillOfferData*> offers = pdm->GetCreditCardOffers();
ASSERT_EQ(1uL, offers.size());
EXPECT_EQ(999, offers[0]->offer_id);
EXPECT_EQ(1U, offers[0]->eligible_instrument_id.size());
// Update the data.
offer.eligible_instrument_id.push_back(222222);
GetFakeServer()->SetOfferData({CreateSyncCardLinkedOffer(offer)});
WaitForOnPersonalDataChanged(pdm);
// Make sure the data is present on the client.
pdm = GetPersonalDataManager(0);
ASSERT_NE(nullptr, pdm);
offers = pdm->GetCreditCardOffers();
ASSERT_EQ(1uL, offers.size());
EXPECT_EQ(999, offers[0]->offer_id);
EXPECT_EQ(2U, offers[0]->eligible_instrument_id.size());
}
// Offer data should get cleared from the database when the Autofill sync type
// flag is disabled.
IN_PROC_BROWSER_TEST_F(SingleClientOfferSyncTest, ClearOnDisableWalletSync) {
GetFakeServer()->SetOfferData({CreateDefaultSyncCardLinkedOffer()});
ASSERT_TRUE(SetupSync());
autofill::PersonalDataManager* pdm = GetPersonalDataManager(0);
ASSERT_NE(nullptr, pdm);
// Make sure the data is in the DB.
ASSERT_EQ(1uL, pdm->GetCreditCardOffers().size());
// Turn off autofill sync, the data should be gone.
ASSERT_TRUE(
GetClient(0)->DisableSyncForType(syncer::UserSelectableType::kAutofill));
WaitForNumberOfOffers(0, pdm);
EXPECT_EQ(0uL, pdm->GetCreditCardOffers().size());
}
......@@ -63,7 +63,7 @@ using wallet_helper::GetPersonalDataManager;
using wallet_helper::GetProfileWebDataService;
using wallet_helper::GetServerAddressesMetadata;
using wallet_helper::GetServerCardsMetadata;
using wallet_helper::GetWalletDataModelTypeState;
using wallet_helper::GetWalletModelTypeState;
using wallet_helper::kDefaultBillingAddressID;
using wallet_helper::kDefaultCardID;
using wallet_helper::kDefaultCreditCardCloudTokenDataID;
......@@ -110,62 +110,6 @@ class AutofillWebDataServiceConsumer : public WebDataServiceConsumer {
DISALLOW_COPY_AND_ASSIGN(AutofillWebDataServiceConsumer);
};
class WaitForWalletUpdateChecker : public StatusChangeChecker,
public syncer::SyncServiceObserver {
public:
WaitForWalletUpdateChecker(base::Time min_required_progress_marker_timestamp,
syncer::SyncService* service)
: min_required_progress_marker_timestamp_(
min_required_progress_marker_timestamp),
service_(service) {
scoped_observer_.Add(service);
}
bool IsExitConditionSatisfied(std::ostream* os) override {
// GetLastCycleSnapshot() returns by value, so make sure to capture it for
// iterator use.
const syncer::SyncCycleSnapshot snap =
service_->GetLastCycleSnapshotForDebugging();
const syncer::ProgressMarkerMap& progress_markers =
snap.download_progress_markers();
auto marker_it = progress_markers.find(syncer::AUTOFILL_WALLET_DATA);
if (marker_it == progress_markers.end()) {
*os << "Waiting for an updated Wallet progress marker timestamp "
<< min_required_progress_marker_timestamp_
<< "; actual: no progress marker in last sync cycle";
return false;
}
sync_pb::DataTypeProgressMarker progress_marker;
bool success = progress_marker.ParseFromString(marker_it->second);
DCHECK(success);
const base::Time actual_timestamp =
fake_server::FakeServer::GetWalletProgressMarkerTimestamp(
progress_marker);
*os << "Waiting for an updated Wallet progress marker timestamp "
<< min_required_progress_marker_timestamp_ << "; actual "
<< actual_timestamp;
return actual_timestamp >= min_required_progress_marker_timestamp_;
}
// syncer::SyncServiceObserver implementation.
void OnSyncCycleCompleted(syncer::SyncService* sync) override {
CheckExitCondition();
}
private:
const base::Time min_required_progress_marker_timestamp_;
const syncer::SyncService* const service_;
ScopedObserver<syncer::SyncService, syncer::SyncServiceObserver>
scoped_observer_{this};
DISALLOW_COPY_AND_ASSIGN(WaitForWalletUpdateChecker);
};
std::vector<std::unique_ptr<CreditCard>> GetServerCards(
scoped_refptr<autofill::AutofillWebDataService> service) {
AutofillWebDataServiceConsumer<std::vector<std::unique_ptr<CreditCard>>>
......@@ -264,7 +208,9 @@ class SingleClientWalletSyncTest : public SyncTest {
// Trigger a sync and wait for the new data to arrive.
TriggerSyncForModelTypes(
0, syncer::ModelTypeSet(syncer::AUTOFILL_WALLET_DATA));
return WaitForWalletUpdateChecker(now, GetSyncService(0)).Wait();
return FullUpdateTypeProgressMarkerChecker(now, GetSyncService(0),
syncer::AUTOFILL_WALLET_DATA)
.Wait();
}
void AdvanceAutofillClockByOneDay() {
......@@ -670,12 +616,14 @@ IN_PROC_BROWSER_TEST_F(SingleClientWalletSyncTest, EmptyUpdatesAreIgnored) {
EXPECT_EQ("data-1", cloud_token_data[0]->instrument_token);
// Trigger a sync and wait for the new data to arrive.
sync_pb::ModelTypeState state_before = GetWalletDataModelTypeState(0);
sync_pb::ModelTypeState state_before =
GetWalletModelTypeState(syncer::AUTOFILL_WALLET_DATA, 0);
ASSERT_TRUE(TriggerGetUpdatesAndWait());
// Check that the new progress marker is stored for empty updates. This is a
// regression check for crbug.com/924447.
sync_pb::ModelTypeState state_after = GetWalletDataModelTypeState(0);
sync_pb::ModelTypeState state_after =
GetWalletModelTypeState(syncer::AUTOFILL_WALLET_DATA, 0);
EXPECT_NE(state_before.progress_marker().token(),
state_after.progress_marker().token());
......
......@@ -213,13 +213,13 @@ void GetServerAddressesMetadataOnDBSequence(
->GetServerAddressesMetadata(addresses_metadata);
}
void GetWalletDataModelTypeStateOnDBSequence(
void GetModelTypeStateOnDBSequence(syncer::ModelType model_type,
AutofillWebDataService* wds,
sync_pb::ModelTypeState* model_type_state) {
DCHECK(wds->GetDBTaskRunner()->RunsTasksInCurrentSequence());
syncer::MetadataBatch metadata_batch;
AutofillTable::FromWebDatabase(wds->GetDatabase())
->GetAllSyncMetadata(syncer::AUTOFILL_WALLET_DATA, &metadata_batch);
->GetAllSyncMetadata(model_type, &metadata_batch);
*model_type_state = metadata_batch.GetModelTypeState();
}
......@@ -321,11 +321,14 @@ std::map<std::string, AutofillMetadata> GetServerAddressesMetadata(
return addresses_metadata;
}
sync_pb::ModelTypeState GetWalletDataModelTypeState(int profile) {
sync_pb::ModelTypeState GetWalletModelTypeState(syncer::ModelType model_type,
int profile) {
DCHECK(model_type == syncer::AUTOFILL_WALLET_DATA ||
model_type == syncer::AUTOFILL_WALLET_OFFER);
sync_pb::ModelTypeState result;
scoped_refptr<AutofillWebDataService> wds = GetProfileWebDataService(profile);
wds->GetDBTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(&GetWalletDataModelTypeStateOnDBSequence,
FROM_HERE, base::BindOnce(&GetModelTypeStateOnDBSequence, model_type,
base::Unretained(wds.get()), &result));
WaitForCurrentTasksToComplete(wds->GetDBTaskRunner());
return result;
......@@ -669,3 +672,53 @@ bool AutofillWalletMetadataSizeChecker::IsExitConditionSatisfiedImpl() {
}
return true;
}
FullUpdateTypeProgressMarkerChecker::FullUpdateTypeProgressMarkerChecker(
base::Time min_required_progress_marker_timestamp,
syncer::SyncService* service,
syncer::ModelType model_type)
: min_required_progress_marker_timestamp_(
min_required_progress_marker_timestamp),
service_(service),
model_type_(model_type) {
scoped_observer_.Add(service);
}
FullUpdateTypeProgressMarkerChecker::~FullUpdateTypeProgressMarkerChecker() =
default;
bool FullUpdateTypeProgressMarkerChecker::IsExitConditionSatisfied(
std::ostream* os) {
// GetLastCycleSnapshot() returns by value, so make sure to capture it for
// iterator use.
const syncer::SyncCycleSnapshot snap =
service_->GetLastCycleSnapshotForDebugging();
const syncer::ProgressMarkerMap& progress_markers =
snap.download_progress_markers();
auto marker_it = progress_markers.find(model_type_);
if (marker_it == progress_markers.end()) {
*os << "Waiting for an updated progress marker timestamp "
<< min_required_progress_marker_timestamp_
<< "; actual: no progress marker in last sync cycle";
return false;
}
sync_pb::DataTypeProgressMarker progress_marker;
bool success = progress_marker.ParseFromString(marker_it->second);
DCHECK(success);
const base::Time actual_timestamp =
fake_server::FakeServer::GetProgressMarkerTimestamp(progress_marker);
*os << "Waiting for an updated progress marker timestamp "
<< min_required_progress_marker_timestamp_ << "; actual "
<< actual_timestamp;
return actual_timestamp >= min_required_progress_marker_timestamp_;
}
// syncer::SyncServiceObserver implementation.
void FullUpdateTypeProgressMarkerChecker::OnSyncCycleCompleted(
syncer::SyncService* sync) {
CheckExitCondition();
}
......@@ -27,6 +27,7 @@ class PersonalDataManager;
namespace sync_pb {
class SyncEntity;
class ModelType;
class ModelTypeState;
}
......@@ -79,7 +80,9 @@ std::map<std::string, autofill::AutofillMetadata> GetServerCardsMetadata(
std::map<std::string, autofill::AutofillMetadata> GetServerAddressesMetadata(
int profile);
sync_pb::ModelTypeState GetWalletDataModelTypeState(int profile);
// Function supports AUTOFILL_WALLET_DATA and AUTOFILL_WALLET_OFFER.
sync_pb::ModelTypeState GetWalletModelTypeState(syncer::ModelType type,
int profile);
void UnmaskServerCard(int profile,
const autofill::CreditCard& credit_card,
......@@ -189,4 +192,35 @@ class AutofillWalletMetadataSizeChecker
bool checking_exit_condition_in_flight_ = false;
};
// Checker to block until a new progress marker with correct timestamp is
// received.
class FullUpdateTypeProgressMarkerChecker : public StatusChangeChecker,
public syncer::SyncServiceObserver {
public:
FullUpdateTypeProgressMarkerChecker(
base::Time min_required_progress_marker_timestamp,
syncer::SyncService* service,
syncer::ModelType model_type);
~FullUpdateTypeProgressMarkerChecker() override;
FullUpdateTypeProgressMarkerChecker(
const FullUpdateTypeProgressMarkerChecker&) = delete;
FullUpdateTypeProgressMarkerChecker& operator=(
const FullUpdateTypeProgressMarkerChecker&) = delete;
// StatusChangeChecker:
bool IsExitConditionSatisfied(std::ostream* os) override;
// syncer::SyncServiceObserver:
void OnSyncCycleCompleted(syncer::SyncService* sync) override;
private:
const base::Time min_required_progress_marker_timestamp_;
const syncer::SyncService* const service_;
const syncer::ModelType model_type_;
ScopedObserver<syncer::SyncService, syncer::SyncServiceObserver>
scoped_observer_{this};
};
#endif // CHROME_BROWSER_SYNC_TEST_INTEGRATION_WALLET_HELPER_H_
......@@ -6852,6 +6852,8 @@ if (!is_fuchsia) {
"../browser/sync/test/integration/migration_waiter.h",
"../browser/sync/test/integration/migration_watcher.cc",
"../browser/sync/test/integration/migration_watcher.h",
"../browser/sync/test/integration/offer_helper.cc",
"../browser/sync/test/integration/offer_helper.h",
"../browser/sync/test/integration/passwords_helper.cc",
"../browser/sync/test/integration/passwords_helper.h",
"../browser/sync/test/integration/preferences_helper.cc",
......@@ -6964,6 +6966,7 @@ if (!is_fuchsia && !is_android) {
"../browser/sync/test/integration/single_client_extensions_sync_test.cc",
"../browser/sync/test/integration/single_client_history_delete_directives_sync_test.cc",
"../browser/sync/test/integration/single_client_nigori_sync_test.cc",
"../browser/sync/test/integration/single_client_offer_sync_test.cc",
"../browser/sync/test/integration/single_client_passwords_sync_test.cc",
"../browser/sync/test/integration/single_client_polling_sync_test.cc",
"../browser/sync/test/integration/single_client_preferences_sync_test.cc",
......
......@@ -80,14 +80,17 @@ struct HashAndTime {
};
std::unique_ptr<sync_pb::DataTypeProgressMarker>
RemoveWalletProgressMarkerIfExists(sync_pb::ClientToServerMessage* message) {
RemoveFullUpdateTypeProgressMarkerIfExists(
ModelType model_type,
sync_pb::ClientToServerMessage* message) {
DCHECK(model_type == syncer::AUTOFILL_WALLET_DATA ||
model_type == syncer::AUTOFILL_WALLET_OFFER);
google::protobuf::RepeatedPtrField<sync_pb::DataTypeProgressMarker>*
progress_markers =
message->mutable_get_updates()->mutable_from_progress_marker();
for (int index = 0; index < progress_markers->size(); ++index) {
if (syncer::GetModelTypeFromSpecificsFieldNumber(
progress_markers->Get(index).data_type_id()) ==
syncer::AUTOFILL_WALLET_DATA) {
progress_markers->Get(index).data_type_id()) == model_type) {
auto result = std::make_unique<sync_pb::DataTypeProgressMarker>(
progress_markers->Get(index));
progress_markers->erase(progress_markers->begin() + index);
......@@ -97,20 +100,22 @@ RemoveWalletProgressMarkerIfExists(sync_pb::ClientToServerMessage* message) {
return nullptr;
}
void VerifyNoWalletDataProgressMarkerExists(
void VerifyNoProgressMarkerExistsInResponseForFullUpdateType(
sync_pb::GetUpdatesResponse* gu_response) {
for (const sync_pb::DataTypeProgressMarker& marker :
gu_response->new_progress_marker()) {
DCHECK_NE(
syncer::GetModelTypeFromSpecificsFieldNumber(marker.data_type_id()),
syncer::AUTOFILL_WALLET_DATA);
ModelType type =
syncer::GetModelTypeFromSpecificsFieldNumber(marker.data_type_id());
// Verified there is no progress marker for the full sync type we cared
// about.
DCHECK(type != syncer::AUTOFILL_WALLET_DATA &&
type != syncer::AUTOFILL_WALLET_OFFER);
}
}
// Returns a hash representing |entities| including each entity's ID and
// version, in a way that the order of the entities is irrelevant.
uint64_t ComputeWalletEntitiesHash(
const std::vector<sync_pb::SyncEntity>& entities) {
uint64_t ComputeEntitiesHash(const std::vector<sync_pb::SyncEntity>& entities) {
// Make sure to pick a token that will be consistent across clients when
// receiving the same data. We sum up the hashes which has the nice side
// effect of being independent of the order.
......@@ -124,14 +129,14 @@ uint64_t ComputeWalletEntitiesHash(
// Encodes a hash and timestamp in a string that is meant to be used as progress
// marker token.
std::string PackWalletProgressMarkerToken(const HashAndTime& hash_and_time) {
std::string PackProgressMarkerToken(const HashAndTime& hash_and_time) {
return base::NumberToString(hash_and_time.hash) + " " +
base::NumberToString(
hash_and_time.time.ToDeltaSinceWindowsEpoch().InMicroseconds());
}
// Reverse for PackWalletProgressMarkerToken.
HashAndTime UnpackWalletProgressMarkerToken(const std::string& token) {
// Reverse for PackProgressMarkerToken.
HashAndTime UnpackProgressMarkerToken(const std::string& token) {
// The hash is stored as a first piece of the string (space delimited), the
// second piece is the timestamp.
HashAndTime hash_and_time;
......@@ -152,31 +157,25 @@ HashAndTime UnpackWalletProgressMarkerToken(const std::string& token) {
return hash_and_time;
}
void PopulateWalletResults(
void PopulateFullUpdateTypeResults(
const std::vector<sync_pb::SyncEntity>& entities,
const sync_pb::DataTypeProgressMarker& old_wallet_marker,
const sync_pb::DataTypeProgressMarker& old_marker,
sync_pb::GetUpdatesResponse* gu_response) {
// The response from the loopback server should never have an existing
// progress marker for wallet data (because FakeServer removes it from the
// request).
VerifyNoWalletDataProgressMarkerExists(gu_response);
sync_pb::DataTypeProgressMarker* new_wallet_marker =
sync_pb::DataTypeProgressMarker* new_marker =
gu_response->add_new_progress_marker();
new_wallet_marker->set_data_type_id(
GetSpecificsFieldNumberFromModelType(syncer::AUTOFILL_WALLET_DATA));
new_marker->set_data_type_id(old_marker.data_type_id());
uint64_t hash = ComputeWalletEntitiesHash(entities);
uint64_t hash = ComputeEntitiesHash(entities);
// We also include information about the fetch time in the token. This is
// in-line with the server behavior and -- as it keeps changing -- allows
// integration tests to wait for a GetUpdates call to finish, even if they
// don't contain data updates.
new_wallet_marker->set_token(
PackWalletProgressMarkerToken({hash, base::Time::Now()}));
new_marker->set_token(PackProgressMarkerToken({hash, base::Time::Now()}));
if (!old_wallet_marker.has_token() ||
!AreWalletDataProgressMarkersEquivalent(old_wallet_marker,
*new_wallet_marker)) {
if (!old_marker.has_token() ||
!AreFullUpdateTypeDataProgressMarkersEquivalent(old_marker,
*new_marker)) {
// New data available; include new elements and tell the client to drop all
// previous data.
int64_t version =
......@@ -188,10 +187,9 @@ void PopulateWalletResults(
}
// Set the GC directive to implement non-incremental reads.
new_wallet_marker->mutable_gc_directive()->set_type(
new_marker->mutable_gc_directive()->set_type(
sync_pb::GarbageCollectionDirective::VERSION_WATERMARK);
new_wallet_marker->mutable_gc_directive()->set_version_watermark(version -
1);
new_marker->mutable_gc_directive()->set_version_watermark(version - 1);
}
}
......@@ -204,11 +202,11 @@ std::string PrettyPrintValue(std::unique_ptr<base::DictionaryValue> value) {
} // namespace
bool AreWalletDataProgressMarkersEquivalent(
bool AreFullUpdateTypeDataProgressMarkersEquivalent(
const sync_pb::DataTypeProgressMarker& marker1,
const sync_pb::DataTypeProgressMarker& marker2) {
return UnpackWalletProgressMarkerToken(marker1.token()).hash ==
UnpackWalletProgressMarkerToken(marker2.token()).hash;
return UnpackProgressMarkerToken(marker1.token()).hash ==
UnpackProgressMarkerToken(marker2.token()).hash;
}
net::HttpStatusCode FakeServer::HandleCommand(const std::string& request,
......@@ -282,29 +280,45 @@ net::HttpStatusCode FakeServer::HandleParsedCommand(
// Don't care.
}
// The loopback server does not know how to handle Wallet requests -- and
// should not. The FakeServer is handling those instead. The loopback server
// has a strong expectations about how progress tokens are structured. To
// not interfere with this, we remove wallet progress markers before passing
// the request to the loopback server.
sync_pb::ClientToServerMessage message_without_wallet = message;
// The loopback server does not know how to handle Wallet or Offer requests
// -- and should not. The FakeServer is handling those instead. The
// loopback server has a strong expectations about how progress tokens are
// structured. To not interfere with this, we remove progress markers for
// full-update types before passing the request to the loopback server.
sync_pb::ClientToServerMessage message_without_full_update_type = message;
std::unique_ptr<sync_pb::DataTypeProgressMarker> wallet_marker =
RemoveWalletProgressMarkerIfExists(&message_without_wallet);
RemoveFullUpdateTypeProgressMarkerIfExists(
syncer::AUTOFILL_WALLET_DATA, &message_without_full_update_type);
std::unique_ptr<sync_pb::DataTypeProgressMarker> offer_marker =
RemoveFullUpdateTypeProgressMarkerIfExists(
syncer::AUTOFILL_WALLET_OFFER, &message_without_full_update_type);
net::HttpStatusCode http_status_code =
SendToLoopbackServer(message_without_wallet, response);
SendToLoopbackServer(message_without_full_update_type, response);
if (response->has_get_updates() && disallow_sending_encryption_keys_) {
response->mutable_get_updates()->clear_encryption_keys();
}
if (wallet_marker != nullptr && http_status_code == net::HTTP_OK &&
if (http_status_code == net::HTTP_OK &&
message.message_contents() ==
sync_pb::ClientToServerMessage::GET_UPDATES) {
PopulateWalletResults(wallet_entities_, *wallet_marker,
// The response from the loopback server should never have an existing
// progress marker for full-update types (because FakeServer removes it from
// the request).
VerifyNoProgressMarkerExistsInResponseForFullUpdateType(
response->mutable_get_updates());
if (wallet_marker != nullptr) {
PopulateFullUpdateTypeResults(wallet_entities_, *wallet_marker,
response->mutable_get_updates());
}
if (offer_marker != nullptr) {
PopulateFullUpdateTypeResults(offer_entities_, *offer_marker,
response->mutable_get_updates());
}
}
if (http_status_code == net::HTTP_OK &&
response->error_code() == sync_pb::SyncEnums::SUCCESS) {
*response->mutable_client_command() = client_command_;
......@@ -381,8 +395,10 @@ void FakeServer::TriggerKeystoreKeyRotation() {
void FakeServer::InjectEntity(std::unique_ptr<LoopbackServerEntity> entity) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(entity->GetModelType() != syncer::AUTOFILL_WALLET_DATA)
<< "Wallet data must be injected via SetWalletData()";
DCHECK(entity->GetModelType() != syncer::AUTOFILL_WALLET_DATA &&
entity->GetModelType() != syncer::AUTOFILL_WALLET_OFFER)
<< "Wallet/Offer data must be injected via "
"SetWalletData()/SetOfferData().";
const ModelType model_type = entity->GetModelType();
......@@ -395,19 +411,21 @@ void FakeServer::InjectEntity(std::unique_ptr<LoopbackServerEntity> entity) {
base::Time FakeServer::SetWalletData(
const std::vector<sync_pb::SyncEntity>& wallet_entities) {
DCHECK(!wallet_entities.empty());
ModelType model_type =
GetModelTypeFromSpecifics(wallet_entities[0].specifics());
DCHECK(model_type == syncer::AUTOFILL_WALLET_DATA);
wallet_entities_ = wallet_entities;
const base::Time now = base::Time::Now();
const int64_t version = (now - base::Time::UnixEpoch()).InMilliseconds();
for (sync_pb::SyncEntity& entity : wallet_entities_) {
DCHECK_EQ(GetModelTypeFromSpecifics(entity.specifics()),
syncer::AUTOFILL_WALLET_DATA);
DCHECK(!entity.has_client_defined_unique_tag())
<< "The sync server doesn not provide a client tag for wallet entries";
<< "The sync server doesn not provide a client tag for wallet entries.";
DCHECK(!entity.id_string().empty()) << "server id required!";
// The version is overriden during serving of the entities, but is useful
// The version is overridden during serving of the entities, but is useful
// here to influence the entities' hash.
entity.set_version(version);
}
......@@ -418,10 +436,37 @@ base::Time FakeServer::SetWalletData(
return now;
}
base::Time FakeServer::SetOfferData(
const std::vector<sync_pb::SyncEntity>& offer_entities) {
DCHECK(!offer_entities.empty());
ModelType model_type =
GetModelTypeFromSpecifics(offer_entities[0].specifics());
DCHECK(model_type == syncer::AUTOFILL_WALLET_OFFER);
offer_entities_ = offer_entities;
const base::Time now = base::Time::Now();
const int64_t version = (now - base::Time::UnixEpoch()).InMilliseconds();
for (sync_pb::SyncEntity& entity : offer_entities_) {
DCHECK(!entity.has_client_defined_unique_tag())
<< "The sync server doesn not provide a client tag for offer entries.";
DCHECK(!entity.id_string().empty()) << "server id required!";
// The version is overridden during serving of the entities, but is useful
// here to influence the entities' hash.
entity.set_version(version);
}
OnCommit(/*committer_id=*/std::string(),
/*committed_model_types=*/{syncer::AUTOFILL_WALLET_OFFER});
return now;
}
// static
base::Time FakeServer::GetWalletProgressMarkerTimestamp(
base::Time FakeServer::GetProgressMarkerTimestamp(
const sync_pb::DataTypeProgressMarker& progress_marker) {
return UnpackWalletProgressMarkerToken(progress_marker.token()).time;
return UnpackProgressMarkerToken(progress_marker.token()).time;
}
bool FakeServer::ModifyEntitySpecifics(
......
......@@ -38,7 +38,7 @@ namespace fake_server {
// is in-line with the server behavior and -- as it keeps changing -- allows
// integration tests to wait for a GetUpdates call to finish, even if they don't
// contain data updates.
bool AreWalletDataProgressMarkersEquivalent(
bool AreFullUpdateTypeDataProgressMarkersEquivalent(
const sync_pb::DataTypeProgressMarker& marker1,
const sync_pb::DataTypeProgressMarker& marker2);
......@@ -111,14 +111,24 @@ class FakeServer : public syncer::LoopbackServer::ObserverForTests {
//
// The returned value represents the timestamp of the write, such that any
// progress marker greater or equal to this timestamp must have processed the
// changes. See GetWalletProgressMarkerTimestamp() below.
// changes. See GetProgressMarkerTimestamp() below.
base::Time SetWalletData(
const std::vector<sync_pb::SyncEntity>& wallet_entities);
// Allows the caller to know the wallet timestamp corresponding to
// Sets the Autofill offer data to be served in following GetUpdates
// requests (any further GetUpdates response will be empty, indicating no
// change, if the client already has received |offer_entities|).
//
// The returned value represents the timestamp of the write, such that any
// progress marker greater or equal to this timestamp must have processed the
// changes. See GetProgressMarkerTimestamp() below.
base::Time SetOfferData(
const std::vector<sync_pb::SyncEntity>& offer_entities);
// Allows the caller to know the timestamp corresponding to
// |progress_marker| as annotated by the FakeServer during the GetUpdates
// request that returned the progress marker.
static base::Time GetWalletProgressMarkerTimestamp(
static base::Time GetProgressMarkerTimestamp(
const sync_pb::DataTypeProgressMarker& progress_marker);
// Modifies the entity on the server with the given |id|. The entity's
......@@ -297,6 +307,10 @@ class FakeServer : public syncer::LoopbackServer::ObserverForTests {
// the FakeServer handles those itself.
std::vector<sync_pb::SyncEntity> wallet_entities_;
// The LoopbackServer does not know how to handle offer data properly, so
// the FakeServer handles those itself.
std::vector<sync_pb::SyncEntity> offer_entities_;
// Creates WeakPtr versions of the current FakeServer. This must be the last
// data member!
base::WeakPtrFactory<FakeServer> weak_ptr_factory_{this};
......
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