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