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