| 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
|
| deleted file mode 100644
|
| index 02ee57a969777c9e34b3cb755f96a8d7e99fe7d8..0000000000000000000000000000000000000000
|
| --- a/sync/internal_api/public/base/unique_position_unittest.cc
|
| +++ /dev/null
|
| @@ -1,704 +0,0 @@
|
| -// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "sync/internal_api/public/base/unique_position.h"
|
| -
|
| -#include <stddef.h>
|
| -#include <stdint.h>
|
| -
|
| -#include <algorithm>
|
| -#include <functional>
|
| -#include <memory>
|
| -#include <string>
|
| -#include <vector>
|
| -
|
| -#include "base/base64.h"
|
| -#include "base/logging.h"
|
| -#include "base/macros.h"
|
| -#include "base/sha1.h"
|
| -#include "base/strings/string_number_conversions.h"
|
| -#include "sync/protocol/unique_position.pb.h"
|
| -#include "testing/gtest/include/gtest/gtest.h"
|
| -
|
| -namespace syncer {
|
| -
|
| -namespace {
|
| -
|
| -class UniquePositionTest : public ::testing::Test {
|
| - protected:
|
| - // 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.
|
| - //
|
| - // 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(const UniquePosition& pos) {
|
| - sync_pb::UniquePosition proto;
|
| - pos.ToProto(&proto);
|
| - return proto.ByteSize();
|
| - }
|
| -};
|
| -
|
| -// This function exploits internal knowledge of how the protobufs are serialized
|
| -// to help us build UniquePositions from strings described in this file.
|
| -static UniquePosition FromBytes(const std::string& bytes) {
|
| - sync_pb::UniquePosition proto;
|
| - proto.set_value(bytes);
|
| - return UniquePosition::FromProto(proto);
|
| -}
|
| -
|
| -const size_t kMinLength = UniquePosition::kSuffixLength;
|
| -const size_t kGenericPredecessorLength = kMinLength + 2;
|
| -const size_t kGenericSuccessorLength = kMinLength + 1;
|
| -const size_t kBigPositionLength = kMinLength;
|
| -const size_t kSmallPositionLength = kMinLength;
|
| -
|
| -// Be careful when adding more prefixes to this list.
|
| -// We have to manually ensure each has a unique suffix.
|
| -const UniquePosition kGenericPredecessor = FromBytes(
|
| - (std::string(kGenericPredecessorLength, '\x23') + '\xFF'));
|
| -const UniquePosition kGenericSuccessor = FromBytes(
|
| - std::string(kGenericSuccessorLength, '\xAB') + '\xFF');
|
| -const UniquePosition kBigPosition = FromBytes(
|
| - std::string(kBigPositionLength - 1, '\xFF') + '\xFE' + '\xFF');
|
| -const UniquePosition kBigPositionLessTwo = FromBytes(
|
| - std::string(kBigPositionLength - 1, '\xFF') + '\xFC' + '\xFF');
|
| -const UniquePosition kBiggerPosition = FromBytes(
|
| - std::string(kBigPositionLength, '\xFF') + '\xFF');
|
| -const UniquePosition kSmallPosition = FromBytes(
|
| - std::string(kSmallPositionLength - 1, '\x00') + '\x01' + '\xFF');
|
| -const UniquePosition kSmallPositionPlusOne = FromBytes(
|
| - std::string(kSmallPositionLength - 1, '\x00') + '\x02' + '\xFF');
|
| -const UniquePosition kHugePosition = FromBytes(
|
| - std::string(UniquePosition::kCompressBytesThreshold, '\xFF') + '\xAB');
|
| -
|
| -const std::string kMinSuffix =
|
| - std::string(UniquePosition::kSuffixLength - 1, '\x00') + '\x01';
|
| -const std::string kMaxSuffix(UniquePosition::kSuffixLength, '\xFF');
|
| -const std::string kNormalSuffix(
|
| - "\x68\x44\x6C\x6B\x32\x58\x78\x34\x69\x70\x46\x34\x79\x49"
|
| - "\x44\x4F\x66\x4C\x58\x41\x31\x34\x68\x59\x56\x43\x6F\x3D");
|
| -
|
| -::testing::AssertionResult LessThan(const char* m_expr,
|
| - const char* n_expr,
|
| - const UniquePosition &m,
|
| - const UniquePosition &n) {
|
| - if (m.LessThan(n))
|
| - return ::testing::AssertionSuccess();
|
| -
|
| - return ::testing::AssertionFailure()
|
| - << m_expr << " is not less than " << n_expr
|
| - << " (" << m.ToDebugString() << " and " << n.ToDebugString() << ")";
|
| -}
|
| -
|
| -::testing::AssertionResult Equals(const char* m_expr,
|
| - const char* n_expr,
|
| - const UniquePosition &m,
|
| - const UniquePosition &n) {
|
| - if (m.Equals(n))
|
| - return ::testing::AssertionSuccess();
|
| -
|
| - return ::testing::AssertionFailure()
|
| - << m_expr << " is not equal to " << n_expr
|
| - << " (" << m.ToDebugString() << " != " << n.ToDebugString() << ")";
|
| -}
|
| -
|
| -// Test that the code can read the uncompressed serialization format.
|
| -TEST_F(UniquePositionTest, DeserializeObsoleteUncompressedPosition) {
|
| - // We no longer support the encoding data in this format. This hard-coded
|
| - // input is a serialization of kGenericPredecessor created by an older version
|
| - // of this code.
|
| - const char kSerializedCstr[] = {
|
| - '\x0a', '\x1f', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23',
|
| - '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23',
|
| - '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23',
|
| - '\x23', '\x23', '\x23', '\x23', '\x23', '\xff' };
|
| - const std::string serialized(kSerializedCstr, sizeof(kSerializedCstr));
|
| -
|
| - sync_pb::UniquePosition proto;
|
| - proto.ParseFromString(serialized);
|
| -
|
| - // Double-check that this test is testing what we think it tests.
|
| - EXPECT_TRUE(proto.has_value());
|
| - EXPECT_FALSE(proto.has_compressed_value());
|
| - EXPECT_FALSE(proto.has_uncompressed_length());
|
| -
|
| - UniquePosition pos = UniquePosition::FromProto(proto);
|
| - EXPECT_PRED_FORMAT2(Equals, kGenericPredecessor, pos);
|
| -}
|
| -
|
| -// Test that the code can read the gzip serialization format.
|
| -TEST_F(UniquePositionTest, DeserializeObsoleteGzippedPosition) {
|
| - // We no longer support the encoding data in this format. This hard-coded
|
| - // input is a serialization of kHugePosition created by an older version of
|
| - // this code.
|
| - const char kSerializedCstr[] = {
|
| - '\x12', '\x0d', '\x78', '\x9c', '\xfb', '\xff', '\x7f', '\x60', '\xc1',
|
| - '\x6a', '\x00', '\xa2', '\x4c', '\x80', '\x2c', '\x18', '\x81', '\x01' };
|
| - const std::string serialized(kSerializedCstr, sizeof(kSerializedCstr));
|
| -
|
| - sync_pb::UniquePosition proto;
|
| - proto.ParseFromString(serialized);
|
| -
|
| - // Double-check that this test is testing what we think it tests.
|
| - EXPECT_FALSE(proto.has_value());
|
| - EXPECT_TRUE(proto.has_compressed_value());
|
| - EXPECT_TRUE(proto.has_uncompressed_length());
|
| -
|
| - UniquePosition pos = UniquePosition::FromProto(proto);
|
| - EXPECT_PRED_FORMAT2(Equals, kHugePosition, pos);
|
| -}
|
| -
|
| -class RelativePositioningTest : public UniquePositionTest { };
|
| -
|
| -const UniquePosition kPositionArray[] = {
|
| - kGenericPredecessor,
|
| - kGenericSuccessor,
|
| - kBigPosition,
|
| - kBigPositionLessTwo,
|
| - kBiggerPosition,
|
| - kSmallPosition,
|
| - kSmallPositionPlusOne,
|
| -};
|
| -
|
| -const UniquePosition kSortedPositionArray[] = {
|
| - kSmallPosition,
|
| - kSmallPositionPlusOne,
|
| - kGenericPredecessor,
|
| - kGenericSuccessor,
|
| - kBigPositionLessTwo,
|
| - kBigPosition,
|
| - kBiggerPosition,
|
| -};
|
| -
|
| -static const size_t kNumPositions = arraysize(kPositionArray);
|
| -static const size_t kNumSortedPositions = arraysize(kSortedPositionArray);
|
| -
|
| -struct PositionLessThan {
|
| - bool operator()(const UniquePosition& a, const UniquePosition& b) {
|
| - return a.LessThan(b);
|
| - }
|
| -};
|
| -
|
| -// Returns true iff the given position's suffix matches the input parameter.
|
| -static bool IsSuffixInUse(
|
| - const UniquePosition& pos, const std::string& suffix) {
|
| - return pos.GetSuffixForTest() == suffix;
|
| -}
|
| -
|
| -// Test some basic properties of comparison and equality.
|
| -TEST_F(RelativePositioningTest, ComparisonSanityTest1) {
|
| - const UniquePosition& a = kPositionArray[0];
|
| - ASSERT_TRUE(a.IsValid());
|
| -
|
| - // Necessarily true for any non-invalid positions.
|
| - EXPECT_TRUE(a.Equals(a));
|
| - EXPECT_FALSE(a.LessThan(a));
|
| -}
|
| -
|
| -// Test some more properties of comparison and equality.
|
| -TEST_F(RelativePositioningTest, ComparisonSanityTest2) {
|
| - const UniquePosition& a = kPositionArray[0];
|
| - const UniquePosition& b = kPositionArray[1];
|
| -
|
| - // These should pass for the specific a and b we have chosen (a < b).
|
| - EXPECT_FALSE(a.Equals(b));
|
| - EXPECT_TRUE(a.LessThan(b));
|
| - EXPECT_FALSE(b.LessThan(a));
|
| -}
|
| -
|
| -// Exercise comparision functions by sorting and re-sorting the list.
|
| -TEST_F(RelativePositioningTest, SortPositions) {
|
| - ASSERT_EQ(kNumPositions, kNumSortedPositions);
|
| - UniquePosition positions[arraysize(kPositionArray)];
|
| - for (size_t i = 0; i < kNumPositions; ++i) {
|
| - positions[i] = kPositionArray[i];
|
| - }
|
| -
|
| - std::sort(&positions[0], &positions[kNumPositions], PositionLessThan());
|
| - for (size_t i = 0; i < kNumPositions; ++i) {
|
| - EXPECT_TRUE(positions[i].Equals(kSortedPositionArray[i]))
|
| - << "i: " << i << ", "
|
| - << positions[i].ToDebugString() << " != "
|
| - << kSortedPositionArray[i].ToDebugString();
|
| - }
|
| -}
|
| -
|
| -// Some more exercise for the comparison function.
|
| -TEST_F(RelativePositioningTest, ReverseSortPositions) {
|
| - ASSERT_EQ(kNumPositions, kNumSortedPositions);
|
| - UniquePosition positions[arraysize(kPositionArray)];
|
| - for (size_t i = 0; i < kNumPositions; ++i) {
|
| - positions[i] = kPositionArray[i];
|
| - }
|
| -
|
| - std::reverse(&positions[0], &positions[kNumPositions]);
|
| - std::sort(&positions[0], &positions[kNumPositions], PositionLessThan());
|
| - for (size_t i = 0; i < kNumPositions; ++i) {
|
| - EXPECT_TRUE(positions[i].Equals(kSortedPositionArray[i]))
|
| - << "i: " << i << ", "
|
| - << positions[i].ToDebugString() << " != "
|
| - << kSortedPositionArray[i].ToDebugString();
|
| - }
|
| -}
|
| -
|
| -class PositionInsertTest :
|
| - public RelativePositioningTest,
|
| - public ::testing::WithParamInterface<std::string> { };
|
| -
|
| -// Exercise InsertBetween with various insertion operations.
|
| -TEST_P(PositionInsertTest, InsertBetween) {
|
| - const std::string suffix = GetParam();
|
| - ASSERT_TRUE(UniquePosition::IsValidSuffix(suffix));
|
| -
|
| - for (size_t i = 0; i < kNumSortedPositions; ++i) {
|
| - const UniquePosition& predecessor = kSortedPositionArray[i];
|
| - // Verify our suffixes are unique before we continue.
|
| - if (IsSuffixInUse(predecessor, suffix))
|
| - continue;
|
| -
|
| - for (size_t j = i + 1; j < kNumSortedPositions; ++j) {
|
| - const UniquePosition& successor = kSortedPositionArray[j];
|
| -
|
| - // Another guard against non-unique suffixes.
|
| - if (IsSuffixInUse(successor, suffix))
|
| - continue;
|
| -
|
| - UniquePosition midpoint =
|
| - UniquePosition::Between(predecessor, successor, suffix);
|
| -
|
| - EXPECT_PRED_FORMAT2(LessThan, predecessor, midpoint);
|
| - EXPECT_PRED_FORMAT2(LessThan, midpoint, successor);
|
| - }
|
| - }
|
| -}
|
| -
|
| -TEST_P(PositionInsertTest, InsertBefore) {
|
| - const std::string suffix = GetParam();
|
| - for (size_t i = 0; i < kNumSortedPositions; ++i) {
|
| - const UniquePosition& successor = kSortedPositionArray[i];
|
| - // Verify our suffixes are unique before we continue.
|
| - if (IsSuffixInUse(successor, suffix))
|
| - continue;
|
| -
|
| - UniquePosition before = UniquePosition::Before(successor, suffix);
|
| -
|
| - EXPECT_PRED_FORMAT2(LessThan, before, successor);
|
| - }
|
| -}
|
| -
|
| -TEST_P(PositionInsertTest, InsertAfter) {
|
| - const std::string suffix = GetParam();
|
| - for (size_t i = 0; i < kNumSortedPositions; ++i) {
|
| - const UniquePosition& predecessor = kSortedPositionArray[i];
|
| - // Verify our suffixes are unique before we continue.
|
| - if (IsSuffixInUse(predecessor, suffix))
|
| - continue;
|
| -
|
| - UniquePosition after = UniquePosition::After(predecessor, suffix);
|
| -
|
| - EXPECT_PRED_FORMAT2(LessThan, predecessor, after);
|
| - }
|
| -}
|
| -
|
| -TEST_P(PositionInsertTest, StressInsertAfter) {
|
| - // Use two different suffixes to not violate our suffix uniqueness guarantee.
|
| - const std::string& suffix_a = GetParam();
|
| - std::string suffix_b = suffix_a;
|
| - suffix_b[10] = suffix_b[10] ^ 0xff;
|
| -
|
| - UniquePosition pos = UniquePosition::InitialPosition(suffix_a);
|
| - for (int i = 0; i < 1024; i++) {
|
| - const std::string& suffix = (i % 2 == 0) ? suffix_b : suffix_a;
|
| - UniquePosition next_pos = UniquePosition::After(pos, suffix);
|
| - ASSERT_PRED_FORMAT2(LessThan, pos, next_pos);
|
| - pos = next_pos;
|
| - }
|
| -
|
| - VLOG(1) << "Length: " << GetLength(pos);
|
| -}
|
| -
|
| -TEST_P(PositionInsertTest, StressInsertBefore) {
|
| - // Use two different suffixes to not violate our suffix uniqueness guarantee.
|
| - const std::string& suffix_a = GetParam();
|
| - std::string suffix_b = suffix_a;
|
| - suffix_b[10] = suffix_b[10] ^ 0xff;
|
| -
|
| - UniquePosition pos = UniquePosition::InitialPosition(suffix_a);
|
| - for (int i = 0; i < 1024; i++) {
|
| - const std::string& suffix = (i % 2 == 0) ? suffix_b : suffix_a;
|
| - UniquePosition prev_pos = UniquePosition::Before(pos, suffix);
|
| - ASSERT_PRED_FORMAT2(LessThan, prev_pos, pos);
|
| - pos = prev_pos;
|
| - }
|
| -
|
| - VLOG(1) << "Length: " << GetLength(pos);
|
| -}
|
| -
|
| -TEST_P(PositionInsertTest, StressLeftInsertBetween) {
|
| - // Use different suffixes to not violate our suffix uniqueness guarantee.
|
| - const std::string& suffix_a = GetParam();
|
| - std::string suffix_b = suffix_a;
|
| - suffix_b[10] = suffix_b[10] ^ 0xff;
|
| - std::string suffix_c = suffix_a;
|
| - suffix_c[10] = suffix_c[10] ^ 0xf0;
|
| -
|
| - UniquePosition right_pos = UniquePosition::InitialPosition(suffix_c);
|
| - UniquePosition left_pos = UniquePosition::Before(right_pos, suffix_a);
|
| - for (int i = 0; i < 1024; i++) {
|
| - const std::string& suffix = (i % 2 == 0) ? suffix_b : suffix_a;
|
| - UniquePosition new_pos =
|
| - UniquePosition::Between(left_pos, right_pos, suffix);
|
| - ASSERT_PRED_FORMAT2(LessThan, left_pos, new_pos);
|
| - ASSERT_PRED_FORMAT2(LessThan, new_pos, right_pos);
|
| - left_pos = new_pos;
|
| - }
|
| -
|
| - VLOG(1) << "Lengths: " << GetLength(left_pos) << ", " << GetLength(right_pos);
|
| -}
|
| -
|
| -TEST_P(PositionInsertTest, StressRightInsertBetween) {
|
| - // Use different suffixes to not violate our suffix uniqueness guarantee.
|
| - const std::string& suffix_a = GetParam();
|
| - std::string suffix_b = suffix_a;
|
| - suffix_b[10] = suffix_b[10] ^ 0xff;
|
| - std::string suffix_c = suffix_a;
|
| - suffix_c[10] = suffix_c[10] ^ 0xf0;
|
| -
|
| - UniquePosition right_pos = UniquePosition::InitialPosition(suffix_a);
|
| - UniquePosition left_pos = UniquePosition::Before(right_pos, suffix_c);
|
| - for (int i = 0; i < 1024; i++) {
|
| - const std::string& suffix = (i % 2 == 0) ? suffix_b : suffix_a;
|
| - UniquePosition new_pos =
|
| - UniquePosition::Between(left_pos, right_pos, suffix);
|
| - ASSERT_PRED_FORMAT2(LessThan, left_pos, new_pos);
|
| - ASSERT_PRED_FORMAT2(LessThan, new_pos, right_pos);
|
| - right_pos = new_pos;
|
| - }
|
| -
|
| - 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;
|
| - base::Base64Encode(base::SHA1HashString(input), &output);
|
| - return output;
|
| - }
|
| -
|
| - private:
|
| - const std::string cache_guid_;
|
| - int64_t 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,
|
| - ::testing::Values(kMaxSuffix));
|
| -INSTANTIATE_TEST_CASE_P(NormalSuffix, PositionInsertTest,
|
| - ::testing::Values(kNormalSuffix));
|
| -
|
| -class PositionFromIntTest : public UniquePositionTest {
|
| - public:
|
| - PositionFromIntTest()
|
| - : generator_(std::string(kCacheGuidStr1, arraysize(kCacheGuidStr1)-1)) {
|
| - }
|
| -
|
| - protected:
|
| - static const int64_t kTestValues[];
|
| - static const size_t kNumTestValues;
|
| -
|
| - std::string NextSuffix() {
|
| - return generator_.NextSuffix();
|
| - }
|
| -
|
| - private:
|
| - SuffixGenerator generator_;
|
| -};
|
| -
|
| -const int64_t PositionFromIntTest::kTestValues[] = {0LL,
|
| - 1LL,
|
| - -1LL,
|
| - 2LL,
|
| - -2LL,
|
| - 3LL,
|
| - -3LL,
|
| - 0x79LL,
|
| - -0x79LL,
|
| - 0x80LL,
|
| - -0x80LL,
|
| - 0x81LL,
|
| - -0x81LL,
|
| - 0xFELL,
|
| - -0xFELL,
|
| - 0xFFLL,
|
| - -0xFFLL,
|
| - 0x100LL,
|
| - -0x100LL,
|
| - 0x101LL,
|
| - -0x101LL,
|
| - 0xFA1AFELL,
|
| - -0xFA1AFELL,
|
| - 0xFFFFFFFELL,
|
| - -0xFFFFFFFELL,
|
| - 0xFFFFFFFFLL,
|
| - -0xFFFFFFFFLL,
|
| - 0x100000000LL,
|
| - -0x100000000LL,
|
| - 0x100000001LL,
|
| - -0x100000001LL,
|
| - 0xFFFFFFFFFFLL,
|
| - -0xFFFFFFFFFFLL,
|
| - 0x112358132134LL,
|
| - -0x112358132134LL,
|
| - 0xFEFFBEEFABC1234LL,
|
| - -0xFEFFBEEFABC1234LL,
|
| - INT64_MAX,
|
| - INT64_MIN,
|
| - INT64_MIN + 1,
|
| - INT64_MAX - 1};
|
| -
|
| -const size_t PositionFromIntTest::kNumTestValues =
|
| -arraysize(PositionFromIntTest::kTestValues);
|
| -
|
| -TEST_F(PositionFromIntTest, IsValid) {
|
| - for (size_t i = 0; i < kNumTestValues; ++i) {
|
| - const UniquePosition pos =
|
| - UniquePosition::FromInt64(kTestValues[i], NextSuffix());
|
| - EXPECT_TRUE(pos.IsValid()) << "i = " << i << "; " << pos.ToDebugString();
|
| - }
|
| -}
|
| -
|
| -TEST_F(PositionFromIntTest, RoundTripConversion) {
|
| - for (size_t i = 0; i < kNumTestValues; ++i) {
|
| - const int64_t expected_value = kTestValues[i];
|
| - const UniquePosition pos =
|
| - UniquePosition::FromInt64(kTestValues[i], NextSuffix());
|
| - const int64_t value = pos.ToInt64();
|
| - EXPECT_EQ(expected_value, value) << "i = " << i;
|
| - }
|
| -}
|
| -
|
| -template <typename T, typename LessThan = std::less<T> >
|
| -class IndexedLessThan {
|
| - public:
|
| - explicit IndexedLessThan(const T* values) : values_(values) {}
|
| -
|
| - bool operator()(int i1, int i2) {
|
| - return less_than_(values_[i1], values_[i2]);
|
| - }
|
| -
|
| - private:
|
| - const T* values_;
|
| - LessThan less_than_;
|
| -};
|
| -
|
| -TEST_F(PositionFromIntTest, ConsistentOrdering) {
|
| - UniquePosition positions[kNumTestValues];
|
| - std::vector<int> original_ordering(kNumTestValues);
|
| - std::vector<int> int64_ordering(kNumTestValues);
|
| - std::vector<int> position_ordering(kNumTestValues);
|
| - for (size_t i = 0; i < kNumTestValues; ++i) {
|
| - positions[i] = UniquePosition::FromInt64(
|
| - kTestValues[i], NextSuffix());
|
| - original_ordering[i] = int64_ordering[i] = position_ordering[i] = i;
|
| - }
|
| -
|
| - std::sort(int64_ordering.begin(), int64_ordering.end(),
|
| - IndexedLessThan<int64_t>(kTestValues));
|
| - std::sort(position_ordering.begin(), position_ordering.end(),
|
| - IndexedLessThan<UniquePosition, PositionLessThan>(positions));
|
| - EXPECT_NE(original_ordering, int64_ordering);
|
| - EXPECT_EQ(int64_ordering, position_ordering);
|
| -}
|
| -
|
| -class CompressedPositionTest : public UniquePositionTest {
|
| - public:
|
| - CompressedPositionTest() {
|
| - positions_.push_back(MakePosition( // Prefix starts with 256 0x00s
|
| - std::string("\x00\x00\x00\x00\xFF\xFF\xFE\xFF"
|
| - "\x01",
|
| - 9),
|
| - MakeSuffix('\x04')));
|
| - positions_.push_back(MakePosition( // Prefix starts with four 0x00s
|
| - std::string("\x00\x00\x00\x00\xFF\xFF\xFF\xFB"
|
| - "\x01",
|
| - 9),
|
| - MakeSuffix('\x03')));
|
| - positions_.push_back(MakePosition( // Prefix starts with four 0xFFs
|
| - std::string("\xFF\xFF\xFF\xFF\x00\x00\x00\x04"
|
| - "\x01",
|
| - 9),
|
| - MakeSuffix('\x01')));
|
| - positions_.push_back(MakePosition( // Prefix starts with 256 0xFFs
|
| - std::string("\xFF\xFF\xFF\xFF\x00\x00\x01\x00"
|
| - "\x01",
|
| - 9),
|
| - MakeSuffix('\x02')));
|
| - }
|
| -
|
| - private:
|
| - UniquePosition MakePosition(const std::string& compressed_prefix,
|
| - const std::string& compressed_suffix);
|
| - std::string MakeSuffix(char unique_value);
|
| -
|
| - protected:
|
| - std::vector<UniquePosition> positions_;
|
| -};
|
| -
|
| -UniquePosition CompressedPositionTest::MakePosition(
|
| - const std::string& compressed_prefix,
|
| - const std::string& compressed_suffix) {
|
| - sync_pb::UniquePosition proto;
|
| - proto.set_custom_compressed_v1(
|
| - std::string(compressed_prefix + compressed_suffix));
|
| - return UniquePosition::FromProto(proto);
|
| -}
|
| -
|
| -std::string CompressedPositionTest::MakeSuffix(char unique_value) {
|
| - // We're dealing in compressed positions in this test. That means the
|
| - // suffix should be compressed, too. To avoid complication, we use suffixes
|
| - // that don't have any repeating digits, and therefore are identical in
|
| - // compressed and uncompressed form.
|
| - std::string suffix;
|
| - for (size_t i = 0; i < UniquePosition::kSuffixLength; ++i) {
|
| - suffix.push_back(static_cast<char>(i));
|
| - }
|
| - suffix[UniquePosition::kSuffixLength-1] = unique_value;
|
| - return suffix;
|
| -}
|
| -
|
| -// Make sure that serialization and deserialization routines are correct.
|
| -TEST_F(CompressedPositionTest, SerializeAndDeserialize) {
|
| - for (std::vector<UniquePosition>::const_iterator it = positions_.begin();
|
| - it != positions_.end(); ++it) {
|
| - SCOPED_TRACE("iteration: " + it->ToDebugString());
|
| -
|
| - sync_pb::UniquePosition proto;
|
| - it->ToProto(&proto);
|
| - UniquePosition deserialized = UniquePosition::FromProto(proto);
|
| -
|
| - EXPECT_PRED_FORMAT2(Equals, *it, deserialized);
|
| - }
|
| -}
|
| -
|
| -// Test that deserialization failures of protobufs where we know none of its
|
| -// fields is not catastrophic. This may happen if all the fields currently
|
| -// known to this client become deprecated in the future.
|
| -TEST_F(CompressedPositionTest, DeserializeProtobufFromTheFuture) {
|
| - sync_pb::UniquePosition proto;
|
| - UniquePosition deserialized = UniquePosition::FromProto(proto);
|
| - EXPECT_FALSE(deserialized.IsValid());
|
| -}
|
| -
|
| -// Make sure the comparison functions are working correctly.
|
| -// This requires values in the test harness to be hard-coded in ascending order.
|
| -TEST_F(CompressedPositionTest, OrderingTest) {
|
| - for (size_t i = 0; i < positions_.size()-1; ++i) {
|
| - EXPECT_PRED_FORMAT2(LessThan, positions_[i], positions_[i+1]);
|
| - }
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -} // namespace syncer
|
|
|