- add sources.
[platform/framework/web/crosswalk.git] / src / sync / internal_api / public / base / unique_position_unittest.cc
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/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"
18
19 namespace syncer {
20
21 namespace {
22
23 class UniquePositionTest : public ::testing::Test {
24  protected:
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.
28   //
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
31   // scenarios.
32   size_t GetLength(const UniquePosition& pos) {
33     sync_pb::UniquePosition proto;
34     pos.ToProto(&proto);
35     return proto.ByteSize();
36   }
37 };
38
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);
45 }
46
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;
52
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');
71
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");
78
79 ::testing::AssertionResult LessThan(const char* m_expr,
80                                     const char* n_expr,
81                                     const UniquePosition &m,
82                                     const UniquePosition &n) {
83   if (m.LessThan(n))
84     return ::testing::AssertionSuccess();
85
86   return ::testing::AssertionFailure()
87       << m_expr << " is not less than " << n_expr
88       << " (" << m.ToDebugString() << " and " << n.ToDebugString() << ")";
89 }
90
91 ::testing::AssertionResult Equals(const char* m_expr,
92                                   const char* n_expr,
93                                   const UniquePosition &m,
94                                   const UniquePosition &n) {
95   if (m.Equals(n))
96     return ::testing::AssertionSuccess();
97
98   return ::testing::AssertionFailure()
99       << m_expr << " is not equal to " << n_expr
100       << " (" << m.ToDebugString() << " != " << n.ToDebugString() << ")";
101 }
102
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
107   // of this code.
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));
114
115   sync_pb::UniquePosition proto;
116   proto.ParseFromString(serialized);
117
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());
122
123   UniquePosition pos = UniquePosition::FromProto(proto);
124   EXPECT_PRED_FORMAT2(Equals, kGenericPredecessor, pos);
125 }
126
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
131   // this code.
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));
136
137   sync_pb::UniquePosition proto;
138   proto.ParseFromString(serialized);
139
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());
144
145   UniquePosition pos = UniquePosition::FromProto(proto);
146   EXPECT_PRED_FORMAT2(Equals, kHugePosition, pos);
147 }
148
149 class RelativePositioningTest : public UniquePositionTest { };
150
151 const UniquePosition kPositionArray[] = {
152   kGenericPredecessor,
153   kGenericSuccessor,
154   kBigPosition,
155   kBigPositionLessTwo,
156   kBiggerPosition,
157   kSmallPosition,
158   kSmallPositionPlusOne,
159 };
160
161 const UniquePosition kSortedPositionArray[] = {
162   kSmallPosition,
163   kSmallPositionPlusOne,
164   kGenericPredecessor,
165   kGenericSuccessor,
166   kBigPositionLessTwo,
167   kBigPosition,
168   kBiggerPosition,
169 };
170
171 static const size_t kNumPositions = arraysize(kPositionArray);
172 static const size_t kNumSortedPositions = arraysize(kSortedPositionArray);
173
174 struct PositionLessThan {
175   bool operator()(const UniquePosition& a, const UniquePosition& b) {
176     return a.LessThan(b);
177   }
178 };
179
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;
184 }
185
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());
190
191   // Necessarily true for any non-invalid positions.
192   EXPECT_TRUE(a.Equals(a));
193   EXPECT_FALSE(a.LessThan(a));
194 }
195
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];
200
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));
205 }
206
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];
213   }
214
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();
221   }
222
223 }
224
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];
231   }
232
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();
240   }
241 }
242
243 class PositionInsertTest :
244     public RelativePositioningTest,
245     public ::testing::WithParamInterface<std::string> { };
246
247 // Exercise InsertBetween with various insertion operations.
248 TEST_P(PositionInsertTest, InsertBetween) {
249   const std::string suffix = GetParam();
250   ASSERT_TRUE(UniquePosition::IsValidSuffix(suffix));
251
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))
256       continue;
257
258     for (size_t j = i + 1; j < kNumSortedPositions; ++j) {
259       const UniquePosition& successor = kSortedPositionArray[j];
260
261       // Another guard against non-unique suffixes.
262       if (IsSuffixInUse(successor, suffix))
263         continue;
264
265       UniquePosition midpoint =
266           UniquePosition::Between(predecessor, successor, suffix);
267
268       EXPECT_PRED_FORMAT2(LessThan, predecessor, midpoint);
269       EXPECT_PRED_FORMAT2(LessThan, midpoint, successor);
270     }
271   }
272 }
273
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))
280       continue;
281
282     UniquePosition before = UniquePosition::Before(successor, suffix);
283
284     EXPECT_PRED_FORMAT2(LessThan, before, successor);
285   }
286 }
287
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))
294       continue;
295
296     UniquePosition after = UniquePosition::After(predecessor, suffix);
297
298     EXPECT_PRED_FORMAT2(LessThan, predecessor, after);
299   }
300 }
301
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;
307
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);
313     pos = next_pos;
314   }
315
316   VLOG(1) << "Length: " << GetLength(pos);
317 }
318
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;
324
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);
330     pos = prev_pos;
331   }
332
333   VLOG(1) << "Length: " << GetLength(pos);
334 }
335
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;
343
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);
352     left_pos = new_pos;
353   }
354
355   VLOG(1) << "Lengths: " << GetLength(left_pos) << ", " << GetLength(right_pos);
356 }
357
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;
365
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);
374     right_pos = new_pos;
375   }
376
377   VLOG(1) << "Lengths: " << GetLength(left_pos) << ", " << GetLength(right_pos);
378 }
379
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 {
383  public:
384   explicit SuffixGenerator(const std::string& cache_guid)
385       : cache_guid_(cache_guid),
386         next_id_(-65535) {
387   }
388
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
392     // random anyway.
393     std::string input = cache_guid_ + base::Int64ToString(next_id_--);
394     std::string output;
395     CHECK(base::Base64Encode(base::SHA1HashString(input), &output));
396     return output;
397   }
398
399  private:
400   const std::string cache_guid_;
401   int64 next_id_;
402 };
403
404 // Cache guids generated in the same style as real clients.
405 static const char kCacheGuidStr1[] = "tuiWdG8hV+8y4RT9N5Aikg==";
406 static const char kCacheGuidStr2[] = "yaKb7zHtY06aue9a0vlZgw==";
407
408 class PositionScenariosTest : public UniquePositionTest {
409  public:
410   PositionScenariosTest()
411       : generator1_(std::string(kCacheGuidStr1, arraysize(kCacheGuidStr1)-1)),
412         generator2_(std::string(kCacheGuidStr2, arraysize(kCacheGuidStr2)-1)) {
413   }
414
415   std::string NextClient1Suffix() {
416     return generator1_.NextSuffix();
417   }
418
419   std::string NextClient2Suffix() {
420     return generator2_.NextSuffix();
421   }
422
423  private:
424   SuffixGenerator generator1_;
425   SuffixGenerator generator2_;
426 };
427
428 // One client creating new bookmarks, always inserting at the end.
429 TEST_F(PositionScenariosTest, OneClientInsertAtEnd) {
430   UniquePosition pos =
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);
436     pos = new_pos;
437   }
438
439   VLOG(1) << "Length: " << GetLength(pos);
440
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);
445 }
446
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) {
450   UniquePosition pos =
451       UniquePosition::InitialPosition(NextClient1Suffix());
452   for (int i = 0; i < 1024; i++) {
453     std::string suffix;
454     if (i % 5 == 0) {
455       suffix = NextClient2Suffix();
456     } else {
457       suffix = NextClient1Suffix();
458     }
459
460     UniquePosition new_pos = UniquePosition::After(pos, suffix);
461     ASSERT_PRED_FORMAT2(LessThan, pos, new_pos);
462     pos = new_pos;
463   }
464
465   VLOG(1) << "Length: " << GetLength(pos);
466   EXPECT_LT(GetLength(pos), 500U);
467 }
468
469 // Two clients alternately inserting entries at the end.
470 TEST_F(PositionScenariosTest, TwoClientsInsertAtEnd_B) {
471   UniquePosition pos =
472       UniquePosition::InitialPosition(NextClient1Suffix());
473   for (int i = 0; i < 1024; i++) {
474     std::string suffix;
475     if (i % 2 == 0) {
476       suffix = NextClient1Suffix();
477     } else {
478       suffix = NextClient2Suffix();
479     }
480
481     UniquePosition new_pos = UniquePosition::After(pos, suffix);
482     ASSERT_PRED_FORMAT2(LessThan, pos, new_pos);
483     pos = new_pos;
484   }
485
486   VLOG(1) << "Length: " << GetLength(pos);
487   EXPECT_LT(GetLength(pos), 500U);
488 }
489
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));
496
497 class PositionFromIntTest : public UniquePositionTest {
498  public:
499   PositionFromIntTest()
500       : generator_(std::string(kCacheGuidStr1, arraysize(kCacheGuidStr1)-1)) {
501   }
502
503  protected:
504   static const int64 kTestValues[];
505   static const size_t kNumTestValues;
506
507   std::string NextSuffix() {
508     return generator_.NextSuffix();
509   }
510
511  private:
512   SuffixGenerator generator_;
513 };
514
515 const int64 PositionFromIntTest::kTestValues[] = {
516   0LL,
517   1LL, -1LL,
518   2LL, -2LL,
519   3LL, -3LL,
520   0x79LL, -0x79LL,
521   0x80LL, -0x80LL,
522   0x81LL, -0x81LL,
523   0xFELL, -0xFELL,
524   0xFFLL, -0xFFLL,
525   0x100LL, -0x100LL,
526   0x101LL, -0x101LL,
527   0xFA1AFELL, -0xFA1AFELL,
528   0xFFFFFFFELL, -0xFFFFFFFELL,
529   0xFFFFFFFFLL, -0xFFFFFFFFLL,
530   0x100000000LL, -0x100000000LL,
531   0x100000001LL, -0x100000001LL,
532   0xFFFFFFFFFFLL, -0xFFFFFFFFFFLL,
533   0x112358132134LL, -0x112358132134LL,
534   0xFEFFBEEFABC1234LL, -0xFEFFBEEFABC1234LL,
535   kint64max,
536   kint64min,
537   kint64min + 1,
538   kint64max - 1
539 };
540
541 const size_t PositionFromIntTest::kNumTestValues =
542 arraysize(PositionFromIntTest::kTestValues);
543
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();
549   }
550 }
551
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;
559   }
560 }
561
562 template <typename T, typename LessThan = std::less<T> >
563 class IndexedLessThan {
564  public:
565   IndexedLessThan(const T* values) : values_(values) {}
566
567   bool operator()(int i1, int i2) {
568     return less_than_(values_[i1], values_[i2]);
569   }
570
571  private:
572   const T* values_;
573   LessThan less_than_;
574 };
575
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;
585   }
586
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);
593 }
594
595 class CompressedPositionTest : public UniquePositionTest {
596  public:
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')));
614   }
615
616  private:
617   UniquePosition MakePosition(const std::string& compressed_prefix,
618                               const std::string& compressed_suffix);
619   std::string MakeSuffix(char unique_value);
620
621  protected:
622   std::vector<UniquePosition> positions_;
623 };
624
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);
632 }
633
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.
639   std::string suffix;
640   for (size_t i = 0; i < UniquePosition::kSuffixLength; ++i) {
641     suffix.push_back(static_cast<char>(i));
642   }
643   suffix[UniquePosition::kSuffixLength-1] = unique_value;
644   return suffix;
645 }
646
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());
652
653     sync_pb::UniquePosition proto;
654     it->ToProto(&proto);
655     UniquePosition deserialized = UniquePosition::FromProto(proto);
656
657     EXPECT_PRED_FORMAT2(Equals, *it, deserialized);
658
659   }
660 }
661
662 // Test that redundant serialization for legacy clients is correct, too.
663 TEST_F(CompressedPositionTest, SerializeAndLegacyDeserialize) {
664   for (std::vector<UniquePosition>::const_iterator it = positions_.begin();
665        it != positions_.end(); ++it) {
666     SCOPED_TRACE("iteration: " + it->ToDebugString());
667     sync_pb::UniquePosition proto;
668
669     it->ToProto(&proto);
670
671     // Clear default encoding to force it to use legacy as fallback.
672     proto.clear_custom_compressed_v1();
673     UniquePosition deserialized = UniquePosition::FromProto(proto);
674
675     EXPECT_PRED_FORMAT2(Equals, *it, deserialized);
676   }
677 }
678
679 // Make sure the comparison functions are working correctly.
680 // This requires values in the test harness to be hard-coded in ascending order.
681 TEST_F(CompressedPositionTest, OrderingTest) {
682   for (size_t i = 0; i < positions_.size()-1; ++i) {
683     EXPECT_PRED_FORMAT2(LessThan, positions_[i], positions_[i+1]);
684   }
685 }
686
687 }  // namespace
688
689 }  // namespace syncer