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
SetUp()48 virtual void SetUp() {
49 mBC.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE, GetParam()));
50 }
51
TearDown()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
TEST_P(BlobCacheTest,CacheSingleValueSucceeds)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
TEST_P(BlobCacheTest,CacheTwoValuesSucceeds)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
TEST_P(BlobCacheTest,CacheTwoValuesMallocSucceeds)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
TEST_P(BlobCacheTest,GetOnlyWritesInsideBounds)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
TEST_P(BlobCacheTest,GetOnlyWritesIfBufferIsLargeEnough)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
TEST_P(BlobCacheTest,GetWithFailedAllocator)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
TEST_P(BlobCacheTest,GetDoesntAccessNullBuffer)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
TEST_P(BlobCacheTest,MultipleSetsCacheLatestValue)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
TEST_P(BlobCacheTest,SecondSetKeepsFirstValueIfTooLarge)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
TEST_P(BlobCacheTest,DoesntCacheIfKeyIsTooBig)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
TEST_P(BlobCacheTest,DoesntCacheIfValueIsTooBig)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
TEST_P(BlobCacheTest,DoesntCacheIfKeyValuePairIsTooBig)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
TEST_P(BlobCacheTest,CacheMaxKeySizeSucceeds)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
TEST_P(BlobCacheTest,CacheMaxValueSizeSucceeds)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
TEST_P(BlobCacheTest,CacheMaxKeyValuePairSizeSucceeds)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
TEST_P(BlobCacheTest,CacheMinKeyAndValueSizeSucceeds)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
TEST_P(BlobCacheTest,CacheSizeDoesntExceedTotalLimit)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
TEST_P(BlobCacheTest,ExceedingTotalLimitHalvesCacheSize)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
TEST_P(BlobCacheTest,ExceedingTotalLimitJustFitsSmallEntry)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
TEST_P(BlobCacheTest,ExceedingTotalLimitFitsBigEntry)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
TEST_P(BlobCacheTest,FailedGetWithAllocator)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
TEST_P(BlobCacheTest,ExceedingTotalLimitRemovesLRUEntries)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:
SetUp()504 virtual void SetUp() {
505 BlobCacheTest::SetUp();
506 mBC2.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE, GetParam()));
507 }
508
TearDown()509 virtual void TearDown() {
510 mBC2.reset();
511 BlobCacheTest::TearDown();
512 }
513
roundTrip()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
TEST_P(BlobCacheFlattenTest,FlattenOneValue)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
TEST_P(BlobCacheFlattenTest,FlattenFullCache)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
TEST_P(BlobCacheFlattenTest,FlattenDoesntChangeCache)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
TEST_P(BlobCacheFlattenTest,FlattenCatchesBufferTooSmall)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
TEST_P(BlobCacheFlattenTest,UnflattenCatchesBadMagic)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
TEST_P(BlobCacheFlattenTest,UnflattenCatchesBadBlobCacheVersion)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
TEST_P(BlobCacheFlattenTest,UnflattenCatchesBadBlobCacheDeviceVersion)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
TEST_P(BlobCacheFlattenTest,UnflattenCatchesBufferTooSmall)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