| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/sync/engine/bookmark_update_preprocessing.h" |
| |
| #include <stdint.h> |
| |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/uuid.h" |
| #include "components/sync/base/unique_position.h" |
| #include "components/sync/protocol/bookmark_specifics.pb.h" |
| #include "components/sync/protocol/entity_data.h" |
| #include "components/sync/protocol/entity_specifics.pb.h" |
| #include "components/sync/protocol/sync_entity.pb.h" |
| #include "components/sync/protocol/unique_position.pb.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace syncer { |
| |
| namespace { |
| |
| using testing::Eq; |
| using testing::IsEmpty; |
| using testing::Ne; |
| |
| enum class ExpectedBookmarkGuidSource { |
| kSpecifics = 0, |
| kValidOCII = 1, |
| kDeprecatedLeftEmpty = 2, |
| kInferred = 3, |
| kLeftEmptyPossiblyForClientTag = 4, |
| kMaxValue = kLeftEmptyPossiblyForClientTag, |
| }; |
| |
| TEST(BookmarkUpdatePreprocessingTest, |
| ShouldPropagateUniquePositionFromSpecifics) { |
| const UniquePosition kUniquePosition = |
| UniquePosition::InitialPosition(UniquePosition::RandomSuffix()); |
| |
| sync_pb::SyncEntity entity; |
| *entity.mutable_specifics()->mutable_bookmark()->mutable_unique_position() = |
| *entity.mutable_unique_position() = kUniquePosition.ToProto(); |
| |
| const bool is_preprocessed = |
| AdaptUniquePositionForBookmark(entity, entity.mutable_specifics()); |
| |
| EXPECT_FALSE(is_preprocessed); |
| EXPECT_TRUE( |
| UniquePosition::FromProto(entity.specifics().bookmark().unique_position()) |
| .Equals(kUniquePosition)); |
| } |
| |
| TEST(BookmarkUpdatePreprocessingTest, |
| ShouldPropagateUniquePositionFromSyncEntity) { |
| const UniquePosition kUniquePosition = |
| UniquePosition::InitialPosition(UniquePosition::RandomSuffix()); |
| |
| sync_pb::SyncEntity entity; |
| *entity.mutable_unique_position() = kUniquePosition.ToProto(); |
| |
| const bool is_preprocessed = |
| AdaptUniquePositionForBookmark(entity, entity.mutable_specifics()); |
| |
| EXPECT_TRUE(is_preprocessed); |
| EXPECT_TRUE( |
| UniquePosition::FromProto(entity.specifics().bookmark().unique_position()) |
| .Equals(kUniquePosition)); |
| } |
| |
| TEST(BookmarkUpdatePreprocessingTest, |
| ShouldComputeUniquePositionFromPositionInParent) { |
| sync_pb::SyncEntity entity; |
| entity.set_originator_cache_guid( |
| base::Uuid::GenerateRandomV4().AsLowercaseString()); |
| entity.set_originator_client_item_id("1"); |
| entity.set_position_in_parent(5); |
| |
| sync_pb::EntitySpecifics specifics1; |
| bool is_preprocessed = AdaptUniquePositionForBookmark(entity, &specifics1); |
| EXPECT_TRUE(is_preprocessed); |
| |
| sync_pb::EntitySpecifics specifics2; |
| entity.set_position_in_parent(6); |
| is_preprocessed = AdaptUniquePositionForBookmark(entity, &specifics2); |
| EXPECT_TRUE(is_preprocessed); |
| |
| EXPECT_TRUE(UniquePosition::FromProto(specifics1.bookmark().unique_position()) |
| .IsValid()); |
| EXPECT_TRUE(UniquePosition::FromProto(specifics2.bookmark().unique_position()) |
| .IsValid()); |
| EXPECT_TRUE(UniquePosition::FromProto(specifics1.bookmark().unique_position()) |
| .LessThan(UniquePosition::FromProto( |
| specifics2.bookmark().unique_position()))); |
| } |
| |
| TEST(BookmarkUpdatePreprocessingTest, |
| ShouldComputeUniquePositionFromInsertAfterItemId) { |
| sync_pb::SyncEntity entity; |
| entity.set_originator_cache_guid( |
| base::Uuid::GenerateRandomV4().AsLowercaseString()); |
| entity.set_originator_client_item_id("1"); |
| entity.set_insert_after_item_id("ITEM_ID"); |
| |
| const bool is_preprocessed = |
| AdaptUniquePositionForBookmark(entity, entity.mutable_specifics()); |
| |
| EXPECT_TRUE(is_preprocessed); |
| EXPECT_TRUE( |
| UniquePosition::FromProto(entity.specifics().bookmark().unique_position()) |
| .IsValid()); |
| } |
| |
| TEST(BookmarkUpdatePreprocessingTest, ShouldFallBackToRandomUniquePosition) { |
| sync_pb::SyncEntity entity; |
| const bool is_preprocessed = |
| AdaptUniquePositionForBookmark(entity, entity.mutable_specifics()); |
| |
| EXPECT_TRUE(is_preprocessed); |
| EXPECT_TRUE( |
| UniquePosition::FromProto(entity.specifics().bookmark().unique_position()) |
| .IsValid()); |
| } |
| |
| // Tests that AdaptGuidForBookmark() propagates GUID in specifics if the field |
| // is set (even if it doesn't match the originator client item ID). |
| TEST(BookmarkUpdatePreprocessingTest, ShouldPropagateGuidFromSpecifics) { |
| const std::string kGuidInSpecifics = |
| base::Uuid::GenerateRandomV4().AsLowercaseString(); |
| |
| sync_pb::SyncEntity entity; |
| entity.set_originator_cache_guid( |
| base::Uuid::GenerateRandomV4().AsLowercaseString()); |
| entity.set_originator_client_item_id( |
| base::Uuid::GenerateRandomV4().AsLowercaseString()); |
| entity.mutable_specifics()->mutable_bookmark()->set_guid(kGuidInSpecifics); |
| |
| base::HistogramTester histogram_tester; |
| sync_pb::EntitySpecifics specifics = entity.specifics(); |
| AdaptGuidForBookmark(entity, &specifics); |
| |
| EXPECT_THAT(specifics.bookmark().guid(), Eq(kGuidInSpecifics)); |
| |
| histogram_tester.ExpectUniqueSample("Sync.BookmarkGUIDSource2", |
| /*sample=*/ |
| ExpectedBookmarkGuidSource::kSpecifics, |
| /*expected_bucket_count=*/1); |
| } |
| |
| // Tests that AdaptGuidForBookmark() uses the originator client item ID as GUID |
| // when it is a valid GUID, and the GUID in specifics is not set. |
| TEST(BookmarkUpdatePreprocessingTest, ShouldUseOriginatorClientItemIdAsGuid) { |
| const std::string kOriginatorClientItemId = |
| base::Uuid::GenerateRandomV4().AsLowercaseString(); |
| |
| sync_pb::SyncEntity entity; |
| entity.set_originator_cache_guid( |
| base::Uuid::GenerateRandomV4().AsLowercaseString()); |
| entity.set_originator_client_item_id(kOriginatorClientItemId); |
| entity.mutable_specifics()->mutable_bookmark(); |
| |
| base::HistogramTester histogram_tester; |
| sync_pb::EntitySpecifics specifics = entity.specifics(); |
| AdaptGuidForBookmark(entity, &specifics); |
| |
| EXPECT_THAT(specifics.bookmark().guid(), Eq(kOriginatorClientItemId)); |
| |
| histogram_tester.ExpectUniqueSample("Sync.BookmarkGUIDSource2", |
| /*sample=*/ |
| ExpectedBookmarkGuidSource::kValidOCII, |
| /*expected_bucket_count=*/1); |
| } |
| |
| // Tests that AdaptGuidForBookmark() infers the GUID when the field in specifics |
| // is empty and originator client item ID is not a valid GUID. |
| TEST(BookmarkUpdatePreprocessingTest, ShouldInferGuid) { |
| const std::string kOriginatorClientItemId = "1"; |
| |
| sync_pb::SyncEntity entity; |
| entity.set_originator_cache_guid( |
| base::Uuid::GenerateRandomV4().AsLowercaseString()); |
| entity.set_originator_client_item_id(kOriginatorClientItemId); |
| entity.mutable_specifics()->mutable_bookmark(); |
| |
| base::HistogramTester histogram_tester; |
| sync_pb::EntitySpecifics specifics = entity.specifics(); |
| AdaptGuidForBookmark(entity, &specifics); |
| |
| EXPECT_TRUE( |
| base::Uuid::ParseLowercase(specifics.bookmark().guid()).is_valid()); |
| |
| histogram_tester.ExpectUniqueSample("Sync.BookmarkGUIDSource2", |
| /*sample=*/ |
| ExpectedBookmarkGuidSource::kInferred, |
| /*expected_bucket_count=*/1); |
| } |
| |
| TEST(BookmarkUpdatePreprocessingTest, |
| ShouldNotInferGuidIfNoOriginatorInformation) { |
| const std::string kOriginatorClientItemId = "1"; |
| |
| sync_pb::SyncEntity entity; |
| entity.mutable_specifics()->mutable_bookmark(); |
| |
| base::HistogramTester histogram_tester; |
| sync_pb::EntitySpecifics specifics = entity.specifics(); |
| AdaptGuidForBookmark(entity, &specifics); |
| |
| EXPECT_FALSE(specifics.bookmark().has_guid()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "Sync.BookmarkGUIDSource2", |
| /*sample=*/ |
| ExpectedBookmarkGuidSource::kLeftEmptyPossiblyForClientTag, |
| /*expected_bucket_count=*/1); |
| } |
| |
| // Tests that inferred GUIDs are computed deterministically. |
| TEST(BookmarkUpdatePreprocessingTest, ShouldInferDeterministicGuid) { |
| EXPECT_THAT(InferGuidForLegacyBookmarkForTesting("cacheguid1", "1"), |
| Eq(InferGuidForLegacyBookmarkForTesting("cacheguid1", "1"))); |
| EXPECT_THAT(InferGuidForLegacyBookmarkForTesting("cacheguid1", "2"), |
| Eq(InferGuidForLegacyBookmarkForTesting("cacheguid1", "2"))); |
| } |
| |
| // Tests that unique GUIDs are produced if either of the two involved fields |
| // changes. |
| TEST(BookmarkUpdatePreprocessingTest, ShouldInferDistictGuids) { |
| EXPECT_THAT(InferGuidForLegacyBookmarkForTesting("cacheguid1", "1"), |
| Ne(InferGuidForLegacyBookmarkForTesting("cacheguid1", "2"))); |
| EXPECT_THAT(InferGuidForLegacyBookmarkForTesting("cacheguid1", "1"), |
| Ne(InferGuidForLegacyBookmarkForTesting("cacheguid2", "1"))); |
| } |
| |
| TEST(BookmarkUpdatePreprocessingTest, ShouldUseTypeInSpecifics) { |
| sync_pb::SyncEntity entity; |
| sync_pb::EntitySpecifics specifics; |
| |
| *specifics.mutable_bookmark() = |
| sync_pb::BookmarkSpecifics::default_instance(); |
| specifics.mutable_bookmark()->set_type(sync_pb::BookmarkSpecifics::FOLDER); |
| AdaptTypeForBookmark(entity, &specifics); |
| EXPECT_THAT(specifics.bookmark().type(), |
| Eq(sync_pb::BookmarkSpecifics::FOLDER)); |
| |
| *specifics.mutable_bookmark() = |
| sync_pb::BookmarkSpecifics::default_instance(); |
| specifics.mutable_bookmark()->set_type(sync_pb::BookmarkSpecifics::URL); |
| AdaptTypeForBookmark(entity, &specifics); |
| EXPECT_THAT(specifics.bookmark().type(), Eq(sync_pb::BookmarkSpecifics::URL)); |
| |
| // Even if SyncEntity says otherwise, specifics should prevail. |
| entity.set_folder(true); |
| *specifics.mutable_bookmark() = |
| sync_pb::BookmarkSpecifics::default_instance(); |
| specifics.mutable_bookmark()->set_type(sync_pb::BookmarkSpecifics::URL); |
| AdaptTypeForBookmark(entity, &specifics); |
| EXPECT_THAT(specifics.bookmark().type(), Eq(sync_pb::BookmarkSpecifics::URL)); |
| } |
| |
| TEST(BookmarkUpdatePreprocessingTest, |
| ShouldInferTypeFromFolderFieldInSyncEntity) { |
| sync_pb::SyncEntity entity; |
| sync_pb::EntitySpecifics specifics; |
| |
| *specifics.mutable_bookmark() = |
| sync_pb::BookmarkSpecifics::default_instance(); |
| entity.set_folder(true); |
| AdaptTypeForBookmark(entity, &specifics); |
| EXPECT_THAT(specifics.bookmark().type(), |
| Eq(sync_pb::BookmarkSpecifics::FOLDER)); |
| |
| *specifics.mutable_bookmark() = |
| sync_pb::BookmarkSpecifics::default_instance(); |
| entity.set_folder(false); |
| AdaptTypeForBookmark(entity, &specifics); |
| EXPECT_THAT(specifics.bookmark().type(), Eq(sync_pb::BookmarkSpecifics::URL)); |
| } |
| |
| TEST(BookmarkUpdatePreprocessingTest, |
| ShouldInferTypeFromPresenceOfUrlInSpecifics) { |
| sync_pb::SyncEntity entity; |
| sync_pb::EntitySpecifics specifics; |
| |
| *specifics.mutable_bookmark() = |
| sync_pb::BookmarkSpecifics::default_instance(); |
| AdaptTypeForBookmark(entity, &specifics); |
| EXPECT_THAT(specifics.bookmark().type(), |
| Eq(sync_pb::BookmarkSpecifics::FOLDER)); |
| |
| *specifics.mutable_bookmark() = |
| sync_pb::BookmarkSpecifics::default_instance(); |
| specifics.mutable_bookmark()->set_url("http://foo.com"); |
| AdaptTypeForBookmark(entity, &specifics); |
| EXPECT_THAT(specifics.bookmark().type(), Eq(sync_pb::BookmarkSpecifics::URL)); |
| } |
| |
| } // namespace |
| |
| } // namespace syncer |