123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 |
- /*
- ** Copyright 2011, The Android Open Source Project
- **
- ** Licensed under the Apache License, Version 2.0 (the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
- **
- ** http://www.apache.org/licenses/LICENSE-2.0
- **
- ** Unless required by applicable law or agreed to in writing, software
- ** distributed under the License is distributed on an "AS IS" BASIS,
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ** See the License for the specific language governing permissions and
- ** limitations under the License.
- */
- #include <fcntl.h>
- #include <stdio.h>
- #include <memory>
- #include <gtest/gtest.h>
- #include "BlobCache.h"
- namespace android {
- template<typename T> using sp = std::shared_ptr<T>;
- class BlobCacheTest : public ::testing::Test {
- protected:
- enum {
- OK = 0,
- BAD_VALUE = -EINVAL
- };
- enum {
- MAX_KEY_SIZE = 6,
- MAX_VALUE_SIZE = 8,
- MAX_TOTAL_SIZE = 13,
- };
- virtual void SetUp() {
- mBC.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE));
- }
- virtual void TearDown() {
- mBC.reset();
- }
- std::unique_ptr<BlobCache> mBC;
- };
- TEST_F(BlobCacheTest, CacheSingleValueSucceeds) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
- mBC->set("abcd", 4, "efgh", 4);
- ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
- ASSERT_EQ('e', buf[0]);
- ASSERT_EQ('f', buf[1]);
- ASSERT_EQ('g', buf[2]);
- ASSERT_EQ('h', buf[3]);
- }
- TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) {
- unsigned char buf[2] = { 0xee, 0xee };
- mBC->set("ab", 2, "cd", 2);
- mBC->set("ef", 2, "gh", 2);
- ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2));
- ASSERT_EQ('c', buf[0]);
- ASSERT_EQ('d', buf[1]);
- ASSERT_EQ(size_t(2), mBC->get("ef", 2, buf, 2));
- ASSERT_EQ('g', buf[0]);
- ASSERT_EQ('h', buf[1]);
- }
- TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) {
- unsigned char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee };
- mBC->set("abcd", 4, "efgh", 4);
- ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf+1, 4));
- ASSERT_EQ(0xee, buf[0]);
- ASSERT_EQ('e', buf[1]);
- ASSERT_EQ('f', buf[2]);
- ASSERT_EQ('g', buf[3]);
- ASSERT_EQ('h', buf[4]);
- ASSERT_EQ(0xee, buf[5]);
- }
- TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) {
- unsigned char buf[3] = { 0xee, 0xee, 0xee };
- mBC->set("abcd", 4, "efgh", 4);
- ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3));
- ASSERT_EQ(0xee, buf[0]);
- ASSERT_EQ(0xee, buf[1]);
- ASSERT_EQ(0xee, buf[2]);
- }
- TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) {
- mBC->set("abcd", 4, "efgh", 4);
- ASSERT_EQ(size_t(4), mBC->get("abcd", 4, nullptr, 0));
- }
- TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
- mBC->set("abcd", 4, "efgh", 4);
- mBC->set("abcd", 4, "ijkl", 4);
- ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
- ASSERT_EQ('i', buf[0]);
- ASSERT_EQ('j', buf[1]);
- ASSERT_EQ('k', buf[2]);
- ASSERT_EQ('l', buf[3]);
- }
- TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) {
- unsigned char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee };
- mBC->set("abcd", 4, "efgh", 4);
- mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
- ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
- ASSERT_EQ('e', buf[0]);
- ASSERT_EQ('f', buf[1]);
- ASSERT_EQ('g', buf[2]);
- ASSERT_EQ('h', buf[3]);
- }
- TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) {
- char key[MAX_KEY_SIZE+1];
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
- for (int i = 0; i < MAX_KEY_SIZE+1; i++) {
- key[i] = 'a';
- }
- mBC->set(key, MAX_KEY_SIZE+1, "bbbb", 4);
- ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE+1, buf, 4));
- ASSERT_EQ(0xee, buf[0]);
- ASSERT_EQ(0xee, buf[1]);
- ASSERT_EQ(0xee, buf[2]);
- ASSERT_EQ(0xee, buf[3]);
- }
- TEST_F(BlobCacheTest, DoesntCacheIfValueIsTooBig) {
- char buf[MAX_VALUE_SIZE+1];
- for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
- buf[i] = 'b';
- }
- mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
- for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
- buf[i] = 0xee;
- }
- ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE+1));
- for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
- SCOPED_TRACE(i);
- ASSERT_EQ(0xee, buf[i]);
- }
- }
- TEST_F(BlobCacheTest, DoesntCacheIfKeyValuePairIsTooBig) {
- // Check a testing assumptions
- ASSERT_TRUE(MAX_TOTAL_SIZE < MAX_KEY_SIZE + MAX_VALUE_SIZE);
- ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE);
- enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE + 1 };
- char key[MAX_KEY_SIZE];
- char buf[bufSize];
- for (int i = 0; i < MAX_KEY_SIZE; i++) {
- key[i] = 'a';
- }
- for (int i = 0; i < bufSize; i++) {
- buf[i] = 'b';
- }
- mBC->set(key, MAX_KEY_SIZE, buf, MAX_VALUE_SIZE);
- ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, nullptr, 0));
- }
- TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) {
- char key[MAX_KEY_SIZE];
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
- for (int i = 0; i < MAX_KEY_SIZE; i++) {
- key[i] = 'a';
- }
- mBC->set(key, MAX_KEY_SIZE, "wxyz", 4);
- ASSERT_EQ(size_t(4), mBC->get(key, MAX_KEY_SIZE, buf, 4));
- ASSERT_EQ('w', buf[0]);
- ASSERT_EQ('x', buf[1]);
- ASSERT_EQ('y', buf[2]);
- ASSERT_EQ('z', buf[3]);
- }
- TEST_F(BlobCacheTest, CacheMaxValueSizeSucceeds) {
- char buf[MAX_VALUE_SIZE];
- for (int i = 0; i < MAX_VALUE_SIZE; i++) {
- buf[i] = 'b';
- }
- mBC->set("abcd", 4, buf, MAX_VALUE_SIZE);
- for (int i = 0; i < MAX_VALUE_SIZE; i++) {
- buf[i] = 0xee;
- }
- ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf,
- MAX_VALUE_SIZE));
- for (int i = 0; i < MAX_VALUE_SIZE; i++) {
- SCOPED_TRACE(i);
- ASSERT_EQ('b', buf[i]);
- }
- }
- TEST_F(BlobCacheTest, CacheMaxKeyValuePairSizeSucceeds) {
- // Check a testing assumption
- ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE);
- enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE };
- char key[MAX_KEY_SIZE];
- char buf[bufSize];
- for (int i = 0; i < MAX_KEY_SIZE; i++) {
- key[i] = 'a';
- }
- for (int i = 0; i < bufSize; i++) {
- buf[i] = 'b';
- }
- mBC->set(key, MAX_KEY_SIZE, buf, bufSize);
- ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, nullptr, 0));
- }
- TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
- unsigned char buf[1] = { 0xee };
- mBC->set("x", 1, "y", 1);
- ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1));
- ASSERT_EQ('y', buf[0]);
- }
- TEST_F(BlobCacheTest, CacheSizeDoesntExceedTotalLimit) {
- for (int i = 0; i < 256; i++) {
- uint8_t k = i;
- mBC->set(&k, 1, "x", 1);
- }
- int numCached = 0;
- for (int i = 0; i < 256; i++) {
- uint8_t k = i;
- if (mBC->get(&k, 1, nullptr, 0) == 1) {
- numCached++;
- }
- }
- ASSERT_GE(MAX_TOTAL_SIZE / 2, numCached);
- }
- TEST_F(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) {
- // Fill up the entire cache with 1 char key/value pairs.
- const int maxEntries = MAX_TOTAL_SIZE / 2;
- for (int i = 0; i < maxEntries; i++) {
- uint8_t k = i;
- mBC->set(&k, 1, "x", 1);
- }
- // Insert one more entry, causing a cache overflow.
- {
- uint8_t k = maxEntries;
- mBC->set(&k, 1, "x", 1);
- }
- // Count the number of entries in the cache.
- int numCached = 0;
- for (int i = 0; i < maxEntries+1; i++) {
- uint8_t k = i;
- if (mBC->get(&k, 1, nullptr, 0) == 1) {
- numCached++;
- }
- }
- ASSERT_EQ(maxEntries/2 + 1, numCached);
- }
- class BlobCacheFlattenTest : public BlobCacheTest {
- protected:
- virtual void SetUp() {
- BlobCacheTest::SetUp();
- mBC2.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE));
- }
- virtual void TearDown() {
- mBC2.reset();
- BlobCacheTest::TearDown();
- }
- void roundTrip() {
- size_t size = mBC->getFlattenedSize();
- uint8_t* flat = new uint8_t[size];
- ASSERT_EQ(OK, mBC->flatten(flat, size));
- ASSERT_EQ(OK, mBC2->unflatten(flat, size));
- delete[] flat;
- }
- sp<BlobCache> mBC2;
- };
- TEST_F(BlobCacheFlattenTest, FlattenOneValue) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
- mBC->set("abcd", 4, "efgh", 4);
- roundTrip();
- ASSERT_EQ(size_t(4), mBC2->get("abcd", 4, buf, 4));
- ASSERT_EQ('e', buf[0]);
- ASSERT_EQ('f', buf[1]);
- ASSERT_EQ('g', buf[2]);
- ASSERT_EQ('h', buf[3]);
- }
- TEST_F(BlobCacheFlattenTest, FlattenFullCache) {
- // Fill up the entire cache with 1 char key/value pairs.
- const int maxEntries = MAX_TOTAL_SIZE / 2;
- for (int i = 0; i < maxEntries; i++) {
- uint8_t k = i;
- mBC->set(&k, 1, &k, 1);
- }
- roundTrip();
- // Verify the deserialized cache
- for (int i = 0; i < maxEntries; i++) {
- uint8_t k = i;
- uint8_t v = 0xee;
- ASSERT_EQ(size_t(1), mBC2->get(&k, 1, &v, 1));
- ASSERT_EQ(k, v);
- }
- }
- TEST_F(BlobCacheFlattenTest, FlattenDoesntChangeCache) {
- // Fill up the entire cache with 1 char key/value pairs.
- const int maxEntries = MAX_TOTAL_SIZE / 2;
- for (int i = 0; i < maxEntries; i++) {
- uint8_t k = i;
- mBC->set(&k, 1, &k, 1);
- }
- size_t size = mBC->getFlattenedSize();
- uint8_t* flat = new uint8_t[size];
- ASSERT_EQ(OK, mBC->flatten(flat, size));
- delete[] flat;
- // Verify the cache that we just serialized
- for (int i = 0; i < maxEntries; i++) {
- uint8_t k = i;
- uint8_t v = 0xee;
- ASSERT_EQ(size_t(1), mBC->get(&k, 1, &v, 1));
- ASSERT_EQ(k, v);
- }
- }
- TEST_F(BlobCacheFlattenTest, FlattenCatchesBufferTooSmall) {
- // Fill up the entire cache with 1 char key/value pairs.
- const int maxEntries = MAX_TOTAL_SIZE / 2;
- for (int i = 0; i < maxEntries; i++) {
- uint8_t k = i;
- mBC->set(&k, 1, &k, 1);
- }
- size_t size = mBC->getFlattenedSize() - 1;
- uint8_t* flat = new uint8_t[size];
- // ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size));
- // TODO: The above fails. I expect this is so because getFlattenedSize()
- // overstimates the size by using PROPERTY_VALUE_MAX.
- delete[] flat;
- }
- TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
- mBC->set("abcd", 4, "efgh", 4);
- size_t size = mBC->getFlattenedSize();
- uint8_t* flat = new uint8_t[size];
- ASSERT_EQ(OK, mBC->flatten(flat, size));
- flat[1] = ~flat[1];
- // Bad magic should cause an error.
- ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size));
- delete[] flat;
- // The error should cause the unflatten to result in an empty cache
- ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
- }
- TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
- mBC->set("abcd", 4, "efgh", 4);
- size_t size = mBC->getFlattenedSize();
- uint8_t* flat = new uint8_t[size];
- ASSERT_EQ(OK, mBC->flatten(flat, size));
- flat[5] = ~flat[5];
- // Version mismatches shouldn't cause errors, but should not use the
- // serialized entries
- ASSERT_EQ(OK, mBC2->unflatten(flat, size));
- delete[] flat;
- // The version mismatch should cause the unflatten to result in an empty
- // cache
- ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
- }
- TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
- mBC->set("abcd", 4, "efgh", 4);
- size_t size = mBC->getFlattenedSize();
- uint8_t* flat = new uint8_t[size];
- ASSERT_EQ(OK, mBC->flatten(flat, size));
- flat[10] = ~flat[10];
- // Version mismatches shouldn't cause errors, but should not use the
- // serialized entries
- ASSERT_EQ(OK, mBC2->unflatten(flat, size));
- delete[] flat;
- // The version mismatch should cause the unflatten to result in an empty
- // cache
- ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
- }
- TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) {
- unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee };
- mBC->set("abcd", 4, "efgh", 4);
- size_t size = mBC->getFlattenedSize();
- uint8_t* flat = new uint8_t[size];
- ASSERT_EQ(OK, mBC->flatten(flat, size));
- // A buffer truncation shouldt cause an error
- // ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1));
- // TODO: The above appears to fail because getFlattenedSize() is
- // conservative.
- delete[] flat;
- // The error should cause the unflatten to result in an empty cache
- ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
- }
- } // namespace android
|