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