| // Copyright 2018 The Chromium Authors |
| // 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/wallet_helper.h" |
| |
| #include <stddef.h> |
| |
| #include "base/functional/bind.h" |
| #include "base/run_loop.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "chrome/browser/autofill/personal_data_manager_factory.h" |
| #include "chrome/browser/sync/test/integration/sync_datatype_helper.h" |
| #include "chrome/browser/sync/test/integration/sync_test.h" |
| #include "chrome/browser/webdata_services/web_data_service_factory.h" |
| #include "components/autofill/core/browser/data_manager/payments/payments_data_manager.h" |
| #include "components/autofill/core/browser/data_manager/personal_data_manager.h" |
| #include "components/autofill/core/browser/data_model/payments/payments_metadata.h" |
| #include "components/autofill/core/browser/data_quality/autofill_data_util.h" |
| #include "components/autofill/core/browser/payments/payments_customer_data.h" |
| #include "components/autofill/core/browser/webdata/autofill_sync_metadata_table.h" |
| #include "components/autofill/core/browser/webdata/payments/payments_autofill_table.h" |
| #include "components/autofill/core/common/credit_card_network_identifiers.h" |
| #include "components/keyed_service/core/service_access_type.h" |
| #include "components/sync/model/metadata_batch.h" |
| #include "components/sync/protocol/autofill_wallet_credential_specifics.pb.h" |
| #include "components/sync/protocol/data_type_progress_marker.pb.h" |
| #include "components/sync/protocol/data_type_state.pb.h" |
| |
| using autofill::AutofillWebDataService; |
| using autofill::CreditCard; |
| using autofill::CreditCardCloudTokenData; |
| using autofill::PaymentsAutofillTable; |
| using autofill::PaymentsCustomerData; |
| using autofill::PaymentsDataManager; |
| using autofill::PaymentsMetadata; |
| using autofill::PersonalDataManager; |
| using autofill::ServerCvc; |
| using autofill::data_util::TruncateUTF8; |
| using sync_datatype_helper::test; |
| |
| namespace { |
| |
| // Constants for the credit card. |
| const int kDefaultCardExpMonth = 8; |
| const int kDefaultCardExpYear = 2087; |
| const std::u16string kDefaultCardCvc = u"098"; |
| const int kDefaultCardInstrumentId = 1; |
| const char kDefaultCardLastFour[] = "1234"; |
| const char16_t kDefaultCardLastFour16[] = u"1234"; |
| const char kDefaultCardName[] = "Patrick Valenzuela"; |
| const char16_t kDefaultCardName16[] = u"Patrick Valenzuela"; |
| const sync_pb::WalletMaskedCreditCard_WalletCardType kDefaultCardType = |
| sync_pb::WalletMaskedCreditCard::AMEX; |
| |
| template <class Item> |
| bool ListsMatch(int profile_a, |
| const std::vector<Item*>& list_a, |
| int profile_b, |
| const std::vector<Item*>& list_b) { |
| std::map<std::string, const Item*> list_a_map; |
| for (const Item* item : list_a) { |
| list_a_map[item->server_id()] = item; |
| } |
| |
| // This seems to be a transient state that will eventually be rectified by |
| // data type logic. We don't need to check b for duplicates directly because |
| // after the first is erased from |autofill_profiles_a_map| the second will |
| // not be found. |
| if (list_a.size() != list_a_map.size()) { |
| DVLOG(1) << "Profile " << profile_a << " contains duplicate server_ids."; |
| return false; |
| } |
| |
| for (const Item* item : list_b) { |
| if (!list_a_map.count(item->server_id())) { |
| DVLOG(1) << "GUID " << item->server_id() << " not found in profile " |
| << profile_b << "."; |
| return false; |
| } |
| const Item* expected_item = list_a_map[item->server_id()]; |
| if (expected_item->Compare(*item) != 0 || |
| expected_item->usage_history().use_count() != |
| item->usage_history().use_count() || |
| expected_item->usage_history().use_date() != |
| item->usage_history().use_date()) { |
| DVLOG(1) << "Mismatch in profile with server_id " << item->server_id() |
| << "."; |
| return false; |
| } |
| list_a_map.erase(item->server_id()); |
| } |
| |
| if (!list_a_map.empty()) { |
| DVLOG(1) << "Entries present in profile " << profile_a |
| << " but not in profile" << profile_b << "."; |
| return false; |
| } |
| return true; |
| } |
| |
| template <class Item> |
| void LogLists(const std::vector<Item*>& list_a, |
| const std::vector<Item*>& list_b) { |
| int x = 0; |
| for (Item* item : list_a) { |
| DVLOG(1) << "A#" << x++ << " " << *item; |
| } |
| x = 0; |
| for (Item* item : list_b) { |
| DVLOG(1) << "B#" << x++ << " " << *item; |
| } |
| } |
| |
| template <class Item, base::RawPtrTraits Traits = base::RawPtrTraits::kEmpty> |
| void LogLists(const std::vector<raw_ptr<Item, Traits>>& list_a, |
| const std::vector<raw_ptr<Item, Traits>>& list_b) { |
| int x = 0; |
| for (Item* item : list_a) { |
| DVLOG(1) << "A#" << x++ << " " << *item; |
| } |
| x = 0; |
| for (Item* item : list_b) { |
| DVLOG(1) << "B#" << x++ << " " << *item; |
| } |
| } |
| |
| template <class Item, base::RawPtrTraits Traits = base::RawPtrTraits::kEmpty> |
| bool ListsMatch(int profile_a, |
| const std::vector<raw_ptr<Item, Traits>>& list_a, |
| int profile_b, |
| const std::vector<raw_ptr<Item, Traits>>& list_b) { |
| std::map<std::string, Item> list_a_map; |
| for (Item* item : list_a) { |
| list_a_map[item->server_id()] = *item; |
| } |
| |
| // This seems to be a transient state that will eventually be rectified by |
| // data type logic. We don't need to check b for duplicates directly because |
| // after the first is erased from |autofill_profiles_a_map| the second will |
| // not be found. |
| if (list_a.size() != list_a_map.size()) { |
| DVLOG(1) << "Profile " << profile_a << " contains duplicate server_ids."; |
| return false; |
| } |
| |
| for (Item* item : list_b) { |
| if (!list_a_map.count(item->server_id())) { |
| DVLOG(1) << "GUID " << item->server_id() << " not found in profile " |
| << profile_b << "."; |
| return false; |
| } |
| Item* expected_item = &list_a_map[item->server_id()]; |
| if (expected_item->Compare(*item) != 0 || |
| expected_item->use_count() != item->use_count() || |
| expected_item->use_date() != item->use_date()) { |
| DVLOG(1) << "Mismatch in profile with server_id " << item->server_id() |
| << "."; |
| return false; |
| } |
| list_a_map.erase(item->server_id()); |
| } |
| |
| if (!list_a_map.empty()) { |
| DVLOG(1) << "Entries present in profile " << profile_a |
| << " but not in profile" << profile_b << "."; |
| return false; |
| } |
| return true; |
| } |
| |
| bool WalletDataAndMetadataMatch( |
| int profile_a, |
| const std::vector<const CreditCard*>& server_cards_a, |
| int profile_b, |
| const std::vector<const CreditCard*>& server_cards_b) { |
| if (!ListsMatch(profile_a, server_cards_a, profile_b, server_cards_b)) { |
| LogLists(server_cards_a, server_cards_b); |
| return false; |
| } |
| return true; |
| } |
| |
| void WaitForCurrentTasksToComplete( |
| scoped_refptr<base::SequencedTaskRunner> task_runner) { |
| // We are fine with the UI thread getting blocked. If using RunLoop here, in |
| // some uses of this functions, we would get nested RunLoops that tend to |
| // cause troubles. This is a more robust solution. |
| base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, |
| base::WaitableEvent::InitialState::NOT_SIGNALED); |
| task_runner->PostTask(FROM_HERE, base::BindOnce(&base::WaitableEvent::Signal, |
| base::Unretained(&event))); |
| event.Wait(); |
| } |
| |
| void WaitForPDMToRefresh(int profile) { |
| PersonalDataManager* pdm = wallet_helper::GetPersonalDataManager(profile); |
| // Get the latest values from the DB: (1) post tasks on the DB sequence; (2) |
| // wait for the DB sequence tasks to finish; (3) wait for the UI sequence |
| // tasks to finish (that update the in-memory cache in PDM). |
| pdm->Refresh(); |
| WaitForCurrentTasksToComplete( |
| wallet_helper::GetProfileWebDataService(profile)->GetDBTaskRunner()); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void SetServerCardsOnDBSequence(AutofillWebDataService* wds, |
| const std::vector<CreditCard>& credit_cards) { |
| DCHECK(wds->GetDBTaskRunner()->RunsTasksInCurrentSequence()); |
| PaymentsAutofillTable::FromWebDatabase(wds->GetDatabase()) |
| ->SetServerCreditCards(credit_cards); |
| } |
| |
| void SetPaymentsCustomerDataOnDBSequence( |
| AutofillWebDataService* wds, |
| const PaymentsCustomerData& customer_data) { |
| DCHECK(wds->GetDBTaskRunner()->RunsTasksInCurrentSequence()); |
| PaymentsAutofillTable::FromWebDatabase(wds->GetDatabase()) |
| ->SetPaymentsCustomerData(&customer_data); |
| } |
| |
| void SetCreditCardCloudTokenDataOnDBSequence( |
| AutofillWebDataService* wds, |
| const std::vector<CreditCardCloudTokenData>& cloud_token_data) { |
| DCHECK(wds->GetDBTaskRunner()->RunsTasksInCurrentSequence()); |
| PaymentsAutofillTable::FromWebDatabase(wds->GetDatabase()) |
| ->SetCreditCardCloudTokenData(cloud_token_data); |
| } |
| |
| void GetServerCardsMetadataOnDBSequence( |
| AutofillWebDataService* wds, |
| std::vector<PaymentsMetadata>* cards_metadata) { |
| DCHECK(wds->GetDBTaskRunner()->RunsTasksInCurrentSequence()); |
| PaymentsAutofillTable::FromWebDatabase(wds->GetDatabase()) |
| ->GetServerCardsMetadata(*cards_metadata); |
| } |
| |
| void GetDataTypeStateOnDBSequence(syncer::DataType data_type, |
| AutofillWebDataService* wds, |
| sync_pb::DataTypeState* data_type_state) { |
| DCHECK(wds->GetDBTaskRunner()->RunsTasksInCurrentSequence()); |
| syncer::MetadataBatch metadata_batch; |
| autofill::AutofillSyncMetadataTable::FromWebDatabase(wds->GetDatabase()) |
| ->GetAllSyncMetadata(data_type, &metadata_batch); |
| *data_type_state = metadata_batch.GetDataTypeState(); |
| } |
| |
| } // namespace |
| |
| namespace wallet_helper { |
| |
| PaymentsDataManager* GetPaymentsDataManager(int index) { |
| PersonalDataManager* pdm = GetPersonalDataManager(index); |
| return pdm ? &pdm->payments_data_manager() : nullptr; |
| } |
| |
| PersonalDataManager* GetPersonalDataManager(int index) { |
| return autofill::PersonalDataManagerFactory::GetForBrowserContext( |
| test()->GetProfile(index)); |
| } |
| |
| scoped_refptr<AutofillWebDataService> GetProfileWebDataService(int index) { |
| return WebDataServiceFactory::GetAutofillWebDataForProfile( |
| test()->GetProfile(index), ServiceAccessType::EXPLICIT_ACCESS); |
| } |
| |
| scoped_refptr<AutofillWebDataService> GetAccountWebDataService(int index) { |
| return WebDataServiceFactory::GetAutofillWebDataForAccount( |
| test()->GetProfile(index), ServiceAccessType::EXPLICIT_ACCESS); |
| } |
| |
| void SetServerCreditCards( |
| int profile, |
| const std::vector<autofill::CreditCard>& credit_cards) { |
| scoped_refptr<AutofillWebDataService> wds = GetProfileWebDataService(profile); |
| wds->GetDBTaskRunner()->PostTask( |
| FROM_HERE, base::BindOnce(&SetServerCardsOnDBSequence, |
| base::Unretained(wds.get()), credit_cards)); |
| WaitForCurrentTasksToComplete(wds->GetDBTaskRunner()); |
| } |
| |
| void SetPaymentsCustomerData( |
| int profile, |
| const autofill::PaymentsCustomerData& customer_data) { |
| scoped_refptr<AutofillWebDataService> wds = GetProfileWebDataService(profile); |
| wds->GetDBTaskRunner()->PostTask( |
| FROM_HERE, base::BindOnce(&SetPaymentsCustomerDataOnDBSequence, |
| base::Unretained(wds.get()), customer_data)); |
| WaitForCurrentTasksToComplete(wds->GetDBTaskRunner()); |
| } |
| |
| void SetCreditCardCloudTokenData( |
| int profile, |
| const std::vector<autofill::CreditCardCloudTokenData>& cloud_token_data) { |
| scoped_refptr<AutofillWebDataService> wds = GetProfileWebDataService(profile); |
| wds->GetDBTaskRunner()->PostTask( |
| FROM_HERE, base::BindOnce(&SetCreditCardCloudTokenDataOnDBSequence, |
| base::Unretained(wds.get()), cloud_token_data)); |
| } |
| |
| void SetServerCardCredentialData(int profile, const CreditCard& credit_card) { |
| scoped_refptr<AutofillWebDataService> wds = GetProfileWebDataService(profile); |
| wds->AddServerCvc(credit_card.instrument_id(), credit_card.cvc()); |
| WaitForCurrentTasksToComplete(wds->GetDBTaskRunner()); |
| } |
| |
| void RemoveServerCardCredentialData(int profile, |
| const CreditCard& credit_card) { |
| scoped_refptr<AutofillWebDataService> wds = GetProfileWebDataService(profile); |
| wds->RemoveServerCvc(credit_card.instrument_id()); |
| WaitForCurrentTasksToComplete(wds->GetDBTaskRunner()); |
| } |
| |
| void UpdateServerCardCredentialData(int profile, |
| const CreditCard& credit_card) { |
| scoped_refptr<AutofillWebDataService> wds = GetProfileWebDataService(profile); |
| wds->UpdateServerCvc(credit_card.instrument_id(), credit_card.cvc()); |
| WaitForCurrentTasksToComplete(wds->GetDBTaskRunner()); |
| } |
| |
| void UpdateServerCardMetadata(int profile, const CreditCard& credit_card) { |
| scoped_refptr<AutofillWebDataService> wds = GetProfileWebDataService(profile); |
| wds->UpdateServerCardMetadata(credit_card); |
| WaitForCurrentTasksToComplete(wds->GetDBTaskRunner()); |
| } |
| |
| std::vector<PaymentsMetadata> GetServerCardsMetadata(int profile) { |
| std::vector<PaymentsMetadata> cards_metadata; |
| scoped_refptr<AutofillWebDataService> wds = GetProfileWebDataService(profile); |
| wds->GetDBTaskRunner()->PostTask( |
| FROM_HERE, base::BindOnce(&GetServerCardsMetadataOnDBSequence, |
| base::Unretained(wds.get()), &cards_metadata)); |
| WaitForCurrentTasksToComplete(wds->GetDBTaskRunner()); |
| return cards_metadata; |
| } |
| |
| sync_pb::DataTypeState GetWalletDataTypeState(syncer::DataType data_type, |
| int profile) { |
| DCHECK(data_type == syncer::AUTOFILL_WALLET_DATA || |
| data_type == syncer::AUTOFILL_WALLET_OFFER); |
| sync_pb::DataTypeState result; |
| scoped_refptr<AutofillWebDataService> wds = GetProfileWebDataService(profile); |
| wds->GetDBTaskRunner()->PostTask( |
| FROM_HERE, base::BindOnce(&GetDataTypeStateOnDBSequence, data_type, |
| base::Unretained(wds.get()), &result)); |
| WaitForCurrentTasksToComplete(wds->GetDBTaskRunner()); |
| return result; |
| } |
| |
| sync_pb::SyncEntity CreateDefaultSyncWalletCard() { |
| return CreateSyncWalletCard(kDefaultCardID, kDefaultCardLastFour, |
| kDefaultBillingAddressID); |
| } |
| |
| sync_pb::SyncEntity CreateSyncWalletCard(const std::string& name, |
| const std::string& last_four, |
| const std::string& billing_address_id, |
| const std::string& nickname, |
| int64_t instrument_id) { |
| sync_pb::SyncEntity entity; |
| entity.set_name(name); |
| entity.set_id_string(name); |
| entity.set_version(0); // Will be overridden by the fake server. |
| entity.set_ctime(12345); |
| entity.set_mtime(12345); |
| sync_pb::AutofillWalletSpecifics* wallet_specifics = |
| entity.mutable_specifics()->mutable_autofill_wallet(); |
| wallet_specifics->set_type( |
| sync_pb::AutofillWalletSpecifics::MASKED_CREDIT_CARD); |
| |
| sync_pb::WalletMaskedCreditCard* credit_card = |
| wallet_specifics->mutable_masked_card(); |
| credit_card->set_id(name); |
| credit_card->set_exp_month(kDefaultCardExpMonth); |
| credit_card->set_exp_year(kDefaultCardExpYear); |
| credit_card->set_last_four(last_four); |
| credit_card->set_name_on_card(kDefaultCardName); |
| credit_card->set_type(kDefaultCardType); |
| credit_card->set_instrument_id(instrument_id); |
| if (!billing_address_id.empty()) { |
| credit_card->set_billing_address_id(billing_address_id); |
| } |
| if (!nickname.empty()) { |
| credit_card->set_nickname(nickname); |
| } |
| return entity; |
| } |
| |
| sync_pb::SyncEntity CreateSyncPaymentsCustomerData( |
| const std::string& customer_id) { |
| sync_pb::SyncEntity entity; |
| entity.set_name(customer_id); |
| entity.set_id_string(customer_id); |
| entity.set_version(0); // Will be overridden by the fake server. |
| entity.set_ctime(12345); |
| entity.set_mtime(12345); |
| sync_pb::AutofillWalletSpecifics* wallet_specifics = |
| entity.mutable_specifics()->mutable_autofill_wallet(); |
| wallet_specifics->set_type(sync_pb::AutofillWalletSpecifics::CUSTOMER_DATA); |
| |
| sync_pb::PaymentsCustomerData* customer_data = |
| wallet_specifics->mutable_customer_data(); |
| customer_data->set_id(customer_id); |
| return entity; |
| } |
| |
| sync_pb::SyncEntity CreateDefaultSyncPaymentsCustomerData() { |
| return CreateSyncPaymentsCustomerData(kDefaultCustomerID); |
| } |
| |
| CreditCard GetDefaultCreditCard() { |
| CreditCard card(CreditCard::RecordType::kMaskedServerCard, kDefaultCardID); |
| card.SetExpirationMonth(kDefaultCardExpMonth); |
| card.SetExpirationYear(kDefaultCardExpYear); |
| card.SetNumber(kDefaultCardLastFour16); |
| card.SetRawInfo(autofill::CREDIT_CARD_NAME_FULL, kDefaultCardName16); |
| card.SetNetworkForMaskedCard(autofill::kAmericanExpressCard); |
| card.set_billing_address_id(kDefaultBillingAddressID); |
| return card; |
| } |
| |
| sync_pb::SyncEntity CreateSyncCreditCardCloudTokenData( |
| const std::string& cloud_token_data_id) { |
| sync_pb::SyncEntity entity; |
| entity.set_name(cloud_token_data_id); |
| entity.set_id_string(cloud_token_data_id); |
| entity.set_version(0); // Will be overridden by the fake server. |
| entity.set_ctime(12345); |
| entity.set_mtime(12345); |
| sync_pb::AutofillWalletSpecifics* wallet_specifics = |
| entity.mutable_specifics()->mutable_autofill_wallet(); |
| wallet_specifics->set_type( |
| sync_pb::AutofillWalletSpecifics::CREDIT_CARD_CLOUD_TOKEN_DATA); |
| sync_pb::WalletCreditCardCloudTokenData* cloud_token_data = |
| wallet_specifics->mutable_cloud_token_data(); |
| cloud_token_data->set_instrument_token(cloud_token_data_id); |
| return entity; |
| } |
| |
| sync_pb::SyncEntity CreateDefaultSyncCreditCardCloudTokenData() { |
| return CreateSyncCreditCardCloudTokenData(kDefaultCreditCardCloudTokenDataID); |
| } |
| |
| sync_pb::SyncEntity CreateDefaultSyncWalletCredential() { |
| return CreateSyncWalletCredential( |
| ServerCvc{kDefaultCardInstrumentId, kDefaultCardCvc, |
| /*last_updated_timestamp=*/base::Time::UnixEpoch() + |
| base::Milliseconds(25000)}); |
| } |
| |
| sync_pb::SyncEntity CreateSyncWalletCredential(const ServerCvc& server_cvc) { |
| sync_pb::SyncEntity entity; |
| entity.set_name(base::NumberToString(server_cvc.instrument_id)); |
| entity.set_id_string(base::NumberToString(server_cvc.instrument_id)); |
| entity.set_version(0); // Will be overridden by the fake server. |
| entity.set_ctime(12345); |
| entity.set_mtime(12345); |
| |
| sync_pb::AutofillWalletCredentialSpecifics* wallet_credential_specifics = |
| entity.mutable_specifics()->mutable_autofill_wallet_credential(); |
| |
| wallet_credential_specifics->set_instrument_id( |
| base::NumberToString(server_cvc.instrument_id)); |
| wallet_credential_specifics->set_cvc(base::UTF16ToUTF8(server_cvc.cvc)); |
| wallet_credential_specifics->set_last_updated_time_unix_epoch_millis( |
| (server_cvc.last_updated_timestamp - base::Time::UnixEpoch()) |
| .InMilliseconds()); |
| |
| return entity; |
| } |
| |
| void ExpectDefaultCreditCardValues(const CreditCard& card) { |
| EXPECT_EQ(CreditCard::RecordType::kMaskedServerCard, card.record_type()); |
| EXPECT_EQ(kDefaultCardID, card.server_id()); |
| EXPECT_EQ(kDefaultCardLastFour16, card.LastFourDigits()); |
| EXPECT_EQ(autofill::kAmericanExpressCard, card.network()); |
| EXPECT_EQ(kDefaultCardExpMonth, card.expiration_month()); |
| EXPECT_EQ(kDefaultCardExpYear, card.expiration_year()); |
| EXPECT_EQ(kDefaultCardName16, |
| card.GetRawInfo(autofill::FieldType::CREDIT_CARD_NAME_FULL)); |
| EXPECT_EQ(kDefaultBillingAddressID, card.billing_address_id()); |
| } |
| |
| void ExpectDefaultWalletCredentialValues(const CreditCard& card) { |
| EXPECT_EQ(kDefaultCardInstrumentId, card.instrument_id()); |
| EXPECT_EQ(kDefaultCardCvc, card.cvc()); |
| } |
| |
| std::vector<const CreditCard*> GetServerCreditCards(int profile) { |
| WaitForPDMToRefresh(profile); |
| PersonalDataManager* pdm = GetPersonalDataManager(profile); |
| return pdm->payments_data_manager().GetServerCreditCards(); |
| } |
| |
| } // namespace wallet_helper |
| |
| AutofillWalletChecker::AutofillWalletChecker(int profile_a, int profile_b) |
| : profile_a_(profile_a), profile_b_(profile_b) { |
| wallet_helper::GetPaymentsDataManager(profile_a_)->AddObserver(this); |
| wallet_helper::GetPaymentsDataManager(profile_b_)->AddObserver(this); |
| } |
| |
| AutofillWalletChecker::~AutofillWalletChecker() { |
| wallet_helper::GetPaymentsDataManager(profile_a_)->RemoveObserver(this); |
| wallet_helper::GetPaymentsDataManager(profile_b_)->RemoveObserver(this); |
| } |
| |
| void AutofillWalletChecker::WillStartWaiting() { |
| // We need to make sure we are not reading before any locally instigated async |
| // writes. This is run exactly one time before the first |
| // IsExitConditionSatisfied() is called. |
| WaitForPDMToRefresh(profile_a_); |
| WaitForPDMToRefresh(profile_b_); |
| } |
| |
| bool AutofillWalletChecker::IsExitConditionSatisfied(std::ostream* os) { |
| *os << "Waiting for matching autofill wallet cards and addresses"; |
| autofill::PaymentsDataManager* paydm_a = |
| wallet_helper::GetPaymentsDataManager(profile_a_); |
| autofill::PaymentsDataManager* paydm_b = |
| wallet_helper::GetPaymentsDataManager(profile_b_); |
| return WalletDataAndMetadataMatch(profile_a_, paydm_a->GetServerCreditCards(), |
| profile_b_, |
| paydm_b->GetServerCreditCards()); |
| } |
| |
| void AutofillWalletChecker::OnPaymentsDataChanged() { |
| CheckExitCondition(); |
| } |
| |
| AutofillWalletMetadataSizeChecker::AutofillWalletMetadataSizeChecker( |
| int profile_a, |
| int profile_b) |
| : profile_a_(profile_a), profile_b_(profile_b) { |
| wallet_helper::GetPaymentsDataManager(profile_a_)->AddObserver(this); |
| wallet_helper::GetPaymentsDataManager(profile_b_)->AddObserver(this); |
| } |
| |
| AutofillWalletMetadataSizeChecker::~AutofillWalletMetadataSizeChecker() { |
| wallet_helper::GetPaymentsDataManager(profile_a_)->RemoveObserver(this); |
| wallet_helper::GetPaymentsDataManager(profile_b_)->RemoveObserver(this); |
| } |
| |
| bool AutofillWalletMetadataSizeChecker::IsExitConditionSatisfied( |
| std::ostream* os) { |
| *os << "Waiting for matching autofill wallet metadata sizes"; |
| // This checker used to be flaky (crbug.com/921386) because of using RunLoops |
| // to load synchronously data from the DB in IsExitConditionSatisfiedImpl. |
| // Such a waiting RunLoop often processed another OnPersonalDataChanged() call |
| // resulting in nested RunLoops. This should be avoided now by blocking using |
| // WaitableEvent, instead. This check enforces that we do not nest it anymore. |
| DCHECK(!checking_exit_condition_in_flight_) |
| << "There should be no nested calls for " |
| "IsExitConditionSatisfied(std::ostream* os)"; |
| checking_exit_condition_in_flight_ = true; |
| bool exit_condition_is_satisfied = IsExitConditionSatisfiedImpl(); |
| checking_exit_condition_in_flight_ = false; |
| return exit_condition_is_satisfied; |
| } |
| |
| void AutofillWalletMetadataSizeChecker::OnPaymentsDataChanged() { |
| CheckExitCondition(); |
| } |
| |
| bool AutofillWalletMetadataSizeChecker::IsExitConditionSatisfiedImpl() { |
| // There could be trailing metadata left on one of the clients. Check that |
| // metadata.size() is the same on both clients. |
| std::vector<PaymentsMetadata> cards_metadata_a = |
| wallet_helper::GetServerCardsMetadata(profile_a_); |
| std::vector<PaymentsMetadata> cards_metadata_b = |
| wallet_helper::GetServerCardsMetadata(profile_b_); |
| if (cards_metadata_a.size() != cards_metadata_b.size()) { |
| DVLOG(1) << "Server cards metadata mismatch, expected " |
| << cards_metadata_a.size() << ", found " |
| << cards_metadata_b.size(); |
| return false; |
| } |
| return true; |
| } |
| |
| FullUpdateTypeProgressMarkerChecker::FullUpdateTypeProgressMarkerChecker( |
| base::Time min_required_progress_marker_timestamp, |
| syncer::SyncService* service, |
| syncer::DataType data_type) |
| : min_required_progress_marker_timestamp_( |
| min_required_progress_marker_timestamp), |
| service_(service), |
| data_type_(data_type) { |
| scoped_observation_.Observe(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(data_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(); |
| } |