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