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 "sync/internal_api/public/base/unique_position.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <string> | 8 #include <string> |
9 | 9 |
| 10 #include "base/base64.h" |
10 #include "base/basictypes.h" | 11 #include "base/basictypes.h" |
11 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/sha1.h" |
| 14 #include "base/string_number_conversions.h" |
12 #include "testing/gtest/include/gtest/gtest.h" | 15 #include "testing/gtest/include/gtest/gtest.h" |
13 | 16 |
14 namespace syncer { | 17 namespace syncer { |
15 | 18 |
16 namespace { | 19 namespace { |
17 | 20 |
18 class UniquePositionTest : public ::testing::Test { | 21 class UniquePositionTest : public ::testing::Test { |
19 protected: | 22 protected: |
20 // Accessor to fetch the length of the position's internal representation. We | 23 // Accessor to fetch the length of the position's internal representation |
21 // don't have any test expectations on it because this is an implementation | 24 // We try to avoid having any test expectations on it because this is an |
22 // detail. | 25 // implementation detail. |
23 // | 26 // |
24 // We use it to see just how big our ordinals are getting in some of the | 27 // If you run the tests with --v=1, we'll print out some of the lengths |
25 // worst-case scenarios that occur in our tests. This can be useful when | 28 // so you can see how well the algorithm performs in various insertion |
26 // testing and comparing alternative implementations. Enable with --v=1. | 29 // scenarios. |
27 size_t GetLength(UniquePosition &pos) { | 30 size_t GetLength(UniquePosition &pos) { |
28 return pos.ToInternalValue().length(); | 31 return pos.ToInternalValue().length(); |
29 } | 32 } |
30 }; | 33 }; |
31 | 34 |
32 const size_t kMinLength = UniquePosition::kSuffixLength; | 35 const size_t kMinLength = UniquePosition::kSuffixLength; |
33 const size_t kGenericPredecessorLength = kMinLength + 2; | 36 const size_t kGenericPredecessorLength = kMinLength + 2; |
34 const size_t kGenericSuccessorLength = kMinLength + 1; | 37 const size_t kGenericSuccessorLength = kMinLength + 1; |
35 const size_t kBigPositionLength = kMinLength; | 38 const size_t kBigPositionLength = kMinLength; |
36 const size_t kSmallPositionLength = kMinLength; | 39 const size_t kSmallPositionLength = kMinLength; |
(...skipping 236 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
273 UniquePosition new_pos = | 276 UniquePosition new_pos = |
274 UniquePosition::Between(left_pos, right_pos, suffix); | 277 UniquePosition::Between(left_pos, right_pos, suffix); |
275 ASSERT_PRED_FORMAT2(LessThan, left_pos, new_pos); | 278 ASSERT_PRED_FORMAT2(LessThan, left_pos, new_pos); |
276 ASSERT_PRED_FORMAT2(LessThan, new_pos, right_pos); | 279 ASSERT_PRED_FORMAT2(LessThan, new_pos, right_pos); |
277 right_pos = new_pos; | 280 right_pos = new_pos; |
278 } | 281 } |
279 | 282 |
280 VLOG(1) << "Lengths: " << GetLength(left_pos) << ", " << GetLength(right_pos); | 283 VLOG(1) << "Lengths: " << GetLength(left_pos) << ", " << GetLength(right_pos); |
281 } | 284 } |
282 | 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 |
283 INSTANTIATE_TEST_CASE_P(MinSuffix, PositionInsertTest, | 396 INSTANTIATE_TEST_CASE_P(MinSuffix, PositionInsertTest, |
284 ::testing::Values(kMinSuffix)); | 397 ::testing::Values(kMinSuffix)); |
285 INSTANTIATE_TEST_CASE_P(MaxSuffix, PositionInsertTest, | 398 INSTANTIATE_TEST_CASE_P(MaxSuffix, PositionInsertTest, |
286 ::testing::Values(kMaxSuffix)); | 399 ::testing::Values(kMaxSuffix)); |
287 INSTANTIATE_TEST_CASE_P(NormalSuffix, PositionInsertTest, | 400 INSTANTIATE_TEST_CASE_P(NormalSuffix, PositionInsertTest, |
288 ::testing::Values(kNormalSuffix)); | 401 ::testing::Values(kNormalSuffix)); |
289 | 402 |
290 class PositionFromIntTest : public UniquePositionTest { | 403 class PositionFromIntTest : public UniquePositionTest { |
| 404 public: |
| 405 PositionFromIntTest() |
| 406 : generator_(std::string(kCacheGuidStr1, arraysize(kCacheGuidStr1)-1)) { |
| 407 } |
| 408 |
291 protected: | 409 protected: |
292 static std::string SuffixFromInt(int64 item_id) { | 410 std::string NextSuffix() { |
293 // Represents a cache_guid-like value. | 411 return generator_.NextSuffix(); |
294 const std::string guid( | 412 } |
295 "\xFF\xFE\xFE\xFF\x00\x01\x01\x00" | |
296 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 16); | |
297 | 413 |
298 return UniquePosition::GenerateBookmarkSuffix(guid, item_id); | 414 private: |
299 } | 415 SuffixGenerator generator_; |
300 }; | 416 }; |
301 | 417 |
302 const int64 kTestValues[] = { | 418 const int64 kTestValues[] = { |
303 0LL, | 419 0LL, |
304 1LL, -1LL, | 420 1LL, -1LL, |
305 2LL, -2LL, | 421 2LL, -2LL, |
306 3LL, -3LL, | 422 3LL, -3LL, |
307 0x79LL, -0x79LL, | 423 0x79LL, -0x79LL, |
308 0x80LL, -0x80LL, | 424 0x80LL, -0x80LL, |
309 0x81LL, -0x81LL, | 425 0x81LL, -0x81LL, |
(...skipping 13 matching lines...) Expand all Loading... |
323 kint64min, | 439 kint64min, |
324 kint64min + 1, | 440 kint64min + 1, |
325 kint64max - 1 | 441 kint64max - 1 |
326 }; | 442 }; |
327 | 443 |
328 const size_t kNumTestValues = arraysize(kTestValues); | 444 const size_t kNumTestValues = arraysize(kTestValues); |
329 | 445 |
330 TEST_F(PositionFromIntTest, IsValid) { | 446 TEST_F(PositionFromIntTest, IsValid) { |
331 for (size_t i = 0; i < kNumTestValues; ++i) { | 447 for (size_t i = 0; i < kNumTestValues; ++i) { |
332 const UniquePosition pos = | 448 const UniquePosition pos = |
333 UniquePosition::FromInt64(kTestValues[i], SuffixFromInt(i)); | 449 UniquePosition::FromInt64(kTestValues[i], NextSuffix()); |
334 EXPECT_TRUE(pos.IsValid()) << "i = " << i << "; " << pos.ToDebugString(); | 450 EXPECT_TRUE(pos.IsValid()) << "i = " << i << "; " << pos.ToDebugString(); |
335 } | 451 } |
336 } | 452 } |
337 | 453 |
338 TEST_F(PositionFromIntTest, RoundTripConversion) { | 454 TEST_F(PositionFromIntTest, RoundTripConversion) { |
339 for (size_t i = 0; i < kNumTestValues; ++i) { | 455 for (size_t i = 0; i < kNumTestValues; ++i) { |
340 const int64 expected_value = kTestValues[i]; | 456 const int64 expected_value = kTestValues[i]; |
341 const UniquePosition pos = | 457 const UniquePosition pos = |
342 UniquePosition::FromInt64(kTestValues[i], SuffixFromInt(i)); | 458 UniquePosition::FromInt64(kTestValues[i], NextSuffix()); |
343 const int64 value = pos.ToInt64(); | 459 const int64 value = pos.ToInt64(); |
344 EXPECT_EQ(expected_value, value) << "i = " << i; | 460 EXPECT_EQ(expected_value, value) << "i = " << i; |
345 } | 461 } |
346 } | 462 } |
347 | 463 |
348 template <typename T, typename LessThan = std::less<T> > | 464 template <typename T, typename LessThan = std::less<T> > |
349 class IndexedLessThan { | 465 class IndexedLessThan { |
350 public: | 466 public: |
351 IndexedLessThan(const T* values) : values_(values) {} | 467 IndexedLessThan(const T* values) : values_(values) {} |
352 | 468 |
353 bool operator()(int i1, int i2) { | 469 bool operator()(int i1, int i2) { |
354 return less_than_(values_[i1], values_[i2]); | 470 return less_than_(values_[i1], values_[i2]); |
355 } | 471 } |
356 | 472 |
357 private: | 473 private: |
358 const T* values_; | 474 const T* values_; |
359 LessThan less_than_; | 475 LessThan less_than_; |
360 }; | 476 }; |
361 | 477 |
362 TEST_F(PositionFromIntTest, ConsistentOrdering) { | 478 TEST_F(PositionFromIntTest, ConsistentOrdering) { |
363 UniquePosition positions[kNumTestValues]; | 479 UniquePosition positions[kNumTestValues]; |
364 std::vector<int> original_ordering(kNumTestValues); | 480 std::vector<int> original_ordering(kNumTestValues); |
365 std::vector<int> int64_ordering(kNumTestValues); | 481 std::vector<int> int64_ordering(kNumTestValues); |
366 std::vector<int> position_ordering(kNumTestValues); | 482 std::vector<int> position_ordering(kNumTestValues); |
367 for (size_t i = 0; i < kNumTestValues; ++i) { | 483 for (size_t i = 0; i < kNumTestValues; ++i) { |
368 positions[i] = UniquePosition::FromInt64( | 484 positions[i] = UniquePosition::FromInt64( |
369 kTestValues[i], SuffixFromInt(i)); | 485 kTestValues[i], NextSuffix()); |
370 original_ordering[i] = int64_ordering[i] = position_ordering[i] = i; | 486 original_ordering[i] = int64_ordering[i] = position_ordering[i] = i; |
371 } | 487 } |
372 | 488 |
373 std::sort(int64_ordering.begin(), int64_ordering.end(), | 489 std::sort(int64_ordering.begin(), int64_ordering.end(), |
374 IndexedLessThan<int64>(kTestValues)); | 490 IndexedLessThan<int64>(kTestValues)); |
375 std::sort(position_ordering.begin(), position_ordering.end(), | 491 std::sort(position_ordering.begin(), position_ordering.end(), |
376 IndexedLessThan<UniquePosition, PositionLessThan>(positions)); | 492 IndexedLessThan<UniquePosition, PositionLessThan>(positions)); |
377 EXPECT_NE(original_ordering, int64_ordering); | 493 EXPECT_NE(original_ordering, int64_ordering); |
378 EXPECT_EQ(int64_ordering, position_ordering); | 494 EXPECT_EQ(int64_ordering, position_ordering); |
379 } | 495 } |
380 | 496 |
381 } // namespace | 497 } // namespace |
382 | 498 |
383 } // namespace syncer | 499 } // namespace syncer |
OLD | NEW |