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.
5 #include "leveldb/cache.h"
8 #include "util/coding.h"
9 #include "util/testharness.h"
13 // Conversions between numeric keys/values and the types expected by Cache.
14 static std::string
EncodeKey(int k
) {
16 PutFixed32(&result
, k
);
19 static int DecodeKey(const Slice
& k
) {
20 assert(k
.size() == 4);
21 return DecodeFixed32(k
.data());
23 static void* EncodeValue(uintptr_t v
) { return reinterpret_cast<void*>(v
); }
24 static int DecodeValue(void* v
) { return reinterpret_cast<uintptr_t>(v
); }
28 static CacheTest
* current_
;
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
));
35 static const int kCacheSize
= 1000;
36 std::vector
<int> deleted_keys_
;
37 std::vector
<int> deleted_values_
;
40 CacheTest() : cache_(NewLRUCache(kCacheSize
)) {
49 Cache::Handle
* handle
= cache_
->Lookup(EncodeKey(key
));
50 const int r
= (handle
== NULL
) ? -1 : DecodeValue(cache_
->Value(handle
));
52 cache_
->Release(handle
);
57 void Insert(int key
, int value
, int charge
= 1) {
58 cache_
->Release(cache_
->Insert(EncodeKey(key
), EncodeValue(value
), charge
,
59 &CacheTest::Deleter
));
62 Cache::Handle
* InsertAndReturnHandle(int key
, int value
, int charge
= 1) {
63 return cache_
->Insert(EncodeKey(key
), EncodeValue(value
), charge
,
68 cache_
->Erase(EncodeKey(key
));
71 CacheTest
* CacheTest::current_
;
73 TEST(CacheTest
, HitAndMiss
) {
74 ASSERT_EQ(-1, Lookup(100));
77 ASSERT_EQ(101, Lookup(100));
78 ASSERT_EQ(-1, Lookup(200));
79 ASSERT_EQ(-1, Lookup(300));
82 ASSERT_EQ(101, Lookup(100));
83 ASSERT_EQ(201, Lookup(200));
84 ASSERT_EQ(-1, Lookup(300));
87 ASSERT_EQ(102, Lookup(100));
88 ASSERT_EQ(201, Lookup(200));
89 ASSERT_EQ(-1, Lookup(300));
91 ASSERT_EQ(1, deleted_keys_
.size());
92 ASSERT_EQ(100, deleted_keys_
[0]);
93 ASSERT_EQ(101, deleted_values_
[0]);
96 TEST(CacheTest
, Erase
) {
98 ASSERT_EQ(0, deleted_keys_
.size());
103 ASSERT_EQ(-1, Lookup(100));
104 ASSERT_EQ(201, Lookup(200));
105 ASSERT_EQ(1, deleted_keys_
.size());
106 ASSERT_EQ(100, deleted_keys_
[0]);
107 ASSERT_EQ(101, deleted_values_
[0]);
110 ASSERT_EQ(-1, Lookup(100));
111 ASSERT_EQ(201, Lookup(200));
112 ASSERT_EQ(1, deleted_keys_
.size());
115 TEST(CacheTest
, EntriesArePinned
) {
117 Cache::Handle
* h1
= cache_
->Lookup(EncodeKey(100));
118 ASSERT_EQ(101, DecodeValue(cache_
->Value(h1
)));
121 Cache::Handle
* h2
= cache_
->Lookup(EncodeKey(100));
122 ASSERT_EQ(102, DecodeValue(cache_
->Value(h2
)));
123 ASSERT_EQ(0, deleted_keys_
.size());
126 ASSERT_EQ(1, deleted_keys_
.size());
127 ASSERT_EQ(100, deleted_keys_
[0]);
128 ASSERT_EQ(101, deleted_values_
[0]);
131 ASSERT_EQ(-1, Lookup(100));
132 ASSERT_EQ(1, deleted_keys_
.size());
135 ASSERT_EQ(2, deleted_keys_
.size());
136 ASSERT_EQ(100, deleted_keys_
[1]);
137 ASSERT_EQ(102, deleted_values_
[1]);
140 TEST(CacheTest
, EvictionPolicy
) {
144 Cache::Handle
* h
= cache_
->Lookup(EncodeKey(300));
146 // Frequently used entry must be kept around,
147 // as must things that are still in use.
148 for (int i
= 0; i
< kCacheSize
+ 100; i
++) {
149 Insert(1000+i
, 2000+i
);
150 ASSERT_EQ(2000+i
, Lookup(1000+i
));
151 ASSERT_EQ(101, Lookup(100));
153 ASSERT_EQ(101, Lookup(100));
154 ASSERT_EQ(-1, Lookup(200));
155 ASSERT_EQ(301, Lookup(300));
159 TEST(CacheTest
, UseExceedsCacheSize
) {
160 // Overfill the cache, keeping handles on all inserted entries.
161 std::vector
<Cache::Handle
*> h
;
162 for (int i
= 0; i
< kCacheSize
+ 100; i
++) {
163 h
.push_back(InsertAndReturnHandle(1000+i
, 2000+i
));
166 // Check that all the entries can be found in the cache.
167 for (int i
= 0; i
< h
.size(); i
++) {
168 ASSERT_EQ(2000+i
, Lookup(1000+i
));
171 for (int i
= 0; i
< h
.size(); i
++) {
172 cache_
->Release(h
[i
]);
176 TEST(CacheTest
, HeavyEntries
) {
177 // Add a bunch of light and heavy entries and then count the combined
178 // size of items still in the cache, which must be approximately the
179 // same as the total capacity.
180 const int kLight
= 1;
181 const int kHeavy
= 10;
184 while (added
< 2*kCacheSize
) {
185 const int weight
= (index
& 1) ? kLight
: kHeavy
;
186 Insert(index
, 1000+index
, weight
);
191 int cached_weight
= 0;
192 for (int i
= 0; i
< index
; i
++) {
193 const int weight
= (i
& 1 ? kLight
: kHeavy
);
196 cached_weight
+= weight
;
197 ASSERT_EQ(1000+i
, r
);
200 ASSERT_LE(cached_weight
, kCacheSize
+ kCacheSize
/10);
203 TEST(CacheTest
, NewId
) {
204 uint64_t a
= cache_
->NewId();
205 uint64_t b
= cache_
->NewId();
209 TEST(CacheTest
, Prune
) {
213 Cache::Handle
* handle
= cache_
->Lookup(EncodeKey(1));
216 cache_
->Release(handle
);
218 ASSERT_EQ(100, Lookup(1));
219 ASSERT_EQ(-1, Lookup(2));
222 } // namespace leveldb
224 int main(int argc
, char** argv
) {
225 return leveldb::test::RunAllTests();