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
26namespace android {
27
28template<typename T> using sp = std::shared_ptr<T>;
29
30class BlobCacheTest : public ::testing::Test {
31protected:
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
44    virtual void SetUp() {
45        mBC.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE));
46    }
47
48    virtual void TearDown() {
49        mBC.reset();
50    }
51
52    std::unique_ptr<BlobCache> mBC;
53};
54
55TEST_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
65TEST_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
77TEST_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
89TEST_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
98TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) {
99    mBC->set("abcd", 4, "efgh", 4);
100    ASSERT_EQ(size_t(4), mBC->get("abcd", 4, NULL, 0));
101}
102
103TEST_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
114TEST_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
125TEST_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
139TEST_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
155TEST_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, NULL, 0));
173}
174
175TEST_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
189TEST_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
206TEST_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, NULL, 0));
223}
224
225TEST_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
232TEST_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, NULL, 0) == 1) {
241            numCached++;
242        }
243    }
244    ASSERT_GE(MAX_TOTAL_SIZE / 2, numCached);
245}
246
247TEST_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, NULL, 0) == 1) {
264            numCached++;
265        }
266    }
267    ASSERT_EQ(maxEntries/2 + 1, numCached);
268}
269
270class BlobCacheFlattenTest : public BlobCacheTest {
271protected:
272    virtual void SetUp() {
273        BlobCacheTest::SetUp();
274        mBC2.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE));
275    }
276
277    virtual void TearDown() {
278        mBC2.reset();
279        BlobCacheTest::TearDown();
280    }
281
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
293TEST_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
304TEST_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
323TEST_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
345TEST_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
361TEST_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
378TEST_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
397TEST_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
416TEST_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