1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "sync/engine/syncer_util.h" 6 7 #include "base/rand_util.h" 8 #include "sync/internal_api/public/base/unique_position.h" 9 #include "sync/internal_api/public/test/test_entry_factory.h" 10 #include "sync/protocol/sync.pb.h" 11 #include "sync/syncable/mutable_entry.h" 12 #include "sync/syncable/syncable_write_transaction.h" 13 #include "sync/test/engine/test_directory_setter_upper.h" 14 #include "testing/gtest/include/gtest/gtest.h" 15 16 namespace syncer { 17 18 class GetUpdatePositionTest : public ::testing::Test { 19 public: 20 virtual void SetUp() { 21 dir_maker_.SetUp(); 22 entry_factory_.reset(new TestEntryFactory(directory())); 23 } 24 25 virtual void TearDown() { 26 dir_maker_.TearDown(); 27 } 28 29 syncable::Directory* directory() { 30 return dir_maker_.directory(); 31 } 32 33 TestEntryFactory* entry_factory() { 34 return entry_factory_.get(); 35 } 36 37 GetUpdatePositionTest() { 38 InitUpdate(); 39 40 // Init test_position to some valid position value, but don't assign 41 // it to the update just yet. 42 std::string pos_suffix = UniquePosition::RandomSuffix(); 43 test_position = UniquePosition::InitialPosition(pos_suffix); 44 } 45 46 void InitUpdate() { 47 update.set_id_string("I"); 48 update.set_parent_id_string("P"); 49 update.set_version(10); 50 update.set_mtime(100); 51 update.set_ctime(100); 52 update.set_deleted(false); 53 update.mutable_specifics()->mutable_bookmark()->set_title("Chrome"); 54 update.mutable_specifics()->mutable_bookmark()-> 55 set_url("https://www.chrome.com"); 56 } 57 58 void InitSuffixIngredients() { 59 update.set_originator_cache_guid("CacheGUID"); 60 update.set_originator_client_item_id("OrigID"); 61 } 62 63 void InitProtoPosition() { 64 test_position.ToProto(update.mutable_unique_position()); 65 } 66 67 void InitInt64Position(int64 pos_value) { 68 update.set_position_in_parent(pos_value); 69 } 70 71 sync_pb::SyncEntity update; 72 UniquePosition test_position; 73 base::MessageLoop message_loop_; 74 TestDirectorySetterUpper dir_maker_; 75 scoped_ptr<TestEntryFactory> entry_factory_; 76 }; 77 78 // Generate a suffix from originator client GUID and client-assigned ID. These 79 // values should always be present in updates sent down to the client, and 80 // combine to create a globally unique value. 81 TEST_F(GetUpdatePositionTest, SuffixFromUpdate) { 82 InitSuffixIngredients(); 83 84 // Expect suffix is valid and consistent. 85 std::string suffix1 = GetUniqueBookmarkTagFromUpdate(update); 86 std::string suffix2 = GetUniqueBookmarkTagFromUpdate(update); 87 88 EXPECT_EQ(suffix1, suffix2); 89 EXPECT_TRUE(UniquePosition::IsValidSuffix(suffix1)); 90 } 91 92 // Receive an update without the ingredients used to make a consistent suffix. 93 // 94 // The server should never send us an update like this. If it does, 95 // that's a bug and it needs to be fixed. Still, we'd like to not 96 // crash and have fairly reasonable results in this scenario. 97 TEST_F(GetUpdatePositionTest, SuffixFromRandom) { 98 // Intentonally do not call InitSuffixIngredients() 99 100 // Expect suffix is valid but inconsistent. 101 std::string suffix1 = GetUniqueBookmarkTagFromUpdate(update); 102 std::string suffix2 = GetUniqueBookmarkTagFromUpdate(update); 103 104 EXPECT_NE(suffix1, suffix2); 105 EXPECT_TRUE(UniquePosition::IsValidSuffix(suffix1)); 106 EXPECT_TRUE(UniquePosition::IsValidSuffix(suffix2)); 107 } 108 109 TEST_F(GetUpdatePositionTest, FromInt64) { 110 InitSuffixIngredients(); 111 InitInt64Position(10); 112 113 std::string suffix = GetUniqueBookmarkTagFromUpdate(update); 114 115 // Expect the result is valid. 116 UniquePosition pos = GetUpdatePosition(update, suffix); 117 EXPECT_TRUE(pos.IsValid()); 118 119 // Expect the position had some effect on ordering. 120 EXPECT_TRUE(pos.LessThan( 121 UniquePosition::FromInt64(11, UniquePosition::RandomSuffix()))); 122 } 123 124 TEST_F(GetUpdatePositionTest, FromProto) { 125 InitSuffixIngredients(); 126 InitInt64Position(10); 127 128 std::string suffix = GetUniqueBookmarkTagFromUpdate(update); 129 130 // The proto position is not set, so we should get one based on the int64. 131 // It should not match the proto we defined in the test harness. 132 UniquePosition int64_pos = GetUpdatePosition(update, suffix); 133 EXPECT_FALSE(int64_pos.Equals(test_position)); 134 135 // Move the test harness' position value into the update proto. 136 // Expect that it takes precedence over the int64-based position. 137 InitProtoPosition(); 138 UniquePosition pos = GetUpdatePosition(update, suffix); 139 EXPECT_TRUE(pos.Equals(test_position)); 140 } 141 142 TEST_F(GetUpdatePositionTest, FromNothing) { 143 // Init none of the ingredients necessary to make a position. 144 // Verify we still generate a valid position locally. 145 146 std::string suffix = GetUniqueBookmarkTagFromUpdate(update); 147 UniquePosition pos = GetUpdatePosition(update, suffix); 148 EXPECT_TRUE(pos.IsValid()); 149 } 150 151 namespace { 152 153 sync_pb::EntitySpecifics DefaultBookmarkSpecifics() { 154 sync_pb::EntitySpecifics result; 155 AddDefaultFieldValue(BOOKMARKS, &result); 156 return result; 157 } 158 159 } // namespace 160 161 // Checks that whole cycle of unique_position updating from 162 // server works fine and does not browser crash. 163 TEST_F(GetUpdatePositionTest, UpdateServerFieldsFromUpdateTest) { 164 InitSuffixIngredients(); // Initialize update with valid data. 165 166 std::string root_server_id = syncable::GetNullId().GetServerId(); 167 int64 handle = entry_factory()->CreateUnappliedNewBookmarkItemWithParent( 168 "I", DefaultBookmarkSpecifics(), root_server_id); 169 170 syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST, directory()); 171 syncable::MutableEntry target(&trans, syncable::GET_BY_HANDLE, handle); 172 173 // Before update, target has invalid bookmark tag and unique position. 174 EXPECT_FALSE(UniquePosition::IsValidSuffix(target.GetUniqueBookmarkTag())); 175 EXPECT_FALSE(target.GetServerUniquePosition().IsValid()); 176 UpdateServerFieldsFromUpdate(&target, update, "name"); 177 178 // After update, target has valid bookmark tag and unique position. 179 EXPECT_TRUE(UniquePosition::IsValidSuffix(target.GetUniqueBookmarkTag())); 180 EXPECT_TRUE(target.GetServerUniquePosition().IsValid()); 181 } 182 183 // Checks that whole cycle of unique_position updating does not 184 // browser crash even data from server is invalid. 185 // It looks like server bug, but browser should not crash and work further. 186 TEST_F(GetUpdatePositionTest, UpdateServerFieldsFromInvalidUpdateTest) { 187 // Do not initialize data in update, update is invalid. 188 189 std::string root_server_id = syncable::GetNullId().GetServerId(); 190 int64 handle = entry_factory()->CreateUnappliedNewBookmarkItemWithParent( 191 "I", DefaultBookmarkSpecifics(), root_server_id); 192 193 syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST, directory()); 194 syncable::MutableEntry target(&trans, syncable::GET_BY_HANDLE, handle); 195 196 // Before update, target has invalid bookmark tag and unique position. 197 EXPECT_FALSE(UniquePosition::IsValidSuffix(target.GetUniqueBookmarkTag())); 198 EXPECT_FALSE(target.GetServerUniquePosition().IsValid()); 199 UpdateServerFieldsFromUpdate(&target, update, "name"); 200 201 // After update, target has valid bookmark tag and unique position. 202 EXPECT_TRUE(UniquePosition::IsValidSuffix(target.GetUniqueBookmarkTag())); 203 EXPECT_TRUE(target.GetServerUniquePosition().IsValid()); 204 } 205 206 } // namespace syncer 207