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 25 namespace android { 26 27 class BlobCacheTest : public ::testing::Test { 28 protected: 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 46 TEST_F(BlobCacheTest, CacheSingleValueSucceeds) { 47 unsigned 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 56 TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) { 57 unsigned 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 68 TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) { 69 unsigned 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 80 TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) { 81 unsigned 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 89 TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) { 90 mBC->set("abcd", 4, "efgh", 4); 91 ASSERT_EQ(size_t(4), mBC->get("abcd", 4, NULL, 0)); 92 } 93 94 TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) { 95 unsigned 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 105 TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) { 106 unsigned 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 116 TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) { 117 char key[MAX_KEY_SIZE+1]; 118 unsigned 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 130 TEST_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 146 TEST_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 166 TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) { 167 char key[MAX_KEY_SIZE]; 168 unsigned 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 180 TEST_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 197 TEST_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 216 TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) { 217 unsigned 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 223 TEST_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 238 TEST_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 261 class BlobCacheFlattenTest : public BlobCacheTest { 262 protected: 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)); 277 ASSERT_EQ(OK, mBC2->unflatten(flat, size)); 278 delete[] flat; 279 } 280 281 sp<BlobCache> mBC2; 282 }; 283 284 TEST_F(BlobCacheFlattenTest, FlattenOneValue) { 285 unsigned 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 295 TEST_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 314 TEST_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)); 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 336 TEST_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)); 347 delete[] flat; 348 } 349 350 TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) { 351 unsigned 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)); 357 flat[1] = ~flat[1]; 358 359 // Bad magic should cause an error. 360 ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size)); 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 367 TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) { 368 unsigned 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)); 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)); 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 386 TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) { 387 unsigned 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)); 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)); 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 405 TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) { 406 unsigned 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)); 412 413 // A buffer truncation shouldt cause an error 414 ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1)); 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