Chromium Code Reviews| 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(UniquePosition &pos) { | |
|
akalin
2012/12/29 10:17:13
const ref, space after & and not before
rlarocque
2013/01/07 23:22:12
Done.
| |
| 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 // Exercise comparision functions by sorting and re-sorting the list. | |
| 114 TEST_F(RelativePositioningTest, SortPositions) { | |
| 115 ASSERT_EQ(kNumPositions, kNumSortedPositions); | |
| 116 UniquePosition positions[arraysize(kPositionArray)]; | |
| 117 for (size_t i = 0; i < kNumPositions; ++i) { | |
| 118 positions[i] = kPositionArray[i]; | |
| 119 } | |
| 120 | |
| 121 std::sort(&positions[0], &positions[kNumPositions], PositionLessThan()); | |
|
akalin
2012/12/29 10:17:13
sorting is undefined if the comparator is inconsis
rlarocque
2013/01/07 23:22:12
I've added some very basic sanity tests in some te
| |
| 122 for (size_t i = 0; i < kNumPositions; ++i) { | |
| 123 EXPECT_TRUE(positions[i].Equals(kSortedPositionArray[i])) | |
| 124 << "i: " << i << ", " | |
| 125 << positions[i].ToDebugString() << " != " | |
| 126 << kSortedPositionArray[i].ToDebugString(); | |
| 127 } | |
| 128 | |
| 129 } | |
| 130 | |
| 131 // Some more exercise for the comparison function. | |
| 132 TEST_F(RelativePositioningTest, ReverseSortPositions) { | |
| 133 ASSERT_EQ(kNumPositions, kNumSortedPositions); | |
| 134 UniquePosition positions[arraysize(kPositionArray)]; | |
| 135 for (size_t i = 0; i < kNumPositions; ++i) { | |
| 136 positions[i] = kPositionArray[i]; | |
| 137 } | |
| 138 | |
| 139 std::reverse(&positions[0], &positions[kNumPositions]); | |
| 140 std::sort(&positions[0], &positions[kNumPositions], PositionLessThan()); | |
| 141 for (size_t i = 0; i < kNumPositions; ++i) { | |
| 142 EXPECT_TRUE(positions[i].Equals(kSortedPositionArray[i])) | |
| 143 << "i: " << i << ", " | |
| 144 << positions[i].ToDebugString() << " != " | |
| 145 << kSortedPositionArray[i].ToDebugString(); | |
| 146 } | |
| 147 } | |
| 148 | |
| 149 class PositionInsertTest : | |
| 150 public RelativePositioningTest, | |
| 151 public ::testing::WithParamInterface<std::string> { }; | |
| 152 | |
| 153 // Exercise InsertBetween with various insertion operations. | |
| 154 TEST_P(PositionInsertTest, InsertBetween) { | |
| 155 const std::string suffix = GetParam(); | |
| 156 ASSERT_TRUE(UniquePosition::IsValidSuffix(suffix)); | |
| 157 | |
| 158 for (size_t i = 0; i < kNumSortedPositions; ++i) { | |
| 159 const UniquePosition& predecessor = kSortedPositionArray[i]; | |
| 160 // Verify our suffixes are unique before we continue. | |
| 161 if (IsSuffixInUse(predecessor, suffix)) | |
| 162 continue; | |
| 163 | |
| 164 for (size_t j = i + 1; j < kNumSortedPositions; ++j) { | |
| 165 const UniquePosition& successor = kSortedPositionArray[j]; | |
| 166 | |
| 167 // Another guard against non-unique suffixes. | |
| 168 if (IsSuffixInUse(successor, suffix)) | |
| 169 continue; | |
| 170 | |
| 171 UniquePosition midpoint = | |
| 172 UniquePosition::Between(predecessor, successor, suffix); | |
| 173 | |
| 174 EXPECT_PRED_FORMAT2(LessThan, predecessor, midpoint); | |
| 175 EXPECT_PRED_FORMAT2(LessThan, midpoint, successor); | |
| 176 } | |
| 177 } | |
| 178 } | |
| 179 | |
| 180 TEST_P(PositionInsertTest, InsertBefore) { | |
| 181 const std::string suffix = GetParam(); | |
| 182 for (size_t i = 0; i < kNumSortedPositions; ++i) { | |
| 183 const UniquePosition& successor = kSortedPositionArray[i]; | |
| 184 // Verify our suffixes are unique before we continue. | |
| 185 if (IsSuffixInUse(successor, suffix)) | |
| 186 continue; | |
| 187 | |
| 188 UniquePosition before = UniquePosition::Before(successor, suffix); | |
| 189 | |
| 190 EXPECT_PRED_FORMAT2(LessThan, before, successor); | |
| 191 } | |
| 192 } | |
| 193 | |
| 194 TEST_P(PositionInsertTest, InsertAfter) { | |
| 195 const std::string suffix = GetParam(); | |
| 196 for (size_t i = 0; i < kNumSortedPositions; ++i) { | |
| 197 const UniquePosition& predecessor = kSortedPositionArray[i]; | |
| 198 // Verify our suffixes are unique before we continue. | |
| 199 if (IsSuffixInUse(predecessor, suffix)) | |
| 200 continue; | |
| 201 | |
| 202 UniquePosition after = UniquePosition::After(predecessor, suffix); | |
| 203 | |
| 204 EXPECT_PRED_FORMAT2(LessThan, predecessor, after); | |
| 205 } | |
| 206 } | |
| 207 | |
| 208 TEST_P(PositionInsertTest, StressInsertAfter) { | |
| 209 // Use two different suffixes to not violate our suffix uniqueness guarantee. | |
| 210 const std::string& suffix_a = GetParam(); | |
| 211 std::string suffix_b = suffix_a; | |
| 212 suffix_b[10] = suffix_b[10] ^ 0xff; | |
| 213 | |
| 214 UniquePosition pos = UniquePosition::InitialPosition(suffix_a); | |
| 215 for (int i = 0; i < 1024; i++) { | |
| 216 const std::string& suffix = (i % 2 == 0) ? suffix_b : suffix_a; | |
| 217 UniquePosition next_pos = UniquePosition::After(pos, suffix); | |
| 218 ASSERT_PRED_FORMAT2(LessThan, pos, next_pos); | |
| 219 pos = next_pos; | |
| 220 } | |
| 221 | |
| 222 VLOG(1) << "Length: " << GetLength(pos); | |
| 223 } | |
| 224 | |
| 225 TEST_P(PositionInsertTest, StressInsertBefore) { | |
| 226 // Use two different suffixes to not violate our suffix uniqueness guarantee. | |
| 227 const std::string& suffix_a = GetParam(); | |
| 228 std::string suffix_b = suffix_a; | |
| 229 suffix_b[10] = suffix_b[10] ^ 0xff; | |
| 230 | |
| 231 UniquePosition pos = UniquePosition::InitialPosition(suffix_a); | |
| 232 for (int i = 0; i < 1024; i++) { | |
| 233 const std::string& suffix = (i % 2 == 0) ? suffix_b : suffix_a; | |
| 234 UniquePosition prev_pos = UniquePosition::Before(pos, suffix); | |
| 235 ASSERT_PRED_FORMAT2(LessThan, prev_pos, pos); | |
| 236 pos = prev_pos; | |
| 237 } | |
| 238 | |
| 239 VLOG(1) << "Length: " << GetLength(pos); | |
| 240 } | |
| 241 | |
| 242 TEST_P(PositionInsertTest, StressLeftInsertBetween) { | |
| 243 // Use different suffixes to not violate our suffix uniqueness guarantee. | |
| 244 const std::string& suffix_a = GetParam(); | |
| 245 std::string suffix_b = suffix_a; | |
| 246 suffix_b[10] = suffix_b[10] ^ 0xff; | |
| 247 std::string suffix_c = suffix_a; | |
| 248 suffix_c[10] = suffix_c[10] ^ 0xf0; | |
| 249 | |
| 250 UniquePosition right_pos = UniquePosition::InitialPosition(suffix_c); | |
| 251 UniquePosition left_pos = UniquePosition::Before(right_pos, suffix_a); | |
| 252 for (int i = 0; i < 1024; i++) { | |
| 253 const std::string& suffix = (i % 2 == 0) ? suffix_b : suffix_a; | |
| 254 UniquePosition new_pos = | |
| 255 UniquePosition::Between(left_pos, right_pos, suffix); | |
| 256 ASSERT_PRED_FORMAT2(LessThan, left_pos, new_pos); | |
| 257 ASSERT_PRED_FORMAT2(LessThan, new_pos, right_pos); | |
| 258 left_pos = new_pos; | |
| 259 } | |
| 260 | |
| 261 VLOG(1) << "Lengths: " << GetLength(left_pos) << ", " << GetLength(right_pos); | |
| 262 } | |
| 263 | |
| 264 TEST_P(PositionInsertTest, StressRightInsertBetween) { | |
| 265 // Use different suffixes to not violate our suffix uniqueness guarantee. | |
| 266 const std::string& suffix_a = GetParam(); | |
| 267 std::string suffix_b = suffix_a; | |
| 268 suffix_b[10] = suffix_b[10] ^ 0xff; | |
| 269 std::string suffix_c = suffix_a; | |
| 270 suffix_c[10] = suffix_c[10] ^ 0xf0; | |
| 271 | |
| 272 UniquePosition right_pos = UniquePosition::InitialPosition(suffix_a); | |
| 273 UniquePosition left_pos = UniquePosition::Before(right_pos, suffix_c); | |
| 274 for (int i = 0; i < 1024; i++) { | |
| 275 const std::string& suffix = (i % 2 == 0) ? suffix_b : suffix_a; | |
| 276 UniquePosition new_pos = | |
| 277 UniquePosition::Between(left_pos, right_pos, suffix); | |
| 278 ASSERT_PRED_FORMAT2(LessThan, left_pos, new_pos); | |
| 279 ASSERT_PRED_FORMAT2(LessThan, new_pos, right_pos); | |
| 280 right_pos = new_pos; | |
| 281 } | |
| 282 | |
| 283 VLOG(1) << "Lengths: " << GetLength(left_pos) << ", " << GetLength(right_pos); | |
| 284 } | |
| 285 | |
| 286 // Generates suffixes similar to those generated by the directory. | |
| 287 // This may become obsolete if the suffix generation code is modified. | |
| 288 class SuffixGenerator { | |
| 289 public: | |
| 290 explicit SuffixGenerator(const std::string& cache_guid) | |
| 291 : cache_guid_(cache_guid), | |
| 292 next_id_(-65535) { | |
| 293 } | |
| 294 | |
| 295 std::string NextSuffix() { | |
| 296 // This is not entirely realistic, but that should be OK. The current | |
| 297 // suffix format is a base64'ed SHA1 hash, which should be fairly close to | |
| 298 // random anyway. | |
| 299 std::string input = cache_guid_ + base::Int64ToString(next_id_--); | |
| 300 std::string output; | |
| 301 CHECK(base::Base64Encode(base::SHA1HashString(input), &output)); | |
| 302 return output; | |
| 303 } | |
| 304 | |
| 305 private: | |
| 306 const std::string cache_guid_; | |
| 307 int64 next_id_; | |
| 308 }; | |
| 309 | |
| 310 // Cache guids generated in the same style as real clients. | |
| 311 static const char kCacheGuidStr1[] = "tuiWdG8hV+8y4RT9N5Aikg=="; | |
| 312 static const char kCacheGuidStr2[] = "yaKb7zHtY06aue9a0vlZgw=="; | |
| 313 | |
| 314 class PositionScenariosTest : public UniquePositionTest { | |
| 315 public: | |
| 316 PositionScenariosTest() | |
| 317 : generator1_(std::string(kCacheGuidStr1, arraysize(kCacheGuidStr1)-1)), | |
| 318 generator2_(std::string(kCacheGuidStr2, arraysize(kCacheGuidStr2)-1)) { | |
| 319 } | |
| 320 | |
| 321 std::string NextClient1Suffix() { | |
| 322 return generator1_.NextSuffix(); | |
| 323 } | |
| 324 | |
| 325 std::string NextClient2Suffix() { | |
| 326 return generator2_.NextSuffix(); | |
| 327 } | |
| 328 | |
| 329 private: | |
| 330 SuffixGenerator generator1_; | |
| 331 SuffixGenerator generator2_; | |
| 332 }; | |
| 333 | |
| 334 // One client creating new bookmarks, always inserting at the end. | |
| 335 TEST_F(PositionScenariosTest, OneClientInsertAtEnd) { | |
| 336 UniquePosition pos = | |
| 337 UniquePosition::InitialPosition(NextClient1Suffix()); | |
| 338 for (int i = 0; i < 1024; i++) { | |
| 339 const std::string suffix = NextClient1Suffix(); | |
| 340 UniquePosition new_pos = UniquePosition::After(pos, suffix); | |
| 341 ASSERT_PRED_FORMAT2(LessThan, pos, new_pos); | |
| 342 pos = new_pos; | |
| 343 } | |
| 344 | |
| 345 VLOG(1) << "Length: " << GetLength(pos); | |
| 346 | |
| 347 // Normally we wouldn't want to make an assertion about the internal | |
| 348 // representation of our data, but we make an exception for this case. | |
| 349 // If this scenario causes lengths to explode, we have a big problem. | |
| 350 EXPECT_LT(GetLength(pos), 500U); | |
| 351 } | |
| 352 | |
| 353 // Two clients alternately inserting entries at the end, with a strong | |
| 354 // bias towards insertions by the first client. | |
| 355 TEST_F(PositionScenariosTest, TwoClientsInsertAtEnd_A) { | |
| 356 UniquePosition pos = | |
| 357 UniquePosition::InitialPosition(NextClient1Suffix()); | |
| 358 for (int i = 0; i < 1024; i++) { | |
| 359 std::string suffix; | |
| 360 if (i % 5 == 0) { | |
| 361 suffix = NextClient2Suffix(); | |
| 362 } else { | |
| 363 suffix = NextClient1Suffix(); | |
| 364 } | |
| 365 | |
| 366 UniquePosition new_pos = UniquePosition::After(pos, suffix); | |
| 367 ASSERT_PRED_FORMAT2(LessThan, pos, new_pos); | |
| 368 pos = new_pos; | |
| 369 } | |
| 370 | |
| 371 VLOG(1) << "Length: " << GetLength(pos); | |
| 372 EXPECT_LT(GetLength(pos), 500U); | |
| 373 } | |
| 374 | |
| 375 // Two clients alternately inserting entries at the end. | |
| 376 TEST_F(PositionScenariosTest, TwoClientsInsertAtEnd_B) { | |
| 377 UniquePosition pos = | |
| 378 UniquePosition::InitialPosition(NextClient1Suffix()); | |
| 379 for (int i = 0; i < 1024; i++) { | |
| 380 std::string suffix; | |
| 381 if (i % 2 == 0) { | |
| 382 suffix = NextClient1Suffix(); | |
| 383 } else { | |
| 384 suffix = NextClient2Suffix(); | |
| 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 INSTANTIATE_TEST_CASE_P(MinSuffix, PositionInsertTest, | |
| 397 ::testing::Values(kMinSuffix)); | |
| 398 INSTANTIATE_TEST_CASE_P(MaxSuffix, PositionInsertTest, | |
| 399 ::testing::Values(kMaxSuffix)); | |
| 400 INSTANTIATE_TEST_CASE_P(NormalSuffix, PositionInsertTest, | |
| 401 ::testing::Values(kNormalSuffix)); | |
| 402 | |
| 403 class PositionFromIntTest : public UniquePositionTest { | |
| 404 public: | |
| 405 PositionFromIntTest() | |
| 406 : generator_(std::string(kCacheGuidStr1, arraysize(kCacheGuidStr1)-1)) { | |
| 407 } | |
| 408 | |
| 409 protected: | |
| 410 std::string NextSuffix() { | |
| 411 return generator_.NextSuffix(); | |
| 412 } | |
| 413 | |
| 414 private: | |
| 415 SuffixGenerator generator_; | |
| 416 }; | |
| 417 | |
| 418 const int64 kTestValues[] = { | |
| 419 0LL, | |
| 420 1LL, -1LL, | |
| 421 2LL, -2LL, | |
| 422 3LL, -3LL, | |
| 423 0x79LL, -0x79LL, | |
| 424 0x80LL, -0x80LL, | |
| 425 0x81LL, -0x81LL, | |
| 426 0xFELL, -0xFELL, | |
| 427 0xFFLL, -0xFFLL, | |
| 428 0x100LL, -0x100LL, | |
| 429 0x101LL, -0x101LL, | |
| 430 0xFA1AFELL, -0xFA1AFELL, | |
| 431 0xFFFFFFFELL, -0xFFFFFFFELL, | |
| 432 0xFFFFFFFFLL, -0xFFFFFFFFLL, | |
| 433 0x100000000LL, -0x100000000LL, | |
| 434 0x100000001LL, -0x100000001LL, | |
| 435 0xFFFFFFFFFFLL, -0xFFFFFFFFFFLL, | |
| 436 0x112358132134LL, -0x112358132134LL, | |
| 437 0xFEFFBEEFABC1234LL, -0xFEFFBEEFABC1234LL, | |
| 438 kint64max, | |
| 439 kint64min, | |
| 440 kint64min + 1, | |
| 441 kint64max - 1 | |
| 442 }; | |
| 443 | |
| 444 const size_t kNumTestValues = arraysize(kTestValues); | |
| 445 | |
| 446 TEST_F(PositionFromIntTest, IsValid) { | |
| 447 for (size_t i = 0; i < kNumTestValues; ++i) { | |
| 448 const UniquePosition pos = | |
| 449 UniquePosition::FromInt64(kTestValues[i], NextSuffix()); | |
| 450 EXPECT_TRUE(pos.IsValid()) << "i = " << i << "; " << pos.ToDebugString(); | |
| 451 } | |
| 452 } | |
| 453 | |
| 454 TEST_F(PositionFromIntTest, RoundTripConversion) { | |
| 455 for (size_t i = 0; i < kNumTestValues; ++i) { | |
| 456 const int64 expected_value = kTestValues[i]; | |
| 457 const UniquePosition pos = | |
| 458 UniquePosition::FromInt64(kTestValues[i], NextSuffix()); | |
| 459 const int64 value = pos.ToInt64(); | |
| 460 EXPECT_EQ(expected_value, value) << "i = " << i; | |
| 461 } | |
| 462 } | |
| 463 | |
| 464 template <typename T, typename LessThan = std::less<T> > | |
| 465 class IndexedLessThan { | |
| 466 public: | |
| 467 IndexedLessThan(const T* values) : values_(values) {} | |
| 468 | |
| 469 bool operator()(int i1, int i2) { | |
| 470 return less_than_(values_[i1], values_[i2]); | |
| 471 } | |
| 472 | |
| 473 private: | |
| 474 const T* values_; | |
| 475 LessThan less_than_; | |
| 476 }; | |
| 477 | |
| 478 TEST_F(PositionFromIntTest, ConsistentOrdering) { | |
| 479 UniquePosition positions[kNumTestValues]; | |
| 480 std::vector<int> original_ordering(kNumTestValues); | |
| 481 std::vector<int> int64_ordering(kNumTestValues); | |
| 482 std::vector<int> position_ordering(kNumTestValues); | |
| 483 for (size_t i = 0; i < kNumTestValues; ++i) { | |
| 484 positions[i] = UniquePosition::FromInt64( | |
| 485 kTestValues[i], NextSuffix()); | |
| 486 original_ordering[i] = int64_ordering[i] = position_ordering[i] = i; | |
| 487 } | |
| 488 | |
| 489 std::sort(int64_ordering.begin(), int64_ordering.end(), | |
| 490 IndexedLessThan<int64>(kTestValues)); | |
| 491 std::sort(position_ordering.begin(), position_ordering.end(), | |
| 492 IndexedLessThan<UniquePosition, PositionLessThan>(positions)); | |
| 493 EXPECT_NE(original_ordering, int64_ordering); | |
| 494 EXPECT_EQ(int64_ordering, position_ordering); | |
| 495 } | |
| 496 | |
| 497 } // namespace | |
| 498 | |
| 499 } // namespace syncer | |
| OLD | NEW |