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
25namespace android {
26
27class BlobCacheTest : public ::testing::Test {
28protected:
29    enum {
30        MAX_KEY_SIZE = 6,
31        MAX_VALUE_SIZE = 8,
32        MAX_TOTAL_SIZE = 13,
33    };
34
35    virtual void SetUp() {
36        mBC = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE);
37    }
38
39    virtual void TearDown() {
40        mBC.clear();
41    }
42
43    sp<BlobCache> mBC;
44};
45
46TEST_F(BlobCacheTest, CacheSingleValueSucceeds) {
47    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
56TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) {
57    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
68TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) {
69    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
80TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) {
81    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
89TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) {
90    mBC->set("abcd", 4, "efgh", 4);
91    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, NULL, 0));
92}
93
94TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) {
95    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
105TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) {
106    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
116TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) {
117    char key[MAX_KEY_SIZE+1];
118    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
130TEST_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
146TEST_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
166TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) {
167    char key[MAX_KEY_SIZE];
168    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
180TEST_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
197TEST_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
216TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
217    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
223TEST_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
238TEST_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
261class BlobCacheFlattenTest : public BlobCacheTest {
262protected:
263    virtual void SetUp() {
264        BlobCacheTest::SetUp();
265        mBC2 = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE);
266    }
267
268    virtual void TearDown() {
269        mBC2.clear();
270        BlobCacheTest::TearDown();
271    }
272
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, NULL, 0));
277        ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0));
278        delete[] flat;
279    }
280
281    sp<BlobCache> mBC2;
282};
283
284TEST_F(BlobCacheFlattenTest, FlattenOneValue) {
285    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
295TEST_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
314TEST_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, NULL, 0));
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
336TEST_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, NULL, 0));
347    delete[] flat;
348}
349
350TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) {
351    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, NULL, 0));
357    flat[1] = ~flat[1];
358
359    // Bad magic should cause an error.
360    ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size, NULL, 0));
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
367TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) {
368    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, NULL, 0));
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, NULL, 0));
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
386TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) {
387    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, NULL, 0));
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, NULL, 0));
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
405TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) {
406    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, NULL, 0));
412
413    // A buffer truncation shouldt cause an error
414    ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1, NULL, 0));
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