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.
5 #include "sync/internal_api/public/base/unique_position.h"
10 #include "base/base64.h"
11 #include "base/basictypes.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/sha1.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "sync/protocol/unique_position.pb.h"
17 #include "testing/gtest/include/gtest/gtest.h"
23 class UniquePositionTest
: public ::testing::Test
{
25 // Accessor to fetch the length of the position's internal representation
26 // We try to avoid having any test expectations on it because this is an
27 // implementation detail.
29 // If you run the tests with --v=1, we'll print out some of the lengths
30 // so you can see how well the algorithm performs in various insertion
32 size_t GetLength(const UniquePosition
& pos
) {
33 sync_pb::UniquePosition proto
;
35 return proto
.ByteSize();
39 // This function exploits internal knowledge of how the protobufs are serialized
40 // to help us build UniquePositions from strings described in this file.
41 static UniquePosition
FromBytes(const std::string
& bytes
) {
42 sync_pb::UniquePosition proto
;
43 proto
.set_value(bytes
);
44 return UniquePosition::FromProto(proto
);
47 const size_t kMinLength
= UniquePosition::kSuffixLength
;
48 const size_t kGenericPredecessorLength
= kMinLength
+ 2;
49 const size_t kGenericSuccessorLength
= kMinLength
+ 1;
50 const size_t kBigPositionLength
= kMinLength
;
51 const size_t kSmallPositionLength
= kMinLength
;
53 // Be careful when adding more prefixes to this list.
54 // We have to manually ensure each has a unique suffix.
55 const UniquePosition kGenericPredecessor
= FromBytes(
56 (std::string(kGenericPredecessorLength
, '\x23') + '\xFF'));
57 const UniquePosition kGenericSuccessor
= FromBytes(
58 std::string(kGenericSuccessorLength
, '\xAB') + '\xFF');
59 const UniquePosition kBigPosition
= FromBytes(
60 std::string(kBigPositionLength
- 1, '\xFF') + '\xFE' + '\xFF');
61 const UniquePosition kBigPositionLessTwo
= FromBytes(
62 std::string(kBigPositionLength
- 1, '\xFF') + '\xFC' + '\xFF');
63 const UniquePosition kBiggerPosition
= FromBytes(
64 std::string(kBigPositionLength
, '\xFF') + '\xFF');
65 const UniquePosition kSmallPosition
= FromBytes(
66 std::string(kSmallPositionLength
- 1, '\x00') + '\x01' + '\xFF');
67 const UniquePosition kSmallPositionPlusOne
= FromBytes(
68 std::string(kSmallPositionLength
- 1, '\x00') + '\x02' + '\xFF');
69 const UniquePosition kHugePosition
= FromBytes(
70 std::string(UniquePosition::kCompressBytesThreshold
, '\xFF') + '\xAB');
72 const std::string kMinSuffix
=
73 std::string(UniquePosition::kSuffixLength
- 1, '\x00') + '\x01';
74 const std::string
kMaxSuffix(UniquePosition::kSuffixLength
, '\xFF');
75 const std::string
kNormalSuffix(
76 "\x68\x44\x6C\x6B\x32\x58\x78\x34\x69\x70\x46\x34\x79\x49"
77 "\x44\x4F\x66\x4C\x58\x41\x31\x34\x68\x59\x56\x43\x6F\x3D");
79 ::testing::AssertionResult
LessThan(const char* m_expr
,
81 const UniquePosition
&m
,
82 const UniquePosition
&n
) {
84 return ::testing::AssertionSuccess();
86 return ::testing::AssertionFailure()
87 << m_expr
<< " is not less than " << n_expr
88 << " (" << m
.ToDebugString() << " and " << n
.ToDebugString() << ")";
91 ::testing::AssertionResult
Equals(const char* m_expr
,
93 const UniquePosition
&m
,
94 const UniquePosition
&n
) {
96 return ::testing::AssertionSuccess();
98 return ::testing::AssertionFailure()
99 << m_expr
<< " is not equal to " << n_expr
100 << " (" << m
.ToDebugString() << " != " << n
.ToDebugString() << ")";
103 // Test that the code can read the uncompressed serialization format.
104 TEST_F(UniquePositionTest
, DeserializeObsoleteUncompressedPosition
) {
105 // We no longer support the encoding data in this format. This hard-coded
106 // input is a serialization of kGenericPredecessor created by an older version
108 const char kSerializedCstr
[] = {
109 '\x0a', '\x1f', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23',
110 '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23',
111 '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23', '\x23',
112 '\x23', '\x23', '\x23', '\x23', '\x23', '\xff' };
113 const std::string
serialized(kSerializedCstr
, sizeof(kSerializedCstr
));
115 sync_pb::UniquePosition proto
;
116 proto
.ParseFromString(serialized
);
118 // Double-check that this test is testing what we think it tests.
119 EXPECT_TRUE(proto
.has_value());
120 EXPECT_FALSE(proto
.has_compressed_value());
121 EXPECT_FALSE(proto
.has_uncompressed_length());
123 UniquePosition pos
= UniquePosition::FromProto(proto
);
124 EXPECT_PRED_FORMAT2(Equals
, kGenericPredecessor
, pos
);
127 // Test that the code can read the gzip serialization format.
128 TEST_F(UniquePositionTest
, DeserializeObsoleteGzippedPosition
) {
129 // We no longer support the encoding data in this format. This hard-coded
130 // input is a serialization of kHugePosition created by an older version of
132 const char kSerializedCstr
[] = {
133 '\x12', '\x0d', '\x78', '\x9c', '\xfb', '\xff', '\x7f', '\x60', '\xc1',
134 '\x6a', '\x00', '\xa2', '\x4c', '\x80', '\x2c', '\x18', '\x81', '\x01' };
135 const std::string
serialized(kSerializedCstr
, sizeof(kSerializedCstr
));
137 sync_pb::UniquePosition proto
;
138 proto
.ParseFromString(serialized
);
140 // Double-check that this test is testing what we think it tests.
141 EXPECT_FALSE(proto
.has_value());
142 EXPECT_TRUE(proto
.has_compressed_value());
143 EXPECT_TRUE(proto
.has_uncompressed_length());
145 UniquePosition pos
= UniquePosition::FromProto(proto
);
146 EXPECT_PRED_FORMAT2(Equals
, kHugePosition
, pos
);
149 class RelativePositioningTest
: public UniquePositionTest
{ };
151 const UniquePosition kPositionArray
[] = {
158 kSmallPositionPlusOne
,
161 const UniquePosition kSortedPositionArray
[] = {
163 kSmallPositionPlusOne
,
171 static const size_t kNumPositions
= arraysize(kPositionArray
);
172 static const size_t kNumSortedPositions
= arraysize(kSortedPositionArray
);
174 struct PositionLessThan
{
175 bool operator()(const UniquePosition
& a
, const UniquePosition
& b
) {
176 return a
.LessThan(b
);
180 // Returns true iff the given position's suffix matches the input parameter.
181 static bool IsSuffixInUse(
182 const UniquePosition
& pos
, const std::string
& suffix
) {
183 return pos
.GetSuffixForTest() == suffix
;
186 // Test some basic properties of comparison and equality.
187 TEST_F(RelativePositioningTest
, ComparisonSanityTest1
) {
188 const UniquePosition
& a
= kPositionArray
[0];
189 ASSERT_TRUE(a
.IsValid());
191 // Necessarily true for any non-invalid positions.
192 EXPECT_TRUE(a
.Equals(a
));
193 EXPECT_FALSE(a
.LessThan(a
));
196 // Test some more properties of comparison and equality.
197 TEST_F(RelativePositioningTest
, ComparisonSanityTest2
) {
198 const UniquePosition
& a
= kPositionArray
[0];
199 const UniquePosition
& b
= kPositionArray
[1];
201 // These should pass for the specific a and b we have chosen (a < b).
202 EXPECT_FALSE(a
.Equals(b
));
203 EXPECT_TRUE(a
.LessThan(b
));
204 EXPECT_FALSE(b
.LessThan(a
));
207 // Exercise comparision functions by sorting and re-sorting the list.
208 TEST_F(RelativePositioningTest
, SortPositions
) {
209 ASSERT_EQ(kNumPositions
, kNumSortedPositions
);
210 UniquePosition positions
[arraysize(kPositionArray
)];
211 for (size_t i
= 0; i
< kNumPositions
; ++i
) {
212 positions
[i
] = kPositionArray
[i
];
215 std::sort(&positions
[0], &positions
[kNumPositions
], PositionLessThan());
216 for (size_t i
= 0; i
< kNumPositions
; ++i
) {
217 EXPECT_TRUE(positions
[i
].Equals(kSortedPositionArray
[i
]))
218 << "i: " << i
<< ", "
219 << positions
[i
].ToDebugString() << " != "
220 << kSortedPositionArray
[i
].ToDebugString();
225 // Some more exercise for the comparison function.
226 TEST_F(RelativePositioningTest
, ReverseSortPositions
) {
227 ASSERT_EQ(kNumPositions
, kNumSortedPositions
);
228 UniquePosition positions
[arraysize(kPositionArray
)];
229 for (size_t i
= 0; i
< kNumPositions
; ++i
) {
230 positions
[i
] = kPositionArray
[i
];
233 std::reverse(&positions
[0], &positions
[kNumPositions
]);
234 std::sort(&positions
[0], &positions
[kNumPositions
], PositionLessThan());
235 for (size_t i
= 0; i
< kNumPositions
; ++i
) {
236 EXPECT_TRUE(positions
[i
].Equals(kSortedPositionArray
[i
]))
237 << "i: " << i
<< ", "
238 << positions
[i
].ToDebugString() << " != "
239 << kSortedPositionArray
[i
].ToDebugString();
243 class PositionInsertTest
:
244 public RelativePositioningTest
,
245 public ::testing::WithParamInterface
<std::string
> { };
247 // Exercise InsertBetween with various insertion operations.
248 TEST_P(PositionInsertTest
, InsertBetween
) {
249 const std::string suffix
= GetParam();
250 ASSERT_TRUE(UniquePosition::IsValidSuffix(suffix
));
252 for (size_t i
= 0; i
< kNumSortedPositions
; ++i
) {
253 const UniquePosition
& predecessor
= kSortedPositionArray
[i
];
254 // Verify our suffixes are unique before we continue.
255 if (IsSuffixInUse(predecessor
, suffix
))
258 for (size_t j
= i
+ 1; j
< kNumSortedPositions
; ++j
) {
259 const UniquePosition
& successor
= kSortedPositionArray
[j
];
261 // Another guard against non-unique suffixes.
262 if (IsSuffixInUse(successor
, suffix
))
265 UniquePosition midpoint
=
266 UniquePosition::Between(predecessor
, successor
, suffix
);
268 EXPECT_PRED_FORMAT2(LessThan
, predecessor
, midpoint
);
269 EXPECT_PRED_FORMAT2(LessThan
, midpoint
, successor
);
274 TEST_P(PositionInsertTest
, InsertBefore
) {
275 const std::string suffix
= GetParam();
276 for (size_t i
= 0; i
< kNumSortedPositions
; ++i
) {
277 const UniquePosition
& successor
= kSortedPositionArray
[i
];
278 // Verify our suffixes are unique before we continue.
279 if (IsSuffixInUse(successor
, suffix
))
282 UniquePosition before
= UniquePosition::Before(successor
, suffix
);
284 EXPECT_PRED_FORMAT2(LessThan
, before
, successor
);
288 TEST_P(PositionInsertTest
, InsertAfter
) {
289 const std::string suffix
= GetParam();
290 for (size_t i
= 0; i
< kNumSortedPositions
; ++i
) {
291 const UniquePosition
& predecessor
= kSortedPositionArray
[i
];
292 // Verify our suffixes are unique before we continue.
293 if (IsSuffixInUse(predecessor
, suffix
))
296 UniquePosition after
= UniquePosition::After(predecessor
, suffix
);
298 EXPECT_PRED_FORMAT2(LessThan
, predecessor
, after
);
302 TEST_P(PositionInsertTest
, StressInsertAfter
) {
303 // Use two different suffixes to not violate our suffix uniqueness guarantee.
304 const std::string
& suffix_a
= GetParam();
305 std::string suffix_b
= suffix_a
;
306 suffix_b
[10] = suffix_b
[10] ^ 0xff;
308 UniquePosition pos
= UniquePosition::InitialPosition(suffix_a
);
309 for (int i
= 0; i
< 1024; i
++) {
310 const std::string
& suffix
= (i
% 2 == 0) ? suffix_b
: suffix_a
;
311 UniquePosition next_pos
= UniquePosition::After(pos
, suffix
);
312 ASSERT_PRED_FORMAT2(LessThan
, pos
, next_pos
);
316 VLOG(1) << "Length: " << GetLength(pos
);
319 TEST_P(PositionInsertTest
, StressInsertBefore
) {
320 // Use two different suffixes to not violate our suffix uniqueness guarantee.
321 const std::string
& suffix_a
= GetParam();
322 std::string suffix_b
= suffix_a
;
323 suffix_b
[10] = suffix_b
[10] ^ 0xff;
325 UniquePosition pos
= UniquePosition::InitialPosition(suffix_a
);
326 for (int i
= 0; i
< 1024; i
++) {
327 const std::string
& suffix
= (i
% 2 == 0) ? suffix_b
: suffix_a
;
328 UniquePosition prev_pos
= UniquePosition::Before(pos
, suffix
);
329 ASSERT_PRED_FORMAT2(LessThan
, prev_pos
, pos
);
333 VLOG(1) << "Length: " << GetLength(pos
);
336 TEST_P(PositionInsertTest
, StressLeftInsertBetween
) {
337 // Use different suffixes to not violate our suffix uniqueness guarantee.
338 const std::string
& suffix_a
= GetParam();
339 std::string suffix_b
= suffix_a
;
340 suffix_b
[10] = suffix_b
[10] ^ 0xff;
341 std::string suffix_c
= suffix_a
;
342 suffix_c
[10] = suffix_c
[10] ^ 0xf0;
344 UniquePosition right_pos
= UniquePosition::InitialPosition(suffix_c
);
345 UniquePosition left_pos
= UniquePosition::Before(right_pos
, suffix_a
);
346 for (int i
= 0; i
< 1024; i
++) {
347 const std::string
& suffix
= (i
% 2 == 0) ? suffix_b
: suffix_a
;
348 UniquePosition new_pos
=
349 UniquePosition::Between(left_pos
, right_pos
, suffix
);
350 ASSERT_PRED_FORMAT2(LessThan
, left_pos
, new_pos
);
351 ASSERT_PRED_FORMAT2(LessThan
, new_pos
, right_pos
);
355 VLOG(1) << "Lengths: " << GetLength(left_pos
) << ", " << GetLength(right_pos
);
358 TEST_P(PositionInsertTest
, StressRightInsertBetween
) {
359 // Use different suffixes to not violate our suffix uniqueness guarantee.
360 const std::string
& suffix_a
= GetParam();
361 std::string suffix_b
= suffix_a
;
362 suffix_b
[10] = suffix_b
[10] ^ 0xff;
363 std::string suffix_c
= suffix_a
;
364 suffix_c
[10] = suffix_c
[10] ^ 0xf0;
366 UniquePosition right_pos
= UniquePosition::InitialPosition(suffix_a
);
367 UniquePosition left_pos
= UniquePosition::Before(right_pos
, suffix_c
);
368 for (int i
= 0; i
< 1024; i
++) {
369 const std::string
& suffix
= (i
% 2 == 0) ? suffix_b
: suffix_a
;
370 UniquePosition new_pos
=
371 UniquePosition::Between(left_pos
, right_pos
, suffix
);
372 ASSERT_PRED_FORMAT2(LessThan
, left_pos
, new_pos
);
373 ASSERT_PRED_FORMAT2(LessThan
, new_pos
, right_pos
);
377 VLOG(1) << "Lengths: " << GetLength(left_pos
) << ", " << GetLength(right_pos
);
380 // Generates suffixes similar to those generated by the directory.
381 // This may become obsolete if the suffix generation code is modified.
382 class SuffixGenerator
{
384 explicit SuffixGenerator(const std::string
& cache_guid
)
385 : cache_guid_(cache_guid
),
389 std::string
NextSuffix() {
390 // This is not entirely realistic, but that should be OK. The current
391 // suffix format is a base64'ed SHA1 hash, which should be fairly close to
393 std::string input
= cache_guid_
+ base::Int64ToString(next_id_
--);
395 base::Base64Encode(base::SHA1HashString(input
), &output
);
400 const std::string cache_guid_
;
404 // Cache guids generated in the same style as real clients.
405 static const char kCacheGuidStr1
[] = "tuiWdG8hV+8y4RT9N5Aikg==";
406 static const char kCacheGuidStr2
[] = "yaKb7zHtY06aue9a0vlZgw==";
408 class PositionScenariosTest
: public UniquePositionTest
{
410 PositionScenariosTest()
411 : generator1_(std::string(kCacheGuidStr1
, arraysize(kCacheGuidStr1
)-1)),
412 generator2_(std::string(kCacheGuidStr2
, arraysize(kCacheGuidStr2
)-1)) {
415 std::string
NextClient1Suffix() {
416 return generator1_
.NextSuffix();
419 std::string
NextClient2Suffix() {
420 return generator2_
.NextSuffix();
424 SuffixGenerator generator1_
;
425 SuffixGenerator generator2_
;
428 // One client creating new bookmarks, always inserting at the end.
429 TEST_F(PositionScenariosTest
, OneClientInsertAtEnd
) {
431 UniquePosition::InitialPosition(NextClient1Suffix());
432 for (int i
= 0; i
< 1024; i
++) {
433 const std::string suffix
= NextClient1Suffix();
434 UniquePosition new_pos
= UniquePosition::After(pos
, suffix
);
435 ASSERT_PRED_FORMAT2(LessThan
, pos
, new_pos
);
439 VLOG(1) << "Length: " << GetLength(pos
);
441 // Normally we wouldn't want to make an assertion about the internal
442 // representation of our data, but we make an exception for this case.
443 // If this scenario causes lengths to explode, we have a big problem.
444 EXPECT_LT(GetLength(pos
), 500U);
447 // Two clients alternately inserting entries at the end, with a strong
448 // bias towards insertions by the first client.
449 TEST_F(PositionScenariosTest
, TwoClientsInsertAtEnd_A
) {
451 UniquePosition::InitialPosition(NextClient1Suffix());
452 for (int i
= 0; i
< 1024; i
++) {
455 suffix
= NextClient2Suffix();
457 suffix
= NextClient1Suffix();
460 UniquePosition new_pos
= UniquePosition::After(pos
, suffix
);
461 ASSERT_PRED_FORMAT2(LessThan
, pos
, new_pos
);
465 VLOG(1) << "Length: " << GetLength(pos
);
466 EXPECT_LT(GetLength(pos
), 500U);
469 // Two clients alternately inserting entries at the end.
470 TEST_F(PositionScenariosTest
, TwoClientsInsertAtEnd_B
) {
472 UniquePosition::InitialPosition(NextClient1Suffix());
473 for (int i
= 0; i
< 1024; i
++) {
476 suffix
= NextClient1Suffix();
478 suffix
= NextClient2Suffix();
481 UniquePosition new_pos
= UniquePosition::After(pos
, suffix
);
482 ASSERT_PRED_FORMAT2(LessThan
, pos
, new_pos
);
486 VLOG(1) << "Length: " << GetLength(pos
);
487 EXPECT_LT(GetLength(pos
), 500U);
490 INSTANTIATE_TEST_CASE_P(MinSuffix
, PositionInsertTest
,
491 ::testing::Values(kMinSuffix
));
492 INSTANTIATE_TEST_CASE_P(MaxSuffix
, PositionInsertTest
,
493 ::testing::Values(kMaxSuffix
));
494 INSTANTIATE_TEST_CASE_P(NormalSuffix
, PositionInsertTest
,
495 ::testing::Values(kNormalSuffix
));
497 class PositionFromIntTest
: public UniquePositionTest
{
499 PositionFromIntTest()
500 : generator_(std::string(kCacheGuidStr1
, arraysize(kCacheGuidStr1
)-1)) {
504 static const int64 kTestValues
[];
505 static const size_t kNumTestValues
;
507 std::string
NextSuffix() {
508 return generator_
.NextSuffix();
512 SuffixGenerator generator_
;
515 const int64
PositionFromIntTest::kTestValues
[] = {
527 0xFA1AFELL
, -0xFA1AFELL
,
528 0xFFFFFFFELL
, -0xFFFFFFFELL
,
529 0xFFFFFFFFLL
, -0xFFFFFFFFLL
,
530 0x100000000LL
, -0x100000000LL
,
531 0x100000001LL
, -0x100000001LL
,
532 0xFFFFFFFFFFLL
, -0xFFFFFFFFFFLL
,
533 0x112358132134LL
, -0x112358132134LL
,
534 0xFEFFBEEFABC1234LL
, -0xFEFFBEEFABC1234LL
,
541 const size_t PositionFromIntTest::kNumTestValues
=
542 arraysize(PositionFromIntTest::kTestValues
);
544 TEST_F(PositionFromIntTest
, IsValid
) {
545 for (size_t i
= 0; i
< kNumTestValues
; ++i
) {
546 const UniquePosition pos
=
547 UniquePosition::FromInt64(kTestValues
[i
], NextSuffix());
548 EXPECT_TRUE(pos
.IsValid()) << "i = " << i
<< "; " << pos
.ToDebugString();
552 TEST_F(PositionFromIntTest
, RoundTripConversion
) {
553 for (size_t i
= 0; i
< kNumTestValues
; ++i
) {
554 const int64 expected_value
= kTestValues
[i
];
555 const UniquePosition pos
=
556 UniquePosition::FromInt64(kTestValues
[i
], NextSuffix());
557 const int64 value
= pos
.ToInt64();
558 EXPECT_EQ(expected_value
, value
) << "i = " << i
;
562 template <typename T
, typename LessThan
= std::less
<T
> >
563 class IndexedLessThan
{
565 IndexedLessThan(const T
* values
) : values_(values
) {}
567 bool operator()(int i1
, int i2
) {
568 return less_than_(values_
[i1
], values_
[i2
]);
576 TEST_F(PositionFromIntTest
, ConsistentOrdering
) {
577 UniquePosition positions
[kNumTestValues
];
578 std::vector
<int> original_ordering(kNumTestValues
);
579 std::vector
<int> int64_ordering(kNumTestValues
);
580 std::vector
<int> position_ordering(kNumTestValues
);
581 for (size_t i
= 0; i
< kNumTestValues
; ++i
) {
582 positions
[i
] = UniquePosition::FromInt64(
583 kTestValues
[i
], NextSuffix());
584 original_ordering
[i
] = int64_ordering
[i
] = position_ordering
[i
] = i
;
587 std::sort(int64_ordering
.begin(), int64_ordering
.end(),
588 IndexedLessThan
<int64
>(kTestValues
));
589 std::sort(position_ordering
.begin(), position_ordering
.end(),
590 IndexedLessThan
<UniquePosition
, PositionLessThan
>(positions
));
591 EXPECT_NE(original_ordering
, int64_ordering
);
592 EXPECT_EQ(int64_ordering
, position_ordering
);
595 class CompressedPositionTest
: public UniquePositionTest
{
597 CompressedPositionTest() {
598 positions_
.push_back(
599 MakePosition( // Prefix starts with 256 0x00s
600 std::string("\x00\x00\x00\x00\xFF\xFF\xFE\xFF" "\x01", 9),
601 MakeSuffix('\x04')));
602 positions_
.push_back(
603 MakePosition( // Prefix starts with four 0x00s
604 std::string("\x00\x00\x00\x00\xFF\xFF\xFF\xFB" "\x01", 9),
605 MakeSuffix('\x03')));
606 positions_
.push_back(
607 MakePosition( // Prefix starts with four 0xFFs
608 std::string("\xFF\xFF\xFF\xFF\x00\x00\x00\x04" "\x01", 9),
609 MakeSuffix('\x01')));
610 positions_
.push_back(
611 MakePosition( // Prefix starts with 256 0xFFs
612 std::string("\xFF\xFF\xFF\xFF\x00\x00\x01\x00" "\x01", 9),
613 MakeSuffix('\x02')));
617 UniquePosition
MakePosition(const std::string
& compressed_prefix
,
618 const std::string
& compressed_suffix
);
619 std::string
MakeSuffix(char unique_value
);
622 std::vector
<UniquePosition
> positions_
;
625 UniquePosition
CompressedPositionTest::MakePosition(
626 const std::string
& compressed_prefix
,
627 const std::string
& compressed_suffix
) {
628 sync_pb::UniquePosition proto
;
629 proto
.set_custom_compressed_v1(
630 std::string(compressed_prefix
+ compressed_suffix
));
631 return UniquePosition::FromProto(proto
);
634 std::string
CompressedPositionTest::MakeSuffix(char unique_value
) {
635 // We're dealing in compressed positions in this test. That means the
636 // suffix should be compressed, too. To avoid complication, we use suffixes
637 // that don't have any repeating digits, and therefore are identical in
638 // compressed and uncompressed form.
640 for (size_t i
= 0; i
< UniquePosition::kSuffixLength
; ++i
) {
641 suffix
.push_back(static_cast<char>(i
));
643 suffix
[UniquePosition::kSuffixLength
-1] = unique_value
;
647 // Make sure that serialization and deserialization routines are correct.
648 TEST_F(CompressedPositionTest
, SerializeAndDeserialize
) {
649 for (std::vector
<UniquePosition
>::const_iterator it
= positions_
.begin();
650 it
!= positions_
.end(); ++it
) {
651 SCOPED_TRACE("iteration: " + it
->ToDebugString());
653 sync_pb::UniquePosition proto
;
655 UniquePosition deserialized
= UniquePosition::FromProto(proto
);
657 EXPECT_PRED_FORMAT2(Equals
, *it
, deserialized
);
661 // Test that deserialization failures of protobufs where we know none of its
662 // fields is not catastrophic. This may happen if all the fields currently
663 // known to this client become deprecated in the future.
664 TEST_F(CompressedPositionTest
, DeserializeProtobufFromTheFuture
) {
665 sync_pb::UniquePosition proto
;
666 UniquePosition deserialized
= UniquePosition::FromProto(proto
);
667 EXPECT_FALSE(deserialized
.IsValid());
670 // Make sure the comparison functions are working correctly.
671 // This requires values in the test harness to be hard-coded in ascending order.
672 TEST_F(CompressedPositionTest
, OrderingTest
) {
673 for (size_t i
= 0; i
< positions_
.size()-1; ++i
) {
674 EXPECT_PRED_FORMAT2(LessThan
, positions_
[i
], positions_
[i
+1]);
680 } // namespace syncer