Home | History | Annotate | Download | only in BlobCache
      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