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