123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- // Copyright 2020 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "components/sync/model/processor_entity_tracker.h"
- #include <utility>
- #include "base/test/scoped_feature_list.h"
- #include "components/sync/base/features.h"
- #include "components/sync/model/metadata_batch.h"
- #include "components/sync/model/processor_entity.h"
- #include "testing/gmock/include/gmock/gmock.h"
- #include "testing/gtest/include/gtest/gtest.h"
- namespace syncer {
- namespace {
- using testing::ElementsAre;
- using testing::IsNull;
- using testing::NotNull;
- using testing::UnorderedElementsAre;
- constexpr char kEmptyStorageKey[] = "";
- constexpr char kStorageKey1[] = "key1";
- constexpr char kStorageKey2[] = "key2";
- constexpr int64_t kServerVersion = 5;
- sync_pb::ModelTypeState GenerateModelTypeState() {
- sync_pb::ModelTypeState model_type_state;
- model_type_state.set_initial_sync_done(true);
- return model_type_state;
- }
- std::unique_ptr<sync_pb::EntityMetadata> GenerateMetadata(
- const std::string& storage_key,
- const ClientTagHash& client_tag_hash) {
- sync_pb::EntityMetadata metadata;
- metadata.set_creation_time(1);
- metadata.set_modification_time(1);
- metadata.set_client_tag_hash(client_tag_hash.value());
- metadata.set_specifics_hash("specifics_hash");
- return std::make_unique<sync_pb::EntityMetadata>(std::move(metadata));
- }
- std::unique_ptr<sync_pb::EntityMetadata> GenerateTombstoneMetadata(
- const std::string& storage_key,
- const ClientTagHash& client_tag_hash) {
- std::unique_ptr<sync_pb::EntityMetadata> metadata =
- GenerateMetadata(storage_key, client_tag_hash);
- metadata->set_is_deleted(true);
- metadata->set_base_specifics_hash(metadata->specifics_hash());
- metadata->clear_specifics_hash();
- return metadata;
- }
- EntityData GenerateEntityData(const std::string& storage_key,
- const ClientTagHash& client_tag_hash) {
- EntityData entity_data;
- entity_data.client_tag_hash = client_tag_hash;
- entity_data.creation_time = base::Time::Now();
- entity_data.modification_time = entity_data.creation_time;
- entity_data.name = storage_key;
- // The tracker requires non-empty specifics with any data type.
- entity_data.specifics.mutable_preference();
- return entity_data;
- }
- UpdateResponseData GenerateUpdate(const std::string& storage_key,
- const ClientTagHash& client_tag_hash,
- int64_t response_version) {
- auto entity = absl::make_unique<EntityData>(
- GenerateEntityData(storage_key, client_tag_hash));
- UpdateResponseData update;
- update.entity = std::move(*entity);
- update.response_version = response_version;
- return update;
- }
- class ProcessorEntityTrackerTest : public ::testing::Test {
- public:
- ProcessorEntityTrackerTest()
- : entity_tracker_(GenerateModelTypeState(), {}) {}
- ~ProcessorEntityTrackerTest() override = default;
- const ClientTagHash kClientTagHash1 =
- ClientTagHash::FromHashed("client_tag_hash_1");
- const ClientTagHash kClientTagHash2 =
- ClientTagHash::FromHashed("client_tag_hash_2");
- protected:
- ProcessorEntityTracker entity_tracker_;
- };
- TEST_F(ProcessorEntityTrackerTest, ShouldLoadFromMetadata) {
- EntityMetadataMap metadata_map;
- metadata_map.emplace(kStorageKey1,
- GenerateMetadata(kStorageKey1, kClientTagHash1));
- metadata_map.emplace(
- kStorageKey2, GenerateTombstoneMetadata(kStorageKey2, kClientTagHash2));
- ProcessorEntityTracker entity_tracker(GenerateModelTypeState(),
- std::move(metadata_map));
- // Check some getters for the entity tracker.
- EXPECT_EQ(2u, entity_tracker.size());
- EXPECT_EQ(1u, entity_tracker.CountNonTombstoneEntries());
- EXPECT_TRUE(entity_tracker.model_type_state().initial_sync_done());
- EXPECT_TRUE(entity_tracker.AllStorageKeysPopulated());
- EXPECT_FALSE(entity_tracker.HasLocalChanges());
- // Check each entity thoroughly.
- const ProcessorEntity* entity =
- entity_tracker.GetEntityForStorageKey(kStorageKey1);
- ASSERT_THAT(entity, NotNull());
- EXPECT_EQ(entity, entity_tracker.GetEntityForTagHash(kClientTagHash1));
- EXPECT_EQ(kStorageKey1, entity->storage_key());
- EXPECT_EQ(1u, entity->metadata().creation_time());
- EXPECT_EQ(1u, entity->metadata().modification_time());
- EXPECT_EQ("specifics_hash", entity->metadata().specifics_hash());
- EXPECT_EQ(entity->metadata().client_tag_hash(), kClientTagHash1.value());
- EXPECT_FALSE(entity->metadata().is_deleted());
- const ProcessorEntity* tombstone_entity =
- entity_tracker.GetEntityForStorageKey(kStorageKey2);
- ASSERT_THAT(tombstone_entity, NotNull());
- EXPECT_EQ(kStorageKey2, tombstone_entity->storage_key());
- EXPECT_EQ(1u, tombstone_entity->metadata().creation_time());
- EXPECT_EQ(1u, tombstone_entity->metadata().modification_time());
- EXPECT_EQ("specifics_hash",
- tombstone_entity->metadata().base_specifics_hash());
- EXPECT_FALSE(tombstone_entity->metadata().has_specifics_hash());
- EXPECT_EQ(tombstone_entity->metadata().client_tag_hash(),
- kClientTagHash2.value());
- EXPECT_TRUE(tombstone_entity->metadata().is_deleted());
- const std::vector<const ProcessorEntity*> all_entities =
- entity_tracker.GetAllEntitiesIncludingTombstones();
- EXPECT_THAT(all_entities, UnorderedElementsAre(entity, tombstone_entity));
- }
- TEST_F(ProcessorEntityTrackerTest, ShouldAddNewLocalEntity) {
- std::unique_ptr<EntityData> entity_data = absl::make_unique<EntityData>(
- GenerateEntityData(kStorageKey1, kClientTagHash1));
- EntityData* entity_data_ptr = entity_data.get();
- const ProcessorEntity* entity = entity_tracker_.AddUnsyncedLocal(
- kStorageKey1, std::move(entity_data), /*trimmed_specifics=*/{});
- ASSERT_THAT(entity, NotNull());
- EXPECT_EQ(1u, entity_tracker_.size());
- EXPECT_EQ(1u, entity_tracker_.CountNonTombstoneEntries());
- EXPECT_EQ(entity, entity_tracker_.GetEntityForTagHash(
- entity_data_ptr->client_tag_hash));
- EXPECT_EQ(entity, entity_tracker_.GetEntityForStorageKey(kStorageKey1));
- EXPECT_TRUE(entity_tracker_.HasLocalChanges());
- EXPECT_EQ(kStorageKey1, entity->storage_key());
- EXPECT_EQ(entity->metadata().client_tag_hash(),
- entity_data_ptr->client_tag_hash.value());
- EXPECT_FALSE(entity->metadata().is_deleted());
- EXPECT_TRUE(entity->IsUnsynced());
- EXPECT_TRUE(entity->HasCommitData());
- }
- TEST_F(ProcessorEntityTrackerTest, ShouldAddNewRemoteEntity) {
- UpdateResponseData update =
- GenerateUpdate(kStorageKey1, kClientTagHash1, kServerVersion);
- const ProcessorEntity* entity =
- entity_tracker_.AddRemote(kStorageKey1, update, /*trimmed_specifics=*/{});
- ASSERT_THAT(entity, NotNull());
- EXPECT_EQ(1u, entity_tracker_.size());
- EXPECT_EQ(1u, entity_tracker_.CountNonTombstoneEntries());
- EXPECT_EQ(entity,
- entity_tracker_.GetEntityForTagHash(update.entity.client_tag_hash));
- EXPECT_EQ(entity, entity_tracker_.GetEntityForStorageKey(kStorageKey1));
- EXPECT_FALSE(entity_tracker_.HasLocalChanges());
- EXPECT_EQ(kStorageKey1, entity->storage_key());
- EXPECT_EQ(entity->metadata().client_tag_hash(),
- update.entity.client_tag_hash.value());
- EXPECT_FALSE(entity->metadata().is_deleted());
- }
- TEST_F(ProcessorEntityTrackerTest, ShouldAddEntityWithoutStorageKey) {
- UpdateResponseData update =
- GenerateUpdate(kStorageKey1, kClientTagHash1, kServerVersion);
- const ProcessorEntity* entity = entity_tracker_.AddRemote(
- kEmptyStorageKey, update, /*trimmed_specifics=*/{});
- ASSERT_THAT(entity, NotNull());
- // The entity should be available by the client tag hash only.
- EXPECT_EQ(kEmptyStorageKey, entity->storage_key());
- EXPECT_EQ(entity, entity_tracker_.GetEntityForTagHash(kClientTagHash1));
- // The empty storage key must not be used.
- EXPECT_THAT(entity_tracker_.GetEntityForStorageKey(kEmptyStorageKey),
- IsNull());
- EXPECT_EQ(1u, entity_tracker_.size());
- EXPECT_EQ(1u, entity_tracker_.CountNonTombstoneEntries());
- EXPECT_EQ(entity->metadata().client_tag_hash(),
- update.entity.client_tag_hash.value());
- EXPECT_FALSE(entity->metadata().is_deleted());
- // Check that tracker is waiting for the storage key to be populated.
- EXPECT_FALSE(entity_tracker_.AllStorageKeysPopulated());
- entity_tracker_.UpdateOrOverrideStorageKey(kClientTagHash1, kStorageKey1);
- EXPECT_EQ(entity, entity_tracker_.GetEntityForStorageKey(kStorageKey1));
- EXPECT_EQ(1u, entity_tracker_.size());
- EXPECT_EQ(1u, entity_tracker_.CountNonTombstoneEntries());
- EXPECT_TRUE(entity_tracker_.AllStorageKeysPopulated());
- }
- TEST_F(ProcessorEntityTrackerTest, ShouldClearStorageKeyForTombstone) {
- ProcessorEntity* entity = entity_tracker_.AddRemote(
- kStorageKey1,
- GenerateUpdate(kStorageKey1, kClientTagHash1, kServerVersion),
- /*trimmed_specifics=*/{});
- ASSERT_EQ(entity, entity_tracker_.GetEntityForStorageKey(kStorageKey1));
- ASSERT_EQ(kStorageKey1, entity->storage_key());
- // Mark the entity as removed.
- entity->RecordLocalDeletion();
- ASSERT_EQ(1u, entity_tracker_.size());
- ASSERT_EQ(0u, entity_tracker_.CountNonTombstoneEntries());
- entity_tracker_.ClearStorageKey(kStorageKey1);
- EXPECT_THAT(entity_tracker_.GetEntityForStorageKey(kStorageKey1), IsNull());
- EXPECT_TRUE(entity->storage_key().empty());
- EXPECT_EQ(1u, entity_tracker_.size());
- EXPECT_EQ(0u, entity_tracker_.CountNonTombstoneEntries());
- }
- TEST_F(ProcessorEntityTrackerTest, ShouldOverrideTombstone) {
- ProcessorEntity* entity = entity_tracker_.AddRemote(
- kStorageKey1,
- GenerateUpdate(kStorageKey1, kClientTagHash1, kServerVersion),
- /*trimmed_specifics=*/{});
- ASSERT_THAT(entity, NotNull());
- ASSERT_EQ(entity, entity_tracker_.GetEntityForStorageKey(kStorageKey1));
- ASSERT_EQ(kStorageKey1, entity->storage_key());
- // Mark the entity as removed.
- entity->RecordLocalDeletion();
- ASSERT_EQ(1u, entity_tracker_.size());
- ASSERT_EQ(0u, entity_tracker_.CountNonTombstoneEntries());
- // Mimic an entity being created with the same client tag hash.
- entity_tracker_.UpdateOrOverrideStorageKey(kClientTagHash1, kStorageKey2);
- EXPECT_EQ(kStorageKey2, entity->storage_key());
- EXPECT_THAT(entity_tracker_.GetEntityForStorageKey(kStorageKey1), IsNull());
- EXPECT_EQ(entity, entity_tracker_.GetEntityForStorageKey(kStorageKey2));
- EXPECT_EQ(1u, entity_tracker_.size());
- EXPECT_EQ(0u, entity_tracker_.CountNonTombstoneEntries());
- }
- TEST_F(ProcessorEntityTrackerTest, ShouldRemoveEntityForStorageKey) {
- const ProcessorEntity* entity = entity_tracker_.AddRemote(
- kStorageKey1,
- GenerateUpdate(kStorageKey1, kClientTagHash1, kServerVersion),
- /*trimmed_specifics=*/{});
- ASSERT_THAT(entity, NotNull());
- ASSERT_EQ(1u, entity_tracker_.size());
- entity_tracker_.RemoveEntityForStorageKey(kStorageKey1);
- EXPECT_EQ(0u, entity_tracker_.size());
- }
- TEST_F(ProcessorEntityTrackerTest, ShouldRemoveEntityForClientTagHash) {
- const ProcessorEntity* entity = entity_tracker_.AddRemote(
- kStorageKey1,
- GenerateUpdate(kStorageKey1, kClientTagHash1, kServerVersion),
- /*trimmed_specifics=*/{});
- ASSERT_THAT(entity, NotNull());
- ASSERT_EQ(entity, entity_tracker_.GetEntityForTagHash(kClientTagHash1));
- const ProcessorEntity* entity_no_key = entity_tracker_.AddRemote(
- kEmptyStorageKey,
- GenerateUpdate(kStorageKey2, kClientTagHash2, kServerVersion),
- /*trimmed_specifics=*/{});
- ASSERT_THAT(entity_no_key, NotNull());
- ASSERT_EQ(entity_no_key,
- entity_tracker_.GetEntityForTagHash(kClientTagHash2));
- ASSERT_EQ(2u, entity_tracker_.size());
- entity_tracker_.RemoveEntityForClientTagHash(kClientTagHash2);
- EXPECT_EQ(1u, entity_tracker_.size());
- EXPECT_THAT(entity_tracker_.GetEntityForTagHash(kClientTagHash2), IsNull());
- // A second call does not affect anything.
- entity_tracker_.RemoveEntityForClientTagHash(kClientTagHash2);
- EXPECT_EQ(1u, entity_tracker_.size());
- entity_tracker_.RemoveEntityForClientTagHash(kClientTagHash1);
- EXPECT_EQ(0u, entity_tracker_.size());
- }
- TEST_F(ProcessorEntityTrackerTest, ShouldReturnLocalChanges) {
- std::unique_ptr<EntityData> entity_data = absl::make_unique<EntityData>(
- GenerateEntityData(kStorageKey1, kClientTagHash1));
- ProcessorEntity* entity = entity_tracker_.AddUnsyncedLocal(
- kStorageKey1, std::move(entity_data), /*trimmed_specifics=*/{});
- ASSERT_THAT(entity, NotNull());
- ASSERT_TRUE(entity->IsUnsynced());
- ASSERT_TRUE(entity->HasCommitData());
- ASSERT_TRUE(entity_tracker_.HasLocalChanges());
- ASSERT_FALSE(
- entity_tracker_.GetEntitiesWithLocalChanges(/*max_entries=*/1).empty());
- // Make some local changes.
- entity->RecordLocalUpdate(std::make_unique<EntityData>(GenerateEntityData(
- kStorageKey1, kClientTagHash1)),
- /*trimmed_specifics=*/{});
- entity_tracker_.IncrementSequenceNumberForAllExcept({});
- EXPECT_TRUE(entity->IsUnsynced());
- EXPECT_TRUE(entity->HasCommitData());
- EXPECT_TRUE(entity_tracker_.HasLocalChanges());
- EXPECT_THAT(entity_tracker_.GetEntitiesWithLocalChanges(/*max_entries=*/2),
- ElementsAre(entity));
- }
- TEST_F(ProcessorEntityTrackerTest, ShouldUpdateSpecificsCacheOnLocalCreation) {
- base::test::ScopedFeatureList feature_list;
- feature_list.InitAndEnableFeature(kCacheBaseEntitySpecificsInMetadata);
- std::unique_ptr<EntityData> entity_data = absl::make_unique<EntityData>(
- GenerateEntityData(kStorageKey1, kClientTagHash1));
- sync_pb::EntitySpecifics specifics_for_caching;
- specifics_for_caching.mutable_preference()->set_name("name");
- specifics_for_caching.mutable_preference()->set_value("value");
- ProcessorEntity* entity = entity_tracker_.AddUnsyncedLocal(
- kStorageKey1, std::move(entity_data), specifics_for_caching);
- EXPECT_EQ(
- specifics_for_caching.SerializeAsString(),
- entity->metadata().possibly_trimmed_base_specifics().SerializeAsString());
- }
- TEST_F(ProcessorEntityTrackerTest, ShouldUpdateSpecificsCacheOnRemoteCreation) {
- base::test::ScopedFeatureList feature_list;
- feature_list.InitAndEnableFeature(kCacheBaseEntitySpecificsInMetadata);
- sync_pb::EntitySpecifics specifics_for_caching;
- specifics_for_caching.mutable_preference()->set_name("name");
- specifics_for_caching.mutable_preference()->set_value("value");
- ProcessorEntity* entity = entity_tracker_.AddRemote(
- kStorageKey1,
- GenerateUpdate(kStorageKey1, kClientTagHash1, kServerVersion),
- specifics_for_caching);
- EXPECT_EQ(
- specifics_for_caching.SerializeAsString(),
- entity->metadata().possibly_trimmed_base_specifics().SerializeAsString());
- }
- } // namespace
- } // namespace syncer
|