1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953 |
- // Copyright 2014 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "components/sync/model/client_tag_based_model_type_processor.h"
- #include <stddef.h>
- #include <stdint.h>
- #include <utility>
- #include <vector>
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/memory/raw_ptr.h"
- #include "base/run_loop.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/test/metrics/histogram_tester.h"
- #include "base/test/scoped_feature_list.h"
- #include "base/test/task_environment.h"
- #include "base/threading/platform_thread.h"
- #include "components/sync/base/client_tag_hash.h"
- #include "components/sync/base/features.h"
- #include "components/sync/base/model_type.h"
- #include "components/sync/base/sync_mode.h"
- #include "components/sync/engine/commit_and_get_updates_types.h"
- #include "components/sync/engine/data_type_activation_response.h"
- #include "components/sync/model/conflict_resolution.h"
- #include "components/sync/model/data_type_activation_request.h"
- #include "components/sync/model/type_entities_count.h"
- #include "components/sync/protocol/entity_metadata.pb.h"
- #include "components/sync/protocol/entity_specifics.pb.h"
- #include "components/sync/protocol/model_type_state.pb.h"
- #include "components/sync/test/fake_model_type_sync_bridge.h"
- #include "components/sync/test/mock_model_type_worker.h"
- #include "testing/gtest/include/gtest/gtest.h"
- using sync_pb::EntityMetadata;
- using sync_pb::EntitySpecifics;
- using sync_pb::ModelTypeState;
- namespace syncer {
- namespace {
- const char kDefaultAuthenticatedAccountId[] = "DefaultAccountId";
- const char kKey1[] = "key1";
- const char kKey2[] = "key2";
- const char kKey3[] = "key3";
- const char kKey4[] = "key4";
- const char kKey5[] = "key5";
- const char kValue1[] = "value1";
- const char kValue2[] = "value2";
- const char kValue3[] = "value3";
- const char kCacheGuid[] = "TestCacheGuid";
- // Typically used for verification after a delete. The specifics given to the
- // worker/processor will not have been initialized and thus empty.
- const EntitySpecifics kEmptySpecifics;
- ClientTagHash GetHash(ModelType type, const std::string& key) {
- return ClientTagHash::FromUnhashed(
- type, FakeModelTypeSyncBridge::ClientTagFromKey(key));
- }
- ClientTagHash GetPrefHash(const std::string& key) {
- return GetHash(PREFERENCES, key);
- }
- EntitySpecifics GeneratePrefSpecifics(const std::string& key,
- const std::string& value) {
- EntitySpecifics specifics;
- specifics.mutable_preference()->set_name(key);
- specifics.mutable_preference()->set_value(value);
- return specifics;
- }
- EntitySpecifics GenerateUserEventSpecifics(int64_t event_time_usec,
- int64_t navigation_id) {
- EntitySpecifics specifics;
- specifics.mutable_user_event()->set_event_time_usec(event_time_usec);
- specifics.mutable_user_event()->set_navigation_id(navigation_id);
- return specifics;
- }
- std::unique_ptr<EntityData> GenerateEntityData(
- ModelType type,
- const std::string& key,
- const EntitySpecifics& specifics) {
- std::unique_ptr<EntityData> entity_data = std::make_unique<EntityData>();
- entity_data->client_tag_hash = GetHash(type, key);
- entity_data->specifics = specifics;
- entity_data->name = key;
- return entity_data;
- }
- std::unique_ptr<EntityData> GeneratePrefEntityData(const std::string& key,
- const std::string& value) {
- return GenerateEntityData(PREFERENCES, key,
- GeneratePrefSpecifics(key, value));
- }
- EntitySpecifics WritePrefItem(FakeModelTypeSyncBridge* bridge,
- const std::string& key,
- const std::string& value) {
- std::unique_ptr<EntityData> entity_data = GeneratePrefEntityData(key, value);
- EntitySpecifics specifics_copy = entity_data->specifics;
- bridge->WriteItem(key, std::move(entity_data));
- return specifics_copy;
- }
- const std::string& GetPrefValue(const EntityData& entity_data) {
- DCHECK(entity_data.specifics.has_preference());
- return entity_data.specifics.preference().value();
- }
- EntitySpecifics WriteUserEventItem(FakeModelTypeSyncBridge* bridge,
- int64_t event_time,
- int64_t navigation_id) {
- std::string key = base::NumberToString(event_time);
- EntitySpecifics specifics =
- GenerateUserEventSpecifics(event_time, navigation_id);
- bridge->WriteItem(key, GenerateEntityData(USER_EVENTS, key, specifics));
- return specifics;
- }
- void CaptureCommitRequest(CommitRequestDataList* dst,
- CommitRequestDataList&& src) {
- *dst = std::move(src);
- }
- void CaptureTypeEntitiesCount(TypeEntitiesCount* dst,
- const TypeEntitiesCount& count) {
- *dst = count;
- }
- class TestModelTypeSyncBridge : public FakeModelTypeSyncBridge {
- public:
- TestModelTypeSyncBridge(ModelType model_type,
- bool supports_incremental_updates)
- : FakeModelTypeSyncBridge(
- model_type,
- std::make_unique<ClientTagBasedModelTypeProcessor>(
- model_type,
- /*dump_stack=*/base::RepeatingClosure())),
- supports_incremental_updates_(supports_incremental_updates) {}
- TestModelTypeSyncBridge(std::unique_ptr<TestModelTypeSyncBridge> other,
- ModelType model_type,
- bool supports_clear_all)
- : TestModelTypeSyncBridge(model_type, supports_clear_all) {
- std::swap(db_, other->db_);
- }
- ~TestModelTypeSyncBridge() override {
- EXPECT_FALSE(synchronous_data_callback_);
- EXPECT_FALSE(data_callback_);
- }
- void OnSyncStarting(
- const syncer::DataTypeActivationRequest& request) override {
- FakeModelTypeSyncBridge::OnSyncStarting(request);
- sync_started_ = true;
- }
- void ApplyStopSyncChanges(std::unique_ptr<MetadataChangeList>
- delete_metadata_change_list) override {
- sync_started_ = false;
- FakeModelTypeSyncBridge::ApplyStopSyncChanges(
- std::move(delete_metadata_change_list));
- }
- std::string GetStorageKey(const EntityData& entity_data) override {
- get_storage_key_call_count_++;
- return FakeModelTypeSyncBridge::GetStorageKey(entity_data);
- }
- sync_pb::EntitySpecifics TrimRemoteSpecificsForCaching(
- const sync_pb::EntitySpecifics& entity_specifics) const override {
- if (entity_specifics.has_preference()) {
- sync_pb::EntitySpecifics trimmed_specifics = entity_specifics;
- trimmed_specifics.mutable_preference()->clear_value();
- return trimmed_specifics;
- }
- return FakeModelTypeSyncBridge::TrimRemoteSpecificsForCaching(
- entity_specifics);
- }
- void OnCommitDataLoaded() {
- ASSERT_TRUE(data_callback_) << "GetData() wasn't called before";
- std::move(data_callback_).Run();
- }
- base::OnceClosure GetDataCallback() { return std::move(data_callback_); }
- void SetInitialSyncDone(bool is_done) {
- ModelTypeState model_type_state(db().model_type_state());
- model_type_state.set_initial_sync_done(is_done);
- model_type_state.set_cache_guid(kCacheGuid);
- model_type_state.mutable_progress_marker()->set_data_type_id(
- GetSpecificsFieldNumberFromModelType(type()));
- model_type_state.set_authenticated_account_id(
- kDefaultAuthenticatedAccountId);
- db_->set_model_type_state(model_type_state);
- }
- // Expect a GetData call in the future and return its data immediately.
- void ExpectSynchronousDataCallback() { synchronous_data_callback_ = true; }
- int merge_call_count() const { return merge_call_count_; }
- int apply_call_count() const { return apply_call_count_; }
- int get_storage_key_call_count() const { return get_storage_key_call_count_; }
- int commit_failures_count() const { return commit_failures_count_; }
- bool sync_started() const { return sync_started_; }
- // FakeModelTypeSyncBridge overrides.
- bool SupportsIncrementalUpdates() const override {
- return supports_incremental_updates_;
- }
- absl::optional<ModelError> MergeSyncData(
- std::unique_ptr<MetadataChangeList> metadata_change_list,
- EntityChangeList entity_data) override {
- merge_call_count_++;
- if (!SupportsIncrementalUpdates()) {
- // If the bridge does not support incremental updates, it should clear
- // local data in MergeSyncData.
- db_->ClearAllData();
- }
- return FakeModelTypeSyncBridge::MergeSyncData(
- std::move(metadata_change_list), std::move(entity_data));
- }
- absl::optional<ModelError> ApplySyncChanges(
- std::unique_ptr<MetadataChangeList> metadata_change_list,
- EntityChangeList entity_changes) override {
- apply_call_count_++;
- return FakeModelTypeSyncBridge::ApplySyncChanges(
- std::move(metadata_change_list), std::move(entity_changes));
- }
- void GetData(StorageKeyList keys, DataCallback callback) override {
- if (synchronous_data_callback_) {
- synchronous_data_callback_ = false;
- FakeModelTypeSyncBridge::GetData(keys, std::move(callback));
- } else {
- FakeModelTypeSyncBridge::GetData(
- keys, base::BindOnce(&TestModelTypeSyncBridge::CaptureDataCallback,
- base::Unretained(this), std::move(callback)));
- }
- }
- void OnCommitAttemptErrors(
- const FailedCommitResponseDataList& error_response_list) override {
- if (!on_commit_attempt_errors_callback_.is_null()) {
- std::move(on_commit_attempt_errors_callback_).Run(error_response_list);
- }
- }
- CommitAttemptFailedBehavior OnCommitAttemptFailed(
- syncer::SyncCommitError commit_error) override {
- commit_failures_count_++;
- return commit_attempt_failed_behaviour_;
- }
- void SetOnCommitAttemptErrorsCallback(
- base::OnceCallback<void(const FailedCommitResponseDataList&)> callback) {
- on_commit_attempt_errors_callback_ = std::move(callback);
- }
- void EnableRetriesOnCommitFailure() {
- commit_attempt_failed_behaviour_ =
- CommitAttemptFailedBehavior::kShouldRetryOnNextCycle;
- }
- private:
- void CaptureDataCallback(DataCallback callback,
- std::unique_ptr<DataBatch> data) {
- EXPECT_FALSE(data_callback_);
- data_callback_ = base::BindOnce(std::move(callback), std::move(data));
- }
- const bool supports_incremental_updates_;
- bool sync_started_ = false;
- // The number of times MergeSyncData has been called.
- int merge_call_count_ = 0;
- int apply_call_count_ = 0;
- int get_storage_key_call_count_ = 0;
- int commit_failures_count_ = 0;
- // Stores the data callback between GetData() and OnCommitDataLoaded().
- base::OnceClosure data_callback_;
- // Whether to return GetData results synchronously. Overrides the default
- // callback capture behavior if set to true.
- bool synchronous_data_callback_ = false;
- CommitAttemptFailedBehavior commit_attempt_failed_behaviour_ =
- CommitAttemptFailedBehavior::kDontRetryOnNextCycle;
- base::OnceCallback<void(const FailedCommitResponseDataList&)>
- on_commit_attempt_errors_callback_;
- };
- } // namespace
- // Tests the various functionality of ClientTagBasedModelTypeProcessor.
- //
- // The processor sits between the bridge (implemented by this test class) and
- // the worker, which is represented by a MockModelTypeWorker. This test suite
- // exercises the initialization flows (whether initial sync is done, performing
- // the initial merge, etc) as well as normal functionality:
- //
- // - Initialization before the initial sync and merge correctly performs a merge
- // and initializes the metadata in storage.
- // - Initialization after the initial sync correctly loads metadata and queues
- // any pending commits.
- // - Put and Delete calls from the bridge result in the correct metadata in
- // storage and the correct commit requests on the worker side.
- // - Updates and commit responses from the worker correctly affect data and
- // metadata in storage on the bridge side.
- class ClientTagBasedModelTypeProcessorTest : public ::testing::Test {
- public:
- ClientTagBasedModelTypeProcessorTest() = default;
- ~ClientTagBasedModelTypeProcessorTest() override { CheckPostConditions(); }
- void SetUp() override {
- bridge_ = std::make_unique<TestModelTypeSyncBridge>(
- GetModelType(), SupportsIncrementalUpdates());
- histogram_tester_ = std::make_unique<base::HistogramTester>();
- }
- void InitializeToMetadataLoaded(bool initial_sync_done = true) {
- bridge()->SetInitialSyncDone(initial_sync_done);
- ModelReadyToSync();
- }
- // Initialize to a "ready-to-commit" state.
- void InitializeToReadyState() {
- InitializeToMetadataLoaded();
- OnSyncStarting();
- }
- void ModelReadyToSync() {
- type_processor()->ModelReadyToSync(db()->CreateMetadataBatch());
- WaitForStartCallbackIfNeeded();
- }
- void OnCommitDataLoaded() { bridge()->OnCommitDataLoaded(); }
- void OnSyncStarting(const std::string& authenticated_account_id =
- kDefaultAuthenticatedAccountId,
- const std::string& cache_guid = kCacheGuid,
- SyncMode sync_mode = SyncMode::kFull) {
- DataTypeActivationRequest request;
- request.error_handler = base::BindRepeating(
- &ClientTagBasedModelTypeProcessorTest::ErrorReceived,
- base::Unretained(this));
- request.cache_guid = cache_guid;
- request.authenticated_account_id = CoreAccountId(authenticated_account_id);
- request.sync_mode = sync_mode;
- request.configuration_start_time = base::Time::Now();
- // |run_loop_| may exist here if OnSyncStarting is called without resetting
- // state. But it is safe to remove it.
- ASSERT_TRUE(!run_loop_ || !run_loop_->running());
- run_loop_ = std::make_unique<base::RunLoop>();
- type_processor()->OnSyncStarting(
- request,
- base::BindOnce(&ClientTagBasedModelTypeProcessorTest::OnReadyToConnect,
- base::Unretained(this)));
- WaitForStartCallbackIfNeeded();
- }
- void DisconnectSync() {
- type_processor()->DisconnectSync();
- worker_ = nullptr;
- }
- // Writes data for |key| and simulates a commit response for it.
- EntitySpecifics WriteItemAndAck(const std::string& key,
- const std::string& value) {
- EntitySpecifics specifics = WritePrefItem(bridge(), key, value);
- base::RunLoop().RunUntilIdle();
- worker()->VerifyPendingCommits({{GetPrefHash(key)}});
- worker()->AckOnePendingCommit();
- EXPECT_EQ(0U, worker()->GetNumPendingCommits());
- return specifics;
- }
- void WriteItemAndAck(const std::string& key,
- std::unique_ptr<EntityData> entity_data) {
- bridge()->WriteItem(key, std::move(entity_data));
- worker()->VerifyPendingCommits({{GetPrefHash(key)}});
- worker()->AckOnePendingCommit();
- EXPECT_EQ(0U, worker()->GetNumPendingCommits());
- return;
- }
- const ProcessorEntity* GetEntityForStorageKey(
- const std::string& storage_key) {
- return type_processor()->entity_tracker_->GetEntityForStorageKey(
- storage_key);
- }
- void ResetState(bool keep_db) {
- bridge_ = keep_db ? std::make_unique<TestModelTypeSyncBridge>(
- std::move(bridge_), GetModelType(),
- SupportsIncrementalUpdates())
- : std::make_unique<TestModelTypeSyncBridge>(
- GetModelType(), SupportsIncrementalUpdates());
- worker_ = nullptr;
- run_loop_.reset();
- CheckPostConditions();
- histogram_tester_ = std::make_unique<base::HistogramTester>();
- }
- virtual ModelType GetModelType() { return PREFERENCES; }
- virtual bool SupportsIncrementalUpdates() { return true; }
- // Wipes existing DB and simulates a pending update of a server-known item.
- EntitySpecifics ResetStateWriteItem(const std::string& name,
- const std::string& value) {
- ResetState(false);
- InitializeToReadyState();
- EXPECT_EQ(0U, ProcessorEntityCount());
- WriteItemAndAck(name, "acked-value");
- EntitySpecifics specifics = WritePrefItem(bridge(), name, value);
- EXPECT_EQ(1U, ProcessorEntityCount());
- ResetState(true);
- return specifics;
- }
- // Wipes existing DB and simulates a pending deletion of a server-known item.
- void ResetStateDeleteItem(const std::string& name, const std::string& value) {
- ResetState(false);
- InitializeToReadyState();
- EXPECT_EQ(0U, ProcessorEntityCount());
- WriteItemAndAck(name, value);
- EXPECT_EQ(1U, ProcessorEntityCount());
- bridge()->DeleteItem(name);
- EXPECT_EQ(1U, ProcessorEntityCount());
- ResetState(true);
- }
- // Return the number of entities the processor has metadata for.
- size_t ProcessorEntityCount() const {
- if (type_processor()->entity_tracker_)
- return type_processor()->entity_tracker_->size();
- return 0;
- }
- // Expect to receive an error from the processor.
- void ExpectError(ClientTagBasedModelTypeProcessor::ErrorSite error_site) {
- EXPECT_FALSE(expect_error_);
- expect_error_ = error_site;
- }
- TestModelTypeSyncBridge* bridge() const { return bridge_.get(); }
- FakeModelTypeSyncBridge::Store* db() const { return bridge()->mutable_db(); }
- MockModelTypeWorker* worker() const { return worker_; }
- ClientTagBasedModelTypeProcessor* type_processor() const {
- return static_cast<ClientTagBasedModelTypeProcessor*>(
- bridge()->change_processor());
- }
- protected:
- void CheckPostConditions() { EXPECT_FALSE(expect_error_); }
- void OnReadyToConnect(std::unique_ptr<DataTypeActivationResponse> context) {
- model_type_state_ = context->model_type_state;
- std::unique_ptr<MockModelTypeWorker> worker =
- std::make_unique<MockModelTypeWorker>(model_type_state_,
- type_processor());
- // Keep an unsafe pointer to the commit queue the processor will use.
- worker_ = worker.get();
- // The context contains a proxy to the processor, but this call is
- // side-stepping that completely and connecting directly to the real
- // processor, since these tests are single-threaded and don't need proxies.
- type_processor()->ConnectSync(std::move(worker));
- ASSERT_TRUE(run_loop_);
- run_loop_->Quit();
- }
- void ErrorReceived(const ModelError& error) {
- EXPECT_TRUE(expect_error_);
- histogram_tester_->ExpectBucketCount("Sync.ModelTypeErrorSite.PREFERENCE",
- *expect_error_, /*count=*/1);
- expect_error_ = absl::nullopt;
- // Do not expect for a start callback anymore.
- if (run_loop_) {
- run_loop_->Quit();
- }
- }
- sync_pb::ModelTypeState model_type_state() { return model_type_state_; }
- void WaitForStartCallbackIfNeeded() {
- if (!type_processor()->IsModelReadyToSyncForTest() ||
- !bridge()->sync_started()) {
- return;
- }
- ASSERT_TRUE(run_loop_);
- run_loop_->Run();
- }
- private:
- std::unique_ptr<TestModelTypeSyncBridge> bridge_;
- sync_pb::ModelTypeState model_type_state_;
- // This sets SequencedTaskRunnerHandle on the current thread, which the type
- // processor will pick up as the sync task runner.
- base::test::SingleThreadTaskEnvironment task_environment_;
- // This run loop is used to wait for OnReadyToConnect is called.
- std::unique_ptr<base::RunLoop> run_loop_;
- // The current mock queue, which is owned by |type_processor()|.
- raw_ptr<MockModelTypeWorker> worker_;
- // Whether to expect an error from the processor (and from which site).
- absl::optional<ClientTagBasedModelTypeProcessor::ErrorSite> expect_error_;
- std::unique_ptr<base::HistogramTester> histogram_tester_;
- };
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldExposeNewlyTrackedAccountId) {
- ModelReadyToSync();
- ASSERT_EQ("", type_processor()->TrackedAccountId());
- OnSyncStarting();
- worker()->UpdateFromServer();
- EXPECT_EQ(kDefaultAuthenticatedAccountId,
- type_processor()->TrackedAccountId());
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldExposePreviouslyTrackedAccountId) {
- std::unique_ptr<MetadataBatch> metadata_batch = db()->CreateMetadataBatch();
- sync_pb::ModelTypeState model_type_state(metadata_batch->GetModelTypeState());
- model_type_state.set_initial_sync_done(true);
- model_type_state.set_cache_guid(kCacheGuid);
- model_type_state.set_authenticated_account_id("PersistedAccountId");
- model_type_state.mutable_progress_marker()->set_data_type_id(
- GetSpecificsFieldNumberFromModelType(GetModelType()));
- metadata_batch->SetModelTypeState(model_type_state);
- type_processor()->ModelReadyToSync(std::move(metadata_batch));
- // Even prior to starting sync, the account ID should already be tracked.
- EXPECT_EQ("PersistedAccountId", type_processor()->TrackedAccountId());
- // If sync gets started, the account should still be tracked.
- OnSyncStarting("PersistedAccountId");
- EXPECT_EQ("PersistedAccountId", type_processor()->TrackedAccountId());
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldExposeNewlyTrackedAccountIdIfChanged) {
- std::unique_ptr<MetadataBatch> metadata_batch = db()->CreateMetadataBatch();
- sync_pb::ModelTypeState model_type_state(metadata_batch->GetModelTypeState());
- model_type_state.set_initial_sync_done(true);
- model_type_state.set_cache_guid(kCacheGuid);
- model_type_state.set_authenticated_account_id("PersistedAccountId");
- model_type_state.mutable_progress_marker()->set_data_type_id(
- GetSpecificsFieldNumberFromModelType(GetModelType()));
- metadata_batch->SetModelTypeState(model_type_state);
- type_processor()->ModelReadyToSync(std::move(metadata_batch));
- // Even prior to starting sync, the account ID should already be tracked.
- ASSERT_EQ("PersistedAccountId", type_processor()->TrackedAccountId());
- // If sync gets started, the new account should be tracked.
- OnSyncStarting("NewAccountId");
- EXPECT_TRUE(type_processor()->IsTrackingMetadata());
- EXPECT_EQ("NewAccountId", type_processor()->TrackedAccountId());
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldExposeNewlyTrackedCacheGuid) {
- ModelReadyToSync();
- ASSERT_EQ("", type_processor()->TrackedCacheGuid());
- OnSyncStarting();
- worker()->UpdateFromServer();
- EXPECT_EQ(kCacheGuid, type_processor()->TrackedCacheGuid());
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldExposePreviouslyTrackedCacheGuid) {
- std::unique_ptr<MetadataBatch> metadata_batch = db()->CreateMetadataBatch();
- sync_pb::ModelTypeState model_type_state(metadata_batch->GetModelTypeState());
- model_type_state.set_initial_sync_done(true);
- model_type_state.set_cache_guid("PersistedCacheGuid");
- model_type_state.set_authenticated_account_id(kDefaultAuthenticatedAccountId);
- model_type_state.mutable_progress_marker()->set_data_type_id(
- GetSpecificsFieldNumberFromModelType(GetModelType()));
- metadata_batch->SetModelTypeState(model_type_state);
- type_processor()->ModelReadyToSync(std::move(metadata_batch));
- // Even prior to starting sync, the cache guid should be set.
- EXPECT_EQ("PersistedCacheGuid", type_processor()->TrackedCacheGuid());
- // If sync gets started, the cache guid should still be set.
- OnSyncStarting(kDefaultAuthenticatedAccountId, "PersistedCacheGuid");
- EXPECT_EQ("PersistedCacheGuid", type_processor()->TrackedCacheGuid());
- }
- // Test that an initial sync handles local and remote items properly.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldMergeLocalAndRemoteChanges) {
- base::test::ScopedFeatureList feature_list;
- feature_list.InitAndEnableFeature(kCacheBaseEntitySpecificsInMetadata);
- ModelReadyToSync();
- OnSyncStarting();
- // Local write before initial sync.
- WritePrefItem(bridge(), kKey1, kValue1);
- // Has data, but no metadata, entity in the processor, or commit request.
- EXPECT_EQ(1U, db()->data_count());
- EXPECT_EQ(0U, db()->metadata_count());
- EXPECT_EQ(0U, ProcessorEntityCount());
- EXPECT_EQ(0U, worker()->GetNumPendingCommits());
- EXPECT_EQ(0, bridge()->merge_call_count());
- // Initial sync with one server item.
- base::HistogramTester histogram_tester;
- worker()->UpdateFromServer(GetPrefHash(kKey2),
- GeneratePrefSpecifics(kKey2, kValue2));
- EXPECT_EQ(1, bridge()->merge_call_count());
- histogram_tester.ExpectUniqueSample(
- "Sync.ModelTypeInitialUpdateReceived",
- /*sample=*/syncer::ModelTypeHistogramValue(GetModelType()),
- /*expected_count=*/1);
- // Now have data and metadata for both items, as well as a commit request for
- // the local item.
- EXPECT_EQ(2U, db()->data_count());
- EXPECT_EQ(2U, db()->metadata_count());
- EXPECT_EQ(2U, ProcessorEntityCount());
- EXPECT_EQ(1, db()->GetMetadata(kKey1).sequence_number());
- EXPECT_EQ(0, db()->GetMetadata(kKey2).sequence_number());
- EXPECT_TRUE(db()->GetMetadata(kKey1).has_possibly_trimmed_base_specifics());
- EXPECT_TRUE(db()->GetMetadata(kKey2).has_possibly_trimmed_base_specifics());
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}});
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldExposePossiblyTrimmedRemoteSpecifics) {
- base::test::ScopedFeatureList feature_list;
- feature_list.InitAndEnableFeature(kCacheBaseEntitySpecificsInMetadata);
- ModelReadyToSync();
- OnSyncStarting();
- sync_pb::EntitySpecifics specififcs = GeneratePrefSpecifics(kKey1, kValue1);
- *specififcs.mutable_preference()->mutable_unknown_fields() = kValue2;
- worker()->UpdateFromServer(GetPrefHash(kKey1), specififcs);
- sync_pb::PreferenceSpecifics cached_preference =
- type_processor()->GetPossiblyTrimmedRemoteSpecifics(kKey1).preference();
- // Below verifies that
- // TestModelTypeSyncBridge::TrimRemoteSpecificsForCaching() is honored.
- // Preserved fields.
- EXPECT_EQ(cached_preference.name(), kKey1);
- EXPECT_EQ(cached_preference.unknown_fields(), kValue2);
- // Trimmed field.
- EXPECT_FALSE(cached_preference.has_value());
- }
- // Test that an initial sync filters out tombstones in the processor.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldFilterOutInitialTombstones) {
- ModelReadyToSync();
- OnSyncStarting();
- EXPECT_EQ(0, bridge()->merge_call_count());
- // Initial sync with a tombstone. The fake bridge checks that it doesn't get
- // any tombstones in its MergeSyncData function.
- worker()->TombstoneFromServer(GetPrefHash(kKey1));
- EXPECT_EQ(1, bridge()->merge_call_count());
- // Should still have no data, metadata, or commit requests.
- EXPECT_EQ(0U, db()->data_count());
- EXPECT_EQ(0U, db()->metadata_count());
- EXPECT_EQ(0U, ProcessorEntityCount());
- EXPECT_EQ(0U, worker()->GetNumPendingCommits());
- }
- // Test that an initial sync filters out updates for root nodes in the
- // processor.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldFilterOutInitialRootNodes) {
- ModelReadyToSync();
- OnSyncStarting();
- UpdateResponseDataList update;
- update.push_back(worker()->GenerateTypeRootUpdateData(ModelType::SESSIONS));
- worker()->UpdateFromServer(std::move(update));
- // Root node update should be filtered out.
- EXPECT_EQ(0U, ProcessorEntityCount());
- }
- // Test that subsequent starts don't call MergeSyncData.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldApplyIncrementalUpdates) {
- // This sets initial_sync_done to true.
- InitializeToMetadataLoaded();
- // Write an item before sync connects.
- WritePrefItem(bridge(), kKey1, kValue1);
- EXPECT_EQ(1U, db()->data_count());
- EXPECT_EQ(1U, db()->metadata_count());
- // Check that data coming from sync is treated as a normal GetUpdates.
- OnSyncStarting();
- base::HistogramTester histogram_tester;
- worker()->UpdateFromServer(GetPrefHash(kKey2),
- GeneratePrefSpecifics(kKey2, kValue2));
- EXPECT_EQ(0, bridge()->merge_call_count());
- EXPECT_EQ(1, bridge()->apply_call_count());
- EXPECT_EQ(2U, db()->data_count());
- EXPECT_EQ(2U, db()->metadata_count());
- histogram_tester.ExpectUniqueSample(
- "Sync.ModelTypeIncrementalUpdateReceived",
- /*sample=*/syncer::ModelTypeHistogramValue(GetModelType()),
- /*expected_count=*/1);
- }
- // Test that an error during the merge is propagated to the error handler.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldReportErrorDuringMerge) {
- ModelReadyToSync();
- OnSyncStarting();
- bridge()->ErrorOnNextCall();
- ExpectError(ClientTagBasedModelTypeProcessor::ErrorSite::kApplyFullUpdates);
- worker()->UpdateFromServer();
- }
- // Test that errors before it's called are passed to |start_callback| correctly.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldDeferErrorsBeforeStart) {
- type_processor()->ReportError({FROM_HERE, "boom"});
- ExpectError(ClientTagBasedModelTypeProcessor::ErrorSite::kBridgeInitiated);
- OnSyncStarting();
- // Test OnSyncStarting happening first.
- ResetState(false);
- OnSyncStarting();
- ExpectError(ClientTagBasedModelTypeProcessor::ErrorSite::kBridgeInitiated);
- type_processor()->ReportError({FROM_HERE, "boom"});
- // Test an error loading pending data.
- ResetStateWriteItem(kKey1, kValue1);
- bridge()->ErrorOnNextCall();
- InitializeToMetadataLoaded();
- ExpectError(ClientTagBasedModelTypeProcessor::ErrorSite::kBridgeInitiated);
- OnSyncStarting();
- // Test an error prior to metadata load.
- ResetState(false);
- type_processor()->ReportError({FROM_HERE, "boom"});
- ExpectError(ClientTagBasedModelTypeProcessor::ErrorSite::kBridgeInitiated);
- OnSyncStarting();
- ModelReadyToSync();
- // Test an error prior to pending data load.
- ResetStateWriteItem(kKey1, kValue1);
- InitializeToMetadataLoaded();
- type_processor()->ReportError({FROM_HERE, "boom"});
- ExpectError(ClientTagBasedModelTypeProcessor::ErrorSite::kBridgeInitiated);
- OnSyncStarting();
- }
- // This test covers race conditions during loading pending data. All cases
- // start with no processor and one acked (committed to the server) item with a
- // pending commit. There are three different events that occur once metadata
- // is loaded:
- //
- // - Sync gets connected once sync in ready.
- // - Commit data is loaded. This happens only after Sync gets connected.
- // - Optionally, a put or delete happens to the item.
- //
- // This results in 1 + 6 = 7 orderings of the events.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldLoadDataForPendingCommit) {
- // Connect, data.
- EntitySpecifics specifics2 = ResetStateWriteItem(kKey1, kValue1);
- InitializeToMetadataLoaded();
- OnSyncStarting();
- OnCommitDataLoaded();
- EXPECT_EQ(1U, worker()->GetNumPendingCommits());
- worker()->VerifyNthPendingCommit(0, {GetPrefHash(kKey1)}, {specifics2});
- // Connect, data, put.
- EntitySpecifics specifics6 = ResetStateWriteItem(kKey1, kValue1);
- InitializeToMetadataLoaded();
- OnSyncStarting();
- OnCommitDataLoaded();
- EntitySpecifics specifics7 = WritePrefItem(bridge(), kKey1, kValue2);
- EXPECT_EQ(2U, worker()->GetNumPendingCommits());
- worker()->VerifyNthPendingCommit(0, {GetPrefHash(kKey1)}, {specifics6});
- worker()->VerifyNthPendingCommit(1, {GetPrefHash(kKey1)}, {specifics7});
- // Connect, put, data.
- EntitySpecifics specifics100 = ResetStateWriteItem(kKey1, kValue1);
- InitializeToMetadataLoaded();
- OnSyncStarting();
- EntitySpecifics specifics8 = WritePrefItem(bridge(), kKey1, kValue2);
- OnCommitDataLoaded();
- EXPECT_EQ(2U, worker()->GetNumPendingCommits());
- worker()->VerifyNthPendingCommit(0, {GetPrefHash(kKey1)}, {specifics8});
- // GetData was launched as a result of GetLocalChanges call(). Since all data
- // are in memory, the 2nd pending commit should be empty.
- worker()->VerifyNthPendingCommit(1, {}, {});
- // Put, connect, data.
- ResetStateWriteItem(kKey1, kValue1);
- InitializeToMetadataLoaded();
- EntitySpecifics specifics10 = WritePrefItem(bridge(), kKey1, kValue2);
- OnSyncStarting();
- EXPECT_FALSE(bridge()->GetDataCallback());
- EXPECT_EQ(1U, worker()->GetNumPendingCommits());
- worker()->VerifyNthPendingCommit(0, {GetPrefHash(kKey1)}, {specifics10});
- // Connect, data, delete.
- EntitySpecifics specifics12 = ResetStateWriteItem(kKey1, kValue1);
- InitializeToMetadataLoaded();
- OnSyncStarting();
- OnCommitDataLoaded();
- bridge()->DeleteItem(kKey1);
- EXPECT_EQ(2U, worker()->GetNumPendingCommits());
- worker()->VerifyNthPendingCommit(0, {GetPrefHash(kKey1)}, {specifics12});
- worker()->VerifyNthPendingCommit(1, {GetPrefHash(kKey1)}, {kEmptySpecifics});
- // Connect, delete, data.
- ResetStateWriteItem(kKey1, kValue1);
- InitializeToMetadataLoaded();
- OnSyncStarting();
- bridge()->DeleteItem(kKey1);
- OnCommitDataLoaded();
- EXPECT_EQ(2U, worker()->GetNumPendingCommits());
- worker()->VerifyNthPendingCommit(0, {GetPrefHash(kKey1)}, {kEmptySpecifics});
- // GetData was launched as a result of GetLocalChanges call(). Since all data
- // are in memory, the 2nd pending commit should be empty.
- worker()->VerifyNthPendingCommit(1, {}, {});
- // Delete, connect, data.
- ResetStateWriteItem(kKey1, kValue1);
- InitializeToMetadataLoaded();
- bridge()->DeleteItem(kKey1);
- OnSyncStarting();
- EXPECT_FALSE(bridge()->GetDataCallback());
- EXPECT_EQ(1U, worker()->GetNumPendingCommits());
- worker()->VerifyNthPendingCommit(0, {GetPrefHash(kKey1)}, {kEmptySpecifics});
- }
- // Tests cases where pending data loads synchronously.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldHandleSynchronousDataLoad) {
- // Model, sync.
- EntitySpecifics specifics1 = ResetStateWriteItem(kKey1, kValue1);
- bridge()->ExpectSynchronousDataCallback();
- InitializeToMetadataLoaded();
- OnSyncStarting();
- EXPECT_EQ(1U, worker()->GetNumPendingCommits());
- worker()->VerifyNthPendingCommit(0, {GetPrefHash(kKey1)}, {specifics1});
- // Sync, model.
- EntitySpecifics specifics2 = ResetStateWriteItem(kKey1, kValue1);
- OnSyncStarting();
- bridge()->ExpectSynchronousDataCallback();
- InitializeToMetadataLoaded();
- EXPECT_EQ(1U, worker()->GetNumPendingCommits());
- worker()->VerifyNthPendingCommit(0, {GetPrefHash(kKey1)}, {specifics2});
- }
- // This test covers race conditions during loading a pending delete. All cases
- // start with no processor and one item with a pending delete. There are two
- // different events that can occur in any order once metadata is loaded, since
- // for a deletion there is no data to load:
- //
- // - Sync gets connected.
- // - Optionally, a put or delete happens to the item (repeated deletes should be
- // handled properly).
- //
- // This results in 1 + 4 = 5 orderings of the events.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldLoadPendingDelete) {
- // Connect.
- ResetStateDeleteItem(kKey1, kValue1);
- InitializeToMetadataLoaded();
- OnSyncStarting();
- EXPECT_EQ(1U, worker()->GetNumPendingCommits());
- worker()->VerifyNthPendingCommit(0, {GetPrefHash(kKey1)}, {kEmptySpecifics});
- // Connect, put.
- ResetStateDeleteItem(kKey1, kValue1);
- InitializeToMetadataLoaded();
- OnSyncStarting();
- EXPECT_EQ(1U, worker()->GetNumPendingCommits());
- EntitySpecifics specifics1 = WritePrefItem(bridge(), kKey1, kValue2);
- EXPECT_EQ(2U, worker()->GetNumPendingCommits());
- worker()->VerifyNthPendingCommit(0, {GetPrefHash(kKey1)}, {kEmptySpecifics});
- worker()->VerifyNthPendingCommit(1, {GetPrefHash(kKey1)}, {specifics1});
- // Put, connect.
- ResetStateDeleteItem(kKey1, kValue1);
- InitializeToMetadataLoaded();
- EntitySpecifics specifics2 = WritePrefItem(bridge(), kKey1, kValue2);
- OnSyncStarting();
- EXPECT_EQ(1U, worker()->GetNumPendingCommits());
- worker()->VerifyNthPendingCommit(0, {GetPrefHash(kKey1)}, {specifics2});
- // Connect, delete.
- ResetStateDeleteItem(kKey1, kValue1);
- InitializeToMetadataLoaded();
- OnSyncStarting();
- EXPECT_EQ(1U, worker()->GetNumPendingCommits());
- bridge()->DeleteItem(kKey1);
- EXPECT_EQ(2U, worker()->GetNumPendingCommits());
- worker()->VerifyNthPendingCommit(0, {GetPrefHash(kKey1)}, {kEmptySpecifics});
- worker()->VerifyNthPendingCommit(1, {GetPrefHash(kKey1)}, {kEmptySpecifics});
- // Delete, connect.
- ResetStateDeleteItem(kKey1, kValue1);
- InitializeToMetadataLoaded();
- bridge()->DeleteItem(kKey1);
- OnSyncStarting();
- EXPECT_EQ(1U, worker()->GetNumPendingCommits());
- worker()->VerifyNthPendingCommit(0, {GetPrefHash(kKey1)}, {kEmptySpecifics});
- }
- // Test that loading a committed item does not queue another commit.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldNotQueueAnotherCommitIfAlreadyCommitted) {
- InitializeToReadyState();
- WriteItemAndAck(kKey1, kValue1);
- ResetState(true);
- // Test that a new processor loads the metadata without committing.
- InitializeToReadyState();
- EXPECT_EQ(1U, ProcessorEntityCount());
- EXPECT_EQ(0U, worker()->GetNumPendingCommits());
- }
- // Creates a new item locally.
- // Thoroughly tests the data generated by a local item creation.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldCommitLocalCreation) {
- base::test::ScopedFeatureList feature_list;
- feature_list.InitAndEnableFeature(kCacheBaseEntitySpecificsInMetadata);
- base::HistogramTester histogram_tester;
- InitializeToReadyState();
- ASSERT_EQ(0U, worker()->GetNumPendingCommits());
- ASSERT_FALSE(type_processor()->IsEntityUnsynced(kKey1));
- WritePrefItem(bridge(), kKey1, kValue1);
- EXPECT_TRUE(type_processor()->IsEntityUnsynced(kKey1));
- EXPECT_FALSE(type_processor()->GetEntityCreationTime(kKey1).is_null());
- EXPECT_EQ(type_processor()->GetEntityCreationTime(kKey1),
- type_processor()->GetEntityModificationTime(kKey1));
- // Verify the commit request this operation has triggered.
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}});
- const CommitRequestData* tag1_request_data =
- worker()->GetLatestPendingCommitForHash(GetPrefHash(kKey1));
- ASSERT_TRUE(tag1_request_data);
- const EntityData& tag1_data = *tag1_request_data->entity;
- EXPECT_EQ(kUncommittedVersion, tag1_request_data->base_version);
- EXPECT_TRUE(tag1_data.id.empty());
- EXPECT_FALSE(tag1_data.creation_time.is_null());
- EXPECT_FALSE(tag1_data.modification_time.is_null());
- EXPECT_EQ(kKey1, tag1_data.name);
- EXPECT_FALSE(tag1_data.is_deleted());
- EXPECT_EQ(kKey1, tag1_data.specifics.preference().name());
- EXPECT_EQ(kValue1, tag1_data.specifics.preference().value());
- EXPECT_EQ(1U, db()->metadata_count());
- const EntityMetadata metadata = db()->GetMetadata(kKey1);
- EXPECT_TRUE(metadata.has_client_tag_hash());
- EXPECT_FALSE(metadata.has_server_id());
- EXPECT_FALSE(metadata.is_deleted());
- EXPECT_EQ(1, metadata.sequence_number());
- EXPECT_EQ(0, metadata.acked_sequence_number());
- EXPECT_EQ(kUncommittedVersion, metadata.server_version());
- EXPECT_TRUE(metadata.has_creation_time());
- EXPECT_TRUE(metadata.has_modification_time());
- EXPECT_TRUE(metadata.has_specifics_hash());
- EXPECT_TRUE(metadata.has_possibly_trimmed_base_specifics());
- worker()->AckOnePendingCommit();
- EXPECT_FALSE(type_processor()->IsEntityUnsynced(kKey1));
- EXPECT_EQ(1U, db()->metadata_count());
- const EntityMetadata acked_metadata = db()->GetMetadata(kKey1);
- EXPECT_TRUE(acked_metadata.has_server_id());
- EXPECT_EQ(1, acked_metadata.sequence_number());
- EXPECT_EQ(1, acked_metadata.acked_sequence_number());
- EXPECT_EQ(1, acked_metadata.server_version());
- histogram_tester.ExpectBucketCount(
- "Sync.ModelTypeOrphanMetadata.Put",
- /*bucket=*/ModelTypeHistogramValue(GetModelType()), /*count=*/0);
- }
- // Creates a new item locally while another item exists for the same client tag
- // hash.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- CommitShouldOverwriteExistingItem) {
- base::test::ScopedFeatureList feature_list;
- feature_list.InitAndEnableFeature(kCacheBaseEntitySpecificsInMetadata);
- base::HistogramTester histogram_tester;
- // Provide custom client tags for this test.
- bridge()->SetSupportsGetClientTag(false);
- const syncer::ClientTagHash kClientTagHash =
- ClientTagHash::FromUnhashed(PREFERENCES, "tag");
- InitializeToReadyState();
- EXPECT_EQ(0U, worker()->GetNumPendingCommits());
- std::unique_ptr<EntityData> entity_data1 =
- GeneratePrefEntityData(kKey1, kValue1);
- // Use a custom client tag hash - independent of the storage key.
- entity_data1->client_tag_hash = kClientTagHash;
- bridge()->WriteItem(kKey1, std::move(entity_data1));
- EXPECT_EQ(1U, db()->metadata_count());
- worker()->VerifyPendingCommits({{kClientTagHash}});
- std::unique_ptr<EntityData> entity_data2 =
- GeneratePrefEntityData(kKey2, kValue2);
- // Use the same custom client tag hash as for entity 1.
- entity_data2->client_tag_hash = kClientTagHash;
- bridge()->WriteItem(kKey2, std::move(entity_data2));
- EXPECT_EQ(1U, db()->metadata_count());
- EXPECT_TRUE(db()->GetMetadata(kKey2).has_possibly_trimmed_base_specifics());
- histogram_tester.ExpectBucketCount(
- "Sync.ModelTypeOrphanMetadata.Put",
- /*bucket=*/ModelTypeHistogramValue(GetModelType()),
- /*count=*/1);
- }
- // Test that an error applying metadata changes from a commit response is
- // propagated to the error handler.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldReportErrorApplyingAck) {
- InitializeToReadyState();
- WritePrefItem(bridge(), kKey1, kValue1);
- bridge()->ErrorOnNextCall();
- ExpectError(ClientTagBasedModelTypeProcessor::ErrorSite::
- kApplyUpdatesOnCommitResponse);
- worker()->AckOnePendingCommit();
- }
- // The purpose of this test case is to test setting |client_tag_hash| and |id|
- // on the EntityData object as we pass it into the Put method of the processor.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldOverrideFieldsForLocalUpdate) {
- const std::string kId1 = "cid1";
- const std::string kId2 = "cid2";
- InitializeToReadyState();
- EXPECT_EQ(0U, worker()->GetNumPendingCommits());
- std::unique_ptr<EntityData> entity_data = std::make_unique<EntityData>();
- entity_data->specifics.mutable_preference()->set_name(kKey1);
- entity_data->specifics.mutable_preference()->set_value(kValue1);
- entity_data->name = kKey1;
- entity_data->client_tag_hash = GetPrefHash(kKey1);
- entity_data->id = kId1;
- bridge()->WriteItem(kKey1, std::move(entity_data));
- EXPECT_EQ(1U, worker()->GetNumPendingCommits());
- ASSERT_FALSE(worker()->HasPendingCommitForHash(GetPrefHash(kKey3)));
- ASSERT_TRUE(worker()->HasPendingCommitForHash(GetPrefHash(kKey1)));
- EXPECT_EQ(1U, db()->metadata_count());
- ASSERT_TRUE(worker()->GetLatestPendingCommitForHash(GetPrefHash(kKey1)));
- const EntityData& out_entity1 =
- *worker()->GetLatestPendingCommitForHash(GetPrefHash(kKey1))->entity;
- const EntityMetadata metadata_v1 = db()->GetMetadata(kKey1);
- EXPECT_EQ(kId1, out_entity1.id);
- EXPECT_NE(GetPrefHash(kKey3), out_entity1.client_tag_hash);
- EXPECT_EQ(kValue1, out_entity1.specifics.preference().value());
- EXPECT_EQ(kId1, metadata_v1.server_id());
- EXPECT_EQ(metadata_v1.client_tag_hash(), out_entity1.client_tag_hash.value());
- entity_data = std::make_unique<EntityData>();
- // This is a sketchy move here, changing the name will change the generated
- // storage key and client tag values.
- entity_data->specifics.mutable_preference()->set_name(kKey2);
- entity_data->specifics.mutable_preference()->set_value(kValue2);
- entity_data->name = kKey2;
- entity_data->client_tag_hash = GetPrefHash(kKey3);
- // Make sure ID isn't overwritten either.
- entity_data->id = kId2;
- bridge()->WriteItem(kKey1, std::move(entity_data));
- EXPECT_EQ(2U, worker()->GetNumPendingCommits());
- ASSERT_FALSE(worker()->HasPendingCommitForHash(GetPrefHash(kKey3)));
- ASSERT_TRUE(worker()->HasPendingCommitForHash(GetPrefHash(kKey1)));
- EXPECT_EQ(1U, db()->metadata_count());
- ASSERT_TRUE(worker()->GetLatestPendingCommitForHash(GetPrefHash(kKey1)));
- const EntityData& out_entity2 =
- *worker()->GetLatestPendingCommitForHash(GetPrefHash(kKey1))->entity;
- const EntityMetadata metadata_v2 = db()->GetMetadata(kKey1);
- EXPECT_EQ(kValue2, out_entity2.specifics.preference().value());
- // Should still see old cid1 value, override is not respected on update.
- EXPECT_EQ(kId1, out_entity2.id);
- EXPECT_EQ(kId1, metadata_v2.server_id());
- EXPECT_EQ(metadata_v2.client_tag_hash(), out_entity2.client_tag_hash.value());
- // Specifics have changed so the hashes should not match.
- EXPECT_NE(metadata_v1.specifics_hash(), metadata_v2.specifics_hash());
- }
- // Creates a new local item, then modifies it after it has been acked.
- // Thoroughly tests data generated by modification of server-unknown item.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldCommitLocalUpdate) {
- InitializeToReadyState();
- WritePrefItem(bridge(), kKey1, kValue1);
- ASSERT_EQ(1U, db()->metadata_count());
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}});
- const EntityMetadata metadata_v1 = db()->GetMetadata(kKey1);
- int64_t request_data_v1_sequence_number;
- {
- // request_data_v1 is valid only while the commit is still pending.
- const CommitRequestData* request_data_v1 =
- worker()->GetLatestPendingCommitForHash(GetPrefHash(kKey1));
- ASSERT_TRUE(request_data_v1);
- const EntityData& data_v1 = *request_data_v1->entity;
- EXPECT_EQ(data_v1.specifics.preference().value(), kValue1);
- request_data_v1_sequence_number = request_data_v1->sequence_number;
- }
- worker()->AckOnePendingCommit();
- ASSERT_FALSE(type_processor()->IsEntityUnsynced(kKey1));
- const base::Time ctime = type_processor()->GetEntityCreationTime(kKey1);
- ASSERT_FALSE(ctime.is_null());
- ASSERT_EQ(ctime, type_processor()->GetEntityModificationTime(kKey1));
- // Make sure the clock advances.
- base::PlatformThread::Sleep(base::Milliseconds(1));
- ASSERT_NE(ctime, base::Time::Now());
- WritePrefItem(bridge(), kKey1, kValue2);
- EXPECT_EQ(1U, db()->metadata_count());
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}});
- EXPECT_TRUE(type_processor()->IsEntityUnsynced(kKey1));
- EXPECT_EQ(ctime, type_processor()->GetEntityCreationTime(kKey1));
- const base::Time mtime = type_processor()->GetEntityModificationTime(kKey1);
- EXPECT_NE(ctime, mtime);
- const CommitRequestData* request_data_v2 =
- worker()->GetLatestPendingCommitForHash(GetPrefHash(kKey1));
- ASSERT_TRUE(request_data_v2);
- const EntityData& data_v2 = *request_data_v2->entity;
- const EntityMetadata metadata_v2 = db()->GetMetadata(kKey1);
- // Test some of the relations between old and new commit requests.
- EXPECT_GT(request_data_v2->sequence_number, request_data_v1_sequence_number);
- EXPECT_EQ(data_v2.specifics.preference().value(), kValue2);
- // Perform a thorough examination of the update-generated request.
- EXPECT_NE(kUncommittedVersion, request_data_v2->base_version);
- EXPECT_FALSE(data_v2.id.empty());
- EXPECT_EQ(ctime, data_v2.creation_time);
- EXPECT_EQ(mtime, data_v2.modification_time);
- EXPECT_EQ(kKey1, data_v2.name);
- EXPECT_FALSE(data_v2.is_deleted());
- EXPECT_EQ(kKey1, data_v2.specifics.preference().name());
- EXPECT_EQ(kValue2, data_v2.specifics.preference().value());
- // Perform a thorough examination of the local sync metadata.
- EXPECT_FALSE(metadata_v1.has_server_id());
- EXPECT_FALSE(metadata_v1.is_deleted());
- EXPECT_EQ(1, metadata_v1.sequence_number());
- EXPECT_EQ(0, metadata_v1.acked_sequence_number());
- EXPECT_EQ(kUncommittedVersion, metadata_v1.server_version());
- EXPECT_FALSE(metadata_v2.server_id().empty());
- EXPECT_FALSE(metadata_v2.is_deleted());
- EXPECT_EQ(2, metadata_v2.sequence_number());
- EXPECT_EQ(1, metadata_v2.acked_sequence_number());
- EXPECT_NE(kUncommittedVersion, metadata_v2.server_version());
- EXPECT_EQ(metadata_v1.client_tag_hash(), metadata_v2.client_tag_hash());
- EXPECT_NE(metadata_v1.specifics_hash(), metadata_v2.specifics_hash());
- }
- // Same as above, but modifies the item BEFORE it has been acked.
- // Thoroughly tests data generated by modification of server-unknown item.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldCommitLocalUpdateBeforeCreationAck) {
- InitializeToReadyState();
- WritePrefItem(bridge(), kKey1, kValue1);
- ASSERT_EQ(1U, db()->metadata_count());
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}});
- const CommitRequestData* request_data_v1 =
- worker()->GetLatestPendingCommitForHash(GetPrefHash(kKey1));
- ASSERT_TRUE(request_data_v1);
- const EntityData& data_v1 = *request_data_v1->entity;
- const EntityMetadata metadata_v1 = db()->GetMetadata(kKey1);
- ASSERT_TRUE(type_processor()->IsEntityUnsynced(kKey1));
- const base::Time ctime = type_processor()->GetEntityCreationTime(kKey1);
- ASSERT_EQ(ctime, type_processor()->GetEntityModificationTime(kKey1));
- ASSERT_FALSE(ctime.is_null());
- // Make sure the clock advances.
- base::PlatformThread::Sleep(base::Milliseconds(1));
- ASSERT_NE(ctime, base::Time::Now());
- WritePrefItem(bridge(), kKey1, kValue2);
- EXPECT_EQ(1U, db()->metadata_count());
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}, {GetPrefHash(kKey1)}});
- EXPECT_TRUE(type_processor()->IsEntityUnsynced(kKey1));
- EXPECT_EQ(ctime, type_processor()->GetEntityCreationTime(kKey1));
- const base::Time mtime = type_processor()->GetEntityModificationTime(kKey1);
- EXPECT_NE(mtime, ctime);
- const CommitRequestData* request_data_v2 =
- worker()->GetLatestPendingCommitForHash(GetPrefHash(kKey1));
- ASSERT_TRUE(request_data_v2);
- const EntityData& data_v2 = *request_data_v2->entity;
- const EntityMetadata metadata_v2 = db()->GetMetadata(kKey1);
- // Test some of the relations between old and new commit requests.
- EXPECT_GT(request_data_v2->sequence_number, request_data_v1->sequence_number);
- EXPECT_EQ(data_v1.specifics.preference().value(), kValue1);
- // Perform a thorough examination of the update-generated request.
- EXPECT_EQ(kUncommittedVersion, request_data_v2->base_version);
- EXPECT_TRUE(data_v2.id.empty());
- EXPECT_EQ(ctime, data_v2.creation_time);
- EXPECT_EQ(mtime, data_v2.modification_time);
- EXPECT_EQ(kKey1, data_v2.name);
- EXPECT_FALSE(data_v2.is_deleted());
- EXPECT_EQ(kKey1, data_v2.specifics.preference().name());
- EXPECT_EQ(kValue2, data_v2.specifics.preference().value());
- // Perform a thorough examination of the local sync metadata.
- EXPECT_FALSE(metadata_v1.has_server_id());
- EXPECT_FALSE(metadata_v1.is_deleted());
- EXPECT_EQ(1, metadata_v1.sequence_number());
- EXPECT_EQ(0, metadata_v1.acked_sequence_number());
- EXPECT_EQ(kUncommittedVersion, metadata_v1.server_version());
- EXPECT_FALSE(metadata_v2.has_server_id());
- EXPECT_FALSE(metadata_v2.is_deleted());
- EXPECT_EQ(2, metadata_v2.sequence_number());
- EXPECT_EQ(0, metadata_v2.acked_sequence_number());
- EXPECT_EQ(kUncommittedVersion, metadata_v2.server_version());
- EXPECT_EQ(metadata_v1.client_tag_hash(), metadata_v2.client_tag_hash());
- EXPECT_NE(metadata_v1.specifics_hash(), metadata_v2.specifics_hash());
- }
- // Tests that a local update that doesn't change specifics doesn't generate a
- // commit request.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldIgnoreRedundantLocalUpdate) {
- InitializeToReadyState();
- WritePrefItem(bridge(), kKey1, kValue1);
- ASSERT_EQ(1U, db()->metadata_count());
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}});
- const base::Time ctime = type_processor()->GetEntityCreationTime(kKey1);
- const base::Time mtime = type_processor()->GetEntityModificationTime(kKey1);
- ASSERT_FALSE(ctime.is_null());
- ASSERT_FALSE(mtime.is_null());
- WritePrefItem(bridge(), kKey1, kValue1);
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}});
- EXPECT_EQ(ctime, type_processor()->GetEntityCreationTime(kKey1));
- EXPECT_EQ(mtime, type_processor()->GetEntityModificationTime(kKey1));
- }
- // Test that an error applying changes from a server update is
- // propagated to the error handler.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldReportErrorApplyingUpdate) {
- InitializeToReadyState();
- bridge()->ErrorOnNextCall();
- ExpectError(
- ClientTagBasedModelTypeProcessor::ErrorSite::kApplyIncrementalUpdates);
- worker()->UpdateFromServer(GetPrefHash(kKey1),
- GeneratePrefSpecifics(kKey1, kValue1));
- }
- // Tests locally deleting an acknowledged item.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldCommitLocalDeletion) {
- InitializeToReadyState();
- WriteItemAndAck(kKey1, kValue1);
- EXPECT_EQ(0U, worker()->GetNumPendingCommits());
- const EntityMetadata metadata_v1 = db()->GetMetadata(kKey1);
- EXPECT_FALSE(metadata_v1.is_deleted());
- EXPECT_EQ(1, metadata_v1.sequence_number());
- EXPECT_EQ(1, metadata_v1.acked_sequence_number());
- EXPECT_EQ(1, metadata_v1.server_version());
- bridge()->DeleteItem(kKey1);
- EXPECT_EQ(0U, db()->data_count());
- // Metadata is not removed until the commit response comes back.
- EXPECT_EQ(1U, db()->metadata_count());
- EXPECT_EQ(1U, ProcessorEntityCount());
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}});
- const EntityMetadata metadata_v2 = db()->GetMetadata(kKey1);
- EXPECT_TRUE(metadata_v2.is_deleted());
- EXPECT_EQ(2, metadata_v2.sequence_number());
- EXPECT_EQ(1, metadata_v2.acked_sequence_number());
- EXPECT_EQ(1, metadata_v2.server_version());
- // Ack the delete and check that the metadata is cleared.
- worker()->AckOnePendingCommit();
- EXPECT_EQ(0U, db()->metadata_count());
- EXPECT_EQ(0U, ProcessorEntityCount());
- // Create item again.
- WriteItemAndAck(kKey1, kValue1);
- const EntityMetadata metadata_v3 = db()->GetMetadata(kKey1);
- EXPECT_FALSE(metadata_v3.is_deleted());
- EXPECT_EQ(1, metadata_v3.sequence_number());
- EXPECT_EQ(1, metadata_v3.acked_sequence_number());
- EXPECT_EQ(3, metadata_v3.server_version());
- }
- // Tests that item created and deleted before sync cycle doesn't get committed.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldNotCommitLocalDeletionOfUncommittedEntity) {
- InitializeToMetadataLoaded();
- WritePrefItem(bridge(), kKey1, kValue1);
- bridge()->DeleteItem(kKey1);
- OnSyncStarting();
- EXPECT_EQ(0U, worker()->GetNumPendingCommits());
- }
- // Tests creating and deleting an item locally before receiving a commit
- // response, then getting the commit responses.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldHandleLocalDeletionDuringLocalCreationCommit) {
- InitializeToReadyState();
- WritePrefItem(bridge(), kKey1, kValue1);
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}});
- const CommitRequestData* data_v1 =
- worker()->GetLatestPendingCommitForHash(GetPrefHash(kKey1));
- ASSERT_TRUE(data_v1);
- const EntityMetadata metadata_v1 = db()->GetMetadata(kKey1);
- EXPECT_FALSE(metadata_v1.is_deleted());
- EXPECT_EQ(1, metadata_v1.sequence_number());
- EXPECT_EQ(0, metadata_v1.acked_sequence_number());
- EXPECT_EQ(kUncommittedVersion, metadata_v1.server_version());
- bridge()->DeleteItem(kKey1);
- EXPECT_EQ(0U, db()->data_count());
- EXPECT_EQ(1U, db()->metadata_count());
- EXPECT_EQ(1U, ProcessorEntityCount());
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}, {GetPrefHash(kKey1)}});
- const CommitRequestData* data_v2 =
- worker()->GetLatestPendingCommitForHash(GetPrefHash(kKey1));
- ASSERT_TRUE(data_v2);
- EXPECT_GT(data_v2->sequence_number, data_v1->sequence_number);
- EXPECT_TRUE(data_v2->entity->id.empty());
- EXPECT_EQ(kUncommittedVersion, data_v2->base_version);
- EXPECT_TRUE(data_v2->entity->is_deleted());
- const EntityMetadata metadata_v2 = db()->GetMetadata(kKey1);
- EXPECT_TRUE(metadata_v2.is_deleted());
- EXPECT_EQ(2, metadata_v2.sequence_number());
- EXPECT_EQ(0, metadata_v2.acked_sequence_number());
- EXPECT_EQ(kUncommittedVersion, metadata_v2.server_version());
- // A response for the first commit doesn't change much.
- worker()->AckOnePendingCommit();
- EXPECT_EQ(0U, db()->data_count());
- EXPECT_EQ(1U, db()->metadata_count());
- EXPECT_EQ(1U, ProcessorEntityCount());
- const EntityMetadata metadata_v3 = db()->GetMetadata(kKey1);
- EXPECT_TRUE(metadata_v3.is_deleted());
- EXPECT_EQ(2, metadata_v3.sequence_number());
- EXPECT_EQ(1, metadata_v3.acked_sequence_number());
- EXPECT_EQ(1, metadata_v3.server_version());
- worker()->AckOnePendingCommit();
- // The delete was acked so the metadata should now be cleared.
- EXPECT_EQ(0U, db()->metadata_count());
- EXPECT_EQ(0U, ProcessorEntityCount());
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldProcessRemoteDeletion) {
- InitializeToReadyState();
- WriteItemAndAck(kKey1, kValue1);
- EXPECT_EQ(1U, ProcessorEntityCount());
- EXPECT_EQ(1U, db()->metadata_count());
- EXPECT_EQ(1U, db()->data_count());
- EXPECT_EQ(0U, worker()->GetNumPendingCommits());
- worker()->TombstoneFromServer(GetPrefHash(kKey1));
- // Delete from server should clear the data and all the metadata.
- EXPECT_EQ(0U, db()->data_count());
- EXPECT_EQ(0U, db()->metadata_count());
- EXPECT_EQ(0U, ProcessorEntityCount());
- EXPECT_EQ(0U, worker()->GetNumPendingCommits());
- // Create item again.
- WriteItemAndAck(kKey1, kValue1);
- const EntityMetadata metadata = db()->GetMetadata(kKey1);
- EXPECT_FALSE(metadata.is_deleted());
- EXPECT_EQ(1, metadata.sequence_number());
- EXPECT_EQ(1, metadata.acked_sequence_number());
- EXPECT_EQ(3, metadata.server_version());
- }
- // Deletes an item we've never seen before.
- // Should have no effect and not crash.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldIgnoreLocalDeletionOfUnknownEntity) {
- InitializeToReadyState();
- bridge()->DeleteItem(kKey1);
- EXPECT_EQ(0U, db()->data_count());
- EXPECT_EQ(0U, db()->metadata_count());
- EXPECT_EQ(0U, ProcessorEntityCount());
- EXPECT_EQ(0U, worker()->GetNumPendingCommits());
- }
- // Tests that after committing entity fails, processor includes this entity in
- // consecutive commits.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldRetryCommitAfterServerError) {
- InitializeToReadyState();
- WritePrefItem(bridge(), kKey1, kValue1);
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}});
- // Entity is sent to server. Processor shouldn't include it in local changes.
- CommitRequestDataList commit_request;
- type_processor()->GetLocalChanges(
- INT_MAX, base::BindOnce(&CaptureCommitRequest, &commit_request));
- EXPECT_TRUE(commit_request.empty());
- // Fail commit from worker side indicating this entity was not committed.
- // Processor should include it in consecutive GetLocalChanges responses.
- worker()->FailOneCommit();
- type_processor()->GetLocalChanges(
- INT_MAX, base::BindOnce(&CaptureCommitRequest, &commit_request));
- OnCommitDataLoaded();
- EXPECT_EQ(1U, commit_request.size());
- EXPECT_EQ(GetPrefHash(kKey1), commit_request[0]->entity->client_tag_hash);
- }
- // Tests that after committing entity fails, processor includes this entity in
- // consecutive commits. This test differs from the above one for the case when
- // there is an HTTP error.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldRetryCommitAfterFullCommitFailure) {
- InitializeToReadyState();
- bridge()->EnableRetriesOnCommitFailure();
- WritePrefItem(bridge(), kKey1, kValue1);
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}});
- // Entity is sent to server. Processor shouldn't include it in local changes.
- CommitRequestDataList commit_request;
- type_processor()->GetLocalChanges(
- INT_MAX, base::BindOnce(&CaptureCommitRequest, &commit_request));
- EXPECT_TRUE(commit_request.empty());
- // Fail commit from worker side indicating this entity was not committed.
- // Processor should include it in consecutive GetLocalChanges responses.
- worker()->FailFullCommitRequest();
- type_processor()->GetLocalChanges(
- INT_MAX, base::BindOnce(&CaptureCommitRequest, &commit_request));
- OnCommitDataLoaded();
- ASSERT_EQ(1U, commit_request.size());
- EXPECT_EQ(GetPrefHash(kKey1), commit_request[0]->entity->client_tag_hash);
- }
- // Tests that GetLocalChanges honors max_entries parameter.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldTruncateLocalChangesToMaxSize) {
- InitializeToMetadataLoaded();
- WritePrefItem(bridge(), kKey1, kValue1);
- WritePrefItem(bridge(), kKey2, kValue2);
- // Reqeust at most one intity per batch, ensure that only one was returned.
- CommitRequestDataList commit_request;
- type_processor()->GetLocalChanges(
- 1, base::BindOnce(&CaptureCommitRequest, &commit_request));
- EXPECT_EQ(1U, commit_request.size());
- }
- // Creates two different sync items.
- // Verifies that the second has no effect on the first.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldHandleTwoIndependentItems) {
- InitializeToReadyState();
- EXPECT_EQ(0U, worker()->GetNumPendingCommits());
- WritePrefItem(bridge(), kKey1, kValue1);
- EXPECT_EQ(1U, db()->data_count());
- EXPECT_EQ(1U, db()->metadata_count());
- const EntityMetadata metadata1 = db()->GetMetadata(kKey1);
- // There should be one commit request for this item only.
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}});
- WritePrefItem(bridge(), kKey2, kValue2);
- EXPECT_EQ(2U, db()->data_count());
- EXPECT_EQ(2U, db()->metadata_count());
- const EntityMetadata metadata2 = db()->GetMetadata(kKey2);
- // The second write should trigger another single-item commit request.
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}, {GetPrefHash(kKey2)}});
- EXPECT_FALSE(metadata1.is_deleted());
- EXPECT_EQ(1, metadata1.sequence_number());
- EXPECT_EQ(0, metadata1.acked_sequence_number());
- EXPECT_EQ(kUncommittedVersion, metadata1.server_version());
- EXPECT_FALSE(metadata2.is_deleted());
- EXPECT_EQ(1, metadata2.sequence_number());
- EXPECT_EQ(0, metadata2.acked_sequence_number());
- EXPECT_EQ(kUncommittedVersion, metadata2.server_version());
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldNotTreatMatchingChangesAsConflict) {
- InitializeToReadyState();
- EntitySpecifics specifics = WritePrefItem(bridge(), kKey1, kValue1);
- EXPECT_EQ(1U, db()->data_change_count());
- EXPECT_EQ(kValue1, GetPrefValue(db()->GetData(kKey1)));
- EXPECT_EQ(1U, db()->metadata_change_count());
- EXPECT_EQ(kUncommittedVersion, db()->GetMetadata(kKey1).server_version());
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}});
- worker()->VerifyNthPendingCommit(0, {GetPrefHash(kKey1)}, {specifics});
- // Changes match doesn't call ResolveConflict.
- worker()->UpdateFromServer(GetPrefHash(kKey1), specifics);
- // Updated metadata but not data; no new commit request.
- EXPECT_EQ(1U, db()->data_change_count());
- EXPECT_EQ(1, db()->GetMetadata(kKey1).server_version());
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}});
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldResolveConflictToLocalVersion) {
- InitializeToReadyState();
- // WriteAndAck entity to get id from the server.
- WriteItemAndAck(kKey1, kValue1);
- bridge()->SetConflictResolution(ConflictResolution::kUseLocal);
- // Change value locally and at the same time simulate conflicting update from
- // server.
- EntitySpecifics specifics2 = WritePrefItem(bridge(), kKey1, kValue2);
- worker()->UpdateFromServer(GetPrefHash(kKey1),
- GeneratePrefSpecifics(kKey1, kValue3));
- OnCommitDataLoaded();
- // Updated metadata but not data; new commit request.
- EXPECT_EQ(2U, db()->data_change_count());
- EXPECT_EQ(4U, db()->metadata_change_count());
- EXPECT_EQ(2, db()->GetMetadata(kKey1).server_version());
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}, {GetPrefHash(kKey1)}});
- worker()->VerifyNthPendingCommit(1, {GetPrefHash(kKey1)}, {specifics2});
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldResolveConflictToLocalUndeletion) {
- InitializeToReadyState();
- ASSERT_EQ(0U, worker()->GetNumPendingCommits());
- WritePrefItem(bridge(), kKey1, kValue1);
- ASSERT_EQ(1U, worker()->GetNumPendingCommits());
- ASSERT_TRUE(worker()->GetLatestPendingCommitForHash(GetPrefHash(kKey1)));
- ASSERT_TRUE(worker()
- ->GetLatestPendingCommitForHash(GetPrefHash(kKey1))
- ->entity->id.empty());
- // The update from the server should be mostly ignored because local wins, but
- // the server ID should be updated.
- bridge()->SetConflictResolution(ConflictResolution::kUseLocal);
- worker()->UpdateFromServer(GetPrefHash(kKey1),
- GeneratePrefSpecifics(kKey1, kValue3));
- OnCommitDataLoaded();
- // In this test setup, the processor's nudge for commit immediately pulls
- // updates from the processor and list them as pending commits, so we should
- // see two commits at this point.
- EXPECT_EQ(2U, worker()->GetNumPendingCommits());
- // Verify the commit request this operation has triggered.
- const CommitRequestData* tag1_request_data =
- worker()->GetLatestPendingCommitForHash(GetPrefHash(kKey1));
- ASSERT_TRUE(tag1_request_data);
- const EntityData& tag1_data = *tag1_request_data->entity;
- EXPECT_EQ(1, tag1_request_data->base_version);
- EXPECT_FALSE(tag1_data.id.empty());
- EXPECT_FALSE(tag1_data.creation_time.is_null());
- EXPECT_FALSE(tag1_data.modification_time.is_null());
- EXPECT_EQ(kKey1, tag1_data.name);
- EXPECT_FALSE(tag1_data.is_deleted());
- EXPECT_EQ(kKey1, tag1_data.specifics.preference().name());
- EXPECT_EQ(kValue1, tag1_data.specifics.preference().value());
- EXPECT_EQ(1U, db()->metadata_count());
- const EntityMetadata metadata = db()->GetMetadata(kKey1);
- EXPECT_TRUE(metadata.has_client_tag_hash());
- EXPECT_TRUE(metadata.has_server_id());
- EXPECT_FALSE(metadata.is_deleted());
- EXPECT_EQ(1, metadata.sequence_number());
- EXPECT_EQ(0, metadata.acked_sequence_number());
- EXPECT_EQ(1, metadata.server_version());
- EXPECT_TRUE(metadata.has_creation_time());
- EXPECT_TRUE(metadata.has_modification_time());
- EXPECT_TRUE(metadata.has_specifics_hash());
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldResolveConflictToRemoteUndeletion) {
- InitializeToReadyState();
- WriteItemAndAck(kKey1, kValue1);
- ASSERT_EQ(0U, worker()->GetNumPendingCommits());
- bridge()->DeleteItem(kKey1);
- ASSERT_EQ(1U, worker()->GetNumPendingCommits());
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}});
- ASSERT_TRUE(worker()->GetLatestPendingCommitForHash(GetPrefHash(kKey1)));
- ASSERT_TRUE(worker()
- ->GetLatestPendingCommitForHash(GetPrefHash(kKey1))
- ->entity->is_deleted());
- ASSERT_EQ(2U, db()->data_change_count());
- ASSERT_EQ(3U, db()->metadata_change_count());
- ASSERT_TRUE(type_processor()->IsTrackingEntityForTest(kKey1));
- worker()->UpdateFromServer(GetPrefHash(kKey1),
- GeneratePrefSpecifics(kKey1, kValue2));
- // Updated client data and metadata; no new commit request.
- EXPECT_TRUE(type_processor()->IsTrackingEntityForTest(kKey1));
- EXPECT_EQ(3U, db()->data_change_count());
- EXPECT_EQ(kValue2, GetPrefValue(db()->GetData(kKey1)));
- EXPECT_EQ(4U, db()->metadata_change_count());
- EXPECT_EQ(2, db()->GetMetadata(kKey1).server_version());
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}});
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldResolveConflictToRemoteUndeletionWithUpdateStorageKey) {
- bridge()->SetSupportsGetStorageKey(false);
- InitializeToReadyState();
- WriteItemAndAck(kKey1, kValue1);
- ASSERT_EQ(0U, worker()->GetNumPendingCommits());
- bridge()->DeleteItem(kKey1);
- ASSERT_EQ(1U, worker()->GetNumPendingCommits());
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}});
- ASSERT_TRUE(worker()->GetLatestPendingCommitForHash(GetPrefHash(kKey1)));
- ASSERT_TRUE(worker()
- ->GetLatestPendingCommitForHash(GetPrefHash(kKey1))
- ->entity->is_deleted());
- ASSERT_EQ(2U, db()->data_change_count());
- ASSERT_EQ(3U, db()->metadata_change_count());
- ASSERT_TRUE(type_processor()->IsTrackingEntityForTest(kKey1));
- worker()->UpdateFromServer(GetPrefHash(kKey1),
- GeneratePrefSpecifics(kKey1, kValue2));
- // A new storage key should have been generated, which should replace the
- // previous when it comes to storing data and metadata.
- const std::string new_storage_key = bridge()->GetLastGeneratedStorageKey();
- ASSERT_NE(kKey1, new_storage_key);
- EXPECT_TRUE(db()->HasData(new_storage_key));
- EXPECT_TRUE(db()->HasMetadata(new_storage_key));
- EXPECT_TRUE(type_processor()->IsTrackingEntityForTest(new_storage_key));
- EXPECT_FALSE(db()->HasData(kKey1));
- EXPECT_FALSE(db()->HasMetadata(kKey1));
- EXPECT_FALSE(type_processor()->IsTrackingEntityForTest(kKey1));
- // Updated client data and metadata; no new commit request.
- EXPECT_EQ(3U, db()->data_change_count());
- EXPECT_EQ(kValue2, GetPrefValue(db()->GetData(new_storage_key)));
- EXPECT_EQ(5U, db()->metadata_change_count());
- EXPECT_EQ(2, db()->GetMetadata(new_storage_key).server_version());
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}});
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldResolveConflictToRemoteVersion) {
- InitializeToReadyState();
- WritePrefItem(bridge(), kKey1, kValue1);
- bridge()->SetConflictResolution(ConflictResolution::kUseRemote);
- worker()->UpdateFromServer(GetPrefHash(kKey1),
- GeneratePrefSpecifics(kKey1, kValue2));
- // Updated client data and metadata; no new commit request.
- EXPECT_EQ(2U, db()->data_change_count());
- EXPECT_EQ(kValue2, GetPrefValue(db()->GetData(kKey1)));
- EXPECT_EQ(2U, db()->metadata_change_count());
- EXPECT_EQ(1, db()->GetMetadata(kKey1).server_version());
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}});
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldResolveConflictToRemoteDeletion) {
- InitializeToReadyState();
- WritePrefItem(bridge(), kKey1, kValue1);
- bridge()->SetConflictResolution(ConflictResolution::kUseRemote);
- worker()->TombstoneFromServer(GetPrefHash(kKey1));
- // Updated client data and metadata; no new commit request.
- EXPECT_EQ(0U, db()->data_count());
- EXPECT_EQ(0U, db()->metadata_count());
- EXPECT_EQ(2U, db()->data_change_count());
- EXPECT_EQ(2U, db()->metadata_change_count());
- }
- // Test proper handling of disconnect and reconnect.
- //
- // Creates items in various states of commit and verifies they re-attempt to
- // commit on reconnect.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldDisconnectAndReconnect) {
- InitializeToReadyState();
- // The first item is fully committed.
- WriteItemAndAck(kKey1, kValue1);
- // The second item has a commit request in progress.
- WritePrefItem(bridge(), kKey2, kValue2);
- EXPECT_TRUE(worker()->HasPendingCommitForHash(GetPrefHash(kKey2)));
- DisconnectSync();
- // The third item is added after stopping.
- WritePrefItem(bridge(), kKey3, kValue3);
- // Reconnect.
- OnSyncStarting();
- OnCommitDataLoaded();
- EXPECT_EQ(1U, worker()->GetNumPendingCommits());
- EXPECT_EQ(2U, worker()->GetNthPendingCommit(0).size());
- // The first item was already in sync.
- EXPECT_FALSE(worker()->HasPendingCommitForHash(GetPrefHash(kKey1)));
- // The second item's commit was interrupted and should be retried.
- EXPECT_TRUE(worker()->HasPendingCommitForHash(GetPrefHash(kKey2)));
- // The third item's commit was not started until the reconnect.
- EXPECT_TRUE(worker()->HasPendingCommitForHash(GetPrefHash(kKey3)));
- }
- // Test proper handling of stop (without disabling sync) and re-enable.
- //
- // Creates items in various states of commit and verifies they do NOT attempt to
- // commit on re-enable.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldStopAndKeepMetadata) {
- InitializeToReadyState();
- // The first item is fully committed.
- WriteItemAndAck(kKey1, kValue1);
- // The second item has a commit request in progress.
- WritePrefItem(bridge(), kKey2, kValue2);
- EXPECT_TRUE(worker()->HasPendingCommitForHash(GetPrefHash(kKey2)));
- type_processor()->OnSyncStopping(KEEP_METADATA);
- EXPECT_TRUE(type_processor()->IsTrackingMetadata());
- // The third item is added after disable.
- WritePrefItem(bridge(), kKey3, kValue3);
- // Now we re-enable.
- OnSyncStarting();
- worker()->UpdateFromServer();
- // Once we're ready to commit, only the newest items should be committed.
- worker()->VerifyPendingCommits({{GetPrefHash(kKey3)}});
- }
- // Test proper handling of disable and re-enable.
- //
- // Creates items in various states of commit and verifies they re-attempt to
- // commit on re-enable.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldStopAndClearMetadata) {
- InitializeToReadyState();
- // The first item is fully committed.
- WriteItemAndAck(kKey1, kValue1);
- // The second item has a commit request in progress.
- WritePrefItem(bridge(), kKey2, kValue2);
- EXPECT_TRUE(worker()->HasPendingCommitForHash(GetPrefHash(kKey2)));
- type_processor()->OnSyncStopping(CLEAR_METADATA);
- EXPECT_FALSE(type_processor()->IsTrackingMetadata());
- // The third item is added after disable.
- WritePrefItem(bridge(), kKey3, kValue3);
- // Now we re-enable.
- OnSyncStarting();
- worker()->UpdateFromServer();
- EXPECT_TRUE(type_processor()->IsTrackingMetadata());
- // Once we're ready to commit, all three local items should consider
- // themselves uncommitted and pending for commit.
- worker()->VerifyPendingCommits(
- {{GetPrefHash(kKey1)}, {GetPrefHash(kKey2)}, {GetPrefHash(kKey3)}});
- }
- // Test proper handling of disable-sync before initial sync done.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldNotClearBridgeMetadataPriorToMergeSyncData) {
- // Populate the bridge's metadata with some non-empty values for us to later
- // check that it hasn't been cleared.
- const std::string kTestEncryptionKeyName = "TestEncryptionKey";
- ModelTypeState model_type_state(db()->model_type_state());
- model_type_state.set_encryption_key_name(kTestEncryptionKeyName);
- bridge()->mutable_db()->set_model_type_state(model_type_state);
- ModelReadyToSync();
- OnSyncStarting();
- ASSERT_FALSE(type_processor()->IsTrackingMetadata());
- type_processor()->OnSyncStopping(CLEAR_METADATA);
- EXPECT_FALSE(type_processor()->IsTrackingMetadata());
- EXPECT_EQ(kTestEncryptionKeyName,
- db()->model_type_state().encryption_key_name());
- }
- // Test re-encrypt everything when desired encryption key changes.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldReencryptCommitsWithNewKey) {
- InitializeToReadyState();
- // Commit an item.
- EntitySpecifics specifics1 = WriteItemAndAck(kKey1, kValue1);
- // Create another item and don't wait for its commit response.
- EntitySpecifics specifics2 = WritePrefItem(bridge(), kKey2, kValue2);
- worker()->VerifyPendingCommits({{GetPrefHash(kKey2)}});
- EXPECT_EQ(1U, db()->GetMetadata(kKey1).sequence_number());
- EXPECT_EQ(1U, db()->GetMetadata(kKey2).sequence_number());
- // Receive notice that the account's desired encryption key has changed.
- worker()->UpdateWithEncryptionKey("k1");
- // No pending commits because Tag 1 requires data load.
- ASSERT_EQ(1U, worker()->GetNumPendingCommits());
- // Tag 1 needs to go to the store to load its data before recommitting.
- OnCommitDataLoaded();
- // All data are in memory now.
- ASSERT_EQ(2U, worker()->GetNumPendingCommits());
- worker()->VerifyNthPendingCommit(1, {GetPrefHash(kKey1), GetPrefHash(kKey2)},
- {specifics1, specifics2});
- // Sequence numbers in the store are updated.
- EXPECT_EQ(2U, db()->GetMetadata(kKey1).sequence_number());
- EXPECT_EQ(2U, db()->GetMetadata(kKey2).sequence_number());
- }
- // Test that an error loading pending commit data for re-encryption is
- // propagated to the error handler.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldHandleErrorWhileReencrypting) {
- InitializeToReadyState();
- WriteItemAndAck(kKey1, kValue1);
- bridge()->ErrorOnNextCall();
- ExpectError(
- ClientTagBasedModelTypeProcessor::ErrorSite::kApplyIncrementalUpdates);
- worker()->UpdateWithEncryptionKey("k1");
- }
- // Test receipt of updates with new and old keys.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldReencryptUpdatesWithNewKey) {
- InitializeToReadyState();
- // Receive an unencrypted update.
- worker()->UpdateFromServer(GetPrefHash(kKey1),
- GeneratePrefSpecifics(kKey1, kValue1));
- ASSERT_EQ(0U, worker()->GetNumPendingCommits());
- UpdateResponseDataList update;
- // Receive an entity with old encryption as part of the update.
- update.push_back(worker()->GenerateUpdateData(
- GetPrefHash(kKey2), GeneratePrefSpecifics(kKey2, kValue2), 1, "k1"));
- // Receive an entity with up-to-date encryption as part of the update.
- update.push_back(worker()->GenerateUpdateData(
- GetPrefHash(kKey3), GeneratePrefSpecifics(kKey3, kValue3), 1, "k2"));
- // Set desired encryption key to k2 to force updates to some items.
- worker()->UpdateWithEncryptionKey("k2", std::move(update));
- OnCommitDataLoaded();
- // kKey1 needed data so once that's loaded, kKey1 and kKey2 are queued for
- // commit.
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1), GetPrefHash(kKey2)}});
- // Receive a separate update that was encrypted with key k1.
- worker()->UpdateFromServer(GetPrefHash(kKey4),
- GeneratePrefSpecifics(kKey4, kValue1), 1, "k1");
- OnCommitDataLoaded();
- // Receipt of updates encrypted with old key also forces a re-encrypt commit.
- worker()->VerifyPendingCommits(
- {{GetPrefHash(kKey1), GetPrefHash(kKey2)}, {GetPrefHash(kKey4)}});
- // Receive an update that was encrypted with key k2.
- worker()->UpdateFromServer(GetPrefHash(kKey5),
- GeneratePrefSpecifics(kKey5, kValue1), 1, "k2");
- // That was the correct key, so no re-encryption is required.
- worker()->VerifyPendingCommits(
- {{GetPrefHash(kKey1), GetPrefHash(kKey2)}, {GetPrefHash(kKey4)}});
- }
- // Test that re-encrypting enqueues the right data for kUseLocal conflicts.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldResolveConflictToLocalDuringReencryption) {
- InitializeToReadyState();
- // WriteAndAck entity to get id from the server.
- WriteItemAndAck(kKey1, kValue1);
- worker()->UpdateWithEncryptionKey("k1");
- OnCommitDataLoaded();
- EntitySpecifics specifics = WritePrefItem(bridge(), kKey1, kValue2);
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}, {GetPrefHash(kKey1)}});
- bridge()->SetConflictResolution(ConflictResolution::kUseLocal);
- // Unencrypted update needs to be re-commited with key k1.
- worker()->UpdateFromServer(GetPrefHash(kKey1),
- GeneratePrefSpecifics(kKey1, kValue3), 1, "");
- OnCommitDataLoaded();
- // Ensure the re-commit has the correct value.
- EXPECT_EQ(3U, worker()->GetNumPendingCommits());
- worker()->VerifyNthPendingCommit(2, {GetPrefHash(kKey1)}, {specifics});
- EXPECT_EQ(kValue2, GetPrefValue(db()->GetData(kKey1)));
- }
- // Test that re-encrypting enqueues the right data for kUseRemote conflicts.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldResolveConflictToRemoteDuringReencryption) {
- InitializeToReadyState();
- worker()->UpdateWithEncryptionKey("k1");
- WritePrefItem(bridge(), kKey1, kValue1);
- bridge()->SetConflictResolution(ConflictResolution::kUseRemote);
- // Unencrypted update needs to be re-commited with key k1.
- EntitySpecifics specifics = GeneratePrefSpecifics(kKey1, kValue2);
- worker()->UpdateFromServer(GetPrefHash(kKey1), specifics, 1, "");
- OnCommitDataLoaded();
- // Ensure the re-commit has the correct value.
- EXPECT_EQ(2U, worker()->GetNumPendingCommits());
- worker()->VerifyNthPendingCommit(1, {GetPrefHash(kKey1)}, {specifics});
- EXPECT_EQ(kValue2, GetPrefValue(db()->GetData(kKey1)));
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldHandleConflictWhileLoadingForReencryption) {
- InitializeToReadyState();
- // Create item and ack so its data is no longer cached.
- WriteItemAndAck(kKey1, kValue1);
- // Update key so that it needs to fetch data to re-commit.
- worker()->UpdateWithEncryptionKey("k1");
- EXPECT_EQ(0U, worker()->GetNumPendingCommits());
- OnCommitDataLoaded();
- // Unencrypted update needs to be re-commited with key k1.
- EntitySpecifics specifics = GeneratePrefSpecifics(kKey1, kValue2);
- worker()->UpdateFromServer(GetPrefHash(kKey1), specifics, 1, "");
- OnCommitDataLoaded();
- // Ensure the re-commit has the correct value.
- EXPECT_EQ(2U, worker()->GetNumPendingCommits());
- worker()->VerifyNthPendingCommit(1, {GetPrefHash(kKey1)}, {specifics});
- EXPECT_EQ(kValue2, GetPrefValue(db()->GetData(kKey1)));
- }
- // Tests that a real remote change wins over a local encryption-only change.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldIgnoreLocalEncryptionChange) {
- InitializeToReadyState();
- EntitySpecifics specifics = WriteItemAndAck(kKey1, kValue1);
- worker()->UpdateWithEncryptionKey("k1");
- OnCommitDataLoaded();
- EXPECT_EQ(1U, worker()->GetNumPendingCommits());
- worker()->VerifyNthPendingCommit(0, {GetPrefHash(kKey1)}, {specifics});
- worker()->UpdateFromServer(GetPrefHash(kKey1),
- GeneratePrefSpecifics(kKey1, kValue2));
- EXPECT_EQ(1U, worker()->GetNumPendingCommits());
- }
- // Tests that updates without client tags get dropped.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldDropRemoteUpdatesWithoutClientTags) {
- InitializeToReadyState();
- UpdateResponseDataList updates;
- updates.push_back(worker()->GenerateUpdateData(
- ClientTagHash(), GeneratePrefSpecifics(kKey1, kValue1), 1, "k1"));
- worker()->UpdateFromServer(std::move(updates));
- // Verify that the data wasn't actually stored.
- EXPECT_EQ(0U, db()->metadata_count());
- EXPECT_EQ(0U, db()->data_count());
- }
- // Tests that initial updates for transport-only mode (called "ephemeral
- // storage" for historical reasons) result in reporting setup duration.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldReportEphemeralConfigurationTime) {
- InitializeToMetadataLoaded(/*initial_sync_done=*/false);
- OnSyncStarting(kDefaultAuthenticatedAccountId, kCacheGuid,
- SyncMode::kTransportOnly);
- base::HistogramTester histogram_tester;
- UpdateResponseDataList updates;
- updates.push_back(worker()->GenerateUpdateData(
- ClientTagHash(), GeneratePrefSpecifics(kKey1, kValue1), 1, "k1"));
- worker()->UpdateFromServer(std::move(updates));
- ASSERT_EQ(1, bridge()->merge_call_count());
- // The duration should get recorded into the right histogram.
- histogram_tester.ExpectTotalCount(
- "Sync.ModelTypeConfigurationTime.Ephemeral.PREFERENCE",
- /*count=*/1);
- histogram_tester.ExpectTotalCount(
- "Sync.ModelTypeConfigurationTime.Persistent.PREFERENCE",
- /*count=*/0);
- }
- // Tests that initial updates for full-sync mode (called "persistent storage"
- // for historical reasons) do not result in reporting setup duration.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldReportPersistentConfigurationTime) {
- InitializeToMetadataLoaded(/*initial_sync_done=*/false);
- OnSyncStarting();
- base::HistogramTester histogram_tester;
- UpdateResponseDataList updates;
- updates.push_back(worker()->GenerateUpdateData(
- ClientTagHash(), GeneratePrefSpecifics(kKey1, kValue1), 1, "k1"));
- worker()->UpdateFromServer(std::move(updates));
- ASSERT_EQ(1, bridge()->merge_call_count());
- // The duration should get recorded into the right histogram.
- histogram_tester.ExpectTotalCount(
- "Sync.ModelTypeConfigurationTime.Ephemeral.PREFERENCE",
- /*count=*/0);
- histogram_tester.ExpectTotalCount(
- "Sync.ModelTypeConfigurationTime.Persistent.PREFERENCE",
- /*count=*/1);
- }
- class FullUpdateClientTagBasedModelTypeProcessorTest
- : public ClientTagBasedModelTypeProcessorTest {
- protected:
- bool SupportsIncrementalUpdates() override { return false; }
- };
- // Tests that ClientTagBasedModelTypeProcessor can do garbage collection by
- // version.
- // Garbage collection by version is used by the server to replace all data on
- // the client, and is implemented by calling MergeSyncData on the bridge.
- TEST_F(FullUpdateClientTagBasedModelTypeProcessorTest,
- ShouldApplyGarbageCollectionByVersionFullUpdate) {
- InitializeToReadyState();
- UpdateResponseDataList updates;
- updates.push_back(worker()->GenerateUpdateData(
- ClientTagHash(), GeneratePrefSpecifics(kKey1, kValue1), 1, "k1"));
- updates.push_back(worker()->GenerateUpdateData(
- ClientTagHash(), GeneratePrefSpecifics(kKey2, kValue2), 2, "k2"));
- // Create 2 entries, one is version 3, another is version 1.
- sync_pb::GarbageCollectionDirective garbage_collection_directive;
- garbage_collection_directive.set_version_watermark(1);
- worker()->UpdateWithGarbageCollection(std::move(updates),
- garbage_collection_directive);
- WriteItemAndAck(kKey1, kValue1);
- WriteItemAndAck(kKey2, kValue2);
- // Verify entries are created correctly.
- ASSERT_EQ(2U, ProcessorEntityCount());
- ASSERT_EQ(2U, db()->metadata_count());
- ASSERT_EQ(2U, db()->data_count());
- ASSERT_EQ(0U, worker()->GetNumPendingCommits());
- ASSERT_EQ(1, bridge()->merge_call_count());
- // Tell the client to delete all data.
- sync_pb::GarbageCollectionDirective new_directive;
- new_directive.set_version_watermark(2);
- worker()->UpdateWithGarbageCollection(new_directive);
- // Verify that merge is called on the bridge to replace the current sync data.
- EXPECT_EQ(2, bridge()->merge_call_count());
- // Verify that the processor cleared all metadata.
- EXPECT_EQ(0U, db()->metadata_count());
- EXPECT_EQ(0U, worker()->GetNumPendingCommits());
- }
- // Tests that full updates for transport-only mode (called "ephemeral storage"
- // for historical reasons) result in reporting setup duration.
- TEST_F(FullUpdateClientTagBasedModelTypeProcessorTest,
- ShouldReportEphemeralConfigurationTimeOnlyForFirstFullUpdate) {
- InitializeToMetadataLoaded(/*initial_sync_done=*/false);
- OnSyncStarting(kDefaultAuthenticatedAccountId, kCacheGuid,
- SyncMode::kTransportOnly);
- UpdateResponseDataList updates1;
- updates1.push_back(worker()->GenerateUpdateData(
- ClientTagHash(), GeneratePrefSpecifics(kKey1, kValue1), 1, "k1"));
- sync_pb::GarbageCollectionDirective garbage_collection_directive;
- garbage_collection_directive.set_version_watermark(1);
- {
- base::HistogramTester histogram_tester;
- worker()->UpdateWithGarbageCollection(std::move(updates1),
- garbage_collection_directive);
- ASSERT_EQ(1, bridge()->merge_call_count());
- // The duration should get recorded.
- histogram_tester.ExpectTotalCount(
- "Sync.ModelTypeConfigurationTime.Ephemeral.PREFERENCE",
- /*count=*/1);
- }
- {
- UpdateResponseDataList updates2;
- updates2.push_back(worker()->GenerateUpdateData(
- ClientTagHash(), GeneratePrefSpecifics(kKey1, kValue1), 1, "k1"));
- base::HistogramTester histogram_tester;
- // Send one more update with the same data.
- worker()->UpdateWithGarbageCollection(std::move(updates2),
- garbage_collection_directive);
- ASSERT_EQ(2, bridge()->merge_call_count());
- // The duration should not get recorded again.
- histogram_tester.ExpectTotalCount(
- "Sync.ModelTypeConfigurationTime.Ephemeral.PREFERENCE",
- /*count=*/0);
- }
- }
- // Tests that the processor reports an error for updates without a version GC
- // directive that are received for types that don't support incremental updates.
- TEST_F(FullUpdateClientTagBasedModelTypeProcessorTest,
- ShouldReportErrorForUnsupportedIncrementalUpdate) {
- InitializeToReadyState();
- ExpectError(ClientTagBasedModelTypeProcessor::ErrorSite::
- kSupportsIncrementalUpdatesMismatch);
- worker()->UpdateFromServer(GetPrefHash(kKey1),
- GeneratePrefSpecifics(kKey1, kValue1));
- }
- // Tests that empty updates without a version GC are processed for types that
- // don't support incremental updates. The only outcome if these updates should
- // be storing an updated progress marker.
- TEST_F(FullUpdateClientTagBasedModelTypeProcessorTest,
- ShouldProcessEmptyUpdate) {
- // Override the initial progress marker token so that we can check it gets
- // changed.
- sync_pb::ModelTypeState initial_model_type_state(db()->model_type_state());
- initial_model_type_state.mutable_progress_marker()->set_token("OLD");
- db()->set_model_type_state(initial_model_type_state);
- InitializeToReadyState();
- sync_pb::ModelTypeState new_model_type_state(db()->model_type_state());
- new_model_type_state.mutable_progress_marker()->set_token("NEW");
- worker()->UpdateModelTypeState(new_model_type_state);
- worker()->UpdateFromServer(UpdateResponseDataList());
- // Verify that the empty update was correctly passed into the bridge and that
- // it stored the updated progress marker.
- EXPECT_EQ(0, bridge()->merge_call_count());
- EXPECT_EQ(1, bridge()->apply_call_count());
- EXPECT_EQ("NEW", db()->model_type_state().progress_marker().token());
- }
- // Tests that the processor correctly handles an initial (non-empty) update
- // without any gc directives (as it happens in the migration to USS).
- TEST_F(FullUpdateClientTagBasedModelTypeProcessorTest,
- ShouldProcessInitialUpdate) {
- // Do not set any model type state to emulate that initial sync has not been
- // done yet.
- ModelReadyToSync();
- OnSyncStarting();
- worker()->UpdateFromServer(GetPrefHash(kKey1),
- GeneratePrefSpecifics(kKey1, kValue1));
- }
- // Tests that a real local change wins over a remote encryption-only change.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldIgnoreRemoteEncryption) {
- InitializeToReadyState();
- EntitySpecifics specifics1 = WriteItemAndAck(kKey1, kValue1);
- EntitySpecifics specifics2 = WritePrefItem(bridge(), kKey1, kValue2);
- UpdateResponseDataList update;
- update.push_back(
- worker()->GenerateUpdateData(GetPrefHash(kKey1), specifics1, 1, "k1"));
- worker()->UpdateWithEncryptionKey("k1", std::move(update));
- OnCommitDataLoaded();
- EXPECT_EQ(2U, worker()->GetNumPendingCommits());
- worker()->VerifyNthPendingCommit(1, {GetPrefHash(kKey1)}, {specifics2});
- }
- // Same as above but with two commit requests before one ack.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldIgnoreRemoteEncryptionInterleaved) {
- InitializeToReadyState();
- // WriteAndAck entity to get id from the server.
- WriteItemAndAck(kKey1, kValue1);
- EntitySpecifics specifics1 = WritePrefItem(bridge(), kKey1, kValue2);
- EntitySpecifics specifics2 = WritePrefItem(bridge(), kKey1, kValue3);
- worker()->AckOnePendingCommit();
- // kValue2 is now the base value.
- EXPECT_EQ(1U, worker()->GetNumPendingCommits());
- worker()->VerifyNthPendingCommit(0, {GetPrefHash(kKey1)}, {specifics2});
- UpdateResponseDataList update;
- update.push_back(
- worker()->GenerateUpdateData(GetPrefHash(kKey1), specifics1, 1, "k1"));
- worker()->UpdateWithEncryptionKey("k1", std::move(update));
- OnCommitDataLoaded();
- EXPECT_EQ(2U, worker()->GetNumPendingCommits());
- worker()->VerifyNthPendingCommit(1, {GetPrefHash(kKey1)}, {specifics2});
- }
- // Tests that UpdateStorageKey propagates storage key to ProcessorEntity
- // and updates corresponding entity's metadata in MetadataChangeList, and
- // UntrackEntity will remove corresponding ProcessorEntity and do not add
- // any entity's metadata into MetadataChangeList.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldUpdateStorageKey) {
- // Setup bridge to not support calls to GetStorageKey. This will cause
- // FakeModelTypeSyncBridge to call UpdateStorageKey for new entities and will
- // DCHECK if GetStorageKey gets called.
- bridge()->SetSupportsGetStorageKey(false);
- ModelReadyToSync();
- OnSyncStarting();
- // Initial update from server should be handled by MergeSyncData.
- UpdateResponseDataList updates;
- updates.push_back(worker()->GenerateUpdateData(
- GetPrefHash(kKey1), GeneratePrefSpecifics(kKey1, kValue1)));
- // Create update which will be ignored by bridge.
- updates.push_back(worker()->GenerateUpdateData(
- GetPrefHash(kKey3), GeneratePrefSpecifics(kKey3, kValue3)));
- bridge()->AddPrefValueToIgnore(kValue3);
- worker()->UpdateFromServer(std::move(updates));
- EXPECT_EQ(1, bridge()->merge_call_count());
- EXPECT_EQ(1U, ProcessorEntityCount());
- // Metadata should be written under a new storage key. This means that
- // UpdateStorageKey was called and value of storage key got propagated to
- // MetadataChangeList.
- const std::string storage_key1 = bridge()->GetLastGeneratedStorageKey();
- EXPECT_TRUE(db()->HasMetadata(storage_key1));
- EXPECT_EQ(1U, db()->metadata_count());
- EXPECT_EQ(0, bridge()->get_storage_key_call_count());
- // Local update should affect the same entity. This ensures that storage key
- // to client tag hash mapping was updated on the previous step.
- WritePrefItem(bridge(), storage_key1, kValue2);
- EXPECT_EQ(1U, ProcessorEntityCount());
- EXPECT_EQ(1U, db()->metadata_count());
- // Second update from server should be handled by ApplySyncChanges. Similarly
- // It should call UpdateStorageKey, not GetStorageKey.
- worker()->UpdateFromServer(GetPrefHash(kKey2),
- GeneratePrefSpecifics(kKey2, kValue2));
- EXPECT_EQ(1, bridge()->apply_call_count());
- const std::string storage_key2 = bridge()->GetLastGeneratedStorageKey();
- EXPECT_NE(storage_key1, storage_key2);
- EXPECT_TRUE(db()->HasMetadata(storage_key2));
- EXPECT_EQ(2U, db()->metadata_count());
- EXPECT_EQ(0, bridge()->get_storage_key_call_count());
- }
- // Tests that reencryption scenario works correctly for types that don't support
- // GetStorageKey(). When update from server delivers updated encryption key, all
- // entities should be reencrypted including new entity that just got received
- // from server.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldReencryptDatatypeWithoutStorageKeySupport) {
- bridge()->SetSupportsGetStorageKey(false);
- InitializeToReadyState();
- UpdateResponseDataList update;
- update.push_back(worker()->GenerateUpdateData(
- GetPrefHash(kKey1), GeneratePrefSpecifics(kKey1, kValue1), 1, "ek1"));
- worker()->UpdateWithEncryptionKey("ek2", std::move(update));
- OnCommitDataLoaded();
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}});
- }
- // Tests that UntrackEntity won't propagate storage key to
- // ProcessorEntity, and no entity's metadata are added into
- // MetadataChangeList.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldUntrackEntity) {
- // Setup bridge to not support calls to GetStorageKey. This will cause
- // FakeModelTypeSyncBridge to call UpdateStorageKey for new entities and will
- // DCHECK if GetStorageKey gets called.
- bridge()->SetSupportsGetStorageKey(false);
- bridge()->AddPrefValueToIgnore(kValue1);
- ModelReadyToSync();
- OnSyncStarting();
- // Initial update from server should be handled by MergeSyncData.
- worker()->UpdateFromServer(GetPrefHash(kKey1),
- GeneratePrefSpecifics(kKey1, kValue1));
- EXPECT_EQ(1, bridge()->merge_call_count());
- EXPECT_EQ(0U, ProcessorEntityCount());
- // Metadata should not be written under kUntrackKey1. This means that
- // UntrackEntity was called and corresponding ProcessorEntity is
- // removed and no storage key got propagated to MetadataChangeList.
- EXPECT_FALSE(db()->HasMetadata(kKey1));
- EXPECT_EQ(0U, db()->metadata_count());
- EXPECT_EQ(0, bridge()->get_storage_key_call_count());
- }
- // Tests that UntrackEntityForStorage won't propagate storage key to
- // ProcessorEntity, and no entity's metadata are added into
- // MetadataChangeList.
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldUntrackEntityForStorageKey) {
- InitializeToReadyState();
- WritePrefItem(bridge(), kKey1, kValue1);
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}});
- worker()->AckOnePendingCommit();
- // Check the processor tracks the entity.
- TypeEntitiesCount count(GetModelType());
- type_processor()->GetTypeEntitiesCountForDebugging(
- base::BindOnce(&CaptureTypeEntitiesCount, &count));
- ASSERT_EQ(1, count.non_tombstone_entities);
- ASSERT_NE(nullptr, GetEntityForStorageKey(kKey1));
- // The bridge deletes the data locally and does not want to sync the deletion.
- // It only untracks the entity.
- type_processor()->UntrackEntityForStorageKey(kKey1);
- // The deletion is not synced up.
- worker()->VerifyPendingCommits({});
- // The processor tracks no entity any more.
- type_processor()->GetTypeEntitiesCountForDebugging(
- base::BindOnce(&CaptureTypeEntitiesCount, &count));
- EXPECT_EQ(0, count.non_tombstone_entities);
- EXPECT_EQ(nullptr, GetEntityForStorageKey(kKey1));
- }
- // Tests that UntrackEntityForStorage does not crash if no such entity is being
- // tracked.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldIgnoreUntrackEntityForInexistentStorageKey) {
- InitializeToReadyState();
- // This should not crash for an unknown storage key and simply ignore the
- // call.
- type_processor()->UntrackEntityForStorageKey(kKey1);
- // No deletion is not synced up.
- worker()->VerifyPendingCommits({});
- // The processor tracks no entity.
- TypeEntitiesCount count(GetModelType());
- type_processor()->GetTypeEntitiesCountForDebugging(
- base::BindOnce(&CaptureTypeEntitiesCount, &count));
- EXPECT_EQ(0, count.non_tombstone_entities);
- EXPECT_EQ(nullptr, GetEntityForStorageKey(kKey1));
- }
- // Tests that UntrackEntityForClientTagHash won't propagate storage key to
- // ProcessorEntity, and no entity's metadata are added into MetadataChangeList.
- // This test is pretty same as ShouldUntrackEntityForStorageKey.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldUntrackEntityForClientTagHash) {
- InitializeToReadyState();
- WritePrefItem(bridge(), kKey1, kValue1);
- worker()->VerifyPendingCommits({{GetPrefHash(kKey1)}});
- worker()->AckOnePendingCommit();
- // Check the processor tracks the entity.
- TypeEntitiesCount count(GetModelType());
- type_processor()->GetTypeEntitiesCountForDebugging(
- base::BindOnce(&CaptureTypeEntitiesCount, &count));
- ASSERT_EQ(1, count.non_tombstone_entities);
- ASSERT_NE(nullptr, GetEntityForStorageKey(kKey1));
- // The bridge deletes the data locally and does not want to sync the deletion.
- // It only untracks the entity.
- type_processor()->UntrackEntityForClientTagHash(GetPrefHash(kKey1));
- // The deletion is not synced up.
- worker()->VerifyPendingCommits({});
- // The processor tracks no entity any more.
- type_processor()->GetTypeEntitiesCountForDebugging(
- base::BindOnce(&CaptureTypeEntitiesCount, &count));
- EXPECT_EQ(0, count.non_tombstone_entities);
- EXPECT_EQ(nullptr, GetEntityForStorageKey(kKey1));
- }
- // Tests that the processor reports an error for updates with a version GC
- // directive that are received for types that support incremental updates.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldNotApplyGarbageCollectionByVersion) {
- InitializeToReadyState();
- ExpectError(ClientTagBasedModelTypeProcessor::ErrorSite::
- kSupportsIncrementalUpdatesMismatch);
- sync_pb::GarbageCollectionDirective garbage_collection_directive;
- garbage_collection_directive.set_version_watermark(2);
- worker()->UpdateWithGarbageCollection(garbage_collection_directive);
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldDeleteMetadataWhenCacheGuidMismatch) {
- // Commit item.
- InitializeToReadyState();
- WriteItemAndAck(kKey1, kValue1);
- // Reset the processor to simulate a restart.
- ResetState(/*keep_db=*/true);
- // A new processor loads the metadata after changing the cache GUID.
- bridge()->SetInitialSyncDone(true);
- std::unique_ptr<MetadataBatch> metadata_batch = db()->CreateMetadataBatch();
- sync_pb::ModelTypeState model_type_state(metadata_batch->GetModelTypeState());
- model_type_state.set_cache_guid("WRONG_CACHE_GUID");
- metadata_batch->SetModelTypeState(model_type_state);
- type_processor()->ModelReadyToSync(std::move(metadata_batch));
- ASSERT_TRUE(type_processor()->IsModelReadyToSyncForTest());
- OnSyncStarting("DefaultAuthenticatedAccountId", "TestCacheGuid");
- // Model should still be ready to sync.
- ASSERT_TRUE(type_processor()->IsModelReadyToSyncForTest());
- // OnSyncStarting() should have completed.
- EXPECT_NE(nullptr, worker());
- // Upon a mismatch, metadata should have been cleared.
- EXPECT_EQ(0U, db()->metadata_count());
- EXPECT_FALSE(type_processor()->IsTrackingMetadata());
- // Initial update.
- worker()->UpdateFromServer();
- EXPECT_TRUE(type_processor()->IsTrackingMetadata());
- EXPECT_EQ("TestCacheGuid", type_processor()->TrackedCacheGuid());
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldDeleteMetadataWhenDataTypeIdMismatch) {
- // Commit item.
- InitializeToReadyState();
- WriteItemAndAck(kKey1, kValue1);
- // Reset the processor to simulate a restart.
- ResetState(/*keep_db=*/true);
- // A new processor loads the metadata after changing the data type id.
- bridge()->SetInitialSyncDone(true);
- std::unique_ptr<MetadataBatch> metadata_batch = db()->CreateMetadataBatch();
- sync_pb::ModelTypeState model_type_state(metadata_batch->GetModelTypeState());
- // This processor is supposed to process Preferences. Mark the model type
- // state to be for sessions to simulate a data type id mismatch.
- model_type_state.mutable_progress_marker()->set_data_type_id(
- GetSpecificsFieldNumberFromModelType(SESSIONS));
- metadata_batch->SetModelTypeState(model_type_state);
- type_processor()->ModelReadyToSync(std::move(metadata_batch));
- ASSERT_TRUE(type_processor()->IsModelReadyToSyncForTest());
- OnSyncStarting();
- // Model should still be ready to sync.
- ASSERT_TRUE(type_processor()->IsModelReadyToSyncForTest());
- // OnSyncStarting() should have completed.
- EXPECT_NE(nullptr, worker());
- // Upon a mismatch, metadata should have been cleared.
- EXPECT_EQ(0U, db()->metadata_count());
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldClearOrphanMetadataInGetLocalChangesWhenDataIsMissing) {
- InitializeToReadyState();
- WritePrefItem(bridge(), kKey1, kValue1);
- // Loose the entity in the bridge (keeping the metadata around as an orphan).
- bridge()->MimicBugToLooseItemWithoutNotifyingProcessor(kKey1);
- ASSERT_FALSE(db()->HasData(kKey1));
- ASSERT_TRUE(db()->HasMetadata(kKey1));
- ASSERT_NE(nullptr, GetEntityForStorageKey(kKey1));
- // Reset "the browser" so that the processor looses the copy of the data.
- ResetState(/*keep_db=*/true);
- // Initializing the processor will trigger it to commit again. It does not
- // have a copy of the data so it will ask the bridge.
- {
- base::HistogramTester histogram_tester;
- bridge()->ExpectSynchronousDataCallback();
- InitializeToReadyState();
- histogram_tester.ExpectBucketCount(
- "Sync.ModelTypeOrphanMetadata.GetData",
- /*bucket=*/ModelTypeHistogramValue(GetModelType()), /*count=*/1);
- }
- // Orphan metadata should have been deleted.
- EXPECT_EQ(1, bridge()->apply_call_count());
- EXPECT_FALSE(db()->HasMetadata(kKey1));
- EXPECT_EQ(nullptr, GetEntityForStorageKey(kKey1));
- base::HistogramTester histogram_tester;
- // Do it again, explicitly. The processor does not track the entity so it
- // shouldn't ask the bridge or return local changes.
- CommitRequestDataList commit_request;
- type_processor()->GetLocalChanges(
- INT_MAX, base::BindOnce(&CaptureCommitRequest, &commit_request));
- EXPECT_EQ(0U, commit_request.size());
- EXPECT_TRUE(bridge()->GetDataCallback().is_null());
- // The processor should not report orphan again in UMA.
- histogram_tester.ExpectBucketCount(
- "Sync.ModelTypeOrphanMetadata.GetData",
- /*bucket=*/ModelTypeHistogramValue(GetModelType()), /*count=*/0);
- }
- TEST_F(
- ClientTagBasedModelTypeProcessorTest,
- ShouldNotReportOrphanMetadataInGetLocalChangesWhenDataIsAlreadyUntracked) {
- InitializeToReadyState();
- WritePrefItem(bridge(), kKey1, kValue1);
- // Loose the entity in the bridge (keeping the metadata around as an orphan).
- bridge()->MimicBugToLooseItemWithoutNotifyingProcessor(kKey1);
- ASSERT_FALSE(db()->HasData(kKey1));
- ASSERT_TRUE(db()->HasMetadata(kKey1));
- ASSERT_NE(nullptr, GetEntityForStorageKey(kKey1));
- // Reset "the browser" so that the processor looses the copy of the data.
- ResetState(/*keep_db=*/true);
- // Initializing the processor will trigger it to commit again. It does not
- // have a copy of the data so it will ask the bridge.
- base::HistogramTester histogram_tester;
- InitializeToReadyState();
- // The bridge has not passed the data back to the processor, we untrack the
- // entity.
- type_processor()->UntrackEntityForStorageKey(kKey1);
- // Make the bridge pass the data back to the processor. Because the entity is
- // already deleted in the processor, no further orphan gets reported.
- std::move(bridge()->GetDataCallback()).Run();
- histogram_tester.ExpectTotalCount("Sync.ModelTypeOrphanMetadata.GetData",
- /*count=*/0);
- EXPECT_EQ(0, bridge()->apply_call_count());
- EXPECT_EQ(nullptr, GetEntityForStorageKey(kKey1));
- // The expectation below documents the fact that bridges are responsible for
- // clearing the untracked metadata from their databases.
- EXPECT_TRUE(db()->HasMetadata(kKey1));
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldNotReportOrphanMetadataInGetLocalChangesWhenDataIsAlreadyDeleted) {
- InitializeToReadyState();
- WritePrefItem(bridge(), kKey1, kValue1);
- // Loose the entity in the bridge (keeping the metadata around as an orphan).
- bridge()->MimicBugToLooseItemWithoutNotifyingProcessor(kKey1);
- ASSERT_FALSE(db()->HasData(kKey1));
- ASSERT_TRUE(db()->HasMetadata(kKey1));
- ASSERT_NE(nullptr, GetEntityForStorageKey(kKey1));
- // Reset "the browser" so that the processor looses the copy of the data.
- ResetState(/*keep_db=*/true);
- // Initializing the processor will trigger it to commit again. It does not
- // have a copy of the data so it will ask the bridge.
- base::HistogramTester histogram_tester;
- InitializeToReadyState();
- // The bridge has not passed the data back to the processor, we delete the
- // entity.
- bridge()->DeleteItem(kKey1);
- // Make the bridge pass the data back to the processor. Because the entity is
- // already deleted in the processor, no further orphan gets reported.
- std::move(bridge()->GetDataCallback()).Run();
- histogram_tester.ExpectTotalCount("Sync.ModelTypeOrphanMetadata.GetData",
- /*count=*/0);
- EXPECT_EQ(0, bridge()->apply_call_count());
- EXPECT_EQ(nullptr, GetEntityForStorageKey(kKey1));
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldNotReportOrphanMetadataInGetLocalChangesWhenDataIsPresent) {
- InitializeToReadyState();
- WritePrefItem(bridge(), kKey1, kValue1);
- ASSERT_TRUE(db()->HasData(kKey1));
- ASSERT_TRUE(db()->HasMetadata(kKey1));
- ASSERT_NE(nullptr, GetEntityForStorageKey(kKey1));
- // Reset "the browser" so that the processor looses the copy of the data.
- ResetState(/*keep_db=*/true);
- // Initializing the processor will trigger it to commit again. It does not
- // have a copy of the data so it will ask the bridge.
- base::HistogramTester histogram_tester;
- bridge()->ExpectSynchronousDataCallback();
- InitializeToReadyState();
- // Now everything is committed and GetLocalChanges is empty again.
- CommitRequestDataList commit_request;
- type_processor()->GetLocalChanges(
- INT_MAX, base::BindOnce(&CaptureCommitRequest, &commit_request));
- EXPECT_EQ(0U, commit_request.size());
- // The processor never reports any orphan.
- histogram_tester.ExpectTotalCount("Sync.ModelTypeOrphanMetadata.GetData",
- /*count=*/0);
- EXPECT_TRUE(db()->HasData(kKey1));
- EXPECT_TRUE(db()->HasMetadata(kKey1));
- EXPECT_NE(nullptr, GetEntityForStorageKey(kKey1));
- }
- // This tests the case when the bridge deletes an item, and before it's
- // committed to the server, it created again with a different storage key.
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldDeleteItemAndRecreaeItWithDifferentStorageKey) {
- const std::string kStorageKey1 = "StorageKey1";
- const std::string kStorageKey2 = "StorageKey2";
- // This verifies that the processor doesn't use GetStorageKey() method.
- bridge()->SetSupportsGetStorageKey(false);
- InitializeToReadyState();
- std::unique_ptr<EntityData> entity_data1 =
- GeneratePrefEntityData(kKey1, kValue1);
- bridge()->WriteItem(kStorageKey1, std::move(entity_data1));
- worker()->AckOnePendingCommit();
- EXPECT_TRUE(type_processor()->IsTrackingEntityForTest(kStorageKey1));
- // Delete the data associated with the first storage key.
- bridge()->DeleteItem(kStorageKey1);
- // // Add the same data under a different storage key.
- std::unique_ptr<EntityData> entity_data2 =
- GeneratePrefEntityData(kKey1, kValue1);
- bridge()->WriteItem(kStorageKey2, std::move(entity_data2));
- EXPECT_FALSE(type_processor()->IsTrackingEntityForTest(kStorageKey1));
- EXPECT_FALSE(db()->HasMetadata(kStorageKey1));
- EXPECT_TRUE(type_processor()->IsTrackingEntityForTest(kStorageKey2));
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldPropagateFailedCommitItemsToBridgeWhenCommitCompleted) {
- InitializeToReadyState();
- FailedCommitResponseData response_data;
- response_data.client_tag_hash = GetPrefHash("dummy tag");
- response_data.response_type = sync_pb::CommitResponse::TRANSIENT_ERROR;
- response_data.datatype_specific_error.mutable_sharing_message_error()
- ->set_error_code(sync_pb::SharingMessageCommitError::INVALID_ARGUMENT);
- FailedCommitResponseDataList failed_list;
- failed_list.push_back(response_data);
- FailedCommitResponseDataList actual_error_response_list;
- auto on_commit_attempt_errors_callback = base::BindOnce(
- [](FailedCommitResponseDataList* actual_error_response_list,
- const FailedCommitResponseDataList& error_response_list) {
- // We put expectations outside of the callback, so that they fail if
- // callback is not ran.
- *actual_error_response_list = error_response_list;
- },
- &actual_error_response_list);
- bridge()->SetOnCommitAttemptErrorsCallback(
- std::move(on_commit_attempt_errors_callback));
- type_processor()->OnCommitCompleted(
- model_type_state(),
- /*committed_response_list=*/CommitResponseDataList(), failed_list);
- ASSERT_EQ(1u, actual_error_response_list.size());
- EXPECT_EQ(0, bridge()->commit_failures_count());
- EXPECT_EQ(response_data.client_tag_hash,
- actual_error_response_list[0].client_tag_hash);
- EXPECT_EQ(response_data.response_type,
- actual_error_response_list[0].response_type);
- EXPECT_EQ(response_data.datatype_specific_error.sharing_message_error()
- .error_code(),
- actual_error_response_list[0]
- .datatype_specific_error.sharing_message_error()
- .error_code());
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldNotPropagateFailedCommitAttemptToBridgeWhenNoFailedItems) {
- InitializeToReadyState();
- auto on_commit_attempt_errors_callback = base::BindOnce(
- [](const FailedCommitResponseDataList& error_response_list) {
- ADD_FAILURE()
- << "OnCommitAttemptErrors is called when no failed items.";
- });
- bridge()->SetOnCommitAttemptErrorsCallback(
- std::move(on_commit_attempt_errors_callback));
- type_processor()->OnCommitCompleted(
- model_type_state(),
- /*committed_response_list=*/CommitResponseDataList(),
- /*error_response_list=*/FailedCommitResponseDataList());
- EXPECT_EQ(0, bridge()->commit_failures_count());
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldPropagateFullCommitFailure) {
- InitializeToReadyState();
- ASSERT_EQ(0, bridge()->commit_failures_count());
- type_processor()->OnCommitFailed(syncer::SyncCommitError::kNetworkError);
- EXPECT_EQ(1, bridge()->commit_failures_count());
- }
- class CommitOnlyClientTagBasedModelTypeProcessorTest
- : public ClientTagBasedModelTypeProcessorTest {
- protected:
- ModelType GetModelType() override {
- DCHECK(CommitOnlyTypes().Has(USER_EVENTS));
- return USER_EVENTS;
- }
- };
- TEST_F(CommitOnlyClientTagBasedModelTypeProcessorTest,
- ShouldExposeNewlyTrackedAccountId) {
- ModelReadyToSync();
- ASSERT_EQ("", type_processor()->TrackedAccountId());
- OnSyncStarting();
- EXPECT_EQ(kDefaultAuthenticatedAccountId,
- type_processor()->TrackedAccountId());
- }
- TEST_F(CommitOnlyClientTagBasedModelTypeProcessorTest,
- ShouldExposePreviouslyTrackedAccountId) {
- std::unique_ptr<MetadataBatch> metadata_batch = db()->CreateMetadataBatch();
- sync_pb::ModelTypeState model_type_state(metadata_batch->GetModelTypeState());
- model_type_state.set_initial_sync_done(true);
- model_type_state.set_cache_guid(kCacheGuid);
- model_type_state.set_authenticated_account_id("PersistedAccountId");
- model_type_state.mutable_progress_marker()->set_data_type_id(
- GetSpecificsFieldNumberFromModelType(GetModelType()));
- metadata_batch->SetModelTypeState(model_type_state);
- type_processor()->ModelReadyToSync(std::move(metadata_batch));
- // Even prior to starting sync, the account ID should already be tracked.
- EXPECT_EQ("PersistedAccountId", type_processor()->TrackedAccountId());
- // If sync gets started, the account should still be tracked.
- OnSyncStarting("PersistedAccountId");
- EXPECT_EQ("PersistedAccountId", type_processor()->TrackedAccountId());
- }
- TEST_F(CommitOnlyClientTagBasedModelTypeProcessorTest,
- ShouldCallMergeWhenSyncEnabled) {
- ModelReadyToSync();
- ASSERT_EQ("", type_processor()->TrackedAccountId());
- ASSERT_EQ(0, bridge()->merge_call_count());
- OnSyncStarting();
- EXPECT_EQ(1, bridge()->merge_call_count());
- }
- TEST_F(CommitOnlyClientTagBasedModelTypeProcessorTest,
- ShouldNotCallMergeAfterRestart) {
- std::unique_ptr<MetadataBatch> metadata_batch = db()->CreateMetadataBatch();
- sync_pb::ModelTypeState model_type_state(metadata_batch->GetModelTypeState());
- model_type_state.set_initial_sync_done(true);
- model_type_state.set_cache_guid(kCacheGuid);
- model_type_state.set_authenticated_account_id("PersistedAccountId");
- model_type_state.mutable_progress_marker()->set_data_type_id(
- GetSpecificsFieldNumberFromModelType(GetModelType()));
- metadata_batch->SetModelTypeState(model_type_state);
- type_processor()->ModelReadyToSync(std::move(metadata_batch));
- // Even prior to starting sync, the account ID should already be tracked.
- ASSERT_EQ("PersistedAccountId", type_processor()->TrackedAccountId());
- // When sync gets started, MergeSyncData() should not be called.
- OnSyncStarting("PersistedAccountId");
- ASSERT_EQ(0, bridge()->merge_call_count());
- }
- // Test that commit only types are deleted after commit response.
- TEST_F(CommitOnlyClientTagBasedModelTypeProcessorTest,
- DISABLED_ShouldCommitAndDeleteWhenAcked) {
- InitializeToReadyState();
- EXPECT_TRUE(db()->model_type_state().initial_sync_done());
- const uint64_t key1 = 1234;
- const std::string key1s = base::NumberToString(key1);
- WriteUserEventItem(bridge(), key1, 4321);
- worker()->VerifyPendingCommits({{GetHash(USER_EVENTS, key1s)}});
- EXPECT_EQ(1U, db()->data_count());
- EXPECT_EQ(1U, db()->metadata_count());
- worker()->AckOnePendingCommit();
- EXPECT_EQ(0U, db()->data_count());
- EXPECT_EQ(0U, db()->metadata_count());
- }
- // Test that commit only types maintain tracking of entities while unsynced
- // changes exist.
- TEST_F(CommitOnlyClientTagBasedModelTypeProcessorTest,
- ShouldTrackUnsyncedChangesAfterPartialCommit) {
- InitializeToReadyState();
- const uint64_t key1 = 1234;
- const uint64_t key2 = 2345;
- const std::string key1s = base::NumberToString(key1);
- const std::string key2s = base::NumberToString(key2);
- WriteUserEventItem(bridge(), key1, 4321);
- worker()->VerifyPendingCommits({{GetHash(USER_EVENTS, key1s)}});
- EXPECT_EQ(1U, db()->data_count());
- EXPECT_EQ(1U, db()->metadata_count());
- WriteUserEventItem(bridge(), key1, 5432);
- worker()->VerifyPendingCommits(
- {{GetHash(USER_EVENTS, key1s)}, {GetHash(USER_EVENTS, key1s)}});
- EXPECT_EQ(1U, db()->data_count());
- EXPECT_EQ(1U, db()->metadata_count());
- worker()->AckOnePendingCommit();
- worker()->VerifyPendingCommits({{GetHash(USER_EVENTS, key1s)}});
- EXPECT_EQ(1U, db()->data_count());
- EXPECT_EQ(1U, db()->metadata_count());
- // The version field isn't meaningful on commit only types, so force a value
- // that isn't incremented to verify everything still works.
- worker()->AckOnePendingCommit(0 /* version_offset */);
- worker()->VerifyPendingCommits({});
- EXPECT_EQ(0U, db()->data_count());
- EXPECT_EQ(0U, db()->metadata_count());
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldResetOnInvalidCacheGuid) {
- ResetStateWriteItem(kKey1, kValue1);
- InitializeToMetadataLoaded();
- OnSyncStarting();
- OnCommitDataLoaded();
- ASSERT_EQ(1U, ProcessorEntityCount());
- ResetStateWriteItem(kKey1, kValue1);
- sync_pb::ModelTypeState model_type_state = db()->model_type_state();
- model_type_state.set_cache_guid("OtherCacheGuid");
- db()->set_model_type_state(model_type_state);
- ModelReadyToSync();
- OnSyncStarting();
- EXPECT_EQ(0U, ProcessorEntityCount());
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldResetOnInvalidDataTypeId) {
- ResetStateWriteItem(kKey1, kValue1);
- ASSERT_EQ(0U, ProcessorEntityCount());
- // Initialize change processor, expect to load data from the bridge.
- InitializeToMetadataLoaded();
- OnSyncStarting();
- OnCommitDataLoaded();
- ASSERT_EQ(1U, ProcessorEntityCount());
- ResetStateWriteItem(kKey1, kValue1);
- OnSyncStarting();
- // Set different data type id.
- sync_pb::ModelTypeState model_type_state = db()->model_type_state();
- ASSERT_NE(model_type_state.progress_marker().data_type_id(),
- GetSpecificsFieldNumberFromModelType(AUTOFILL));
- model_type_state.mutable_progress_marker()->set_data_type_id(
- GetSpecificsFieldNumberFromModelType(AUTOFILL));
- db()->set_model_type_state(model_type_state);
- ModelReadyToSync();
- EXPECT_EQ(0U, ProcessorEntityCount());
- }
- TEST_F(ClientTagBasedModelTypeProcessorTest,
- ShouldResetForDuplicateClientTagHash) {
- base::HistogramTester histogram_tester;
- const syncer::ClientTagHash kClientTagHash =
- ClientTagHash::FromUnhashed(AUTOFILL, "tag");
- sync_pb::EntityMetadata entity_metadata1;
- entity_metadata1.set_client_tag_hash(kClientTagHash.value());
- entity_metadata1.set_creation_time(0);
- sync_pb::EntityMetadata entity_metadata2;
- entity_metadata2.set_client_tag_hash(kClientTagHash.value());
- entity_metadata2.set_creation_time(0);
- sync_pb::EntityMetadata entity_metadata3;
- entity_metadata3.set_client_tag_hash(kClientTagHash.value());
- entity_metadata3.set_creation_time(0);
- db()->PutMetadata(kKey1, std::move(entity_metadata1));
- db()->PutMetadata(kKey2, std::move(entity_metadata2));
- db()->PutMetadata(kKey3, std::move(entity_metadata3));
- InitializeToReadyState();
- // With a client tag hash duplicate, metadata should have been cleared.
- EXPECT_EQ(0U, db()->metadata_count());
- EXPECT_EQ(0U, ProcessorEntityCount());
- EXPECT_FALSE(type_processor()->IsTrackingMetadata());
- // Initial update.
- worker()->UpdateFromServer();
- EXPECT_TRUE(type_processor()->IsTrackingMetadata());
- // There were three entities with the same client-tag-hash which indicates
- // that two of them were metadata oprhans.
- histogram_tester.ExpectBucketCount(
- "Sync.ModelTypeOrphanMetadata.ModelReadyToSync",
- /*bucket=*/ModelTypeHistogramValue(GetModelType()),
- /*count=*/2);
- }
- } // namespace syncer
|