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
30namespace android {
31
32template<typename T> using sp = std::shared_ptr<T>;
33
34class BlobCacheTest : public ::testing::TestWithParam<BlobCache::Policy> {
35protected:
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
59INSTANTIATE_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
69TEST_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
79TEST_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
91TEST_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
111TEST_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
123TEST_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
132TEST_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
147TEST_P(BlobCacheTest, GetDoesntAccessNullBuffer) {
148    mBC->set("abcd", 4, "efgh", 4);
149    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, NULL, 0));
150}
151
152TEST_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
163TEST_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
174TEST_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
205TEST_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
221TEST_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
241TEST_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
255TEST_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
272TEST_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
291TEST_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
298TEST_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
313TEST_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
344TEST_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
370TEST_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
431TEST_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
448TEST_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
502class BlobCacheFlattenTest : public BlobCacheTest {
503protected:
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
525INSTANTIATE_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
535TEST_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
546TEST_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
565TEST_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
587TEST_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
603TEST_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
620TEST_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
639TEST_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
658TEST_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