1 // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. See the AUTHORS file for names of contributors. 4 5 #include "leveldb/cache.h" 6 7 #include <vector> 8 #include "util/coding.h" 9 #include "util/testharness.h" 10 11 namespace leveldb { 12 13 // Conversions between numeric keys/values and the types expected by Cache. 14 static std::string EncodeKey(int k) { 15 std::string result; 16 PutFixed32(&result, k); 17 return result; 18 } 19 static int DecodeKey(const Slice& k) { 20 assert(k.size() == 4); 21 return DecodeFixed32(k.data()); 22 } 23 static void* EncodeValue(uintptr_t v) { return reinterpret_cast<void*>(v); } 24 static int DecodeValue(void* v) { return reinterpret_cast<uintptr_t>(v); } 25 26 class CacheTest { 27 public: 28 static CacheTest* current_; 29 30 static void Deleter(const Slice& key, void* v) { 31 current_->deleted_keys_.push_back(DecodeKey(key)); 32 current_->deleted_values_.push_back(DecodeValue(v)); 33 } 34 35 static const int kCacheSize = 1000; 36 std::vector<int> deleted_keys_; 37 std::vector<int> deleted_values_; 38 Cache* cache_; 39 40 CacheTest() : cache_(NewLRUCache(kCacheSize)) { 41 current_ = this; 42 } 43 44 ~CacheTest() { 45 delete cache_; 46 } 47 48 int Lookup(int key) { 49 Cache::Handle* handle = cache_->Lookup(EncodeKey(key)); 50 const int r = (handle == NULL) ? -1 : DecodeValue(cache_->Value(handle)); 51 if (handle != NULL) { 52 cache_->Release(handle); 53 } 54 return r; 55 } 56 57 void Insert(int key, int value, int charge = 1) { 58 cache_->Release(cache_->Insert(EncodeKey(key), EncodeValue(value), charge, 59 &CacheTest::Deleter)); 60 } 61 62 void Erase(int key) { 63 cache_->Erase(EncodeKey(key)); 64 } 65 }; 66 CacheTest* CacheTest::current_; 67 68 TEST(CacheTest, HitAndMiss) { 69 ASSERT_EQ(-1, Lookup(100)); 70 71 Insert(100, 101); 72 ASSERT_EQ(101, Lookup(100)); 73 ASSERT_EQ(-1, Lookup(200)); 74 ASSERT_EQ(-1, Lookup(300)); 75 76 Insert(200, 201); 77 ASSERT_EQ(101, Lookup(100)); 78 ASSERT_EQ(201, Lookup(200)); 79 ASSERT_EQ(-1, Lookup(300)); 80 81 Insert(100, 102); 82 ASSERT_EQ(102, Lookup(100)); 83 ASSERT_EQ(201, Lookup(200)); 84 ASSERT_EQ(-1, Lookup(300)); 85 86 ASSERT_EQ(1, deleted_keys_.size()); 87 ASSERT_EQ(100, deleted_keys_[0]); 88 ASSERT_EQ(101, deleted_values_[0]); 89 } 90 91 TEST(CacheTest, Erase) { 92 Erase(200); 93 ASSERT_EQ(0, deleted_keys_.size()); 94 95 Insert(100, 101); 96 Insert(200, 201); 97 Erase(100); 98 ASSERT_EQ(-1, Lookup(100)); 99 ASSERT_EQ(201, Lookup(200)); 100 ASSERT_EQ(1, deleted_keys_.size()); 101 ASSERT_EQ(100, deleted_keys_[0]); 102 ASSERT_EQ(101, deleted_values_[0]); 103 104 Erase(100); 105 ASSERT_EQ(-1, Lookup(100)); 106 ASSERT_EQ(201, Lookup(200)); 107 ASSERT_EQ(1, deleted_keys_.size()); 108 } 109 110 TEST(CacheTest, EntriesArePinned) { 111 Insert(100, 101); 112 Cache::Handle* h1 = cache_->Lookup(EncodeKey(100)); 113 ASSERT_EQ(101, DecodeValue(cache_->Value(h1))); 114 115 Insert(100, 102); 116 Cache::Handle* h2 = cache_->Lookup(EncodeKey(100)); 117 ASSERT_EQ(102, DecodeValue(cache_->Value(h2))); 118 ASSERT_EQ(0, deleted_keys_.size()); 119 120 cache_->Release(h1); 121 ASSERT_EQ(1, deleted_keys_.size()); 122 ASSERT_EQ(100, deleted_keys_[0]); 123 ASSERT_EQ(101, deleted_values_[0]); 124 125 Erase(100); 126 ASSERT_EQ(-1, Lookup(100)); 127 ASSERT_EQ(1, deleted_keys_.size()); 128 129 cache_->Release(h2); 130 ASSERT_EQ(2, deleted_keys_.size()); 131 ASSERT_EQ(100, deleted_keys_[1]); 132 ASSERT_EQ(102, deleted_values_[1]); 133 } 134 135 TEST(CacheTest, EvictionPolicy) { 136 Insert(100, 101); 137 Insert(200, 201); 138 139 // Frequently used entry must be kept around 140 for (int i = 0; i < kCacheSize + 100; i++) { 141 Insert(1000+i, 2000+i); 142 ASSERT_EQ(2000+i, Lookup(1000+i)); 143 ASSERT_EQ(101, Lookup(100)); 144 } 145 ASSERT_EQ(101, Lookup(100)); 146 ASSERT_EQ(-1, Lookup(200)); 147 } 148 149 TEST(CacheTest, HeavyEntries) { 150 // Add a bunch of light and heavy entries and then count the combined 151 // size of items still in the cache, which must be approximately the 152 // same as the total capacity. 153 const int kLight = 1; 154 const int kHeavy = 10; 155 int added = 0; 156 int index = 0; 157 while (added < 2*kCacheSize) { 158 const int weight = (index & 1) ? kLight : kHeavy; 159 Insert(index, 1000+index, weight); 160 added += weight; 161 index++; 162 } 163 164 int cached_weight = 0; 165 for (int i = 0; i < index; i++) { 166 const int weight = (i & 1 ? kLight : kHeavy); 167 int r = Lookup(i); 168 if (r >= 0) { 169 cached_weight += weight; 170 ASSERT_EQ(1000+i, r); 171 } 172 } 173 ASSERT_LE(cached_weight, kCacheSize + kCacheSize/10); 174 } 175 176 TEST(CacheTest, NewId) { 177 uint64_t a = cache_->NewId(); 178 uint64_t b = cache_->NewId(); 179 ASSERT_NE(a, b); 180 } 181 182 } // namespace leveldb 183 184 int main(int argc, char** argv) { 185 return leveldb::test::RunAllTests(); 186 } 187