1 /* 2 ** Copyright 2011, The Android Open Source Project 3 ** 4 ** Licensed under the Apache License, Version 2.0 (the "License"); 5 ** you may not use this file except in compliance with the License. 6 ** You may obtain a copy of the License at 7 ** 8 ** http://www.apache.org/licenses/LICENSE-2.0 9 ** 10 ** Unless required by applicable law or agreed to in writing, software 11 ** distributed under the License is distributed on an "AS IS" BASIS, 12 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 ** See the License for the specific language governing permissions and 14 ** limitations under the License. 15 */ 16 17 #include <fcntl.h> 18 #include <stdio.h> 19 #include <stdlib.h> 20 21 #include <algorithm> 22 #include <memory> 23 #include <numeric> 24 #include <random> 25 26 #include <gtest/gtest.h> 27 28 #include "BlobCache.h" 29 30 namespace android { 31 32 template<typename T> using sp = std::shared_ptr<T>; 33 34 class BlobCacheTest : public ::testing::TestWithParam<BlobCache::Policy> { 35 protected: 36 37 enum { 38 OK = 0, 39 BAD_VALUE = -EINVAL 40 }; 41 42 enum { 43 MAX_KEY_SIZE = 6, 44 MAX_VALUE_SIZE = 8, 45 MAX_TOTAL_SIZE = 13, 46 }; 47 48 virtual void SetUp() { 49 mBC.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE, GetParam())); 50 } 51 52 virtual void TearDown() { 53 mBC.reset(); 54 } 55 56 std::unique_ptr<BlobCache> mBC; 57 }; 58 59 INSTANTIATE_TEST_CASE_P(Policy, BlobCacheTest, 60 ::testing::Values(BlobCache::Policy(BlobCache::Select::RANDOM, BlobCache::Capacity::HALVE), 61 BlobCache::Policy(BlobCache::Select::LRU, BlobCache::Capacity::HALVE), 62 63 BlobCache::Policy(BlobCache::Select::RANDOM, BlobCache::Capacity::FIT), 64 BlobCache::Policy(BlobCache::Select::LRU, BlobCache::Capacity::FIT), 65 66 BlobCache::Policy(BlobCache::Select::RANDOM, BlobCache::Capacity::FIT_HALVE), 67 BlobCache::Policy(BlobCache::Select::LRU, BlobCache::Capacity::FIT_HALVE))); 68 69 TEST_P(BlobCacheTest, CacheSingleValueSucceeds) { 70 unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; 71 mBC->set("abcd", 4, "efgh", 4); 72 ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); 73 ASSERT_EQ('e', buf[0]); 74 ASSERT_EQ('f', buf[1]); 75 ASSERT_EQ('g', buf[2]); 76 ASSERT_EQ('h', buf[3]); 77 } 78 79 TEST_P(BlobCacheTest, CacheTwoValuesSucceeds) { 80 unsigned char buf[2] = { 0xee, 0xee }; 81 mBC->set("ab", 2, "cd", 2); 82 mBC->set("ef", 2, "gh", 2); 83 ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2)); 84 ASSERT_EQ('c', buf[0]); 85 ASSERT_EQ('d', buf[1]); 86 ASSERT_EQ(size_t(2), mBC->get("ef", 2, buf, 2)); 87 ASSERT_EQ('g', buf[0]); 88 ASSERT_EQ('h', buf[1]); 89 } 90 91 TEST_P(BlobCacheTest, CacheTwoValuesMallocSucceeds) { 92 unsigned char *bufPtr; 93 mBC->set("ab", 2, "cd", 2); 94 mBC->set("ef", 2, "gh", 2); 95 96 bufPtr = nullptr; 97 ASSERT_EQ(size_t(2), mBC->get("ab", 2, &bufPtr, malloc)); 98 ASSERT_NE(nullptr, bufPtr); 99 ASSERT_EQ('c', bufPtr[0]); 100 ASSERT_EQ('d', bufPtr[1]); 101 free(bufPtr); 102 103 bufPtr = nullptr; 104 ASSERT_EQ(size_t(2), mBC->get("ef", 2, &bufPtr, malloc)); 105 ASSERT_NE(nullptr, bufPtr); 106 ASSERT_EQ('g', bufPtr[0]); 107 ASSERT_EQ('h', bufPtr[1]); 108 free(bufPtr); 109 } 110 111 TEST_P(BlobCacheTest, GetOnlyWritesInsideBounds) { 112 unsigned char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee }; 113 mBC->set("abcd", 4, "efgh", 4); 114 ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf+1, 4)); 115 ASSERT_EQ(0xee, buf[0]); 116 ASSERT_EQ('e', buf[1]); 117 ASSERT_EQ('f', buf[2]); 118 ASSERT_EQ('g', buf[3]); 119 ASSERT_EQ('h', buf[4]); 120 ASSERT_EQ(0xee, buf[5]); 121 } 122 123 TEST_P(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) { 124 unsigned char buf[3] = { 0xee, 0xee, 0xee }; 125 mBC->set("abcd", 4, "efgh", 4); 126 ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3)); 127 ASSERT_EQ(0xee, buf[0]); 128 ASSERT_EQ(0xee, buf[1]); 129 ASSERT_EQ(0xee, buf[2]); 130 } 131 132 TEST_P(BlobCacheTest, GetWithFailedAllocator) { 133 unsigned char buf[3] = { 0xee, 0xee, 0xee }; 134 mBC->set("abcd", 4, "efgh", 4); 135 136 // If allocator fails, verify that we set the value pointer to 137 // nullptr, and that we do not modify the buffer that the value 138 // pointer originally pointed to. 139 unsigned char *bufPtr = &buf[0]; 140 ASSERT_EQ(size_t(4), mBC->get("abcd", 4, &bufPtr, [](size_t) -> void* { return nullptr; })); 141 ASSERT_EQ(nullptr, bufPtr); 142 ASSERT_EQ(0xee, buf[0]); 143 ASSERT_EQ(0xee, buf[1]); 144 ASSERT_EQ(0xee, buf[2]); 145 } 146 147 TEST_P(BlobCacheTest, GetDoesntAccessNullBuffer) { 148 mBC->set("abcd", 4, "efgh", 4); 149 ASSERT_EQ(size_t(4), mBC->get("abcd", 4, NULL, 0)); 150 } 151 152 TEST_P(BlobCacheTest, MultipleSetsCacheLatestValue) { 153 unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; 154 mBC->set("abcd", 4, "efgh", 4); 155 mBC->set("abcd", 4, "ijkl", 4); 156 ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); 157 ASSERT_EQ('i', buf[0]); 158 ASSERT_EQ('j', buf[1]); 159 ASSERT_EQ('k', buf[2]); 160 ASSERT_EQ('l', buf[3]); 161 } 162 163 TEST_P(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) { 164 unsigned char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee }; 165 mBC->set("abcd", 4, "efgh", 4); 166 mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1); 167 ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); 168 ASSERT_EQ('e', buf[0]); 169 ASSERT_EQ('f', buf[1]); 170 ASSERT_EQ('g', buf[2]); 171 ASSERT_EQ('h', buf[3]); 172 } 173 174 TEST_P(BlobCacheTest, DoesntCacheIfKeyIsTooBig) { 175 char key[MAX_KEY_SIZE+1]; 176 unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; 177 for (int i = 0; i < MAX_KEY_SIZE+1; i++) { 178 key[i] = 'a'; 179 } 180 mBC->set(key, MAX_KEY_SIZE+1, "bbbb", 4); 181 182 ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE+1, buf, 4)); 183 ASSERT_EQ(0xee, buf[0]); 184 ASSERT_EQ(0xee, buf[1]); 185 ASSERT_EQ(0xee, buf[2]); 186 ASSERT_EQ(0xee, buf[3]); 187 188 // If key is too large, verify that we do not call the allocator, 189 // that we set the value pointer to nullptr, and that we do not 190 // modify the buffer that the value pointer originally pointed to. 191 unsigned char *bufPtr = &buf[0]; 192 bool calledAlloc = false; 193 ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE+1, &bufPtr, 194 [&calledAlloc](size_t) -> void* { 195 calledAlloc = true; 196 return nullptr; })); 197 ASSERT_EQ(false, calledAlloc); 198 ASSERT_EQ(nullptr, bufPtr); 199 ASSERT_EQ(0xee, buf[0]); 200 ASSERT_EQ(0xee, buf[1]); 201 ASSERT_EQ(0xee, buf[2]); 202 ASSERT_EQ(0xee, buf[3]); 203 } 204 205 TEST_P(BlobCacheTest, DoesntCacheIfValueIsTooBig) { 206 unsigned char buf[MAX_VALUE_SIZE+1]; 207 for (int i = 0; i < MAX_VALUE_SIZE+1; i++) { 208 buf[i] = 'b'; 209 } 210 mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1); 211 for (int i = 0; i < MAX_VALUE_SIZE+1; i++) { 212 buf[i] = 0xee; 213 } 214 ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE+1)); 215 for (int i = 0; i < MAX_VALUE_SIZE+1; i++) { 216 SCOPED_TRACE(i); 217 ASSERT_EQ(0xee, buf[i]); 218 } 219 } 220 221 TEST_P(BlobCacheTest, DoesntCacheIfKeyValuePairIsTooBig) { 222 // Check a testing assumptions 223 ASSERT_TRUE(MAX_TOTAL_SIZE < MAX_KEY_SIZE + MAX_VALUE_SIZE); 224 ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE); 225 226 enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE + 1 }; 227 228 char key[MAX_KEY_SIZE]; 229 char buf[bufSize]; 230 for (int i = 0; i < MAX_KEY_SIZE; i++) { 231 key[i] = 'a'; 232 } 233 for (int i = 0; i < bufSize; i++) { 234 buf[i] = 'b'; 235 } 236 237 mBC->set(key, MAX_KEY_SIZE, buf, MAX_VALUE_SIZE); 238 ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, NULL, 0)); 239 } 240 241 TEST_P(BlobCacheTest, CacheMaxKeySizeSucceeds) { 242 char key[MAX_KEY_SIZE]; 243 unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; 244 for (int i = 0; i < MAX_KEY_SIZE; i++) { 245 key[i] = 'a'; 246 } 247 mBC->set(key, MAX_KEY_SIZE, "wxyz", 4); 248 ASSERT_EQ(size_t(4), mBC->get(key, MAX_KEY_SIZE, buf, 4)); 249 ASSERT_EQ('w', buf[0]); 250 ASSERT_EQ('x', buf[1]); 251 ASSERT_EQ('y', buf[2]); 252 ASSERT_EQ('z', buf[3]); 253 } 254 255 TEST_P(BlobCacheTest, CacheMaxValueSizeSucceeds) { 256 char buf[MAX_VALUE_SIZE]; 257 for (int i = 0; i < MAX_VALUE_SIZE; i++) { 258 buf[i] = 'b'; 259 } 260 mBC->set("abcd", 4, buf, MAX_VALUE_SIZE); 261 for (int i = 0; i < MAX_VALUE_SIZE; i++) { 262 buf[i] = 0xee; 263 } 264 ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf, 265 MAX_VALUE_SIZE)); 266 for (int i = 0; i < MAX_VALUE_SIZE; i++) { 267 SCOPED_TRACE(i); 268 ASSERT_EQ('b', buf[i]); 269 } 270 } 271 272 TEST_P(BlobCacheTest, CacheMaxKeyValuePairSizeSucceeds) { 273 // Check a testing assumption 274 ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE); 275 276 enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE }; 277 278 char key[MAX_KEY_SIZE]; 279 char buf[bufSize]; 280 for (int i = 0; i < MAX_KEY_SIZE; i++) { 281 key[i] = 'a'; 282 } 283 for (int i = 0; i < bufSize; i++) { 284 buf[i] = 'b'; 285 } 286 287 mBC->set(key, MAX_KEY_SIZE, buf, bufSize); 288 ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, NULL, 0)); 289 } 290 291 TEST_P(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) { 292 unsigned char buf[1] = { 0xee }; 293 mBC->set("x", 1, "y", 1); 294 ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1)); 295 ASSERT_EQ('y', buf[0]); 296 } 297 298 TEST_P(BlobCacheTest, CacheSizeDoesntExceedTotalLimit) { 299 for (int i = 0; i < 256; i++) { 300 uint8_t k = i; 301 mBC->set(&k, 1, "x", 1); 302 } 303 int numCached = 0; 304 for (int i = 0; i < 256; i++) { 305 uint8_t k = i; 306 if (mBC->get(&k, 1, NULL, 0) == 1) { 307 numCached++; 308 } 309 } 310 ASSERT_GE(MAX_TOTAL_SIZE / 2, numCached); 311 } 312 313 TEST_P(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) { 314 if (GetParam().second == BlobCache::Capacity::FIT) 315 return; // test doesn't apply for this policy 316 317 // Fill up the entire cache with 1 char key/value pairs. 318 const int maxEntries = MAX_TOTAL_SIZE / 2; 319 for (int i = 0; i < maxEntries; i++) { 320 uint8_t k = i; 321 mBC->set(&k, 1, "x", 1); 322 } 323 // Insert one more entry, causing a cache overflow. 324 { 325 uint8_t k = maxEntries; 326 mBC->set(&k, 1, "x", 1); 327 } 328 // Count the number of entries in the cache; and check which 329 // entries they are. 330 int numCached = 0; 331 for (int i = 0; i < maxEntries+1; i++) { 332 uint8_t k = i; 333 bool found = (mBC->get(&k, 1, NULL, 0) == 1); 334 if (found) 335 numCached++; 336 if (GetParam().first == BlobCache::Select::LRU) { 337 SCOPED_TRACE(i); 338 ASSERT_EQ(found, i >= maxEntries/2); 339 } 340 } 341 ASSERT_EQ(maxEntries/2 + 1, numCached); 342 } 343 344 TEST_P(BlobCacheTest, ExceedingTotalLimitJustFitsSmallEntry) { 345 if (GetParam().second != BlobCache::Capacity::FIT) 346 return; // test doesn't apply for this policy 347 348 // Fill up the entire cache with 1 char key/value pairs. 349 const int maxEntries = MAX_TOTAL_SIZE / 2; 350 for (int i = 0; i < maxEntries; i++) { 351 uint8_t k = i; 352 mBC->set(&k, 1, "x", 1); 353 } 354 // Insert one more entry, causing a cache overflow. 355 { 356 uint8_t k = maxEntries; 357 mBC->set(&k, 1, "x", 1); 358 } 359 // Count the number of entries in the cache. 360 int numCached = 0; 361 for (int i = 0; i < maxEntries+1; i++) { 362 uint8_t k = i; 363 if (mBC->get(&k, 1, NULL, 0) == 1) 364 numCached++; 365 } 366 ASSERT_EQ(maxEntries, numCached); 367 } 368 369 // Also see corresponding test in nnCache_test.cpp 370 TEST_P(BlobCacheTest, ExceedingTotalLimitFitsBigEntry) { 371 // Fill up the entire cache with 1 char key/value pairs. 372 const int maxEntries = MAX_TOTAL_SIZE / 2; 373 for (int i = 0; i < maxEntries; i++) { 374 uint8_t k = i; 375 mBC->set(&k, 1, "x", 1); 376 } 377 // Insert one more entry, causing a cache overflow. 378 const int bigValueSize = std::min((MAX_TOTAL_SIZE * 3) / 4 - 1, int(MAX_VALUE_SIZE)); 379 ASSERT_GT(bigValueSize+1, MAX_TOTAL_SIZE / 2); // Check testing assumption 380 { 381 unsigned char buf[MAX_VALUE_SIZE]; 382 for (int i = 0; i < bigValueSize; i++) 383 buf[i] = 0xee; 384 uint8_t k = maxEntries; 385 mBC->set(&k, 1, buf, bigValueSize); 386 } 387 // Count the number and size of entries in the cache. 388 int numCached = 0; 389 size_t sizeCached = 0; 390 for (int i = 0; i < maxEntries+1; i++) { 391 uint8_t k = i; 392 size_t size = mBC->get(&k, 1, NULL, 0); 393 if (size) { 394 numCached++; 395 sizeCached += (size + 1); 396 } 397 } 398 switch (GetParam().second) { 399 case BlobCache::Capacity::HALVE: 400 // New value is too big for this cleaning algorithm. So 401 // we cleaned the cache, but did not insert the new value. 402 ASSERT_EQ(maxEntries/2, numCached); 403 ASSERT_EQ(size_t((maxEntries/2)*2), sizeCached); 404 break; 405 case BlobCache::Capacity::FIT: 406 case BlobCache::Capacity::FIT_HALVE: { 407 // We had to clean more than half the cache to fit the new 408 // value. 409 const int initialNumEntries = maxEntries; 410 const int initialSizeCached = initialNumEntries * 2; 411 const int initialFreeSpace = MAX_TOTAL_SIZE - initialSizeCached; 412 413 // (bigValueSize + 1) = value size + key size 414 // trailing "+ 1" is in order to round up 415 // "/ 2" is because initial entries are size 2 (1 byte key, 1 byte value) 416 const int cleanNumEntries = ((bigValueSize + 1) - initialFreeSpace + 1) / 2; 417 418 const int cleanSpace = cleanNumEntries * 2; 419 const int postCleanNumEntries = initialNumEntries - cleanNumEntries; 420 const int postCleanSizeCached = initialSizeCached - cleanSpace; 421 ASSERT_EQ(postCleanNumEntries + 1, numCached); 422 ASSERT_EQ(size_t(postCleanSizeCached + bigValueSize + 1), sizeCached); 423 424 break; 425 } 426 default: 427 FAIL() << "Unknown Capacity value"; 428 } 429 } 430 431 TEST_P(BlobCacheTest, FailedGetWithAllocator) { 432 // If get doesn't find anything, verify that we do not call the 433 // allocator, that we set the value pointer to nullptr, and that 434 // we do not modify the buffer that the value pointer originally 435 // pointed to. 436 unsigned char buf[1] = { 0xee }; 437 unsigned char *bufPtr = &buf[0]; 438 bool calledAlloc = false; 439 ASSERT_EQ(size_t(0), mBC->get("a", 1, &bufPtr, 440 [&calledAlloc](size_t) -> void* { 441 calledAlloc = true; 442 return nullptr; })); 443 ASSERT_EQ(false, calledAlloc); 444 ASSERT_EQ(nullptr, bufPtr); 445 ASSERT_EQ(0xee, buf[0]); 446 } 447 448 TEST_P(BlobCacheTest, ExceedingTotalLimitRemovesLRUEntries) { 449 if (GetParam().first != BlobCache::Select::LRU) 450 return; // test doesn't apply for this policy 451 452 // Fill up the entire cache with 1 char key/value pairs. 453 static const int maxEntries = MAX_TOTAL_SIZE / 2; 454 for (int i = 0; i < maxEntries; i++) { 455 uint8_t k = i; 456 mBC->set(&k, 1, "x", 1); 457 } 458 459 // Access entries in some known pseudorandom order. 460 int accessSequence[maxEntries]; 461 std::iota(&accessSequence[0], &accessSequence[maxEntries], 0); 462 std::mt19937 randomEngine(MAX_TOTAL_SIZE /* seed */); 463 std::shuffle(&accessSequence[0], &accessSequence[maxEntries], randomEngine); 464 for (int i = 0; i < maxEntries; i++) { 465 uint8_t k = accessSequence[i]; 466 uint8_t buf[1]; 467 // If we were to pass NULL to get() as the value pointer, this 468 // won't count as an access for LRU purposes. 469 mBC->get(&k, 1, buf, 1); 470 } 471 472 // Insert one more entry, causing a cache overflow. 473 { 474 uint8_t k = maxEntries; 475 mBC->set(&k, 1, "x", 1); 476 } 477 478 // Check which entries are in the cache. We expect to see the 479 // "one more entry" we just added, and also the most-recently 480 // accessed (according to accessSequence). That is, we should 481 // find exactly the entries with the following keys: 482 // . maxEntries 483 // . accessSequence[j..maxEntries-1] for some 0 <= j < maxEntries 484 uint8_t k = maxEntries; 485 ASSERT_EQ(size_t(1), mBC->get(&k, 1, NULL, 0)); 486 bool foundAny = false; 487 for (int i = 0; i < maxEntries; i++) { 488 uint8_t k = accessSequence[i]; 489 bool found = (mBC->get(&k, 1, NULL, 0) == 1); 490 if (foundAny == found) 491 continue; 492 if (!foundAny) { 493 // found == true, so we just discovered j == i 494 foundAny = true; 495 } else { 496 // foundAny == true, found == false -- oops 497 FAIL() << "found [" << i-1 << "]th entry but not [" << i << "]th entry"; 498 } 499 } 500 } 501 502 class BlobCacheFlattenTest : public BlobCacheTest { 503 protected: 504 virtual void SetUp() { 505 BlobCacheTest::SetUp(); 506 mBC2.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE, GetParam())); 507 } 508 509 virtual void TearDown() { 510 mBC2.reset(); 511 BlobCacheTest::TearDown(); 512 } 513 514 void roundTrip() { 515 size_t size = mBC->getFlattenedSize(); 516 uint8_t* flat = new uint8_t[size]; 517 ASSERT_EQ(OK, mBC->flatten(flat, size)); 518 ASSERT_EQ(OK, mBC2->unflatten(flat, size)); 519 delete[] flat; 520 } 521 522 sp<BlobCache> mBC2; 523 }; 524 525 INSTANTIATE_TEST_CASE_P(Policy, BlobCacheFlattenTest, 526 ::testing::Values(BlobCache::Policy(BlobCache::Select::RANDOM, BlobCache::Capacity::HALVE), 527 BlobCache::Policy(BlobCache::Select::LRU, BlobCache::Capacity::HALVE), 528 529 BlobCache::Policy(BlobCache::Select::RANDOM, BlobCache::Capacity::FIT), 530 BlobCache::Policy(BlobCache::Select::LRU, BlobCache::Capacity::FIT), 531 532 BlobCache::Policy(BlobCache::Select::RANDOM, BlobCache::Capacity::FIT_HALVE), 533 BlobCache::Policy(BlobCache::Select::LRU, BlobCache::Capacity::FIT_HALVE))); 534 535 TEST_P(BlobCacheFlattenTest, FlattenOneValue) { 536 unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; 537 mBC->set("abcd", 4, "efgh", 4); 538 roundTrip(); 539 ASSERT_EQ(size_t(4), mBC2->get("abcd", 4, buf, 4)); 540 ASSERT_EQ('e', buf[0]); 541 ASSERT_EQ('f', buf[1]); 542 ASSERT_EQ('g', buf[2]); 543 ASSERT_EQ('h', buf[3]); 544 } 545 546 TEST_P(BlobCacheFlattenTest, FlattenFullCache) { 547 // Fill up the entire cache with 1 char key/value pairs. 548 const int maxEntries = MAX_TOTAL_SIZE / 2; 549 for (int i = 0; i < maxEntries; i++) { 550 uint8_t k = i; 551 mBC->set(&k, 1, &k, 1); 552 } 553 554 roundTrip(); 555 556 // Verify the deserialized cache 557 for (int i = 0; i < maxEntries; i++) { 558 uint8_t k = i; 559 uint8_t v = 0xee; 560 ASSERT_EQ(size_t(1), mBC2->get(&k, 1, &v, 1)); 561 ASSERT_EQ(k, v); 562 } 563 } 564 565 TEST_P(BlobCacheFlattenTest, FlattenDoesntChangeCache) { 566 // Fill up the entire cache with 1 char key/value pairs. 567 const int maxEntries = MAX_TOTAL_SIZE / 2; 568 for (int i = 0; i < maxEntries; i++) { 569 uint8_t k = i; 570 mBC->set(&k, 1, &k, 1); 571 } 572 573 size_t size = mBC->getFlattenedSize(); 574 uint8_t* flat = new uint8_t[size]; 575 ASSERT_EQ(OK, mBC->flatten(flat, size)); 576 delete[] flat; 577 578 // Verify the cache that we just serialized 579 for (int i = 0; i < maxEntries; i++) { 580 uint8_t k = i; 581 uint8_t v = 0xee; 582 ASSERT_EQ(size_t(1), mBC->get(&k, 1, &v, 1)); 583 ASSERT_EQ(k, v); 584 } 585 } 586 587 TEST_P(BlobCacheFlattenTest, FlattenCatchesBufferTooSmall) { 588 // Fill up the entire cache with 1 char key/value pairs. 589 const int maxEntries = MAX_TOTAL_SIZE / 2; 590 for (int i = 0; i < maxEntries; i++) { 591 uint8_t k = i; 592 mBC->set(&k, 1, &k, 1); 593 } 594 595 size_t size = mBC->getFlattenedSize() - 1; 596 uint8_t* flat = new uint8_t[size]; 597 // ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size)); 598 // TODO: The above fails. I expect this is so because getFlattenedSize() 599 // overstimates the size by using PROPERTY_VALUE_MAX. 600 delete[] flat; 601 } 602 603 TEST_P(BlobCacheFlattenTest, UnflattenCatchesBadMagic) { 604 unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; 605 mBC->set("abcd", 4, "efgh", 4); 606 607 size_t size = mBC->getFlattenedSize(); 608 uint8_t* flat = new uint8_t[size]; 609 ASSERT_EQ(OK, mBC->flatten(flat, size)); 610 flat[1] = ~flat[1]; 611 612 // Bad magic should cause an error. 613 ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size)); 614 delete[] flat; 615 616 // The error should cause the unflatten to result in an empty cache 617 ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4)); 618 } 619 620 TEST_P(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) { 621 unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; 622 mBC->set("abcd", 4, "efgh", 4); 623 624 size_t size = mBC->getFlattenedSize(); 625 uint8_t* flat = new uint8_t[size]; 626 ASSERT_EQ(OK, mBC->flatten(flat, size)); 627 flat[5] = ~flat[5]; 628 629 // Version mismatches shouldn't cause errors, but should not use the 630 // serialized entries 631 ASSERT_EQ(OK, mBC2->unflatten(flat, size)); 632 delete[] flat; 633 634 // The version mismatch should cause the unflatten to result in an empty 635 // cache 636 ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4)); 637 } 638 639 TEST_P(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) { 640 unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; 641 mBC->set("abcd", 4, "efgh", 4); 642 643 size_t size = mBC->getFlattenedSize(); 644 uint8_t* flat = new uint8_t[size]; 645 ASSERT_EQ(OK, mBC->flatten(flat, size)); 646 flat[10] = ~flat[10]; 647 648 // Version mismatches shouldn't cause errors, but should not use the 649 // serialized entries 650 ASSERT_EQ(OK, mBC2->unflatten(flat, size)); 651 delete[] flat; 652 653 // The version mismatch should cause the unflatten to result in an empty 654 // cache 655 ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4)); 656 } 657 658 TEST_P(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) { 659 unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; 660 mBC->set("abcd", 4, "efgh", 4); 661 662 size_t size = mBC->getFlattenedSize(); 663 uint8_t* flat = new uint8_t[size]; 664 ASSERT_EQ(OK, mBC->flatten(flat, size)); 665 666 // A buffer truncation shouldt cause an error 667 // ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1)); 668 // TODO: The above appears to fail because getFlattenedSize() is 669 // conservative. 670 delete[] flat; 671 672 // The error should cause the unflatten to result in an empty cache 673 ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4)); 674 } 675 676 } // namespace android 677