Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Side by Side Diff: sync/internal_api/public/base/unique_position_unittest.cc

Issue 11569045: Initial UniquePositions implementation (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Update class comments Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « sync/internal_api/public/base/unique_position.cc ('k') | sync/sync.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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/sha1.h"
14 #include "base/string_number_conversions.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16
17 namespace syncer {
18
19 namespace {
20
21 class UniquePositionTest : public ::testing::Test {
22 protected:
23 // Accessor to fetch the length of the position's internal representation
24 // We try to avoid having any test expectations on it because this is an
25 // implementation detail.
26 //
27 // If you run the tests with --v=1, we'll print out some of the lengths
28 // so you can see how well the algorithm performs in various insertion
29 // scenarios.
30 size_t GetLength(const UniquePosition& pos) {
31 return pos.ToInternalValue().length();
32 }
33 };
34
35 const size_t kMinLength = UniquePosition::kSuffixLength;
36 const size_t kGenericPredecessorLength = kMinLength + 2;
37 const size_t kGenericSuccessorLength = kMinLength + 1;
38 const size_t kBigPositionLength = kMinLength;
39 const size_t kSmallPositionLength = kMinLength;
40
41 // Be careful when adding more prefixes to this list.
42 // We have to manually ensure each has a unique suffix.
43 const UniquePosition kGenericPredecessor = UniquePosition::FromBytes(
44 (std::string(kGenericPredecessorLength, '\x23') + '\xFF'));
45 const UniquePosition kGenericSuccessor = UniquePosition::FromBytes(
46 std::string(kGenericSuccessorLength, '\xAB') + '\xFF');
47 const UniquePosition kBigPosition = UniquePosition::FromBytes(
48 std::string(kBigPositionLength - 1, '\xFF') + '\xFE' + '\xFF');
49 const UniquePosition kBigPositionLessTwo = UniquePosition::FromBytes(
50 std::string(kBigPositionLength - 1, '\xFF') + '\xFC' + '\xFF');
51 const UniquePosition kBiggerPosition = UniquePosition::FromBytes(
52 std::string(kBigPositionLength, '\xFF') + '\xFF');
53 const UniquePosition kSmallPosition = UniquePosition::FromBytes(
54 std::string(kSmallPositionLength - 1, '\x00') + '\x01' + '\xFF');
55 const UniquePosition kSmallPositionPlusOne = UniquePosition::FromBytes(
56 std::string(kSmallPositionLength - 1, '\x00') + '\x02' + '\xFF');
57
58 const std::string kMinSuffix =
59 std::string(UniquePosition::kSuffixLength - 1, '\x00') + '\x01';
60 const std::string kMaxSuffix(UniquePosition::kSuffixLength, '\xFF');
61 const std::string kNormalSuffix(UniquePosition::kSuffixLength, '\xAB');
62
63 ::testing::AssertionResult LessThan(const char* m_expr,
64 const char* n_expr,
65 const UniquePosition &m,
66 const UniquePosition &n) {
67 if (m.LessThan(n))
68 return ::testing::AssertionSuccess();
69
70 return ::testing::AssertionFailure()
71 << m_expr << " is not less than " << n_expr
72 << " (" << m.ToDebugString() << " and " << n.ToDebugString() << ")";
73 }
74
75 class RelativePositioningTest : public UniquePositionTest { };
76
77 const UniquePosition kPositionArray[] = {
78 kGenericPredecessor,
79 kGenericSuccessor,
80 kBigPosition,
81 kBigPositionLessTwo,
82 kBiggerPosition,
83 kSmallPosition,
84 kSmallPositionPlusOne,
85 };
86
87 const UniquePosition kSortedPositionArray[] = {
88 kSmallPosition,
89 kSmallPositionPlusOne,
90 kGenericPredecessor,
91 kGenericSuccessor,
92 kBigPositionLessTwo,
93 kBigPosition,
94 kBiggerPosition,
95 };
96
97 static const size_t kNumPositions = arraysize(kPositionArray);
98 static const size_t kNumSortedPositions = arraysize(kSortedPositionArray);
99
100 struct PositionLessThan {
101 bool operator()(const UniquePosition& a, const UniquePosition& b) {
102 return a.LessThan(b);
103 }
104 };
105
106 static bool IsSuffixInUse(
107 const UniquePosition& pos, const std::string& suffix) {
108 const std::string& pos_bytes = pos.ToInternalValue();
109 const size_t prefix_len = pos_bytes.length() - UniquePosition::kSuffixLength;
110 return pos_bytes.substr(prefix_len, std::string::npos) == suffix;
111 }
112
113 // Test some basic properties of comparison and equality.
114 TEST_F(RelativePositioningTest, ComparisonSanityTest1) {
115 const UniquePosition& a = kPositionArray[0];
116 ASSERT_TRUE(a.IsValid());
117
118 // Necessarily true for any non-invalid positions.
119 EXPECT_TRUE(a.Equals(a));
120 EXPECT_FALSE(a.LessThan(a));
121 }
122
123 // Test some more properties of comparison and equality.
124 TEST_F(RelativePositioningTest, ComparisonSanityTest2) {
125 const UniquePosition& a = kPositionArray[0];
126 const UniquePosition& b = kPositionArray[1];
127
128 // These should pass for the specific a and b we have chosen (a < b).
129 EXPECT_FALSE(a.Equals(b));
130 EXPECT_TRUE(a.LessThan(b));
131 EXPECT_FALSE(b.LessThan(a));
132 }
133
134 // Exercise comparision functions by sorting and re-sorting the list.
135 TEST_F(RelativePositioningTest, SortPositions) {
136 ASSERT_EQ(kNumPositions, kNumSortedPositions);
137 UniquePosition positions[arraysize(kPositionArray)];
138 for (size_t i = 0; i < kNumPositions; ++i) {
139 positions[i] = kPositionArray[i];
140 }
141
142 std::sort(&positions[0], &positions[kNumPositions], PositionLessThan());
143 for (size_t i = 0; i < kNumPositions; ++i) {
144 EXPECT_TRUE(positions[i].Equals(kSortedPositionArray[i]))
145 << "i: " << i << ", "
146 << positions[i].ToDebugString() << " != "
147 << kSortedPositionArray[i].ToDebugString();
148 }
149
150 }
151
152 // Some more exercise for the comparison function.
153 TEST_F(RelativePositioningTest, ReverseSortPositions) {
154 ASSERT_EQ(kNumPositions, kNumSortedPositions);
155 UniquePosition positions[arraysize(kPositionArray)];
156 for (size_t i = 0; i < kNumPositions; ++i) {
157 positions[i] = kPositionArray[i];
158 }
159
160 std::reverse(&positions[0], &positions[kNumPositions]);
161 std::sort(&positions[0], &positions[kNumPositions], PositionLessThan());
162 for (size_t i = 0; i < kNumPositions; ++i) {
163 EXPECT_TRUE(positions[i].Equals(kSortedPositionArray[i]))
164 << "i: " << i << ", "
165 << positions[i].ToDebugString() << " != "
166 << kSortedPositionArray[i].ToDebugString();
167 }
168 }
169
170 class PositionInsertTest :
171 public RelativePositioningTest,
172 public ::testing::WithParamInterface<std::string> { };
173
174 // Exercise InsertBetween with various insertion operations.
175 TEST_P(PositionInsertTest, InsertBetween) {
176 const std::string suffix = GetParam();
177 ASSERT_TRUE(UniquePosition::IsValidSuffix(suffix));
178
179 for (size_t i = 0; i < kNumSortedPositions; ++i) {
180 const UniquePosition& predecessor = kSortedPositionArray[i];
181 // Verify our suffixes are unique before we continue.
182 if (IsSuffixInUse(predecessor, suffix))
183 continue;
184
185 for (size_t j = i + 1; j < kNumSortedPositions; ++j) {
186 const UniquePosition& successor = kSortedPositionArray[j];
187
188 // Another guard against non-unique suffixes.
189 if (IsSuffixInUse(successor, suffix))
190 continue;
191
192 UniquePosition midpoint =
193 UniquePosition::Between(predecessor, successor, suffix);
194
195 EXPECT_PRED_FORMAT2(LessThan, predecessor, midpoint);
196 EXPECT_PRED_FORMAT2(LessThan, midpoint, successor);
197 }
198 }
199 }
200
201 TEST_P(PositionInsertTest, InsertBefore) {
202 const std::string suffix = GetParam();
203 for (size_t i = 0; i < kNumSortedPositions; ++i) {
204 const UniquePosition& successor = kSortedPositionArray[i];
205 // Verify our suffixes are unique before we continue.
206 if (IsSuffixInUse(successor, suffix))
207 continue;
208
209 UniquePosition before = UniquePosition::Before(successor, suffix);
210
211 EXPECT_PRED_FORMAT2(LessThan, before, successor);
212 }
213 }
214
215 TEST_P(PositionInsertTest, InsertAfter) {
216 const std::string suffix = GetParam();
217 for (size_t i = 0; i < kNumSortedPositions; ++i) {
218 const UniquePosition& predecessor = kSortedPositionArray[i];
219 // Verify our suffixes are unique before we continue.
220 if (IsSuffixInUse(predecessor, suffix))
221 continue;
222
223 UniquePosition after = UniquePosition::After(predecessor, suffix);
224
225 EXPECT_PRED_FORMAT2(LessThan, predecessor, after);
226 }
227 }
228
229 TEST_P(PositionInsertTest, StressInsertAfter) {
230 // Use two different suffixes to not violate our suffix uniqueness guarantee.
231 const std::string& suffix_a = GetParam();
232 std::string suffix_b = suffix_a;
233 suffix_b[10] = suffix_b[10] ^ 0xff;
234
235 UniquePosition pos = UniquePosition::InitialPosition(suffix_a);
236 for (int i = 0; i < 1024; i++) {
237 const std::string& suffix = (i % 2 == 0) ? suffix_b : suffix_a;
238 UniquePosition next_pos = UniquePosition::After(pos, suffix);
239 ASSERT_PRED_FORMAT2(LessThan, pos, next_pos);
240 pos = next_pos;
241 }
242
243 VLOG(1) << "Length: " << GetLength(pos);
244 }
245
246 TEST_P(PositionInsertTest, StressInsertBefore) {
247 // Use two different suffixes to not violate our suffix uniqueness guarantee.
248 const std::string& suffix_a = GetParam();
249 std::string suffix_b = suffix_a;
250 suffix_b[10] = suffix_b[10] ^ 0xff;
251
252 UniquePosition pos = UniquePosition::InitialPosition(suffix_a);
253 for (int i = 0; i < 1024; i++) {
254 const std::string& suffix = (i % 2 == 0) ? suffix_b : suffix_a;
255 UniquePosition prev_pos = UniquePosition::Before(pos, suffix);
256 ASSERT_PRED_FORMAT2(LessThan, prev_pos, pos);
257 pos = prev_pos;
258 }
259
260 VLOG(1) << "Length: " << GetLength(pos);
261 }
262
263 TEST_P(PositionInsertTest, StressLeftInsertBetween) {
264 // Use different suffixes to not violate our suffix uniqueness guarantee.
265 const std::string& suffix_a = GetParam();
266 std::string suffix_b = suffix_a;
267 suffix_b[10] = suffix_b[10] ^ 0xff;
268 std::string suffix_c = suffix_a;
269 suffix_c[10] = suffix_c[10] ^ 0xf0;
270
271 UniquePosition right_pos = UniquePosition::InitialPosition(suffix_c);
272 UniquePosition left_pos = UniquePosition::Before(right_pos, suffix_a);
273 for (int i = 0; i < 1024; i++) {
274 const std::string& suffix = (i % 2 == 0) ? suffix_b : suffix_a;
275 UniquePosition new_pos =
276 UniquePosition::Between(left_pos, right_pos, suffix);
277 ASSERT_PRED_FORMAT2(LessThan, left_pos, new_pos);
278 ASSERT_PRED_FORMAT2(LessThan, new_pos, right_pos);
279 left_pos = new_pos;
280 }
281
282 VLOG(1) << "Lengths: " << GetLength(left_pos) << ", " << GetLength(right_pos);
283 }
284
285 TEST_P(PositionInsertTest, StressRightInsertBetween) {
286 // Use different suffixes to not violate our suffix uniqueness guarantee.
287 const std::string& suffix_a = GetParam();
288 std::string suffix_b = suffix_a;
289 suffix_b[10] = suffix_b[10] ^ 0xff;
290 std::string suffix_c = suffix_a;
291 suffix_c[10] = suffix_c[10] ^ 0xf0;
292
293 UniquePosition right_pos = UniquePosition::InitialPosition(suffix_a);
294 UniquePosition left_pos = UniquePosition::Before(right_pos, suffix_c);
295 for (int i = 0; i < 1024; i++) {
296 const std::string& suffix = (i % 2 == 0) ? suffix_b : suffix_a;
297 UniquePosition new_pos =
298 UniquePosition::Between(left_pos, right_pos, suffix);
299 ASSERT_PRED_FORMAT2(LessThan, left_pos, new_pos);
300 ASSERT_PRED_FORMAT2(LessThan, new_pos, right_pos);
301 right_pos = new_pos;
302 }
303
304 VLOG(1) << "Lengths: " << GetLength(left_pos) << ", " << GetLength(right_pos);
305 }
306
307 // Generates suffixes similar to those generated by the directory.
308 // This may become obsolete if the suffix generation code is modified.
309 class SuffixGenerator {
310 public:
311 explicit SuffixGenerator(const std::string& cache_guid)
312 : cache_guid_(cache_guid),
313 next_id_(-65535) {
314 }
315
316 std::string NextSuffix() {
317 // This is not entirely realistic, but that should be OK. The current
318 // suffix format is a base64'ed SHA1 hash, which should be fairly close to
319 // random anyway.
320 std::string input = cache_guid_ + base::Int64ToString(next_id_--);
321 std::string output;
322 CHECK(base::Base64Encode(base::SHA1HashString(input), &output));
323 return output;
324 }
325
326 private:
327 const std::string cache_guid_;
328 int64 next_id_;
329 };
330
331 // Cache guids generated in the same style as real clients.
332 static const char kCacheGuidStr1[] = "tuiWdG8hV+8y4RT9N5Aikg==";
333 static const char kCacheGuidStr2[] = "yaKb7zHtY06aue9a0vlZgw==";
334
335 class PositionScenariosTest : public UniquePositionTest {
336 public:
337 PositionScenariosTest()
338 : generator1_(std::string(kCacheGuidStr1, arraysize(kCacheGuidStr1)-1)),
339 generator2_(std::string(kCacheGuidStr2, arraysize(kCacheGuidStr2)-1)) {
340 }
341
342 std::string NextClient1Suffix() {
343 return generator1_.NextSuffix();
344 }
345
346 std::string NextClient2Suffix() {
347 return generator2_.NextSuffix();
348 }
349
350 private:
351 SuffixGenerator generator1_;
352 SuffixGenerator generator2_;
353 };
354
355 // One client creating new bookmarks, always inserting at the end.
356 TEST_F(PositionScenariosTest, OneClientInsertAtEnd) {
357 UniquePosition pos =
358 UniquePosition::InitialPosition(NextClient1Suffix());
359 for (int i = 0; i < 1024; i++) {
360 const std::string suffix = NextClient1Suffix();
361 UniquePosition new_pos = UniquePosition::After(pos, suffix);
362 ASSERT_PRED_FORMAT2(LessThan, pos, new_pos);
363 pos = new_pos;
364 }
365
366 VLOG(1) << "Length: " << GetLength(pos);
367
368 // Normally we wouldn't want to make an assertion about the internal
369 // representation of our data, but we make an exception for this case.
370 // If this scenario causes lengths to explode, we have a big problem.
371 EXPECT_LT(GetLength(pos), 500U);
372 }
373
374 // Two clients alternately inserting entries at the end, with a strong
375 // bias towards insertions by the first client.
376 TEST_F(PositionScenariosTest, TwoClientsInsertAtEnd_A) {
377 UniquePosition pos =
378 UniquePosition::InitialPosition(NextClient1Suffix());
379 for (int i = 0; i < 1024; i++) {
380 std::string suffix;
381 if (i % 5 == 0) {
382 suffix = NextClient2Suffix();
383 } else {
384 suffix = NextClient1Suffix();
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
396 // Two clients alternately inserting entries at the end.
397 TEST_F(PositionScenariosTest, TwoClientsInsertAtEnd_B) {
398 UniquePosition pos =
399 UniquePosition::InitialPosition(NextClient1Suffix());
400 for (int i = 0; i < 1024; i++) {
401 std::string suffix;
402 if (i % 2 == 0) {
403 suffix = NextClient1Suffix();
404 } else {
405 suffix = NextClient2Suffix();
406 }
407
408 UniquePosition new_pos = UniquePosition::After(pos, suffix);
409 ASSERT_PRED_FORMAT2(LessThan, pos, new_pos);
410 pos = new_pos;
411 }
412
413 VLOG(1) << "Length: " << GetLength(pos);
414 EXPECT_LT(GetLength(pos), 500U);
415 }
416
417 INSTANTIATE_TEST_CASE_P(MinSuffix, PositionInsertTest,
418 ::testing::Values(kMinSuffix));
419 INSTANTIATE_TEST_CASE_P(MaxSuffix, PositionInsertTest,
420 ::testing::Values(kMaxSuffix));
421 INSTANTIATE_TEST_CASE_P(NormalSuffix, PositionInsertTest,
422 ::testing::Values(kNormalSuffix));
423
424 class PositionFromIntTest : public UniquePositionTest {
425 public:
426 PositionFromIntTest()
427 : generator_(std::string(kCacheGuidStr1, arraysize(kCacheGuidStr1)-1)) {
428 }
429
430 protected:
431 std::string NextSuffix() {
432 return generator_.NextSuffix();
433 }
434
435 private:
436 SuffixGenerator generator_;
437 };
438
439 const int64 kTestValues[] = {
440 0LL,
441 1LL, -1LL,
442 2LL, -2LL,
443 3LL, -3LL,
444 0x79LL, -0x79LL,
445 0x80LL, -0x80LL,
446 0x81LL, -0x81LL,
447 0xFELL, -0xFELL,
448 0xFFLL, -0xFFLL,
449 0x100LL, -0x100LL,
450 0x101LL, -0x101LL,
451 0xFA1AFELL, -0xFA1AFELL,
452 0xFFFFFFFELL, -0xFFFFFFFELL,
453 0xFFFFFFFFLL, -0xFFFFFFFFLL,
454 0x100000000LL, -0x100000000LL,
455 0x100000001LL, -0x100000001LL,
456 0xFFFFFFFFFFLL, -0xFFFFFFFFFFLL,
457 0x112358132134LL, -0x112358132134LL,
458 0xFEFFBEEFABC1234LL, -0xFEFFBEEFABC1234LL,
459 kint64max,
460 kint64min,
461 kint64min + 1,
462 kint64max - 1
463 };
464
465 const size_t kNumTestValues = arraysize(kTestValues);
466
467 TEST_F(PositionFromIntTest, IsValid) {
468 for (size_t i = 0; i < kNumTestValues; ++i) {
469 const UniquePosition pos =
470 UniquePosition::FromInt64(kTestValues[i], NextSuffix());
471 EXPECT_TRUE(pos.IsValid()) << "i = " << i << "; " << pos.ToDebugString();
472 }
473 }
474
475 TEST_F(PositionFromIntTest, RoundTripConversion) {
476 for (size_t i = 0; i < kNumTestValues; ++i) {
477 const int64 expected_value = kTestValues[i];
478 const UniquePosition pos =
479 UniquePosition::FromInt64(kTestValues[i], NextSuffix());
480 const int64 value = pos.ToInt64();
481 EXPECT_EQ(expected_value, value) << "i = " << i;
482 }
483 }
484
485 template <typename T, typename LessThan = std::less<T> >
486 class IndexedLessThan {
487 public:
488 IndexedLessThan(const T* values) : values_(values) {}
489
490 bool operator()(int i1, int i2) {
491 return less_than_(values_[i1], values_[i2]);
492 }
493
494 private:
495 const T* values_;
496 LessThan less_than_;
497 };
498
499 TEST_F(PositionFromIntTest, ConsistentOrdering) {
500 UniquePosition positions[kNumTestValues];
501 std::vector<int> original_ordering(kNumTestValues);
502 std::vector<int> int64_ordering(kNumTestValues);
503 std::vector<int> position_ordering(kNumTestValues);
504 for (size_t i = 0; i < kNumTestValues; ++i) {
505 positions[i] = UniquePosition::FromInt64(
506 kTestValues[i], NextSuffix());
507 original_ordering[i] = int64_ordering[i] = position_ordering[i] = i;
508 }
509
510 std::sort(int64_ordering.begin(), int64_ordering.end(),
511 IndexedLessThan<int64>(kTestValues));
512 std::sort(position_ordering.begin(), position_ordering.end(),
513 IndexedLessThan<UniquePosition, PositionLessThan>(positions));
514 EXPECT_NE(original_ordering, int64_ordering);
515 EXPECT_EQ(int64_ordering, position_ordering);
516 }
517
518 } // namespace
519
520 } // namespace syncer
OLDNEW
« no previous file with comments | « sync/internal_api/public/base/unique_position.cc ('k') | sync/sync.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698