OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "sync/internal_api/public/base/unique_position.h" |
| 6 |
| 7 #include <algorithm> |
| 8 #include <string> |
| 9 |
| 10 #include "base/base64.h" |
| 11 #include "base/basictypes.h" |
| 12 #include "base/logging.h" |
| 13 #include "base/sha1.h" |
| 14 #include "base/string_number_conversions.h" |
| 15 #include "testing/gtest/include/gtest/gtest.h" |
| 16 |
| 17 namespace syncer { |
| 18 |
| 19 namespace { |
| 20 |
| 21 class UniquePositionTest : public ::testing::Test { |
| 22 protected: |
| 23 // Accessor to fetch the length of the position's internal representation |
| 24 // We try to avoid having any test expectations on it because this is an |
| 25 // implementation detail. |
| 26 // |
| 27 // If you run the tests with --v=1, we'll print out some of the lengths |
| 28 // so you can see how well the algorithm performs in various insertion |
| 29 // scenarios. |
| 30 size_t GetLength(const UniquePosition& pos) { |
| 31 return pos.ToInternalValue().length(); |
| 32 } |
| 33 }; |
| 34 |
| 35 const size_t kMinLength = UniquePosition::kSuffixLength; |
| 36 const size_t kGenericPredecessorLength = kMinLength + 2; |
| 37 const size_t kGenericSuccessorLength = kMinLength + 1; |
| 38 const size_t kBigPositionLength = kMinLength; |
| 39 const size_t kSmallPositionLength = kMinLength; |
| 40 |
| 41 // Be careful when adding more prefixes to this list. |
| 42 // We have to manually ensure each has a unique suffix. |
| 43 const UniquePosition kGenericPredecessor = UniquePosition::FromBytes( |
| 44 (std::string(kGenericPredecessorLength, '\x23') + '\xFF')); |
| 45 const UniquePosition kGenericSuccessor = UniquePosition::FromBytes( |
| 46 std::string(kGenericSuccessorLength, '\xAB') + '\xFF'); |
| 47 const UniquePosition kBigPosition = UniquePosition::FromBytes( |
| 48 std::string(kBigPositionLength - 1, '\xFF') + '\xFE' + '\xFF'); |
| 49 const UniquePosition kBigPositionLessTwo = UniquePosition::FromBytes( |
| 50 std::string(kBigPositionLength - 1, '\xFF') + '\xFC' + '\xFF'); |
| 51 const UniquePosition kBiggerPosition = UniquePosition::FromBytes( |
| 52 std::string(kBigPositionLength, '\xFF') + '\xFF'); |
| 53 const UniquePosition kSmallPosition = UniquePosition::FromBytes( |
| 54 std::string(kSmallPositionLength - 1, '\x00') + '\x01' + '\xFF'); |
| 55 const UniquePosition kSmallPositionPlusOne = UniquePosition::FromBytes( |
| 56 std::string(kSmallPositionLength - 1, '\x00') + '\x02' + '\xFF'); |
| 57 |
| 58 const std::string kMinSuffix = |
| 59 std::string(UniquePosition::kSuffixLength - 1, '\x00') + '\x01'; |
| 60 const std::string kMaxSuffix(UniquePosition::kSuffixLength, '\xFF'); |
| 61 const std::string kNormalSuffix(UniquePosition::kSuffixLength, '\xAB'); |
| 62 |
| 63 ::testing::AssertionResult LessThan(const char* m_expr, |
| 64 const char* n_expr, |
| 65 const UniquePosition &m, |
| 66 const UniquePosition &n) { |
| 67 if (m.LessThan(n)) |
| 68 return ::testing::AssertionSuccess(); |
| 69 |
| 70 return ::testing::AssertionFailure() |
| 71 << m_expr << " is not less than " << n_expr |
| 72 << " (" << m.ToDebugString() << " and " << n.ToDebugString() << ")"; |
| 73 } |
| 74 |
| 75 class RelativePositioningTest : public UniquePositionTest { }; |
| 76 |
| 77 const UniquePosition kPositionArray[] = { |
| 78 kGenericPredecessor, |
| 79 kGenericSuccessor, |
| 80 kBigPosition, |
| 81 kBigPositionLessTwo, |
| 82 kBiggerPosition, |
| 83 kSmallPosition, |
| 84 kSmallPositionPlusOne, |
| 85 }; |
| 86 |
| 87 const UniquePosition kSortedPositionArray[] = { |
| 88 kSmallPosition, |
| 89 kSmallPositionPlusOne, |
| 90 kGenericPredecessor, |
| 91 kGenericSuccessor, |
| 92 kBigPositionLessTwo, |
| 93 kBigPosition, |
| 94 kBiggerPosition, |
| 95 }; |
| 96 |
| 97 static const size_t kNumPositions = arraysize(kPositionArray); |
| 98 static const size_t kNumSortedPositions = arraysize(kSortedPositionArray); |
| 99 |
| 100 struct PositionLessThan { |
| 101 bool operator()(const UniquePosition& a, const UniquePosition& b) { |
| 102 return a.LessThan(b); |
| 103 } |
| 104 }; |
| 105 |
| 106 static bool IsSuffixInUse( |
| 107 const UniquePosition& pos, const std::string& suffix) { |
| 108 const std::string& pos_bytes = pos.ToInternalValue(); |
| 109 const size_t prefix_len = pos_bytes.length() - UniquePosition::kSuffixLength; |
| 110 return pos_bytes.substr(prefix_len, std::string::npos) == suffix; |
| 111 } |
| 112 |
| 113 // Test some basic properties of comparison and equality. |
| 114 TEST_F(RelativePositioningTest, ComparisonSanityTest1) { |
| 115 const UniquePosition& a = kPositionArray[0]; |
| 116 ASSERT_TRUE(a.IsValid()); |
| 117 |
| 118 // Necessarily true for any non-invalid positions. |
| 119 EXPECT_TRUE(a.Equals(a)); |
| 120 EXPECT_FALSE(a.LessThan(a)); |
| 121 } |
| 122 |
| 123 // Test some more properties of comparison and equality. |
| 124 TEST_F(RelativePositioningTest, ComparisonSanityTest2) { |
| 125 const UniquePosition& a = kPositionArray[0]; |
| 126 const UniquePosition& b = kPositionArray[1]; |
| 127 |
| 128 // These should pass for the specific a and b we have chosen (a < b). |
| 129 EXPECT_FALSE(a.Equals(b)); |
| 130 EXPECT_TRUE(a.LessThan(b)); |
| 131 EXPECT_FALSE(b.LessThan(a)); |
| 132 } |
| 133 |
| 134 // Exercise comparision functions by sorting and re-sorting the list. |
| 135 TEST_F(RelativePositioningTest, SortPositions) { |
| 136 ASSERT_EQ(kNumPositions, kNumSortedPositions); |
| 137 UniquePosition positions[arraysize(kPositionArray)]; |
| 138 for (size_t i = 0; i < kNumPositions; ++i) { |
| 139 positions[i] = kPositionArray[i]; |
| 140 } |
| 141 |
| 142 std::sort(&positions[0], &positions[kNumPositions], PositionLessThan()); |
| 143 for (size_t i = 0; i < kNumPositions; ++i) { |
| 144 EXPECT_TRUE(positions[i].Equals(kSortedPositionArray[i])) |
| 145 << "i: " << i << ", " |
| 146 << positions[i].ToDebugString() << " != " |
| 147 << kSortedPositionArray[i].ToDebugString(); |
| 148 } |
| 149 |
| 150 } |
| 151 |
| 152 // Some more exercise for the comparison function. |
| 153 TEST_F(RelativePositioningTest, ReverseSortPositions) { |
| 154 ASSERT_EQ(kNumPositions, kNumSortedPositions); |
| 155 UniquePosition positions[arraysize(kPositionArray)]; |
| 156 for (size_t i = 0; i < kNumPositions; ++i) { |
| 157 positions[i] = kPositionArray[i]; |
| 158 } |
| 159 |
| 160 std::reverse(&positions[0], &positions[kNumPositions]); |
| 161 std::sort(&positions[0], &positions[kNumPositions], PositionLessThan()); |
| 162 for (size_t i = 0; i < kNumPositions; ++i) { |
| 163 EXPECT_TRUE(positions[i].Equals(kSortedPositionArray[i])) |
| 164 << "i: " << i << ", " |
| 165 << positions[i].ToDebugString() << " != " |
| 166 << kSortedPositionArray[i].ToDebugString(); |
| 167 } |
| 168 } |
| 169 |
| 170 class PositionInsertTest : |
| 171 public RelativePositioningTest, |
| 172 public ::testing::WithParamInterface<std::string> { }; |
| 173 |
| 174 // Exercise InsertBetween with various insertion operations. |
| 175 TEST_P(PositionInsertTest, InsertBetween) { |
| 176 const std::string suffix = GetParam(); |
| 177 ASSERT_TRUE(UniquePosition::IsValidSuffix(suffix)); |
| 178 |
| 179 for (size_t i = 0; i < kNumSortedPositions; ++i) { |
| 180 const UniquePosition& predecessor = kSortedPositionArray[i]; |
| 181 // Verify our suffixes are unique before we continue. |
| 182 if (IsSuffixInUse(predecessor, suffix)) |
| 183 continue; |
| 184 |
| 185 for (size_t j = i + 1; j < kNumSortedPositions; ++j) { |
| 186 const UniquePosition& successor = kSortedPositionArray[j]; |
| 187 |
| 188 // Another guard against non-unique suffixes. |
| 189 if (IsSuffixInUse(successor, suffix)) |
| 190 continue; |
| 191 |
| 192 UniquePosition midpoint = |
| 193 UniquePosition::Between(predecessor, successor, suffix); |
| 194 |
| 195 EXPECT_PRED_FORMAT2(LessThan, predecessor, midpoint); |
| 196 EXPECT_PRED_FORMAT2(LessThan, midpoint, successor); |
| 197 } |
| 198 } |
| 199 } |
| 200 |
| 201 TEST_P(PositionInsertTest, InsertBefore) { |
| 202 const std::string suffix = GetParam(); |
| 203 for (size_t i = 0; i < kNumSortedPositions; ++i) { |
| 204 const UniquePosition& successor = kSortedPositionArray[i]; |
| 205 // Verify our suffixes are unique before we continue. |
| 206 if (IsSuffixInUse(successor, suffix)) |
| 207 continue; |
| 208 |
| 209 UniquePosition before = UniquePosition::Before(successor, suffix); |
| 210 |
| 211 EXPECT_PRED_FORMAT2(LessThan, before, successor); |
| 212 } |
| 213 } |
| 214 |
| 215 TEST_P(PositionInsertTest, InsertAfter) { |
| 216 const std::string suffix = GetParam(); |
| 217 for (size_t i = 0; i < kNumSortedPositions; ++i) { |
| 218 const UniquePosition& predecessor = kSortedPositionArray[i]; |
| 219 // Verify our suffixes are unique before we continue. |
| 220 if (IsSuffixInUse(predecessor, suffix)) |
| 221 continue; |
| 222 |
| 223 UniquePosition after = UniquePosition::After(predecessor, suffix); |
| 224 |
| 225 EXPECT_PRED_FORMAT2(LessThan, predecessor, after); |
| 226 } |
| 227 } |
| 228 |
| 229 TEST_P(PositionInsertTest, StressInsertAfter) { |
| 230 // Use two different suffixes to not violate our suffix uniqueness guarantee. |
| 231 const std::string& suffix_a = GetParam(); |
| 232 std::string suffix_b = suffix_a; |
| 233 suffix_b[10] = suffix_b[10] ^ 0xff; |
| 234 |
| 235 UniquePosition pos = UniquePosition::InitialPosition(suffix_a); |
| 236 for (int i = 0; i < 1024; i++) { |
| 237 const std::string& suffix = (i % 2 == 0) ? suffix_b : suffix_a; |
| 238 UniquePosition next_pos = UniquePosition::After(pos, suffix); |
| 239 ASSERT_PRED_FORMAT2(LessThan, pos, next_pos); |
| 240 pos = next_pos; |
| 241 } |
| 242 |
| 243 VLOG(1) << "Length: " << GetLength(pos); |
| 244 } |
| 245 |
| 246 TEST_P(PositionInsertTest, StressInsertBefore) { |
| 247 // Use two different suffixes to not violate our suffix uniqueness guarantee. |
| 248 const std::string& suffix_a = GetParam(); |
| 249 std::string suffix_b = suffix_a; |
| 250 suffix_b[10] = suffix_b[10] ^ 0xff; |
| 251 |
| 252 UniquePosition pos = UniquePosition::InitialPosition(suffix_a); |
| 253 for (int i = 0; i < 1024; i++) { |
| 254 const std::string& suffix = (i % 2 == 0) ? suffix_b : suffix_a; |
| 255 UniquePosition prev_pos = UniquePosition::Before(pos, suffix); |
| 256 ASSERT_PRED_FORMAT2(LessThan, prev_pos, pos); |
| 257 pos = prev_pos; |
| 258 } |
| 259 |
| 260 VLOG(1) << "Length: " << GetLength(pos); |
| 261 } |
| 262 |
| 263 TEST_P(PositionInsertTest, StressLeftInsertBetween) { |
| 264 // Use different suffixes to not violate our suffix uniqueness guarantee. |
| 265 const std::string& suffix_a = GetParam(); |
| 266 std::string suffix_b = suffix_a; |
| 267 suffix_b[10] = suffix_b[10] ^ 0xff; |
| 268 std::string suffix_c = suffix_a; |
| 269 suffix_c[10] = suffix_c[10] ^ 0xf0; |
| 270 |
| 271 UniquePosition right_pos = UniquePosition::InitialPosition(suffix_c); |
| 272 UniquePosition left_pos = UniquePosition::Before(right_pos, suffix_a); |
| 273 for (int i = 0; i < 1024; i++) { |
| 274 const std::string& suffix = (i % 2 == 0) ? suffix_b : suffix_a; |
| 275 UniquePosition new_pos = |
| 276 UniquePosition::Between(left_pos, right_pos, suffix); |
| 277 ASSERT_PRED_FORMAT2(LessThan, left_pos, new_pos); |
| 278 ASSERT_PRED_FORMAT2(LessThan, new_pos, right_pos); |
| 279 left_pos = new_pos; |
| 280 } |
| 281 |
| 282 VLOG(1) << "Lengths: " << GetLength(left_pos) << ", " << GetLength(right_pos); |
| 283 } |
| 284 |
| 285 TEST_P(PositionInsertTest, StressRightInsertBetween) { |
| 286 // Use different suffixes to not violate our suffix uniqueness guarantee. |
| 287 const std::string& suffix_a = GetParam(); |
| 288 std::string suffix_b = suffix_a; |
| 289 suffix_b[10] = suffix_b[10] ^ 0xff; |
| 290 std::string suffix_c = suffix_a; |
| 291 suffix_c[10] = suffix_c[10] ^ 0xf0; |
| 292 |
| 293 UniquePosition right_pos = UniquePosition::InitialPosition(suffix_a); |
| 294 UniquePosition left_pos = UniquePosition::Before(right_pos, suffix_c); |
| 295 for (int i = 0; i < 1024; i++) { |
| 296 const std::string& suffix = (i % 2 == 0) ? suffix_b : suffix_a; |
| 297 UniquePosition new_pos = |
| 298 UniquePosition::Between(left_pos, right_pos, suffix); |
| 299 ASSERT_PRED_FORMAT2(LessThan, left_pos, new_pos); |
| 300 ASSERT_PRED_FORMAT2(LessThan, new_pos, right_pos); |
| 301 right_pos = new_pos; |
| 302 } |
| 303 |
| 304 VLOG(1) << "Lengths: " << GetLength(left_pos) << ", " << GetLength(right_pos); |
| 305 } |
| 306 |
| 307 // Generates suffixes similar to those generated by the directory. |
| 308 // This may become obsolete if the suffix generation code is modified. |
| 309 class SuffixGenerator { |
| 310 public: |
| 311 explicit SuffixGenerator(const std::string& cache_guid) |
| 312 : cache_guid_(cache_guid), |
| 313 next_id_(-65535) { |
| 314 } |
| 315 |
| 316 std::string NextSuffix() { |
| 317 // This is not entirely realistic, but that should be OK. The current |
| 318 // suffix format is a base64'ed SHA1 hash, which should be fairly close to |
| 319 // random anyway. |
| 320 std::string input = cache_guid_ + base::Int64ToString(next_id_--); |
| 321 std::string output; |
| 322 CHECK(base::Base64Encode(base::SHA1HashString(input), &output)); |
| 323 return output; |
| 324 } |
| 325 |
| 326 private: |
| 327 const std::string cache_guid_; |
| 328 int64 next_id_; |
| 329 }; |
| 330 |
| 331 // Cache guids generated in the same style as real clients. |
| 332 static const char kCacheGuidStr1[] = "tuiWdG8hV+8y4RT9N5Aikg=="; |
| 333 static const char kCacheGuidStr2[] = "yaKb7zHtY06aue9a0vlZgw=="; |
| 334 |
| 335 class PositionScenariosTest : public UniquePositionTest { |
| 336 public: |
| 337 PositionScenariosTest() |
| 338 : generator1_(std::string(kCacheGuidStr1, arraysize(kCacheGuidStr1)-1)), |
| 339 generator2_(std::string(kCacheGuidStr2, arraysize(kCacheGuidStr2)-1)) { |
| 340 } |
| 341 |
| 342 std::string NextClient1Suffix() { |
| 343 return generator1_.NextSuffix(); |
| 344 } |
| 345 |
| 346 std::string NextClient2Suffix() { |
| 347 return generator2_.NextSuffix(); |
| 348 } |
| 349 |
| 350 private: |
| 351 SuffixGenerator generator1_; |
| 352 SuffixGenerator generator2_; |
| 353 }; |
| 354 |
| 355 // One client creating new bookmarks, always inserting at the end. |
| 356 TEST_F(PositionScenariosTest, OneClientInsertAtEnd) { |
| 357 UniquePosition pos = |
| 358 UniquePosition::InitialPosition(NextClient1Suffix()); |
| 359 for (int i = 0; i < 1024; i++) { |
| 360 const std::string suffix = NextClient1Suffix(); |
| 361 UniquePosition new_pos = UniquePosition::After(pos, suffix); |
| 362 ASSERT_PRED_FORMAT2(LessThan, pos, new_pos); |
| 363 pos = new_pos; |
| 364 } |
| 365 |
| 366 VLOG(1) << "Length: " << GetLength(pos); |
| 367 |
| 368 // Normally we wouldn't want to make an assertion about the internal |
| 369 // representation of our data, but we make an exception for this case. |
| 370 // If this scenario causes lengths to explode, we have a big problem. |
| 371 EXPECT_LT(GetLength(pos), 500U); |
| 372 } |
| 373 |
| 374 // Two clients alternately inserting entries at the end, with a strong |
| 375 // bias towards insertions by the first client. |
| 376 TEST_F(PositionScenariosTest, TwoClientsInsertAtEnd_A) { |
| 377 UniquePosition pos = |
| 378 UniquePosition::InitialPosition(NextClient1Suffix()); |
| 379 for (int i = 0; i < 1024; i++) { |
| 380 std::string suffix; |
| 381 if (i % 5 == 0) { |
| 382 suffix = NextClient2Suffix(); |
| 383 } else { |
| 384 suffix = NextClient1Suffix(); |
| 385 } |
| 386 |
| 387 UniquePosition new_pos = UniquePosition::After(pos, suffix); |
| 388 ASSERT_PRED_FORMAT2(LessThan, pos, new_pos); |
| 389 pos = new_pos; |
| 390 } |
| 391 |
| 392 VLOG(1) << "Length: " << GetLength(pos); |
| 393 EXPECT_LT(GetLength(pos), 500U); |
| 394 } |
| 395 |
| 396 // Two clients alternately inserting entries at the end. |
| 397 TEST_F(PositionScenariosTest, TwoClientsInsertAtEnd_B) { |
| 398 UniquePosition pos = |
| 399 UniquePosition::InitialPosition(NextClient1Suffix()); |
| 400 for (int i = 0; i < 1024; i++) { |
| 401 std::string suffix; |
| 402 if (i % 2 == 0) { |
| 403 suffix = NextClient1Suffix(); |
| 404 } else { |
| 405 suffix = NextClient2Suffix(); |
| 406 } |
| 407 |
| 408 UniquePosition new_pos = UniquePosition::After(pos, suffix); |
| 409 ASSERT_PRED_FORMAT2(LessThan, pos, new_pos); |
| 410 pos = new_pos; |
| 411 } |
| 412 |
| 413 VLOG(1) << "Length: " << GetLength(pos); |
| 414 EXPECT_LT(GetLength(pos), 500U); |
| 415 } |
| 416 |
| 417 INSTANTIATE_TEST_CASE_P(MinSuffix, PositionInsertTest, |
| 418 ::testing::Values(kMinSuffix)); |
| 419 INSTANTIATE_TEST_CASE_P(MaxSuffix, PositionInsertTest, |
| 420 ::testing::Values(kMaxSuffix)); |
| 421 INSTANTIATE_TEST_CASE_P(NormalSuffix, PositionInsertTest, |
| 422 ::testing::Values(kNormalSuffix)); |
| 423 |
| 424 class PositionFromIntTest : public UniquePositionTest { |
| 425 public: |
| 426 PositionFromIntTest() |
| 427 : generator_(std::string(kCacheGuidStr1, arraysize(kCacheGuidStr1)-1)) { |
| 428 } |
| 429 |
| 430 protected: |
| 431 std::string NextSuffix() { |
| 432 return generator_.NextSuffix(); |
| 433 } |
| 434 |
| 435 private: |
| 436 SuffixGenerator generator_; |
| 437 }; |
| 438 |
| 439 const int64 kTestValues[] = { |
| 440 0LL, |
| 441 1LL, -1LL, |
| 442 2LL, -2LL, |
| 443 3LL, -3LL, |
| 444 0x79LL, -0x79LL, |
| 445 0x80LL, -0x80LL, |
| 446 0x81LL, -0x81LL, |
| 447 0xFELL, -0xFELL, |
| 448 0xFFLL, -0xFFLL, |
| 449 0x100LL, -0x100LL, |
| 450 0x101LL, -0x101LL, |
| 451 0xFA1AFELL, -0xFA1AFELL, |
| 452 0xFFFFFFFELL, -0xFFFFFFFELL, |
| 453 0xFFFFFFFFLL, -0xFFFFFFFFLL, |
| 454 0x100000000LL, -0x100000000LL, |
| 455 0x100000001LL, -0x100000001LL, |
| 456 0xFFFFFFFFFFLL, -0xFFFFFFFFFFLL, |
| 457 0x112358132134LL, -0x112358132134LL, |
| 458 0xFEFFBEEFABC1234LL, -0xFEFFBEEFABC1234LL, |
| 459 kint64max, |
| 460 kint64min, |
| 461 kint64min + 1, |
| 462 kint64max - 1 |
| 463 }; |
| 464 |
| 465 const size_t kNumTestValues = arraysize(kTestValues); |
| 466 |
| 467 TEST_F(PositionFromIntTest, IsValid) { |
| 468 for (size_t i = 0; i < kNumTestValues; ++i) { |
| 469 const UniquePosition pos = |
| 470 UniquePosition::FromInt64(kTestValues[i], NextSuffix()); |
| 471 EXPECT_TRUE(pos.IsValid()) << "i = " << i << "; " << pos.ToDebugString(); |
| 472 } |
| 473 } |
| 474 |
| 475 TEST_F(PositionFromIntTest, RoundTripConversion) { |
| 476 for (size_t i = 0; i < kNumTestValues; ++i) { |
| 477 const int64 expected_value = kTestValues[i]; |
| 478 const UniquePosition pos = |
| 479 UniquePosition::FromInt64(kTestValues[i], NextSuffix()); |
| 480 const int64 value = pos.ToInt64(); |
| 481 EXPECT_EQ(expected_value, value) << "i = " << i; |
| 482 } |
| 483 } |
| 484 |
| 485 template <typename T, typename LessThan = std::less<T> > |
| 486 class IndexedLessThan { |
| 487 public: |
| 488 IndexedLessThan(const T* values) : values_(values) {} |
| 489 |
| 490 bool operator()(int i1, int i2) { |
| 491 return less_than_(values_[i1], values_[i2]); |
| 492 } |
| 493 |
| 494 private: |
| 495 const T* values_; |
| 496 LessThan less_than_; |
| 497 }; |
| 498 |
| 499 TEST_F(PositionFromIntTest, ConsistentOrdering) { |
| 500 UniquePosition positions[kNumTestValues]; |
| 501 std::vector<int> original_ordering(kNumTestValues); |
| 502 std::vector<int> int64_ordering(kNumTestValues); |
| 503 std::vector<int> position_ordering(kNumTestValues); |
| 504 for (size_t i = 0; i < kNumTestValues; ++i) { |
| 505 positions[i] = UniquePosition::FromInt64( |
| 506 kTestValues[i], NextSuffix()); |
| 507 original_ordering[i] = int64_ordering[i] = position_ordering[i] = i; |
| 508 } |
| 509 |
| 510 std::sort(int64_ordering.begin(), int64_ordering.end(), |
| 511 IndexedLessThan<int64>(kTestValues)); |
| 512 std::sort(position_ordering.begin(), position_ordering.end(), |
| 513 IndexedLessThan<UniquePosition, PositionLessThan>(positions)); |
| 514 EXPECT_NE(original_ordering, int64_ordering); |
| 515 EXPECT_EQ(int64_ordering, position_ordering); |
| 516 } |
| 517 |
| 518 } // namespace |
| 519 |
| 520 } // namespace syncer |
OLD | NEW |