| Index: sync/internal_api/public/base/unique_position_unittest.cc
|
| diff --git a/sync/internal_api/public/base/unique_position_unittest.cc b/sync/internal_api/public/base/unique_position_unittest.cc
|
| index 001a57558a0a8b75a3afb1cf17baf552fa524284..1afe29415e78a5a36cbc9a7f22237071d55fa7a7 100644
|
| --- a/sync/internal_api/public/base/unique_position_unittest.cc
|
| +++ b/sync/internal_api/public/base/unique_position_unittest.cc
|
| @@ -7,8 +7,11 @@
|
| #include <algorithm>
|
| #include <string>
|
|
|
| +#include "base/base64.h"
|
| #include "base/basictypes.h"
|
| #include "base/logging.h"
|
| +#include "base/sha1.h"
|
| +#include "base/string_number_conversions.h"
|
| #include "testing/gtest/include/gtest/gtest.h"
|
|
|
| namespace syncer {
|
| @@ -17,13 +20,13 @@ namespace {
|
|
|
| class UniquePositionTest : public ::testing::Test {
|
| protected:
|
| - // Accessor to fetch the length of the position's internal representation. We
|
| - // don't have any test expectations on it because this is an implementation
|
| - // detail.
|
| + // Accessor to fetch the length of the position's internal representation
|
| + // We try to avoid having any test expectations on it because this is an
|
| + // implementation detail.
|
| //
|
| - // We use it to see just how big our ordinals are getting in some of the
|
| - // worst-case scenarios that occur in our tests. This can be useful when
|
| - // testing and comparing alternative implementations. Enable with --v=1.
|
| + // If you run the tests with --v=1, we'll print out some of the lengths
|
| + // so you can see how well the algorithm performs in various insertion
|
| + // scenarios.
|
| size_t GetLength(UniquePosition &pos) {
|
| return pos.ToInternalValue().length();
|
| }
|
| @@ -280,6 +283,116 @@ TEST_P(PositionInsertTest, StressRightInsertBetween) {
|
| VLOG(1) << "Lengths: " << GetLength(left_pos) << ", " << GetLength(right_pos);
|
| }
|
|
|
| +// Generates suffixes similar to those generated by the directory.
|
| +// This may become obsolete if the suffix generation code is modified.
|
| +class SuffixGenerator {
|
| + public:
|
| + explicit SuffixGenerator(const std::string& cache_guid)
|
| + : cache_guid_(cache_guid),
|
| + next_id_(-65535) {
|
| + }
|
| +
|
| + std::string NextSuffix() {
|
| + // This is not entirely realistic, but that should be OK. The current
|
| + // suffix format is a base64'ed SHA1 hash, which should be fairly close to
|
| + // random anyway.
|
| + std::string input = cache_guid_ + base::Int64ToString(next_id_--);
|
| + std::string output;
|
| + CHECK(base::Base64Encode(base::SHA1HashString(input), &output));
|
| + return output;
|
| + }
|
| +
|
| + private:
|
| + const std::string cache_guid_;
|
| + int64 next_id_;
|
| +};
|
| +
|
| +// Cache guids generated in the same style as real clients.
|
| +static const char kCacheGuidStr1[] = "tuiWdG8hV+8y4RT9N5Aikg==";
|
| +static const char kCacheGuidStr2[] = "yaKb7zHtY06aue9a0vlZgw==";
|
| +
|
| +class PositionScenariosTest : public UniquePositionTest {
|
| + public:
|
| + PositionScenariosTest()
|
| + : generator1_(std::string(kCacheGuidStr1, arraysize(kCacheGuidStr1)-1)),
|
| + generator2_(std::string(kCacheGuidStr2, arraysize(kCacheGuidStr2)-1)) {
|
| + }
|
| +
|
| + std::string NextClient1Suffix() {
|
| + return generator1_.NextSuffix();
|
| + }
|
| +
|
| + std::string NextClient2Suffix() {
|
| + return generator2_.NextSuffix();
|
| + }
|
| +
|
| + private:
|
| + SuffixGenerator generator1_;
|
| + SuffixGenerator generator2_;
|
| +};
|
| +
|
| +// One client creating new bookmarks, always inserting at the end.
|
| +TEST_F(PositionScenariosTest, OneClientInsertAtEnd) {
|
| + UniquePosition pos =
|
| + UniquePosition::InitialPosition(NextClient1Suffix());
|
| + for (int i = 0; i < 1024; i++) {
|
| + const std::string suffix = NextClient1Suffix();
|
| + UniquePosition new_pos = UniquePosition::After(pos, suffix);
|
| + ASSERT_PRED_FORMAT2(LessThan, pos, new_pos);
|
| + pos = new_pos;
|
| + }
|
| +
|
| + VLOG(1) << "Length: " << GetLength(pos);
|
| +
|
| + // Normally we wouldn't want to make an assertion about the internal
|
| + // representation of our data, but we make an exception for this case.
|
| + // If this scenario causes lengths to explode, we have a big problem.
|
| + EXPECT_LT(GetLength(pos), 500U);
|
| +}
|
| +
|
| +// Two clients alternately inserting entries at the end, with a strong
|
| +// bias towards insertions by the first client.
|
| +TEST_F(PositionScenariosTest, TwoClientsInsertAtEnd_A) {
|
| + UniquePosition pos =
|
| + UniquePosition::InitialPosition(NextClient1Suffix());
|
| + for (int i = 0; i < 1024; i++) {
|
| + std::string suffix;
|
| + if (i % 5 == 0) {
|
| + suffix = NextClient2Suffix();
|
| + } else {
|
| + suffix = NextClient1Suffix();
|
| + }
|
| +
|
| + UniquePosition new_pos = UniquePosition::After(pos, suffix);
|
| + ASSERT_PRED_FORMAT2(LessThan, pos, new_pos);
|
| + pos = new_pos;
|
| + }
|
| +
|
| + VLOG(1) << "Length: " << GetLength(pos);
|
| + EXPECT_LT(GetLength(pos), 500U);
|
| +}
|
| +
|
| +// Two clients alternately inserting entries at the end.
|
| +TEST_F(PositionScenariosTest, TwoClientsInsertAtEnd_B) {
|
| + UniquePosition pos =
|
| + UniquePosition::InitialPosition(NextClient1Suffix());
|
| + for (int i = 0; i < 1024; i++) {
|
| + std::string suffix;
|
| + if (i % 2 == 0) {
|
| + suffix = NextClient1Suffix();
|
| + } else {
|
| + suffix = NextClient2Suffix();
|
| + }
|
| +
|
| + UniquePosition new_pos = UniquePosition::After(pos, suffix);
|
| + ASSERT_PRED_FORMAT2(LessThan, pos, new_pos);
|
| + pos = new_pos;
|
| + }
|
| +
|
| + VLOG(1) << "Length: " << GetLength(pos);
|
| + EXPECT_LT(GetLength(pos), 500U);
|
| +}
|
| +
|
| INSTANTIATE_TEST_CASE_P(MinSuffix, PositionInsertTest,
|
| ::testing::Values(kMinSuffix));
|
| INSTANTIATE_TEST_CASE_P(MaxSuffix, PositionInsertTest,
|
| @@ -288,15 +401,18 @@ INSTANTIATE_TEST_CASE_P(NormalSuffix, PositionInsertTest,
|
| ::testing::Values(kNormalSuffix));
|
|
|
| class PositionFromIntTest : public UniquePositionTest {
|
| - protected:
|
| - static std::string SuffixFromInt(int64 item_id) {
|
| - // Represents a cache_guid-like value.
|
| - const std::string guid(
|
| - "\xFF\xFE\xFE\xFF\x00\x01\x01\x00"
|
| - "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 16);
|
| + public:
|
| + PositionFromIntTest()
|
| + : generator_(std::string(kCacheGuidStr1, arraysize(kCacheGuidStr1)-1)) {
|
| + }
|
|
|
| - return UniquePosition::GenerateBookmarkSuffix(guid, item_id);
|
| + protected:
|
| + std::string NextSuffix() {
|
| + return generator_.NextSuffix();
|
| }
|
| +
|
| + private:
|
| + SuffixGenerator generator_;
|
| };
|
|
|
| const int64 kTestValues[] = {
|
| @@ -330,7 +446,7 @@ const size_t kNumTestValues = arraysize(kTestValues);
|
| TEST_F(PositionFromIntTest, IsValid) {
|
| for (size_t i = 0; i < kNumTestValues; ++i) {
|
| const UniquePosition pos =
|
| - UniquePosition::FromInt64(kTestValues[i], SuffixFromInt(i));
|
| + UniquePosition::FromInt64(kTestValues[i], NextSuffix());
|
| EXPECT_TRUE(pos.IsValid()) << "i = " << i << "; " << pos.ToDebugString();
|
| }
|
| }
|
| @@ -339,7 +455,7 @@ TEST_F(PositionFromIntTest, RoundTripConversion) {
|
| for (size_t i = 0; i < kNumTestValues; ++i) {
|
| const int64 expected_value = kTestValues[i];
|
| const UniquePosition pos =
|
| - UniquePosition::FromInt64(kTestValues[i], SuffixFromInt(i));
|
| + UniquePosition::FromInt64(kTestValues[i], NextSuffix());
|
| const int64 value = pos.ToInt64();
|
| EXPECT_EQ(expected_value, value) << "i = " << i;
|
| }
|
| @@ -366,7 +482,7 @@ TEST_F(PositionFromIntTest, ConsistentOrdering) {
|
| std::vector<int> position_ordering(kNumTestValues);
|
| for (size_t i = 0; i < kNumTestValues; ++i) {
|
| positions[i] = UniquePosition::FromInt64(
|
| - kTestValues[i], SuffixFromInt(i));
|
| + kTestValues[i], NextSuffix());
|
| original_ordering[i] = int64_ordering[i] = position_ordering[i] = i;
|
| }
|
|
|
|
|