OLD | NEW |
(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/basictypes.h" |
| 11 #include "base/logging.h" |
| 12 #include "testing/gtest/include/gtest/gtest.h" |
| 13 |
| 14 namespace syncer { |
| 15 |
| 16 namespace { |
| 17 |
| 18 class UniquePositionTest : public ::testing::Test { |
| 19 protected: |
| 20 // Accessor to fetch the length of the position's internal representation. We |
| 21 // don't have any test expectations on it because this is an implementation |
| 22 // detail. |
| 23 // |
| 24 // We use it to see just how big our ordinals are getting in some of the |
| 25 // worst-case scenarios that occur in our tests. This can be useful when |
| 26 // testing and comparing alternative implementations. Enable with --v=1. |
| 27 size_t GetLength(UniquePosition &pos) { |
| 28 return pos.ToInternalValue().length(); |
| 29 } |
| 30 }; |
| 31 |
| 32 const size_t kMinLength = UniquePosition::kSuffixLength; |
| 33 const size_t kGenericPredecessorLength = kMinLength + 2; |
| 34 const size_t kGenericSuccessorLength = kMinLength + 1; |
| 35 const size_t kBigPositionLength = kMinLength; |
| 36 const size_t kSmallPositionLength = kMinLength; |
| 37 |
| 38 // Be careful when adding more prefixes to this list. |
| 39 // We have to manually ensure each has a unique suffix. |
| 40 const UniquePosition kGenericPredecessor = UniquePosition::FromBytes( |
| 41 (std::string(kGenericPredecessorLength, '\x23') + '\xFF')); |
| 42 const UniquePosition kGenericSuccessor = UniquePosition::FromBytes( |
| 43 std::string(kGenericSuccessorLength, '\xAB') + '\xFF'); |
| 44 const UniquePosition kBigPosition = UniquePosition::FromBytes( |
| 45 std::string(kBigPositionLength - 1, '\xFF') + '\xFE' + '\xFF'); |
| 46 const UniquePosition kBigPositionLessTwo = UniquePosition::FromBytes( |
| 47 std::string(kBigPositionLength - 1, '\xFF') + '\xFC' + '\xFF'); |
| 48 const UniquePosition kBiggerPosition = UniquePosition::FromBytes( |
| 49 std::string(kBigPositionLength, '\xFF') + '\xFF'); |
| 50 const UniquePosition kSmallPosition = UniquePosition::FromBytes( |
| 51 std::string(kSmallPositionLength - 1, '\x00') + '\x01' + '\xFF'); |
| 52 const UniquePosition kSmallPositionPlusOne = UniquePosition::FromBytes( |
| 53 std::string(kSmallPositionLength - 1, '\x00') + '\x02' + '\xFF'); |
| 54 |
| 55 const std::string kMinSuffix = |
| 56 std::string(UniquePosition::kSuffixLength - 1, '\x00') + '\x01'; |
| 57 const std::string kMaxSuffix(UniquePosition::kSuffixLength, '\xFF'); |
| 58 const std::string kNormalSuffix(UniquePosition::kSuffixLength, '\xAB'); |
| 59 |
| 60 ::testing::AssertionResult LessThan(const char* m_expr, |
| 61 const char* n_expr, |
| 62 const UniquePosition &m, |
| 63 const UniquePosition &n) { |
| 64 if (m.LessThan(n)) |
| 65 return ::testing::AssertionSuccess(); |
| 66 |
| 67 return ::testing::AssertionFailure() |
| 68 << m_expr << " is not less than " << n_expr |
| 69 << " (" << m.ToDebugString() << " and " << n.ToDebugString() << ")"; |
| 70 } |
| 71 |
| 72 class RelativePositioningTest : public UniquePositionTest { }; |
| 73 |
| 74 const UniquePosition kPositionArray[] = { |
| 75 kGenericPredecessor, |
| 76 kGenericSuccessor, |
| 77 kBigPosition, |
| 78 kBigPositionLessTwo, |
| 79 kBiggerPosition, |
| 80 kSmallPosition, |
| 81 kSmallPositionPlusOne, |
| 82 }; |
| 83 |
| 84 const UniquePosition kSortedPositionArray[] = { |
| 85 kSmallPosition, |
| 86 kSmallPositionPlusOne, |
| 87 kGenericPredecessor, |
| 88 kGenericSuccessor, |
| 89 kBigPositionLessTwo, |
| 90 kBigPosition, |
| 91 kBiggerPosition, |
| 92 }; |
| 93 |
| 94 static const size_t kNumPositions = arraysize(kPositionArray); |
| 95 static const size_t kNumSortedPositions = arraysize(kSortedPositionArray); |
| 96 |
| 97 struct PositionLessThan { |
| 98 bool operator()(const UniquePosition& a, const UniquePosition& b) { |
| 99 return a.LessThan(b); |
| 100 } |
| 101 }; |
| 102 |
| 103 static bool IsSuffixInUse( |
| 104 const UniquePosition& pos, const std::string& suffix) { |
| 105 const std::string& pos_bytes = pos.ToInternalValue(); |
| 106 const size_t prefix_len = pos_bytes.length() - UniquePosition::kSuffixLength; |
| 107 return pos_bytes.substr(prefix_len, std::string::npos) == suffix; |
| 108 } |
| 109 |
| 110 // Exercise comparision functions by sorting and re-sorting the list. |
| 111 TEST_F(RelativePositioningTest, SortPositions) { |
| 112 ASSERT_EQ(kNumPositions, kNumSortedPositions); |
| 113 UniquePosition positions[arraysize(kPositionArray)]; |
| 114 for (size_t i = 0; i < kNumPositions; ++i) { |
| 115 positions[i] = kPositionArray[i]; |
| 116 } |
| 117 |
| 118 std::sort(&positions[0], &positions[kNumPositions], PositionLessThan()); |
| 119 for (size_t i = 0; i < kNumPositions; ++i) { |
| 120 EXPECT_TRUE(positions[i].Equals(kSortedPositionArray[i])) |
| 121 << "i: " << i << ", " |
| 122 << positions[i].ToDebugString() << " != " |
| 123 << kSortedPositionArray[i].ToDebugString(); |
| 124 } |
| 125 |
| 126 } |
| 127 |
| 128 // Some more exercise for the comparison function. |
| 129 TEST_F(RelativePositioningTest, ReverseSortPositions) { |
| 130 ASSERT_EQ(kNumPositions, kNumSortedPositions); |
| 131 UniquePosition positions[arraysize(kPositionArray)]; |
| 132 for (size_t i = 0; i < kNumPositions; ++i) { |
| 133 positions[i] = kPositionArray[i]; |
| 134 } |
| 135 |
| 136 std::reverse(&positions[0], &positions[kNumPositions]); |
| 137 std::sort(&positions[0], &positions[kNumPositions], PositionLessThan()); |
| 138 for (size_t i = 0; i < kNumPositions; ++i) { |
| 139 EXPECT_TRUE(positions[i].Equals(kSortedPositionArray[i])) |
| 140 << "i: " << i << ", " |
| 141 << positions[i].ToDebugString() << " != " |
| 142 << kSortedPositionArray[i].ToDebugString(); |
| 143 } |
| 144 } |
| 145 |
| 146 class PositionInsertTest : |
| 147 public RelativePositioningTest, |
| 148 public ::testing::WithParamInterface<std::string> { }; |
| 149 |
| 150 // Exercise InsertBetween with various insertion operations. |
| 151 TEST_P(PositionInsertTest, InsertBetween) { |
| 152 const std::string suffix = GetParam(); |
| 153 ASSERT_TRUE(UniquePosition::IsValidSuffix(suffix)); |
| 154 |
| 155 for (size_t i = 0; i < kNumSortedPositions; ++i) { |
| 156 const UniquePosition& predecessor = kSortedPositionArray[i]; |
| 157 // Verify our suffixes are unique before we continue. |
| 158 if (IsSuffixInUse(predecessor, suffix)) |
| 159 continue; |
| 160 |
| 161 for (size_t j = i + 1; j < kNumSortedPositions; ++j) { |
| 162 const UniquePosition& successor = kSortedPositionArray[j]; |
| 163 |
| 164 // Another guard against non-unique suffixes. |
| 165 if (IsSuffixInUse(successor, suffix)) |
| 166 continue; |
| 167 |
| 168 UniquePosition midpoint = |
| 169 UniquePosition::Between(predecessor, successor, suffix); |
| 170 |
| 171 EXPECT_PRED_FORMAT2(LessThan, predecessor, midpoint); |
| 172 EXPECT_PRED_FORMAT2(LessThan, midpoint, successor); |
| 173 } |
| 174 } |
| 175 } |
| 176 |
| 177 TEST_P(PositionInsertTest, InsertBefore) { |
| 178 const std::string suffix = GetParam(); |
| 179 for (size_t i = 0; i < kNumSortedPositions; ++i) { |
| 180 const UniquePosition& successor = kSortedPositionArray[i]; |
| 181 // Verify our suffixes are unique before we continue. |
| 182 if (IsSuffixInUse(successor, suffix)) |
| 183 continue; |
| 184 |
| 185 UniquePosition before = UniquePosition::Before(successor, suffix); |
| 186 |
| 187 EXPECT_PRED_FORMAT2(LessThan, before, successor); |
| 188 } |
| 189 } |
| 190 |
| 191 TEST_P(PositionInsertTest, InsertAfter) { |
| 192 const std::string suffix = GetParam(); |
| 193 for (size_t i = 0; i < kNumSortedPositions; ++i) { |
| 194 const UniquePosition& predecessor = kSortedPositionArray[i]; |
| 195 // Verify our suffixes are unique before we continue. |
| 196 if (IsSuffixInUse(predecessor, suffix)) |
| 197 continue; |
| 198 |
| 199 UniquePosition after = UniquePosition::After(predecessor, suffix); |
| 200 |
| 201 EXPECT_PRED_FORMAT2(LessThan, predecessor, after); |
| 202 } |
| 203 } |
| 204 |
| 205 TEST_P(PositionInsertTest, StressInsertAfter) { |
| 206 // Use two different suffixes to not violate our suffix uniqueness guarantee. |
| 207 const std::string& suffix_a = GetParam(); |
| 208 std::string suffix_b = suffix_a; |
| 209 suffix_b[10] = suffix_b[10] ^ 0xff; |
| 210 |
| 211 UniquePosition pos = UniquePosition::InitialPosition(suffix_a); |
| 212 for (int i = 0; i < 1024; i++) { |
| 213 const std::string& suffix = (i % 2 == 0) ? suffix_b : suffix_a; |
| 214 UniquePosition next_pos = UniquePosition::After(pos, suffix); |
| 215 ASSERT_PRED_FORMAT2(LessThan, pos, next_pos); |
| 216 pos = next_pos; |
| 217 } |
| 218 |
| 219 VLOG(1) << "Length: " << GetLength(pos); |
| 220 } |
| 221 |
| 222 TEST_P(PositionInsertTest, StressInsertBefore) { |
| 223 // Use two different suffixes to not violate our suffix uniqueness guarantee. |
| 224 const std::string& suffix_a = GetParam(); |
| 225 std::string suffix_b = suffix_a; |
| 226 suffix_b[10] = suffix_b[10] ^ 0xff; |
| 227 |
| 228 UniquePosition pos = UniquePosition::InitialPosition(suffix_a); |
| 229 for (int i = 0; i < 1024; i++) { |
| 230 const std::string& suffix = (i % 2 == 0) ? suffix_b : suffix_a; |
| 231 UniquePosition prev_pos = UniquePosition::Before(pos, suffix); |
| 232 ASSERT_PRED_FORMAT2(LessThan, prev_pos, pos); |
| 233 pos = prev_pos; |
| 234 } |
| 235 |
| 236 VLOG(1) << "Length: " << GetLength(pos); |
| 237 } |
| 238 |
| 239 TEST_P(PositionInsertTest, StressLeftInsertBetween) { |
| 240 // Use different suffixes to not violate our suffix uniqueness guarantee. |
| 241 const std::string& suffix_a = GetParam(); |
| 242 std::string suffix_b = suffix_a; |
| 243 suffix_b[10] = suffix_b[10] ^ 0xff; |
| 244 std::string suffix_c = suffix_a; |
| 245 suffix_c[10] = suffix_c[10] ^ 0xf0; |
| 246 |
| 247 UniquePosition right_pos = UniquePosition::InitialPosition(suffix_c); |
| 248 UniquePosition left_pos = UniquePosition::Before(right_pos, suffix_a); |
| 249 for (int i = 0; i < 1024; i++) { |
| 250 const std::string& suffix = (i % 2 == 0) ? suffix_b : suffix_a; |
| 251 UniquePosition new_pos = |
| 252 UniquePosition::Between(left_pos, right_pos, suffix); |
| 253 ASSERT_PRED_FORMAT2(LessThan, left_pos, new_pos); |
| 254 ASSERT_PRED_FORMAT2(LessThan, new_pos, right_pos); |
| 255 left_pos = new_pos; |
| 256 } |
| 257 |
| 258 VLOG(1) << "Lengths: " << GetLength(left_pos) << ", " << GetLength(right_pos); |
| 259 } |
| 260 |
| 261 TEST_P(PositionInsertTest, StressRightInsertBetween) { |
| 262 // Use different suffixes to not violate our suffix uniqueness guarantee. |
| 263 const std::string& suffix_a = GetParam(); |
| 264 std::string suffix_b = suffix_a; |
| 265 suffix_b[10] = suffix_b[10] ^ 0xff; |
| 266 std::string suffix_c = suffix_a; |
| 267 suffix_c[10] = suffix_c[10] ^ 0xf0; |
| 268 |
| 269 UniquePosition right_pos = UniquePosition::InitialPosition(suffix_a); |
| 270 UniquePosition left_pos = UniquePosition::Before(right_pos, suffix_c); |
| 271 for (int i = 0; i < 1024; i++) { |
| 272 const std::string& suffix = (i % 2 == 0) ? suffix_b : suffix_a; |
| 273 UniquePosition new_pos = |
| 274 UniquePosition::Between(left_pos, right_pos, suffix); |
| 275 ASSERT_PRED_FORMAT2(LessThan, left_pos, new_pos); |
| 276 ASSERT_PRED_FORMAT2(LessThan, new_pos, right_pos); |
| 277 right_pos = new_pos; |
| 278 } |
| 279 |
| 280 VLOG(1) << "Lengths: " << GetLength(left_pos) << ", " << GetLength(right_pos); |
| 281 } |
| 282 |
| 283 INSTANTIATE_TEST_CASE_P(MinSuffix, PositionInsertTest, |
| 284 ::testing::Values(kMinSuffix)); |
| 285 INSTANTIATE_TEST_CASE_P(MaxSuffix, PositionInsertTest, |
| 286 ::testing::Values(kMaxSuffix)); |
| 287 INSTANTIATE_TEST_CASE_P(NormalSuffix, PositionInsertTest, |
| 288 ::testing::Values(kNormalSuffix)); |
| 289 |
| 290 class PositionFromIntTest : public UniquePositionTest { |
| 291 protected: |
| 292 static std::string SuffixFromInt(int64 item_id) { |
| 293 // Represents a cache_guid-like value. |
| 294 const std::string guid( |
| 295 "\xFF\xFE\xFE\xFF\x00\x01\x01\x00" |
| 296 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 16); |
| 297 |
| 298 return UniquePosition::GenerateBookmarkSuffix(guid, item_id); |
| 299 } |
| 300 }; |
| 301 |
| 302 const int64 kTestValues[] = { |
| 303 0LL, |
| 304 1LL, -1LL, |
| 305 2LL, -2LL, |
| 306 3LL, -3LL, |
| 307 0x79LL, -0x79LL, |
| 308 0x80LL, -0x80LL, |
| 309 0x81LL, -0x81LL, |
| 310 0xFELL, -0xFELL, |
| 311 0xFFLL, -0xFFLL, |
| 312 0x100LL, -0x100LL, |
| 313 0x101LL, -0x101LL, |
| 314 0xFA1AFELL, -0xFA1AFELL, |
| 315 0xFFFFFFFELL, -0xFFFFFFFELL, |
| 316 0xFFFFFFFFLL, -0xFFFFFFFFLL, |
| 317 0x100000000LL, -0x100000000LL, |
| 318 0x100000001LL, -0x100000001LL, |
| 319 0xFFFFFFFFFFLL, -0xFFFFFFFFFFLL, |
| 320 0x112358132134LL, -0x112358132134LL, |
| 321 0xFEFFBEEFABC1234LL, -0xFEFFBEEFABC1234LL, |
| 322 kint64max, |
| 323 kint64min, |
| 324 kint64min + 1, |
| 325 kint64max - 1 |
| 326 }; |
| 327 |
| 328 const size_t kNumTestValues = arraysize(kTestValues); |
| 329 |
| 330 TEST_F(PositionFromIntTest, IsValid) { |
| 331 for (size_t i = 0; i < kNumTestValues; ++i) { |
| 332 const UniquePosition pos = |
| 333 UniquePosition::FromInt64(kTestValues[i], SuffixFromInt(i)); |
| 334 EXPECT_TRUE(pos.IsValid()) << "i = " << i << "; " << pos.ToDebugString(); |
| 335 } |
| 336 } |
| 337 |
| 338 TEST_F(PositionFromIntTest, RoundTripConversion) { |
| 339 for (size_t i = 0; i < kNumTestValues; ++i) { |
| 340 const int64 expected_value = kTestValues[i]; |
| 341 const UniquePosition pos = |
| 342 UniquePosition::FromInt64(kTestValues[i], SuffixFromInt(i)); |
| 343 const int64 value = pos.ToInt64(); |
| 344 EXPECT_EQ(expected_value, value) << "i = " << i; |
| 345 } |
| 346 } |
| 347 |
| 348 template <typename T, typename LessThan = std::less<T> > |
| 349 class IndexedLessThan { |
| 350 public: |
| 351 IndexedLessThan(const T* values) : values_(values) {} |
| 352 |
| 353 bool operator()(int i1, int i2) { |
| 354 return less_than_(values_[i1], values_[i2]); |
| 355 } |
| 356 |
| 357 private: |
| 358 const T* values_; |
| 359 LessThan less_than_; |
| 360 }; |
| 361 |
| 362 TEST_F(PositionFromIntTest, ConsistentOrdering) { |
| 363 UniquePosition positions[kNumTestValues]; |
| 364 std::vector<int> original_ordering(kNumTestValues); |
| 365 std::vector<int> int64_ordering(kNumTestValues); |
| 366 std::vector<int> position_ordering(kNumTestValues); |
| 367 for (size_t i = 0; i < kNumTestValues; ++i) { |
| 368 positions[i] = UniquePosition::FromInt64( |
| 369 kTestValues[i], SuffixFromInt(i)); |
| 370 original_ordering[i] = int64_ordering[i] = position_ordering[i] = i; |
| 371 } |
| 372 |
| 373 std::sort(int64_ordering.begin(), int64_ordering.end(), |
| 374 IndexedLessThan<int64>(kTestValues)); |
| 375 std::sort(position_ordering.begin(), position_ordering.end(), |
| 376 IndexedLessThan<UniquePosition, PositionLessThan>(positions)); |
| 377 EXPECT_NE(original_ordering, int64_ordering); |
| 378 EXPECT_EQ(int64_ordering, position_ordering); |
| 379 } |
| 380 |
| 381 } // namespace |
| 382 |
| 383 } // namespace syncer |
OLD | NEW |