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
20 #include <gtest/gtest.h>
21
22 #include <utils/BlobCache.h>
23 #include <utils/Errors.h>
24
25 namespace android {
26
27 class BlobCacheTest : public ::testing::Test {
28 protected:
29 enum {
30 MAX_KEY_SIZE = 6,
31 MAX_VALUE_SIZE = 8,
32 MAX_TOTAL_SIZE = 13,
33 };
34
SetUp()35 virtual void SetUp() {
36 mBC = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE);
37 }
38
TearDown()39 virtual void TearDown() {
40 mBC.clear();
41 }
42
43 sp<BlobCache> mBC;
44 };
45
TEST_F(BlobCacheTest,CacheSingleValueSucceeds)46 TEST_F(BlobCacheTest, CacheSingleValueSucceeds) {
47 unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
48 mBC->set("abcd", 4, "efgh", 4);
49 ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
50 ASSERT_EQ('e', buf[0]);
51 ASSERT_EQ('f', buf[1]);
52 ASSERT_EQ('g', buf[2]);
53 ASSERT_EQ('h', buf[3]);
54 }
55
TEST_F(BlobCacheTest,CacheTwoValuesSucceeds)56 TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) {
57 unsigned char buf[2] = { 0xee, 0xee };
58 mBC->set("ab", 2, "cd", 2);
59 mBC->set("ef", 2, "gh", 2);
60 ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2));
61 ASSERT_EQ('c', buf[0]);
62 ASSERT_EQ('d', buf[1]);
63 ASSERT_EQ(size_t(2), mBC->get("ef", 2, buf, 2));
64 ASSERT_EQ('g', buf[0]);
65 ASSERT_EQ('h', buf[1]);
66 }
67
TEST_F(BlobCacheTest,GetOnlyWritesInsideBounds)68 TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) {
69 unsigned char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee };
70 mBC->set("abcd", 4, "efgh", 4);
71 ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf+1, 4));
72 ASSERT_EQ(0xee, buf[0]);
73 ASSERT_EQ('e', buf[1]);
74 ASSERT_EQ('f', buf[2]);
75 ASSERT_EQ('g', buf[3]);
76 ASSERT_EQ('h', buf[4]);
77 ASSERT_EQ(0xee, buf[5]);
78 }
79
TEST_F(BlobCacheTest,GetOnlyWritesIfBufferIsLargeEnough)80 TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) {
81 unsigned char buf[3] = { 0xee, 0xee, 0xee };
82 mBC->set("abcd", 4, "efgh", 4);
83 ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3));
84 ASSERT_EQ(0xee, buf[0]);
85 ASSERT_EQ(0xee, buf[1]);
86 ASSERT_EQ(0xee, buf[2]);
87 }
88
TEST_F(BlobCacheTest,GetDoesntAccessNullBuffer)89 TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) {
90 mBC->set("abcd", 4, "efgh", 4);
91 ASSERT_EQ(size_t(4), mBC->get("abcd", 4, NULL, 0));
92 }
93
TEST_F(BlobCacheTest,MultipleSetsCacheLatestValue)94 TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) {
95 unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
96 mBC->set("abcd", 4, "efgh", 4);
97 mBC->set("abcd", 4, "ijkl", 4);
98 ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
99 ASSERT_EQ('i', buf[0]);
100 ASSERT_EQ('j', buf[1]);
101 ASSERT_EQ('k', buf[2]);
102 ASSERT_EQ('l', buf[3]);
103 }
104
TEST_F(BlobCacheTest,SecondSetKeepsFirstValueIfTooLarge)105 TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) {
106 unsigned char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee };
107 mBC->set("abcd", 4, "efgh", 4);
108 mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
109 ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
110 ASSERT_EQ('e', buf[0]);
111 ASSERT_EQ('f', buf[1]);
112 ASSERT_EQ('g', buf[2]);
113 ASSERT_EQ('h', buf[3]);
114 }
115
TEST_F(BlobCacheTest,DoesntCacheIfKeyIsTooBig)116 TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) {
117 char key[MAX_KEY_SIZE+1];
118 unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
119 for (int i = 0; i < MAX_KEY_SIZE+1; i++) {
120 key[i] = 'a';
121 }
122 mBC->set(key, MAX_KEY_SIZE+1, "bbbb", 4);
123 ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE+1, buf, 4));
124 ASSERT_EQ(0xee, buf[0]);
125 ASSERT_EQ(0xee, buf[1]);
126 ASSERT_EQ(0xee, buf[2]);
127 ASSERT_EQ(0xee, buf[3]);
128 }
129
TEST_F(BlobCacheTest,DoesntCacheIfValueIsTooBig)130 TEST_F(BlobCacheTest, DoesntCacheIfValueIsTooBig) {
131 char buf[MAX_VALUE_SIZE+1];
132 for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
133 buf[i] = 'b';
134 }
135 mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
136 for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
137 buf[i] = 0xee;
138 }
139 ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE+1));
140 for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
141 SCOPED_TRACE(i);
142 ASSERT_EQ(0xee, buf[i]);
143 }
144 }
145
TEST_F(BlobCacheTest,DoesntCacheIfKeyValuePairIsTooBig)146 TEST_F(BlobCacheTest, DoesntCacheIfKeyValuePairIsTooBig) {
147 // Check a testing assumptions
148 ASSERT_TRUE(MAX_TOTAL_SIZE < MAX_KEY_SIZE + MAX_VALUE_SIZE);
149 ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE);
150
151 enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE + 1 };
152
153 char key[MAX_KEY_SIZE];
154 char buf[bufSize];
155 for (int i = 0; i < MAX_KEY_SIZE; i++) {
156 key[i] = 'a';
157 }
158 for (int i = 0; i < bufSize; i++) {
159 buf[i] = 'b';
160 }
161
162 mBC->set(key, MAX_KEY_SIZE, buf, MAX_VALUE_SIZE);
163 ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, NULL, 0));
164 }
165
TEST_F(BlobCacheTest,CacheMaxKeySizeSucceeds)166 TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) {
167 char key[MAX_KEY_SIZE];
168 unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
169 for (int i = 0; i < MAX_KEY_SIZE; i++) {
170 key[i] = 'a';
171 }
172 mBC->set(key, MAX_KEY_SIZE, "wxyz", 4);
173 ASSERT_EQ(size_t(4), mBC->get(key, MAX_KEY_SIZE, buf, 4));
174 ASSERT_EQ('w', buf[0]);
175 ASSERT_EQ('x', buf[1]);
176 ASSERT_EQ('y', buf[2]);
177 ASSERT_EQ('z', buf[3]);
178 }
179
TEST_F(BlobCacheTest,CacheMaxValueSizeSucceeds)180 TEST_F(BlobCacheTest, CacheMaxValueSizeSucceeds) {
181 char buf[MAX_VALUE_SIZE];
182 for (int i = 0; i < MAX_VALUE_SIZE; i++) {
183 buf[i] = 'b';
184 }
185 mBC->set("abcd", 4, buf, MAX_VALUE_SIZE);
186 for (int i = 0; i < MAX_VALUE_SIZE; i++) {
187 buf[i] = 0xee;
188 }
189 ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf,
190 MAX_VALUE_SIZE));
191 for (int i = 0; i < MAX_VALUE_SIZE; i++) {
192 SCOPED_TRACE(i);
193 ASSERT_EQ('b', buf[i]);
194 }
195 }
196
TEST_F(BlobCacheTest,CacheMaxKeyValuePairSizeSucceeds)197 TEST_F(BlobCacheTest, CacheMaxKeyValuePairSizeSucceeds) {
198 // Check a testing assumption
199 ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE);
200
201 enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE };
202
203 char key[MAX_KEY_SIZE];
204 char buf[bufSize];
205 for (int i = 0; i < MAX_KEY_SIZE; i++) {
206 key[i] = 'a';
207 }
208 for (int i = 0; i < bufSize; i++) {
209 buf[i] = 'b';
210 }
211
212 mBC->set(key, MAX_KEY_SIZE, buf, bufSize);
213 ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, NULL, 0));
214 }
215
TEST_F(BlobCacheTest,CacheMinKeyAndValueSizeSucceeds)216 TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
217 unsigned char buf[1] = { 0xee };
218 mBC->set("x", 1, "y", 1);
219 ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1));
220 ASSERT_EQ('y', buf[0]);
221 }
222
TEST_F(BlobCacheTest,CacheSizeDoesntExceedTotalLimit)223 TEST_F(BlobCacheTest, CacheSizeDoesntExceedTotalLimit) {
224 for (int i = 0; i < 256; i++) {
225 uint8_t k = i;
226 mBC->set(&k, 1, "x", 1);
227 }
228 int numCached = 0;
229 for (int i = 0; i < 256; i++) {
230 uint8_t k = i;
231 if (mBC->get(&k, 1, NULL, 0) == 1) {
232 numCached++;
233 }
234 }
235 ASSERT_GE(MAX_TOTAL_SIZE / 2, numCached);
236 }
237
TEST_F(BlobCacheTest,ExceedingTotalLimitHalvesCacheSize)238 TEST_F(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) {
239 // Fill up the entire cache with 1 char key/value pairs.
240 const int maxEntries = MAX_TOTAL_SIZE / 2;
241 for (int i = 0; i < maxEntries; i++) {
242 uint8_t k = i;
243 mBC->set(&k, 1, "x", 1);
244 }
245 // Insert one more entry, causing a cache overflow.
246 {
247 uint8_t k = maxEntries;
248 mBC->set(&k, 1, "x", 1);
249 }
250 // Count the number of entries in the cache.
251 int numCached = 0;
252 for (int i = 0; i < maxEntries+1; i++) {
253 uint8_t k = i;
254 if (mBC->get(&k, 1, NULL, 0) == 1) {
255 numCached++;
256 }
257 }
258 ASSERT_EQ(maxEntries/2 + 1, numCached);
259 }
260
261 class BlobCacheFlattenTest : public BlobCacheTest {
262 protected:
SetUp()263 virtual void SetUp() {
264 BlobCacheTest::SetUp();
265 mBC2 = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE);
266 }
267
TearDown()268 virtual void TearDown() {
269 mBC2.clear();
270 BlobCacheTest::TearDown();
271 }
272
roundTrip()273 void roundTrip() {
274 size_t size = mBC->getFlattenedSize();
275 uint8_t* flat = new uint8_t[size];
276 ASSERT_EQ(OK, mBC->flatten(flat, size));
277 ASSERT_EQ(OK, mBC2->unflatten(flat, size));
278 delete[] flat;
279 }
280
281 sp<BlobCache> mBC2;
282 };
283
TEST_F(BlobCacheFlattenTest,FlattenOneValue)284 TEST_F(BlobCacheFlattenTest, FlattenOneValue) {
285 unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
286 mBC->set("abcd", 4, "efgh", 4);
287 roundTrip();
288 ASSERT_EQ(size_t(4), mBC2->get("abcd", 4, buf, 4));
289 ASSERT_EQ('e', buf[0]);
290 ASSERT_EQ('f', buf[1]);
291 ASSERT_EQ('g', buf[2]);
292 ASSERT_EQ('h', buf[3]);
293 }
294
TEST_F(BlobCacheFlattenTest,FlattenFullCache)295 TEST_F(BlobCacheFlattenTest, FlattenFullCache) {
296 // Fill up the entire cache with 1 char key/value pairs.
297 const int maxEntries = MAX_TOTAL_SIZE / 2;
298 for (int i = 0; i < maxEntries; i++) {
299 uint8_t k = i;
300 mBC->set(&k, 1, &k, 1);
301 }
302
303 roundTrip();
304
305 // Verify the deserialized cache
306 for (int i = 0; i < maxEntries; i++) {
307 uint8_t k = i;
308 uint8_t v = 0xee;
309 ASSERT_EQ(size_t(1), mBC2->get(&k, 1, &v, 1));
310 ASSERT_EQ(k, v);
311 }
312 }
313
TEST_F(BlobCacheFlattenTest,FlattenDoesntChangeCache)314 TEST_F(BlobCacheFlattenTest, FlattenDoesntChangeCache) {
315 // Fill up the entire cache with 1 char key/value pairs.
316 const int maxEntries = MAX_TOTAL_SIZE / 2;
317 for (int i = 0; i < maxEntries; i++) {
318 uint8_t k = i;
319 mBC->set(&k, 1, &k, 1);
320 }
321
322 size_t size = mBC->getFlattenedSize();
323 uint8_t* flat = new uint8_t[size];
324 ASSERT_EQ(OK, mBC->flatten(flat, size));
325 delete[] flat;
326
327 // Verify the cache that we just serialized
328 for (int i = 0; i < maxEntries; i++) {
329 uint8_t k = i;
330 uint8_t v = 0xee;
331 ASSERT_EQ(size_t(1), mBC->get(&k, 1, &v, 1));
332 ASSERT_EQ(k, v);
333 }
334 }
335
TEST_F(BlobCacheFlattenTest,FlattenCatchesBufferTooSmall)336 TEST_F(BlobCacheFlattenTest, FlattenCatchesBufferTooSmall) {
337 // Fill up the entire cache with 1 char key/value pairs.
338 const int maxEntries = MAX_TOTAL_SIZE / 2;
339 for (int i = 0; i < maxEntries; i++) {
340 uint8_t k = i;
341 mBC->set(&k, 1, &k, 1);
342 }
343
344 size_t size = mBC->getFlattenedSize() - 1;
345 uint8_t* flat = new uint8_t[size];
346 ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size));
347 delete[] flat;
348 }
349
TEST_F(BlobCacheFlattenTest,UnflattenCatchesBadMagic)350 TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) {
351 unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
352 mBC->set("abcd", 4, "efgh", 4);
353
354 size_t size = mBC->getFlattenedSize();
355 uint8_t* flat = new uint8_t[size];
356 ASSERT_EQ(OK, mBC->flatten(flat, size));
357 flat[1] = ~flat[1];
358
359 // Bad magic should cause an error.
360 ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size));
361 delete[] flat;
362
363 // The error should cause the unflatten to result in an empty cache
364 ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
365 }
366
TEST_F(BlobCacheFlattenTest,UnflattenCatchesBadBlobCacheVersion)367 TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) {
368 unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
369 mBC->set("abcd", 4, "efgh", 4);
370
371 size_t size = mBC->getFlattenedSize();
372 uint8_t* flat = new uint8_t[size];
373 ASSERT_EQ(OK, mBC->flatten(flat, size));
374 flat[5] = ~flat[5];
375
376 // Version mismatches shouldn't cause errors, but should not use the
377 // serialized entries
378 ASSERT_EQ(OK, mBC2->unflatten(flat, size));
379 delete[] flat;
380
381 // The version mismatch should cause the unflatten to result in an empty
382 // cache
383 ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
384 }
385
TEST_F(BlobCacheFlattenTest,UnflattenCatchesBadBlobCacheDeviceVersion)386 TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) {
387 unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
388 mBC->set("abcd", 4, "efgh", 4);
389
390 size_t size = mBC->getFlattenedSize();
391 uint8_t* flat = new uint8_t[size];
392 ASSERT_EQ(OK, mBC->flatten(flat, size));
393 flat[10] = ~flat[10];
394
395 // Version mismatches shouldn't cause errors, but should not use the
396 // serialized entries
397 ASSERT_EQ(OK, mBC2->unflatten(flat, size));
398 delete[] flat;
399
400 // The version mismatch should cause the unflatten to result in an empty
401 // cache
402 ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
403 }
404
TEST_F(BlobCacheFlattenTest,UnflattenCatchesBufferTooSmall)405 TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) {
406 unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
407 mBC->set("abcd", 4, "efgh", 4);
408
409 size_t size = mBC->getFlattenedSize();
410 uint8_t* flat = new uint8_t[size];
411 ASSERT_EQ(OK, mBC->flatten(flat, size));
412
413 // A buffer truncation shouldt cause an error
414 ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1));
415 delete[] flat;
416
417 // The error should cause the unflatten to result in an empty cache
418 ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
419 }
420
421 } // namespace android
422