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