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