| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "sync/internal_api/public/base/unique_position.h" | 5 #include "components/sync/base/unique_position.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 #include <stdint.h> | 8 #include <stdint.h> |
| 9 | 9 |
| 10 #include <algorithm> | 10 #include <algorithm> |
| 11 #include <functional> | 11 #include <functional> |
| 12 #include <memory> | 12 #include <memory> |
| 13 #include <string> | 13 #include <string> |
| 14 #include <vector> | 14 #include <vector> |
| 15 | 15 |
| 16 #include "base/base64.h" | 16 #include "base/base64.h" |
| 17 #include "base/logging.h" | 17 #include "base/logging.h" |
| 18 #include "base/macros.h" | 18 #include "base/macros.h" |
| 19 #include "base/sha1.h" | 19 #include "base/sha1.h" |
| 20 #include "base/strings/string_number_conversions.h" | 20 #include "base/strings/string_number_conversions.h" |
| 21 #include "sync/protocol/unique_position.pb.h" | 21 #include "components/sync/protocol/unique_position.pb.h" |
| 22 #include "testing/gtest/include/gtest/gtest.h" | 22 #include "testing/gtest/include/gtest/gtest.h" |
| 23 | 23 |
| 24 namespace syncer { | 24 namespace syncer { |
| 25 | 25 |
| 26 namespace { | 26 namespace { |
| 27 | 27 |
| 28 class UniquePositionTest : public ::testing::Test { | 28 class UniquePositionTest : public ::testing::Test { |
| 29 protected: | 29 protected: |
| 30 // Accessor to fetch the length of the position's internal representation | 30 // Accessor to fetch the length of the position's internal representation |
| 31 // We try to avoid having any test expectations on it because this is an | 31 // We try to avoid having any test expectations on it because this is an |
| (...skipping 18 matching lines...) Expand all Loading... |
| 50 } | 50 } |
| 51 | 51 |
| 52 const size_t kMinLength = UniquePosition::kSuffixLength; | 52 const size_t kMinLength = UniquePosition::kSuffixLength; |
| 53 const size_t kGenericPredecessorLength = kMinLength + 2; | 53 const size_t kGenericPredecessorLength = kMinLength + 2; |
| 54 const size_t kGenericSuccessorLength = kMinLength + 1; | 54 const size_t kGenericSuccessorLength = kMinLength + 1; |
| 55 const size_t kBigPositionLength = kMinLength; | 55 const size_t kBigPositionLength = kMinLength; |
| 56 const size_t kSmallPositionLength = kMinLength; | 56 const size_t kSmallPositionLength = kMinLength; |
| 57 | 57 |
| 58 // Be careful when adding more prefixes to this list. | 58 // Be careful when adding more prefixes to this list. |
| 59 // We have to manually ensure each has a unique suffix. | 59 // We have to manually ensure each has a unique suffix. |
| 60 const UniquePosition kGenericPredecessor = FromBytes( | 60 const UniquePosition kGenericPredecessor = |
| 61 (std::string(kGenericPredecessorLength, '\x23') + '\xFF')); | 61 FromBytes((std::string(kGenericPredecessorLength, '\x23') + '\xFF')); |
| 62 const UniquePosition kGenericSuccessor = FromBytes( | 62 const UniquePosition kGenericSuccessor = |
| 63 std::string(kGenericSuccessorLength, '\xAB') + '\xFF'); | 63 FromBytes(std::string(kGenericSuccessorLength, '\xAB') + '\xFF'); |
| 64 const UniquePosition kBigPosition = FromBytes( | 64 const UniquePosition kBigPosition = |
| 65 std::string(kBigPositionLength - 1, '\xFF') + '\xFE' + '\xFF'); | 65 FromBytes(std::string(kBigPositionLength - 1, '\xFF') + '\xFE' + '\xFF'); |
| 66 const UniquePosition kBigPositionLessTwo = FromBytes( | 66 const UniquePosition kBigPositionLessTwo = |
| 67 std::string(kBigPositionLength - 1, '\xFF') + '\xFC' + '\xFF'); | 67 FromBytes(std::string(kBigPositionLength - 1, '\xFF') + '\xFC' + '\xFF'); |
| 68 const UniquePosition kBiggerPosition = FromBytes( | 68 const UniquePosition kBiggerPosition = |
| 69 std::string(kBigPositionLength, '\xFF') + '\xFF'); | 69 FromBytes(std::string(kBigPositionLength, '\xFF') + '\xFF'); |
| 70 const UniquePosition kSmallPosition = FromBytes( | 70 const UniquePosition kSmallPosition = |
| 71 std::string(kSmallPositionLength - 1, '\x00') + '\x01' + '\xFF'); | 71 FromBytes(std::string(kSmallPositionLength - 1, '\x00') + '\x01' + '\xFF'); |
| 72 const UniquePosition kSmallPositionPlusOne = FromBytes( | 72 const UniquePosition kSmallPositionPlusOne = |
| 73 std::string(kSmallPositionLength - 1, '\x00') + '\x02' + '\xFF'); | 73 FromBytes(std::string(kSmallPositionLength - 1, '\x00') + '\x02' + '\xFF'); |
| 74 const UniquePosition kHugePosition = FromBytes( | 74 const UniquePosition kHugePosition = FromBytes( |
| 75 std::string(UniquePosition::kCompressBytesThreshold, '\xFF') + '\xAB'); | 75 std::string(UniquePosition::kCompressBytesThreshold, '\xFF') + '\xAB'); |
| 76 | 76 |
| 77 const std::string kMinSuffix = | 77 const std::string kMinSuffix = |
| 78 std::string(UniquePosition::kSuffixLength - 1, '\x00') + '\x01'; | 78 std::string(UniquePosition::kSuffixLength - 1, '\x00') + '\x01'; |
| 79 const std::string kMaxSuffix(UniquePosition::kSuffixLength, '\xFF'); | 79 const std::string kMaxSuffix(UniquePosition::kSuffixLength, '\xFF'); |
| 80 const std::string kNormalSuffix( | 80 const std::string kNormalSuffix( |
| 81 "\x68\x44\x6C\x6B\x32\x58\x78\x34\x69\x70\x46\x34\x79\x49" | 81 "\x68\x44\x6C\x6B\x32\x58\x78\x34\x69\x70\x46\x34\x79\x49" |
| 82 "\x44\x4F\x66\x4C\x58\x41\x31\x34\x68\x59\x56\x43\x6F\x3D"); | 82 "\x44\x4F\x66\x4C\x58\x41\x31\x34\x68\x59\x56\x43\x6F\x3D"); |
| 83 | 83 |
| 84 ::testing::AssertionResult LessThan(const char* m_expr, | 84 ::testing::AssertionResult LessThan(const char* m_expr, |
| 85 const char* n_expr, | 85 const char* n_expr, |
| 86 const UniquePosition &m, | 86 const UniquePosition& m, |
| 87 const UniquePosition &n) { | 87 const UniquePosition& n) { |
| 88 if (m.LessThan(n)) | 88 if (m.LessThan(n)) |
| 89 return ::testing::AssertionSuccess(); | 89 return ::testing::AssertionSuccess(); |
| 90 | 90 |
| 91 return ::testing::AssertionFailure() | 91 return ::testing::AssertionFailure() << m_expr << " is not less than " |
| 92 << m_expr << " is not less than " << n_expr | 92 << n_expr << " (" << m.ToDebugString() |
| 93 << " (" << m.ToDebugString() << " and " << n.ToDebugString() << ")"; | 93 << " and " << n.ToDebugString() << ")"; |
| 94 } | 94 } |
| 95 | 95 |
| 96 ::testing::AssertionResult Equals(const char* m_expr, | 96 ::testing::AssertionResult Equals(const char* m_expr, |
| 97 const char* n_expr, | 97 const char* n_expr, |
| 98 const UniquePosition &m, | 98 const UniquePosition& m, |
| 99 const UniquePosition &n) { | 99 const UniquePosition& n) { |
| 100 if (m.Equals(n)) | 100 if (m.Equals(n)) |
| 101 return ::testing::AssertionSuccess(); | 101 return ::testing::AssertionSuccess(); |
| 102 | 102 |
| 103 return ::testing::AssertionFailure() | 103 return ::testing::AssertionFailure() << m_expr << " is not equal to " |
| 104 << m_expr << " is not equal to " << n_expr | 104 << n_expr << " (" << m.ToDebugString() |
| 105 << " (" << m.ToDebugString() << " != " << n.ToDebugString() << ")"; | 105 << " != " << n.ToDebugString() << ")"; |
| 106 } | 106 } |
| 107 | 107 |
| 108 // Test that the code can read the uncompressed serialization format. | 108 // Test that the code can read the uncompressed serialization format. |
| 109 TEST_F(UniquePositionTest, DeserializeObsoleteUncompressedPosition) { | 109 TEST_F(UniquePositionTest, DeserializeObsoleteUncompressedPosition) { |
| 110 // We no longer support the encoding data in this format. This hard-coded | 110 // We no longer support the encoding data in this format. This hard-coded |
| 111 // input is a serialization of kGenericPredecessor created by an older version | 111 // input is a serialization of kGenericPredecessor created by an older version |
| 112 // of this code. | 112 // of this code. |
| 113 const char kSerializedCstr[] = { | 113 const char kSerializedCstr[] = { |
| 114 '\x0a', '\x1f', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', | 114 '\x0a', '\x1f', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', |
| 115 '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', | 115 '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', |
| 116 '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', | 116 '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', |
| 117 '\x23', '\x23', '\x23', '\x23', '\x23', '\xff' }; | 117 '\x23', '\x23', '\x23', '\x23', '\x23', '\xff'}; |
| 118 const std::string serialized(kSerializedCstr, sizeof(kSerializedCstr)); | 118 const std::string serialized(kSerializedCstr, sizeof(kSerializedCstr)); |
| 119 | 119 |
| 120 sync_pb::UniquePosition proto; | 120 sync_pb::UniquePosition proto; |
| 121 proto.ParseFromString(serialized); | 121 proto.ParseFromString(serialized); |
| 122 | 122 |
| 123 // Double-check that this test is testing what we think it tests. | 123 // Double-check that this test is testing what we think it tests. |
| 124 EXPECT_TRUE(proto.has_value()); | 124 EXPECT_TRUE(proto.has_value()); |
| 125 EXPECT_FALSE(proto.has_compressed_value()); | 125 EXPECT_FALSE(proto.has_compressed_value()); |
| 126 EXPECT_FALSE(proto.has_uncompressed_length()); | 126 EXPECT_FALSE(proto.has_uncompressed_length()); |
| 127 | 127 |
| 128 UniquePosition pos = UniquePosition::FromProto(proto); | 128 UniquePosition pos = UniquePosition::FromProto(proto); |
| 129 EXPECT_PRED_FORMAT2(Equals, kGenericPredecessor, pos); | 129 EXPECT_PRED_FORMAT2(Equals, kGenericPredecessor, pos); |
| 130 } | 130 } |
| 131 | 131 |
| 132 // Test that the code can read the gzip serialization format. | 132 // Test that the code can read the gzip serialization format. |
| 133 TEST_F(UniquePositionTest, DeserializeObsoleteGzippedPosition) { | 133 TEST_F(UniquePositionTest, DeserializeObsoleteGzippedPosition) { |
| 134 // We no longer support the encoding data in this format. This hard-coded | 134 // We no longer support the encoding data in this format. This hard-coded |
| 135 // input is a serialization of kHugePosition created by an older version of | 135 // input is a serialization of kHugePosition created by an older version of |
| 136 // this code. | 136 // this code. |
| 137 const char kSerializedCstr[] = { | 137 const char kSerializedCstr[] = { |
| 138 '\x12', '\x0d', '\x78', '\x9c', '\xfb', '\xff', '\x7f', '\x60', '\xc1', | 138 '\x12', '\x0d', '\x78', '\x9c', '\xfb', '\xff', '\x7f', '\x60', '\xc1', |
| 139 '\x6a', '\x00', '\xa2', '\x4c', '\x80', '\x2c', '\x18', '\x81', '\x01' }; | 139 '\x6a', '\x00', '\xa2', '\x4c', '\x80', '\x2c', '\x18', '\x81', '\x01'}; |
| 140 const std::string serialized(kSerializedCstr, sizeof(kSerializedCstr)); | 140 const std::string serialized(kSerializedCstr, sizeof(kSerializedCstr)); |
| 141 | 141 |
| 142 sync_pb::UniquePosition proto; | 142 sync_pb::UniquePosition proto; |
| 143 proto.ParseFromString(serialized); | 143 proto.ParseFromString(serialized); |
| 144 | 144 |
| 145 // Double-check that this test is testing what we think it tests. | 145 // Double-check that this test is testing what we think it tests. |
| 146 EXPECT_FALSE(proto.has_value()); | 146 EXPECT_FALSE(proto.has_value()); |
| 147 EXPECT_TRUE(proto.has_compressed_value()); | 147 EXPECT_TRUE(proto.has_compressed_value()); |
| 148 EXPECT_TRUE(proto.has_uncompressed_length()); | 148 EXPECT_TRUE(proto.has_uncompressed_length()); |
| 149 | 149 |
| 150 UniquePosition pos = UniquePosition::FromProto(proto); | 150 UniquePosition pos = UniquePosition::FromProto(proto); |
| 151 EXPECT_PRED_FORMAT2(Equals, kHugePosition, pos); | 151 EXPECT_PRED_FORMAT2(Equals, kHugePosition, pos); |
| 152 } | 152 } |
| 153 | 153 |
| 154 class RelativePositioningTest : public UniquePositionTest { }; | 154 class RelativePositioningTest : public UniquePositionTest {}; |
| 155 | 155 |
| 156 const UniquePosition kPositionArray[] = { | 156 const UniquePosition kPositionArray[] = { |
| 157 kGenericPredecessor, | 157 kGenericPredecessor, kGenericSuccessor, kBigPosition, |
| 158 kGenericSuccessor, | 158 kBigPositionLessTwo, kBiggerPosition, kSmallPosition, |
| 159 kBigPosition, | 159 kSmallPositionPlusOne, |
| 160 kBigPositionLessTwo, | |
| 161 kBiggerPosition, | |
| 162 kSmallPosition, | |
| 163 kSmallPositionPlusOne, | |
| 164 }; | 160 }; |
| 165 | 161 |
| 166 const UniquePosition kSortedPositionArray[] = { | 162 const UniquePosition kSortedPositionArray[] = { |
| 167 kSmallPosition, | 163 kSmallPosition, kSmallPositionPlusOne, kGenericPredecessor, |
| 168 kSmallPositionPlusOne, | 164 kGenericSuccessor, kBigPositionLessTwo, kBigPosition, |
| 169 kGenericPredecessor, | 165 kBiggerPosition, |
| 170 kGenericSuccessor, | |
| 171 kBigPositionLessTwo, | |
| 172 kBigPosition, | |
| 173 kBiggerPosition, | |
| 174 }; | 166 }; |
| 175 | 167 |
| 176 static const size_t kNumPositions = arraysize(kPositionArray); | 168 static const size_t kNumPositions = arraysize(kPositionArray); |
| 177 static const size_t kNumSortedPositions = arraysize(kSortedPositionArray); | 169 static const size_t kNumSortedPositions = arraysize(kSortedPositionArray); |
| 178 | 170 |
| 179 struct PositionLessThan { | 171 struct PositionLessThan { |
| 180 bool operator()(const UniquePosition& a, const UniquePosition& b) { | 172 bool operator()(const UniquePosition& a, const UniquePosition& b) { |
| 181 return a.LessThan(b); | 173 return a.LessThan(b); |
| 182 } | 174 } |
| 183 }; | 175 }; |
| 184 | 176 |
| 185 // Returns true iff the given position's suffix matches the input parameter. | 177 // Returns true iff the given position's suffix matches the input parameter. |
| 186 static bool IsSuffixInUse( | 178 static bool IsSuffixInUse(const UniquePosition& pos, |
| 187 const UniquePosition& pos, const std::string& suffix) { | 179 const std::string& suffix) { |
| 188 return pos.GetSuffixForTest() == suffix; | 180 return pos.GetSuffixForTest() == suffix; |
| 189 } | 181 } |
| 190 | 182 |
| 191 // Test some basic properties of comparison and equality. | 183 // Test some basic properties of comparison and equality. |
| 192 TEST_F(RelativePositioningTest, ComparisonSanityTest1) { | 184 TEST_F(RelativePositioningTest, ComparisonSanityTest1) { |
| 193 const UniquePosition& a = kPositionArray[0]; | 185 const UniquePosition& a = kPositionArray[0]; |
| 194 ASSERT_TRUE(a.IsValid()); | 186 ASSERT_TRUE(a.IsValid()); |
| 195 | 187 |
| 196 // Necessarily true for any non-invalid positions. | 188 // Necessarily true for any non-invalid positions. |
| 197 EXPECT_TRUE(a.Equals(a)); | 189 EXPECT_TRUE(a.Equals(a)); |
| (...skipping 15 matching lines...) Expand all Loading... |
| 213 TEST_F(RelativePositioningTest, SortPositions) { | 205 TEST_F(RelativePositioningTest, SortPositions) { |
| 214 ASSERT_EQ(kNumPositions, kNumSortedPositions); | 206 ASSERT_EQ(kNumPositions, kNumSortedPositions); |
| 215 UniquePosition positions[arraysize(kPositionArray)]; | 207 UniquePosition positions[arraysize(kPositionArray)]; |
| 216 for (size_t i = 0; i < kNumPositions; ++i) { | 208 for (size_t i = 0; i < kNumPositions; ++i) { |
| 217 positions[i] = kPositionArray[i]; | 209 positions[i] = kPositionArray[i]; |
| 218 } | 210 } |
| 219 | 211 |
| 220 std::sort(&positions[0], &positions[kNumPositions], PositionLessThan()); | 212 std::sort(&positions[0], &positions[kNumPositions], PositionLessThan()); |
| 221 for (size_t i = 0; i < kNumPositions; ++i) { | 213 for (size_t i = 0; i < kNumPositions; ++i) { |
| 222 EXPECT_TRUE(positions[i].Equals(kSortedPositionArray[i])) | 214 EXPECT_TRUE(positions[i].Equals(kSortedPositionArray[i])) |
| 223 << "i: " << i << ", " | 215 << "i: " << i << ", " << positions[i].ToDebugString() |
| 224 << positions[i].ToDebugString() << " != " | 216 << " != " << kSortedPositionArray[i].ToDebugString(); |
| 225 << kSortedPositionArray[i].ToDebugString(); | |
| 226 } | 217 } |
| 227 } | 218 } |
| 228 | 219 |
| 229 // Some more exercise for the comparison function. | 220 // Some more exercise for the comparison function. |
| 230 TEST_F(RelativePositioningTest, ReverseSortPositions) { | 221 TEST_F(RelativePositioningTest, ReverseSortPositions) { |
| 231 ASSERT_EQ(kNumPositions, kNumSortedPositions); | 222 ASSERT_EQ(kNumPositions, kNumSortedPositions); |
| 232 UniquePosition positions[arraysize(kPositionArray)]; | 223 UniquePosition positions[arraysize(kPositionArray)]; |
| 233 for (size_t i = 0; i < kNumPositions; ++i) { | 224 for (size_t i = 0; i < kNumPositions; ++i) { |
| 234 positions[i] = kPositionArray[i]; | 225 positions[i] = kPositionArray[i]; |
| 235 } | 226 } |
| 236 | 227 |
| 237 std::reverse(&positions[0], &positions[kNumPositions]); | 228 std::reverse(&positions[0], &positions[kNumPositions]); |
| 238 std::sort(&positions[0], &positions[kNumPositions], PositionLessThan()); | 229 std::sort(&positions[0], &positions[kNumPositions], PositionLessThan()); |
| 239 for (size_t i = 0; i < kNumPositions; ++i) { | 230 for (size_t i = 0; i < kNumPositions; ++i) { |
| 240 EXPECT_TRUE(positions[i].Equals(kSortedPositionArray[i])) | 231 EXPECT_TRUE(positions[i].Equals(kSortedPositionArray[i])) |
| 241 << "i: " << i << ", " | 232 << "i: " << i << ", " << positions[i].ToDebugString() |
| 242 << positions[i].ToDebugString() << " != " | 233 << " != " << kSortedPositionArray[i].ToDebugString(); |
| 243 << kSortedPositionArray[i].ToDebugString(); | |
| 244 } | 234 } |
| 245 } | 235 } |
| 246 | 236 |
| 247 class PositionInsertTest : | 237 class PositionInsertTest : public RelativePositioningTest, |
| 248 public RelativePositioningTest, | 238 public ::testing::WithParamInterface<std::string> {}; |
| 249 public ::testing::WithParamInterface<std::string> { }; | |
| 250 | 239 |
| 251 // Exercise InsertBetween with various insertion operations. | 240 // Exercise InsertBetween with various insertion operations. |
| 252 TEST_P(PositionInsertTest, InsertBetween) { | 241 TEST_P(PositionInsertTest, InsertBetween) { |
| 253 const std::string suffix = GetParam(); | 242 const std::string suffix = GetParam(); |
| 254 ASSERT_TRUE(UniquePosition::IsValidSuffix(suffix)); | 243 ASSERT_TRUE(UniquePosition::IsValidSuffix(suffix)); |
| 255 | 244 |
| 256 for (size_t i = 0; i < kNumSortedPositions; ++i) { | 245 for (size_t i = 0; i < kNumSortedPositions; ++i) { |
| 257 const UniquePosition& predecessor = kSortedPositionArray[i]; | 246 const UniquePosition& predecessor = kSortedPositionArray[i]; |
| 258 // Verify our suffixes are unique before we continue. | 247 // Verify our suffixes are unique before we continue. |
| 259 if (IsSuffixInUse(predecessor, suffix)) | 248 if (IsSuffixInUse(predecessor, suffix)) |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 379 } | 368 } |
| 380 | 369 |
| 381 VLOG(1) << "Lengths: " << GetLength(left_pos) << ", " << GetLength(right_pos); | 370 VLOG(1) << "Lengths: " << GetLength(left_pos) << ", " << GetLength(right_pos); |
| 382 } | 371 } |
| 383 | 372 |
| 384 // Generates suffixes similar to those generated by the directory. | 373 // Generates suffixes similar to those generated by the directory. |
| 385 // This may become obsolete if the suffix generation code is modified. | 374 // This may become obsolete if the suffix generation code is modified. |
| 386 class SuffixGenerator { | 375 class SuffixGenerator { |
| 387 public: | 376 public: |
| 388 explicit SuffixGenerator(const std::string& cache_guid) | 377 explicit SuffixGenerator(const std::string& cache_guid) |
| 389 : cache_guid_(cache_guid), | 378 : cache_guid_(cache_guid), next_id_(-65535) {} |
| 390 next_id_(-65535) { | |
| 391 } | |
| 392 | 379 |
| 393 std::string NextSuffix() { | 380 std::string NextSuffix() { |
| 394 // This is not entirely realistic, but that should be OK. The current | 381 // This is not entirely realistic, but that should be OK. The current |
| 395 // suffix format is a base64'ed SHA1 hash, which should be fairly close to | 382 // suffix format is a base64'ed SHA1 hash, which should be fairly close to |
| 396 // random anyway. | 383 // random anyway. |
| 397 std::string input = cache_guid_ + base::Int64ToString(next_id_--); | 384 std::string input = cache_guid_ + base::Int64ToString(next_id_--); |
| 398 std::string output; | 385 std::string output; |
| 399 base::Base64Encode(base::SHA1HashString(input), &output); | 386 base::Base64Encode(base::SHA1HashString(input), &output); |
| 400 return output; | 387 return output; |
| 401 } | 388 } |
| 402 | 389 |
| 403 private: | 390 private: |
| 404 const std::string cache_guid_; | 391 const std::string cache_guid_; |
| 405 int64_t next_id_; | 392 int64_t next_id_; |
| 406 }; | 393 }; |
| 407 | 394 |
| 408 // Cache guids generated in the same style as real clients. | 395 // Cache guids generated in the same style as real clients. |
| 409 static const char kCacheGuidStr1[] = "tuiWdG8hV+8y4RT9N5Aikg=="; | 396 static const char kCacheGuidStr1[] = "tuiWdG8hV+8y4RT9N5Aikg=="; |
| 410 static const char kCacheGuidStr2[] = "yaKb7zHtY06aue9a0vlZgw=="; | 397 static const char kCacheGuidStr2[] = "yaKb7zHtY06aue9a0vlZgw=="; |
| 411 | 398 |
| 412 class PositionScenariosTest : public UniquePositionTest { | 399 class PositionScenariosTest : public UniquePositionTest { |
| 413 public: | 400 public: |
| 414 PositionScenariosTest() | 401 PositionScenariosTest() |
| 415 : generator1_(std::string(kCacheGuidStr1, arraysize(kCacheGuidStr1)-1)), | 402 : generator1_(std::string(kCacheGuidStr1, arraysize(kCacheGuidStr1) - 1)), |
| 416 generator2_(std::string(kCacheGuidStr2, arraysize(kCacheGuidStr2)-1)) { | 403 generator2_( |
| 417 } | 404 std::string(kCacheGuidStr2, arraysize(kCacheGuidStr2) - 1)) {} |
| 418 | 405 |
| 419 std::string NextClient1Suffix() { | 406 std::string NextClient1Suffix() { return generator1_.NextSuffix(); } |
| 420 return generator1_.NextSuffix(); | |
| 421 } | |
| 422 | 407 |
| 423 std::string NextClient2Suffix() { | 408 std::string NextClient2Suffix() { return generator2_.NextSuffix(); } |
| 424 return generator2_.NextSuffix(); | |
| 425 } | |
| 426 | 409 |
| 427 private: | 410 private: |
| 428 SuffixGenerator generator1_; | 411 SuffixGenerator generator1_; |
| 429 SuffixGenerator generator2_; | 412 SuffixGenerator generator2_; |
| 430 }; | 413 }; |
| 431 | 414 |
| 432 // One client creating new bookmarks, always inserting at the end. | 415 // One client creating new bookmarks, always inserting at the end. |
| 433 TEST_F(PositionScenariosTest, OneClientInsertAtEnd) { | 416 TEST_F(PositionScenariosTest, OneClientInsertAtEnd) { |
| 434 UniquePosition pos = | 417 UniquePosition pos = UniquePosition::InitialPosition(NextClient1Suffix()); |
| 435 UniquePosition::InitialPosition(NextClient1Suffix()); | |
| 436 for (int i = 0; i < 1024; i++) { | 418 for (int i = 0; i < 1024; i++) { |
| 437 const std::string suffix = NextClient1Suffix(); | 419 const std::string suffix = NextClient1Suffix(); |
| 438 UniquePosition new_pos = UniquePosition::After(pos, suffix); | 420 UniquePosition new_pos = UniquePosition::After(pos, suffix); |
| 439 ASSERT_PRED_FORMAT2(LessThan, pos, new_pos); | 421 ASSERT_PRED_FORMAT2(LessThan, pos, new_pos); |
| 440 pos = new_pos; | 422 pos = new_pos; |
| 441 } | 423 } |
| 442 | 424 |
| 443 VLOG(1) << "Length: " << GetLength(pos); | 425 VLOG(1) << "Length: " << GetLength(pos); |
| 444 | 426 |
| 445 // Normally we wouldn't want to make an assertion about the internal | 427 // Normally we wouldn't want to make an assertion about the internal |
| 446 // representation of our data, but we make an exception for this case. | 428 // representation of our data, but we make an exception for this case. |
| 447 // If this scenario causes lengths to explode, we have a big problem. | 429 // If this scenario causes lengths to explode, we have a big problem. |
| 448 EXPECT_LT(GetLength(pos), 500U); | 430 EXPECT_LT(GetLength(pos), 500U); |
| 449 } | 431 } |
| 450 | 432 |
| 451 // Two clients alternately inserting entries at the end, with a strong | 433 // Two clients alternately inserting entries at the end, with a strong |
| 452 // bias towards insertions by the first client. | 434 // bias towards insertions by the first client. |
| 453 TEST_F(PositionScenariosTest, TwoClientsInsertAtEnd_A) { | 435 TEST_F(PositionScenariosTest, TwoClientsInsertAtEnd_A) { |
| 454 UniquePosition pos = | 436 UniquePosition pos = UniquePosition::InitialPosition(NextClient1Suffix()); |
| 455 UniquePosition::InitialPosition(NextClient1Suffix()); | |
| 456 for (int i = 0; i < 1024; i++) { | 437 for (int i = 0; i < 1024; i++) { |
| 457 std::string suffix; | 438 std::string suffix; |
| 458 if (i % 5 == 0) { | 439 if (i % 5 == 0) { |
| 459 suffix = NextClient2Suffix(); | 440 suffix = NextClient2Suffix(); |
| 460 } else { | 441 } else { |
| 461 suffix = NextClient1Suffix(); | 442 suffix = NextClient1Suffix(); |
| 462 } | 443 } |
| 463 | 444 |
| 464 UniquePosition new_pos = UniquePosition::After(pos, suffix); | 445 UniquePosition new_pos = UniquePosition::After(pos, suffix); |
| 465 ASSERT_PRED_FORMAT2(LessThan, pos, new_pos); | 446 ASSERT_PRED_FORMAT2(LessThan, pos, new_pos); |
| 466 pos = new_pos; | 447 pos = new_pos; |
| 467 } | 448 } |
| 468 | 449 |
| 469 VLOG(1) << "Length: " << GetLength(pos); | 450 VLOG(1) << "Length: " << GetLength(pos); |
| 470 EXPECT_LT(GetLength(pos), 500U); | 451 EXPECT_LT(GetLength(pos), 500U); |
| 471 } | 452 } |
| 472 | 453 |
| 473 // Two clients alternately inserting entries at the end. | 454 // Two clients alternately inserting entries at the end. |
| 474 TEST_F(PositionScenariosTest, TwoClientsInsertAtEnd_B) { | 455 TEST_F(PositionScenariosTest, TwoClientsInsertAtEnd_B) { |
| 475 UniquePosition pos = | 456 UniquePosition pos = UniquePosition::InitialPosition(NextClient1Suffix()); |
| 476 UniquePosition::InitialPosition(NextClient1Suffix()); | |
| 477 for (int i = 0; i < 1024; i++) { | 457 for (int i = 0; i < 1024; i++) { |
| 478 std::string suffix; | 458 std::string suffix; |
| 479 if (i % 2 == 0) { | 459 if (i % 2 == 0) { |
| 480 suffix = NextClient1Suffix(); | 460 suffix = NextClient1Suffix(); |
| 481 } else { | 461 } else { |
| 482 suffix = NextClient2Suffix(); | 462 suffix = NextClient2Suffix(); |
| 483 } | 463 } |
| 484 | 464 |
| 485 UniquePosition new_pos = UniquePosition::After(pos, suffix); | 465 UniquePosition new_pos = UniquePosition::After(pos, suffix); |
| 486 ASSERT_PRED_FORMAT2(LessThan, pos, new_pos); | 466 ASSERT_PRED_FORMAT2(LessThan, pos, new_pos); |
| 487 pos = new_pos; | 467 pos = new_pos; |
| 488 } | 468 } |
| 489 | 469 |
| 490 VLOG(1) << "Length: " << GetLength(pos); | 470 VLOG(1) << "Length: " << GetLength(pos); |
| 491 EXPECT_LT(GetLength(pos), 500U); | 471 EXPECT_LT(GetLength(pos), 500U); |
| 492 } | 472 } |
| 493 | 473 |
| 494 INSTANTIATE_TEST_CASE_P(MinSuffix, PositionInsertTest, | 474 INSTANTIATE_TEST_CASE_P(MinSuffix, |
| 475 PositionInsertTest, |
| 495 ::testing::Values(kMinSuffix)); | 476 ::testing::Values(kMinSuffix)); |
| 496 INSTANTIATE_TEST_CASE_P(MaxSuffix, PositionInsertTest, | 477 INSTANTIATE_TEST_CASE_P(MaxSuffix, |
| 478 PositionInsertTest, |
| 497 ::testing::Values(kMaxSuffix)); | 479 ::testing::Values(kMaxSuffix)); |
| 498 INSTANTIATE_TEST_CASE_P(NormalSuffix, PositionInsertTest, | 480 INSTANTIATE_TEST_CASE_P(NormalSuffix, |
| 481 PositionInsertTest, |
| 499 ::testing::Values(kNormalSuffix)); | 482 ::testing::Values(kNormalSuffix)); |
| 500 | 483 |
| 501 class PositionFromIntTest : public UniquePositionTest { | 484 class PositionFromIntTest : public UniquePositionTest { |
| 502 public: | 485 public: |
| 503 PositionFromIntTest() | 486 PositionFromIntTest() |
| 504 : generator_(std::string(kCacheGuidStr1, arraysize(kCacheGuidStr1)-1)) { | 487 : generator_(std::string(kCacheGuidStr1, arraysize(kCacheGuidStr1) - 1)) { |
| 505 } | 488 } |
| 506 | 489 |
| 507 protected: | 490 protected: |
| 508 static const int64_t kTestValues[]; | 491 static const int64_t kTestValues[]; |
| 509 static const size_t kNumTestValues; | 492 static const size_t kNumTestValues; |
| 510 | 493 |
| 511 std::string NextSuffix() { | 494 std::string NextSuffix() { return generator_.NextSuffix(); } |
| 512 return generator_.NextSuffix(); | |
| 513 } | |
| 514 | 495 |
| 515 private: | 496 private: |
| 516 SuffixGenerator generator_; | 497 SuffixGenerator generator_; |
| 517 }; | 498 }; |
| 518 | 499 |
| 519 const int64_t PositionFromIntTest::kTestValues[] = {0LL, | 500 const int64_t PositionFromIntTest::kTestValues[] = {0LL, |
| 520 1LL, | 501 1LL, |
| 521 -1LL, | 502 -1LL, |
| 522 2LL, | 503 2LL, |
| 523 -2LL, | 504 -2LL, |
| (...skipping 28 matching lines...) Expand all Loading... |
| 552 0x112358132134LL, | 533 0x112358132134LL, |
| 553 -0x112358132134LL, | 534 -0x112358132134LL, |
| 554 0xFEFFBEEFABC1234LL, | 535 0xFEFFBEEFABC1234LL, |
| 555 -0xFEFFBEEFABC1234LL, | 536 -0xFEFFBEEFABC1234LL, |
| 556 INT64_MAX, | 537 INT64_MAX, |
| 557 INT64_MIN, | 538 INT64_MIN, |
| 558 INT64_MIN + 1, | 539 INT64_MIN + 1, |
| 559 INT64_MAX - 1}; | 540 INT64_MAX - 1}; |
| 560 | 541 |
| 561 const size_t PositionFromIntTest::kNumTestValues = | 542 const size_t PositionFromIntTest::kNumTestValues = |
| 562 arraysize(PositionFromIntTest::kTestValues); | 543 arraysize(PositionFromIntTest::kTestValues); |
| 563 | 544 |
| 564 TEST_F(PositionFromIntTest, IsValid) { | 545 TEST_F(PositionFromIntTest, IsValid) { |
| 565 for (size_t i = 0; i < kNumTestValues; ++i) { | 546 for (size_t i = 0; i < kNumTestValues; ++i) { |
| 566 const UniquePosition pos = | 547 const UniquePosition pos = |
| 567 UniquePosition::FromInt64(kTestValues[i], NextSuffix()); | 548 UniquePosition::FromInt64(kTestValues[i], NextSuffix()); |
| 568 EXPECT_TRUE(pos.IsValid()) << "i = " << i << "; " << pos.ToDebugString(); | 549 EXPECT_TRUE(pos.IsValid()) << "i = " << i << "; " << pos.ToDebugString(); |
| 569 } | 550 } |
| 570 } | 551 } |
| 571 | 552 |
| 572 TEST_F(PositionFromIntTest, RoundTripConversion) { | 553 TEST_F(PositionFromIntTest, RoundTripConversion) { |
| 573 for (size_t i = 0; i < kNumTestValues; ++i) { | 554 for (size_t i = 0; i < kNumTestValues; ++i) { |
| 574 const int64_t expected_value = kTestValues[i]; | 555 const int64_t expected_value = kTestValues[i]; |
| 575 const UniquePosition pos = | 556 const UniquePosition pos = |
| 576 UniquePosition::FromInt64(kTestValues[i], NextSuffix()); | 557 UniquePosition::FromInt64(kTestValues[i], NextSuffix()); |
| 577 const int64_t value = pos.ToInt64(); | 558 const int64_t value = pos.ToInt64(); |
| 578 EXPECT_EQ(expected_value, value) << "i = " << i; | 559 EXPECT_EQ(expected_value, value) << "i = " << i; |
| 579 } | 560 } |
| 580 } | 561 } |
| 581 | 562 |
| 582 template <typename T, typename LessThan = std::less<T> > | 563 template <typename T, typename LessThan = std::less<T>> |
| 583 class IndexedLessThan { | 564 class IndexedLessThan { |
| 584 public: | 565 public: |
| 585 explicit IndexedLessThan(const T* values) : values_(values) {} | 566 explicit IndexedLessThan(const T* values) : values_(values) {} |
| 586 | 567 |
| 587 bool operator()(int i1, int i2) { | 568 bool operator()(int i1, int i2) { |
| 588 return less_than_(values_[i1], values_[i2]); | 569 return less_than_(values_[i1], values_[i2]); |
| 589 } | 570 } |
| 590 | 571 |
| 591 private: | 572 private: |
| 592 const T* values_; | 573 const T* values_; |
| 593 LessThan less_than_; | 574 LessThan less_than_; |
| 594 }; | 575 }; |
| 595 | 576 |
| 596 TEST_F(PositionFromIntTest, ConsistentOrdering) { | 577 TEST_F(PositionFromIntTest, ConsistentOrdering) { |
| 597 UniquePosition positions[kNumTestValues]; | 578 UniquePosition positions[kNumTestValues]; |
| 598 std::vector<int> original_ordering(kNumTestValues); | 579 std::vector<int> original_ordering(kNumTestValues); |
| 599 std::vector<int> int64_ordering(kNumTestValues); | 580 std::vector<int> int64_ordering(kNumTestValues); |
| 600 std::vector<int> position_ordering(kNumTestValues); | 581 std::vector<int> position_ordering(kNumTestValues); |
| 601 for (size_t i = 0; i < kNumTestValues; ++i) { | 582 for (size_t i = 0; i < kNumTestValues; ++i) { |
| 602 positions[i] = UniquePosition::FromInt64( | 583 positions[i] = UniquePosition::FromInt64(kTestValues[i], NextSuffix()); |
| 603 kTestValues[i], NextSuffix()); | |
| 604 original_ordering[i] = int64_ordering[i] = position_ordering[i] = i; | 584 original_ordering[i] = int64_ordering[i] = position_ordering[i] = i; |
| 605 } | 585 } |
| 606 | 586 |
| 607 std::sort(int64_ordering.begin(), int64_ordering.end(), | 587 std::sort(int64_ordering.begin(), int64_ordering.end(), |
| 608 IndexedLessThan<int64_t>(kTestValues)); | 588 IndexedLessThan<int64_t>(kTestValues)); |
| 609 std::sort(position_ordering.begin(), position_ordering.end(), | 589 std::sort(position_ordering.begin(), position_ordering.end(), |
| 610 IndexedLessThan<UniquePosition, PositionLessThan>(positions)); | 590 IndexedLessThan<UniquePosition, PositionLessThan>(positions)); |
| 611 EXPECT_NE(original_ordering, int64_ordering); | 591 EXPECT_NE(original_ordering, int64_ordering); |
| 612 EXPECT_EQ(int64_ordering, position_ordering); | 592 EXPECT_EQ(int64_ordering, position_ordering); |
| 613 } | 593 } |
| (...skipping 26 matching lines...) Expand all Loading... |
| 640 private: | 620 private: |
| 641 UniquePosition MakePosition(const std::string& compressed_prefix, | 621 UniquePosition MakePosition(const std::string& compressed_prefix, |
| 642 const std::string& compressed_suffix); | 622 const std::string& compressed_suffix); |
| 643 std::string MakeSuffix(char unique_value); | 623 std::string MakeSuffix(char unique_value); |
| 644 | 624 |
| 645 protected: | 625 protected: |
| 646 std::vector<UniquePosition> positions_; | 626 std::vector<UniquePosition> positions_; |
| 647 }; | 627 }; |
| 648 | 628 |
| 649 UniquePosition CompressedPositionTest::MakePosition( | 629 UniquePosition CompressedPositionTest::MakePosition( |
| 650 const std::string& compressed_prefix, | 630 const std::string& compressed_prefix, |
| 651 const std::string& compressed_suffix) { | 631 const std::string& compressed_suffix) { |
| 652 sync_pb::UniquePosition proto; | 632 sync_pb::UniquePosition proto; |
| 653 proto.set_custom_compressed_v1( | 633 proto.set_custom_compressed_v1( |
| 654 std::string(compressed_prefix + compressed_suffix)); | 634 std::string(compressed_prefix + compressed_suffix)); |
| 655 return UniquePosition::FromProto(proto); | 635 return UniquePosition::FromProto(proto); |
| 656 } | 636 } |
| 657 | 637 |
| 658 std::string CompressedPositionTest::MakeSuffix(char unique_value) { | 638 std::string CompressedPositionTest::MakeSuffix(char unique_value) { |
| 659 // We're dealing in compressed positions in this test. That means the | 639 // We're dealing in compressed positions in this test. That means the |
| 660 // suffix should be compressed, too. To avoid complication, we use suffixes | 640 // suffix should be compressed, too. To avoid complication, we use suffixes |
| 661 // that don't have any repeating digits, and therefore are identical in | 641 // that don't have any repeating digits, and therefore are identical in |
| 662 // compressed and uncompressed form. | 642 // compressed and uncompressed form. |
| 663 std::string suffix; | 643 std::string suffix; |
| 664 for (size_t i = 0; i < UniquePosition::kSuffixLength; ++i) { | 644 for (size_t i = 0; i < UniquePosition::kSuffixLength; ++i) { |
| 665 suffix.push_back(static_cast<char>(i)); | 645 suffix.push_back(static_cast<char>(i)); |
| 666 } | 646 } |
| 667 suffix[UniquePosition::kSuffixLength-1] = unique_value; | 647 suffix[UniquePosition::kSuffixLength - 1] = unique_value; |
| 668 return suffix; | 648 return suffix; |
| 669 } | 649 } |
| 670 | 650 |
| 671 // Make sure that serialization and deserialization routines are correct. | 651 // Make sure that serialization and deserialization routines are correct. |
| 672 TEST_F(CompressedPositionTest, SerializeAndDeserialize) { | 652 TEST_F(CompressedPositionTest, SerializeAndDeserialize) { |
| 673 for (std::vector<UniquePosition>::const_iterator it = positions_.begin(); | 653 for (std::vector<UniquePosition>::const_iterator it = positions_.begin(); |
| 674 it != positions_.end(); ++it) { | 654 it != positions_.end(); ++it) { |
| 675 SCOPED_TRACE("iteration: " + it->ToDebugString()); | 655 SCOPED_TRACE("iteration: " + it->ToDebugString()); |
| 676 | 656 |
| 677 sync_pb::UniquePosition proto; | 657 sync_pb::UniquePosition proto; |
| 678 it->ToProto(&proto); | 658 it->ToProto(&proto); |
| 679 UniquePosition deserialized = UniquePosition::FromProto(proto); | 659 UniquePosition deserialized = UniquePosition::FromProto(proto); |
| 680 | 660 |
| 681 EXPECT_PRED_FORMAT2(Equals, *it, deserialized); | 661 EXPECT_PRED_FORMAT2(Equals, *it, deserialized); |
| 682 } | 662 } |
| 683 } | 663 } |
| 684 | 664 |
| 685 // Test that deserialization failures of protobufs where we know none of its | 665 // Test that deserialization failures of protobufs where we know none of its |
| 686 // fields is not catastrophic. This may happen if all the fields currently | 666 // fields is not catastrophic. This may happen if all the fields currently |
| 687 // known to this client become deprecated in the future. | 667 // known to this client become deprecated in the future. |
| 688 TEST_F(CompressedPositionTest, DeserializeProtobufFromTheFuture) { | 668 TEST_F(CompressedPositionTest, DeserializeProtobufFromTheFuture) { |
| 689 sync_pb::UniquePosition proto; | 669 sync_pb::UniquePosition proto; |
| 690 UniquePosition deserialized = UniquePosition::FromProto(proto); | 670 UniquePosition deserialized = UniquePosition::FromProto(proto); |
| 691 EXPECT_FALSE(deserialized.IsValid()); | 671 EXPECT_FALSE(deserialized.IsValid()); |
| 692 } | 672 } |
| 693 | 673 |
| 694 // Make sure the comparison functions are working correctly. | 674 // Make sure the comparison functions are working correctly. |
| 695 // This requires values in the test harness to be hard-coded in ascending order. | 675 // This requires values in the test harness to be hard-coded in ascending order. |
| 696 TEST_F(CompressedPositionTest, OrderingTest) { | 676 TEST_F(CompressedPositionTest, OrderingTest) { |
| 697 for (size_t i = 0; i < positions_.size()-1; ++i) { | 677 for (size_t i = 0; i < positions_.size() - 1; ++i) { |
| 698 EXPECT_PRED_FORMAT2(LessThan, positions_[i], positions_[i+1]); | 678 EXPECT_PRED_FORMAT2(LessThan, positions_[i], positions_[i + 1]); |
| 699 } | 679 } |
| 700 } | 680 } |
| 701 | 681 |
| 702 } // namespace | 682 } // namespace |
| 703 | 683 |
| 704 } // namespace syncer | 684 } // namespace syncer |
| OLD | NEW |