Thanks to visit codestin.com
Credit goes to chromium.googlesource.com

blob: 73d6b5a9f1ce50405310de818389451d614a51b2 [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2011 The Chromium Authors
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit09911bf2008-07-26 23:55:294
cfredric8fdc22432021-10-14 03:24:005#include "base/containers/lru_cache.h"
dcheng093de9b2016-04-04 21:25:516
Daniel Murphy2e5932d2024-09-18 16:23:397#include <algorithm>
Denis Yaroshevskiyb900a2122018-01-29 17:57:138#include <cstddef>
Jan Keitel69411da2025-06-24 05:51:239#include <functional>
Daniel Murphy2e5932d2024-09-18 16:23:3910#include <iterator>
dcheng093de9b2016-04-04 21:25:5111#include <memory>
Will Cassellaa361c7e2022-05-02 23:48:0312#include <string>
dcheng093de9b2016-04-04 21:25:5113
Will Cassellaa361c7e2022-05-02 23:48:0314#include "base/memory/ref_counted.h"
15#include "base/memory/scoped_refptr.h"
Etienne Pierre-doray4c826fa2025-06-04 19:10:5816#include "base/trace_event/memory_usage_estimator.h"
Eric Secklerf6c544f2020-06-02 10:49:2117#include "base/tracing_buildflags.h"
Daniel Murphy2e5932d2024-09-18 16:23:3918#include "testing/gmock/include/gmock/gmock.h"
initial.commit09911bf2008-07-26 23:55:2919#include "testing/gtest/include/gtest/gtest.h"
20
dcheng093de9b2016-04-04 21:25:5121namespace base {
22
initial.commit09911bf2008-07-26 23:55:2923namespace {
24
Daniel Murphy2e5932d2024-09-18 16:23:3925using testing::_;
26using testing::Pair;
27
initial.commit09911bf2008-07-26 23:55:2928int cached_item_live_count = 0;
29
30struct CachedItem {
cfredric8fdc22432021-10-14 03:24:0031 CachedItem() : value(0) { cached_item_live_count++; }
initial.commit09911bf2008-07-26 23:55:2932
[email protected]4a3dab22009-11-11 17:36:5033 explicit CachedItem(int new_value) : value(new_value) {
initial.commit09911bf2008-07-26 23:55:2934 cached_item_live_count++;
35 }
36
Will Cassellaa361c7e2022-05-02 23:48:0337 CachedItem(const CachedItem& other) : value(other.value) {
initial.commit09911bf2008-07-26 23:55:2938 cached_item_live_count++;
39 }
40
cfredric8fdc22432021-10-14 03:24:0041 ~CachedItem() { cached_item_live_count--; }
initial.commit09911bf2008-07-26 23:55:2942
43 int value;
44};
45
46} // namespace
47
Will Cassellaa361c7e2022-05-02 23:48:0348template <typename LRUCacheTemplate>
49class LRUCacheTest : public testing::Test {};
50
51struct LRUCacheTemplate {
52 template <class Key, class Value, class KeyCompare = std::less<Key>>
53 using Type = base::LRUCache<Key, Value, KeyCompare>;
54};
55
56struct HashingLRUCacheTemplate {
57 template <class Key,
58 class Value,
59 class KeyHash = std::hash<Key>,
60 class KeyEqual = std::equal_to<Key>>
61 using Type = base::HashingLRUCache<Key, Value, KeyHash, KeyEqual>;
62};
63
64using LRUCacheTemplates =
65 testing::Types<LRUCacheTemplate, HashingLRUCacheTemplate>;
66TYPED_TEST_SUITE(LRUCacheTest, LRUCacheTemplates);
67
68template <typename LRUCacheSetTemplate>
69class LRUCacheSetTest : public testing::Test {};
70
71struct LRUCacheSetTemplate {
72 template <class Value, class Compare = std::less<Value>>
73 using Type = base::LRUCacheSet<Value, Compare>;
74};
75
76struct HashingLRUCacheSetTemplate {
77 template <class Value,
78 class Hash = std::hash<Value>,
79 class Equal = std::equal_to<Value>>
80 using Type = base::HashingLRUCacheSet<Value, Hash, Equal>;
81};
82
83using LRUCacheSetTemplates =
84 testing::Types<LRUCacheSetTemplate, HashingLRUCacheSetTemplate>;
85TYPED_TEST_SUITE(LRUCacheSetTest, LRUCacheSetTemplates);
86
87TYPED_TEST(LRUCacheTest, Basic) {
88 typedef typename TypeParam::template Type<int, CachedItem> Cache;
initial.commit09911bf2008-07-26 23:55:2989 Cache cache(Cache::NO_AUTO_EVICT);
90
91 // Check failure conditions
92 {
93 CachedItem test_item;
94 EXPECT_TRUE(cache.Get(0) == cache.end());
95 EXPECT_TRUE(cache.Peek(0) == cache.end());
96 }
97
98 static const int kItem1Key = 5;
99 CachedItem item1(10);
jdoerrie1c4b8ff2018-10-03 00:10:57100 auto inserted_item = cache.Put(kItem1Key, item1);
[email protected]6f2660e72008-12-08 14:44:44101 EXPECT_EQ(1U, cache.size());
initial.commit09911bf2008-07-26 23:55:29102
103 // Check that item1 was properly inserted.
104 {
jdoerrie1c4b8ff2018-10-03 00:10:57105 auto found = cache.Get(kItem1Key);
initial.commit09911bf2008-07-26 23:55:29106 EXPECT_TRUE(inserted_item == cache.begin());
107 EXPECT_TRUE(found != cache.end());
108
109 found = cache.Peek(kItem1Key);
110 EXPECT_TRUE(found != cache.end());
111
112 EXPECT_EQ(kItem1Key, found->first);
113 EXPECT_EQ(item1.value, found->second.value);
114 }
115
116 static const int kItem2Key = 7;
117 CachedItem item2(12);
118 cache.Put(kItem2Key, item2);
[email protected]6f2660e72008-12-08 14:44:44119 EXPECT_EQ(2U, cache.size());
initial.commit09911bf2008-07-26 23:55:29120
121 // Check that item1 is the oldest since item2 was added afterwards.
122 {
jdoerrie1c4b8ff2018-10-03 00:10:57123 auto oldest = cache.rbegin();
initial.commit09911bf2008-07-26 23:55:29124 ASSERT_TRUE(oldest != cache.rend());
125 EXPECT_EQ(kItem1Key, oldest->first);
126 EXPECT_EQ(item1.value, oldest->second.value);
127 }
128
129 // Check that item1 is still accessible by key.
130 {
jdoerrie1c4b8ff2018-10-03 00:10:57131 auto test_item = cache.Get(kItem1Key);
initial.commit09911bf2008-07-26 23:55:29132 ASSERT_TRUE(test_item != cache.end());
133 EXPECT_EQ(kItem1Key, test_item->first);
134 EXPECT_EQ(item1.value, test_item->second.value);
135 }
136
137 // Check that retrieving item1 pushed item2 to oldest.
138 {
jdoerrie1c4b8ff2018-10-03 00:10:57139 auto oldest = cache.rbegin();
initial.commit09911bf2008-07-26 23:55:29140 ASSERT_TRUE(oldest != cache.rend());
141 EXPECT_EQ(kItem2Key, oldest->first);
142 EXPECT_EQ(item2.value, oldest->second.value);
143 }
144
145 // Remove the oldest item and check that item1 is now the only member.
146 {
jdoerrie1c4b8ff2018-10-03 00:10:57147 auto next = cache.Erase(cache.rbegin());
initial.commit09911bf2008-07-26 23:55:29148
[email protected]6f2660e72008-12-08 14:44:44149 EXPECT_EQ(1U, cache.size());
initial.commit09911bf2008-07-26 23:55:29150
151 EXPECT_TRUE(next == cache.rbegin());
152 EXPECT_EQ(kItem1Key, next->first);
153 EXPECT_EQ(item1.value, next->second.value);
154
155 cache.Erase(cache.begin());
[email protected]6f2660e72008-12-08 14:44:44156 EXPECT_EQ(0U, cache.size());
initial.commit09911bf2008-07-26 23:55:29157 }
[email protected]eeb090c92009-10-02 20:40:56158
159 // Check that Clear() works properly.
160 cache.Put(kItem1Key, item1);
161 cache.Put(kItem2Key, item2);
162 EXPECT_EQ(2U, cache.size());
163 cache.Clear();
164 EXPECT_EQ(0U, cache.size());
initial.commit09911bf2008-07-26 23:55:29165}
166
Will Cassellaa361c7e2022-05-02 23:48:03167TYPED_TEST(LRUCacheTest, GetVsPeek) {
168 typedef typename TypeParam::template Type<int, CachedItem> Cache;
initial.commit09911bf2008-07-26 23:55:29169 Cache cache(Cache::NO_AUTO_EVICT);
170
171 static const int kItem1Key = 1;
172 CachedItem item1(10);
173 cache.Put(kItem1Key, item1);
174
175 static const int kItem2Key = 2;
176 CachedItem item2(20);
177 cache.Put(kItem2Key, item2);
178
179 // This should do nothing since the size is bigger than the number of items.
180 cache.ShrinkToSize(100);
181
182 // Check that item1 starts out as oldest
183 {
jdoerrie1c4b8ff2018-10-03 00:10:57184 auto iter = cache.rbegin();
initial.commit09911bf2008-07-26 23:55:29185 ASSERT_TRUE(iter != cache.rend());
186 EXPECT_EQ(kItem1Key, iter->first);
187 EXPECT_EQ(item1.value, iter->second.value);
188 }
189
190 // Check that Peek doesn't change ordering
191 {
jdoerrie1c4b8ff2018-10-03 00:10:57192 auto peekiter = cache.Peek(kItem1Key);
initial.commit09911bf2008-07-26 23:55:29193 ASSERT_TRUE(peekiter != cache.end());
194
jdoerrie1c4b8ff2018-10-03 00:10:57195 auto iter = cache.rbegin();
initial.commit09911bf2008-07-26 23:55:29196 ASSERT_TRUE(iter != cache.rend());
197 EXPECT_EQ(kItem1Key, iter->first);
198 EXPECT_EQ(item1.value, iter->second.value);
199 }
200}
201
Will Cassellaa361c7e2022-05-02 23:48:03202TYPED_TEST(LRUCacheTest, KeyReplacement) {
203 typedef typename TypeParam::template Type<int, CachedItem> Cache;
initial.commit09911bf2008-07-26 23:55:29204 Cache cache(Cache::NO_AUTO_EVICT);
205
206 static const int kItem1Key = 1;
207 CachedItem item1(10);
208 cache.Put(kItem1Key, item1);
209
210 static const int kItem2Key = 2;
211 CachedItem item2(20);
212 cache.Put(kItem2Key, item2);
213
214 static const int kItem3Key = 3;
215 CachedItem item3(30);
216 cache.Put(kItem3Key, item3);
217
218 static const int kItem4Key = 4;
219 CachedItem item4(40);
220 cache.Put(kItem4Key, item4);
221
222 CachedItem item5(50);
223 cache.Put(kItem3Key, item5);
224
[email protected]6f2660e72008-12-08 14:44:44225 EXPECT_EQ(4U, cache.size());
initial.commit09911bf2008-07-26 23:55:29226 for (int i = 0; i < 3; ++i) {
jdoerrie1c4b8ff2018-10-03 00:10:57227 auto iter = cache.rbegin();
initial.commit09911bf2008-07-26 23:55:29228 ASSERT_TRUE(iter != cache.rend());
229 }
230
231 // Make it so only the most important element is there.
232 cache.ShrinkToSize(1);
233
jdoerrie1c4b8ff2018-10-03 00:10:57234 auto iter = cache.begin();
initial.commit09911bf2008-07-26 23:55:29235 EXPECT_EQ(kItem3Key, iter->first);
236 EXPECT_EQ(item5.value, iter->second.value);
237}
238
Will Cassellaa361c7e2022-05-02 23:48:03239// Make sure that the cache release its pointers properly.
240TYPED_TEST(LRUCacheTest, Owning) {
241 using Cache =
242 typename TypeParam::template Type<int, std::unique_ptr<CachedItem>>;
initial.commit09911bf2008-07-26 23:55:29243 Cache cache(Cache::NO_AUTO_EVICT);
244
245 int initial_count = cached_item_live_count;
246
247 // First insert and item and then overwrite it.
248 static const int kItem1Key = 1;
David Benjamindd436292018-10-11 16:28:00249 cache.Put(kItem1Key, std::make_unique<CachedItem>(20));
250 cache.Put(kItem1Key, std::make_unique<CachedItem>(22));
initial.commit09911bf2008-07-26 23:55:29251
252 // There should still be one item, and one extra live item.
jdoerrie1c4b8ff2018-10-03 00:10:57253 auto iter = cache.Get(kItem1Key);
[email protected]6f2660e72008-12-08 14:44:44254 EXPECT_EQ(1U, cache.size());
initial.commit09911bf2008-07-26 23:55:29255 EXPECT_TRUE(iter != cache.end());
256 EXPECT_EQ(initial_count + 1, cached_item_live_count);
257
258 // Now remove it.
259 cache.Erase(cache.begin());
260 EXPECT_EQ(initial_count, cached_item_live_count);
261
262 // Now try another cache that goes out of scope to make sure its pointers
263 // go away.
264 {
265 Cache cache2(Cache::NO_AUTO_EVICT);
David Benjamindd436292018-10-11 16:28:00266 cache2.Put(1, std::make_unique<CachedItem>(20));
267 cache2.Put(2, std::make_unique<CachedItem>(20));
initial.commit09911bf2008-07-26 23:55:29268 }
269
270 // There should be no objects leaked.
271 EXPECT_EQ(initial_count, cached_item_live_count);
[email protected]eeb090c92009-10-02 20:40:56272
273 // Check that Clear() also frees things correctly.
274 {
275 Cache cache2(Cache::NO_AUTO_EVICT);
David Benjamindd436292018-10-11 16:28:00276 cache2.Put(1, std::make_unique<CachedItem>(20));
277 cache2.Put(2, std::make_unique<CachedItem>(20));
[email protected]eeb090c92009-10-02 20:40:56278 EXPECT_EQ(initial_count + 2, cached_item_live_count);
279 cache2.Clear();
280 EXPECT_EQ(initial_count, cached_item_live_count);
281 }
initial.commit09911bf2008-07-26 23:55:29282}
283
Will Cassellaa361c7e2022-05-02 23:48:03284TYPED_TEST(LRUCacheTest, AutoEvict) {
285 using Cache =
286 typename TypeParam::template Type<int, std::unique_ptr<CachedItem>>;
287 static const typename Cache::size_type kMaxSize = 3;
initial.commit09911bf2008-07-26 23:55:29288
289 int initial_count = cached_item_live_count;
290
291 {
292 Cache cache(kMaxSize);
293
294 static const int kItem1Key = 1, kItem2Key = 2, kItem3Key = 3, kItem4Key = 4;
Jeremy Roman9532f252017-08-16 23:27:24295 cache.Put(kItem1Key, std::make_unique<CachedItem>(20));
296 cache.Put(kItem2Key, std::make_unique<CachedItem>(21));
297 cache.Put(kItem3Key, std::make_unique<CachedItem>(22));
298 cache.Put(kItem4Key, std::make_unique<CachedItem>(23));
initial.commit09911bf2008-07-26 23:55:29299
300 // The cache should only have kMaxSize items in it even though we inserted
301 // more.
302 EXPECT_EQ(kMaxSize, cache.size());
303 }
304
305 // There should be no objects leaked.
306 EXPECT_EQ(initial_count, cached_item_live_count);
license.botbf09a502008-08-24 00:55:55307}
[email protected]5cf71c52011-08-09 23:30:44308
Will Cassellaa361c7e2022-05-02 23:48:03309TYPED_TEST(LRUCacheTest, HashingLRUCache) {
[email protected]5cf71c52011-08-09 23:30:44310 // Very simple test to make sure that the hashing cache works correctly.
Will Cassellaa361c7e2022-05-02 23:48:03311 typedef typename TypeParam::template Type<std::string, CachedItem> Cache;
[email protected]5cf71c52011-08-09 23:30:44312 Cache cache(Cache::NO_AUTO_EVICT);
313
314 CachedItem one(1);
315 cache.Put("First", one);
316
317 CachedItem two(2);
318 cache.Put("Second", two);
319
320 EXPECT_EQ(one.value, cache.Get("First")->second.value);
321 EXPECT_EQ(two.value, cache.Get("Second")->second.value);
322 cache.ShrinkToSize(1);
323 EXPECT_EQ(two.value, cache.Get("Second")->second.value);
[email protected]4d0f93c2011-09-29 04:43:54324 EXPECT_TRUE(cache.Get("First") == cache.end());
[email protected]5cf71c52011-08-09 23:30:44325}
rtenneti105685d2015-12-18 03:05:43326
Will Cassellaa361c7e2022-05-02 23:48:03327TYPED_TEST(LRUCacheTest, Swap) {
328 typedef typename TypeParam::template Type<int, CachedItem> Cache;
rtenneti105685d2015-12-18 03:05:43329 Cache cache1(Cache::NO_AUTO_EVICT);
330
331 // Insert two items into cache1.
332 static const int kItem1Key = 1;
333 CachedItem item1(2);
jdoerrie1c4b8ff2018-10-03 00:10:57334 auto inserted_item = cache1.Put(kItem1Key, item1);
rtenneti105685d2015-12-18 03:05:43335 EXPECT_EQ(1U, cache1.size());
336
337 static const int kItem2Key = 3;
338 CachedItem item2(4);
339 cache1.Put(kItem2Key, item2);
340 EXPECT_EQ(2U, cache1.size());
341
342 // Verify cache1's elements.
343 {
jdoerrie1c4b8ff2018-10-03 00:10:57344 auto iter = cache1.begin();
rtenneti105685d2015-12-18 03:05:43345 ASSERT_TRUE(iter != cache1.end());
346 EXPECT_EQ(kItem2Key, iter->first);
347 EXPECT_EQ(item2.value, iter->second.value);
348
349 ++iter;
350 ASSERT_TRUE(iter != cache1.end());
351 EXPECT_EQ(kItem1Key, iter->first);
352 EXPECT_EQ(item1.value, iter->second.value);
353 }
354
355 // Create another cache2.
356 Cache cache2(Cache::NO_AUTO_EVICT);
357
358 // Insert three items into cache2.
359 static const int kItem3Key = 5;
360 CachedItem item3(6);
361 inserted_item = cache2.Put(kItem3Key, item3);
362 EXPECT_EQ(1U, cache2.size());
363
364 static const int kItem4Key = 7;
365 CachedItem item4(8);
366 cache2.Put(kItem4Key, item4);
367 EXPECT_EQ(2U, cache2.size());
368
369 static const int kItem5Key = 9;
370 CachedItem item5(10);
371 cache2.Put(kItem5Key, item5);
372 EXPECT_EQ(3U, cache2.size());
373
374 // Verify cache2's elements.
375 {
jdoerrie1c4b8ff2018-10-03 00:10:57376 auto iter = cache2.begin();
rtenneti105685d2015-12-18 03:05:43377 ASSERT_TRUE(iter != cache2.end());
378 EXPECT_EQ(kItem5Key, iter->first);
379 EXPECT_EQ(item5.value, iter->second.value);
380
381 ++iter;
382 ASSERT_TRUE(iter != cache2.end());
383 EXPECT_EQ(kItem4Key, iter->first);
384 EXPECT_EQ(item4.value, iter->second.value);
385
386 ++iter;
387 ASSERT_TRUE(iter != cache2.end());
388 EXPECT_EQ(kItem3Key, iter->first);
389 EXPECT_EQ(item3.value, iter->second.value);
390 }
391
392 // Swap cache1 and cache2 and verify cache2 has cache1's elements and cache1
393 // has cache2's elements.
394 cache2.Swap(cache1);
395
396 EXPECT_EQ(3U, cache1.size());
397 EXPECT_EQ(2U, cache2.size());
398
399 // Verify cache1's elements.
400 {
jdoerrie1c4b8ff2018-10-03 00:10:57401 auto iter = cache1.begin();
rtenneti105685d2015-12-18 03:05:43402 ASSERT_TRUE(iter != cache1.end());
403 EXPECT_EQ(kItem5Key, iter->first);
404 EXPECT_EQ(item5.value, iter->second.value);
405
406 ++iter;
407 ASSERT_TRUE(iter != cache1.end());
408 EXPECT_EQ(kItem4Key, iter->first);
409 EXPECT_EQ(item4.value, iter->second.value);
410
411 ++iter;
412 ASSERT_TRUE(iter != cache1.end());
413 EXPECT_EQ(kItem3Key, iter->first);
414 EXPECT_EQ(item3.value, iter->second.value);
415 }
416
417 // Verify cache2's elements.
418 {
jdoerrie1c4b8ff2018-10-03 00:10:57419 auto iter = cache2.begin();
rtenneti105685d2015-12-18 03:05:43420 ASSERT_TRUE(iter != cache2.end());
421 EXPECT_EQ(kItem2Key, iter->first);
422 EXPECT_EQ(item2.value, iter->second.value);
423
424 ++iter;
425 ASSERT_TRUE(iter != cache2.end());
426 EXPECT_EQ(kItem1Key, iter->first);
427 EXPECT_EQ(item1.value, iter->second.value);
428 }
429}
dcheng093de9b2016-04-04 21:25:51430
Will Cassellaa361c7e2022-05-02 23:48:03431TYPED_TEST(LRUCacheSetTest, SetTest) {
432 typedef typename TypeParam::template Type<std::string> Cache;
433 Cache cache(Cache::NO_AUTO_EVICT);
434
435 cache.Put("Hello");
436 cache.Put("world");
437 cache.Put("foo");
438 cache.Put("bar");
439
440 // Insert a duplicate element
441 cache.Put("foo");
442
443 // Iterate from oldest to newest
444 auto r_iter = cache.rbegin();
445 EXPECT_EQ(*r_iter, "Hello");
446 ++r_iter;
447 EXPECT_EQ(*r_iter, "world");
448 ++r_iter;
449 EXPECT_EQ(*r_iter, "bar");
450 ++r_iter;
451 EXPECT_EQ(*r_iter, "foo");
452 ++r_iter;
453 EXPECT_EQ(r_iter, cache.rend());
454
455 // Iterate from newest to oldest
456 auto iter = cache.begin();
457 EXPECT_EQ(*iter, "foo");
458 ++iter;
459 EXPECT_EQ(*iter, "bar");
460 ++iter;
461 EXPECT_EQ(*iter, "world");
462 ++iter;
463 EXPECT_EQ(*iter, "Hello");
464 ++iter;
465 EXPECT_EQ(iter, cache.end());
466}
467
468// Generalized dereference function. For the base case, this is the identity
469// function.
470template <typename T>
471struct Deref {
472 using Target = T;
473 static const Target& deref(const T& x) { return x; }
474};
475
476// `RefCountedData` wraps a type in an interface that supports refcounting.
477// Deref this as the wrapped type.
478template <typename T>
479struct Deref<RefCountedData<T>> {
480 using Target = typename Deref<T>::Target;
481 static const Target& deref(const RefCountedData<T>& x) {
482 return Deref<T>::deref(x.data);
483 }
484};
485
486// `scoped_refptr` is a smart pointer that implements reference counting.
487// Deref this as the pointee.
488template <typename T>
489struct Deref<scoped_refptr<T>> {
490 using Target = typename Deref<T>::Target;
491 static const Target& deref(const scoped_refptr<T>& x) {
492 return Deref<T>::deref(*x);
493 }
494};
495
496// Implementation of a `std::less`-like type that dereferences the given values
497// before comparison.
498template <typename T>
499struct DerefCompare {
500 bool operator()(const T& lhs, const T& rhs) const {
501 return Deref<T>::deref(lhs) < Deref<T>::deref(rhs);
502 }
503};
504
505// Implementation of a `std::equal_to`-like type that dereferences the given
506// values before comparison.
507template <typename T>
508struct DerefEqual {
509 bool operator()(const T& lhs, const T& rhs) const {
510 return Deref<T>::deref(lhs) == Deref<T>::deref(rhs);
511 }
512};
513
514// Implementation of a `std::hash`-like type that dereferences the given value
515// before calculating the hash.
516template <typename T, template <class> typename HashT = std::hash>
517struct DerefHash {
518 size_t operator()(const T& x) const {
519 return HashT<typename Deref<T>::Target>()(Deref<T>::deref(x));
520 }
521};
522
523// This tests that upon replacing a duplicate element in the cache with `Put`,
524// the element's identity is replaced as well.
525TYPED_TEST(LRUCacheSetTest, ReplacementIdentity) {
526 using Item = RefCountedData<std::string>;
527 using Ptr = scoped_refptr<Item>;
528
529 // Helper to create the correct type of base::*LRUCacheSet, since they have
530 // different template arguments.
Sorin Jianuad4cc6232024-10-08 19:06:01531 constexpr auto kCreateCache = [] {
Will Cassellaa361c7e2022-05-02 23:48:03532 if constexpr (std::is_same_v<TypeParam, LRUCacheSetTemplate>) {
533 using Cache = typename TypeParam::template Type<Ptr, DerefCompare<Ptr>>;
534 return Cache(Cache::NO_AUTO_EVICT);
535 } else if constexpr (std::is_same_v<TypeParam,
536 HashingLRUCacheSetTemplate>) {
537 using Cache = typename TypeParam::template Type<Ptr, DerefHash<Ptr>,
538 DerefEqual<Ptr>>;
539 return Cache(Cache::NO_AUTO_EVICT);
540 } else {
541 static_assert(!sizeof(TypeParam),
542 "This test was only written to support "
543 "`LRUCacheSetTemplate` and `HashingLRUCacheSetTemplate`");
544 }
545 };
546
547 auto cache = kCreateCache();
548 cache.Put(MakeRefCounted<Item>("Hello"));
549 cache.Put(MakeRefCounted<Item>("world"));
550 cache.Put(MakeRefCounted<Item>("foo"));
551 cache.Put(MakeRefCounted<Item>("bar"));
552
553 // Insert a duplicate element
554 {
555 auto foo = MakeRefCounted<Item>("foo");
556 const auto* new_foo_addr = foo.get();
557 const auto* old_foo_addr = cache.Peek(foo)->get();
558 auto iter = cache.Put(std::move(foo));
559 EXPECT_EQ(iter->get(), new_foo_addr);
560 EXPECT_NE(iter->get(), old_foo_addr);
561 }
562
563 // Iterate from oldest to newest
564 auto r_iter = cache.rbegin();
565 EXPECT_EQ((*r_iter)->data, "Hello");
566 ++r_iter;
567 EXPECT_EQ((*r_iter)->data, "world");
568 ++r_iter;
569 EXPECT_EQ((*r_iter)->data, "bar");
570 ++r_iter;
571 EXPECT_EQ((*r_iter)->data, "foo");
572 ++r_iter;
573 EXPECT_EQ(r_iter, cache.rend());
574
575 // Iterate from newest to oldest
576 auto iter = cache.begin();
577 EXPECT_EQ((*iter)->data, "foo");
578 ++iter;
579 EXPECT_EQ((*iter)->data, "bar");
580 ++iter;
581 EXPECT_EQ((*iter)->data, "world");
582 ++iter;
583 EXPECT_EQ((*iter)->data, "Hello");
584 ++iter;
585 EXPECT_EQ(iter, cache.end());
586}
587
Will Cassellaa361c7e2022-05-02 23:48:03588TYPED_TEST(LRUCacheTest, EstimateMemory) {
589 typedef typename TypeParam::template Type<std::string, int> Cache;
590 Cache cache(10);
Denis Yaroshevskiyb900a2122018-01-29 17:57:13591
592 const std::string key(100u, 'a');
593 cache.Put(key, 1);
594
595 EXPECT_GT(trace_event::EstimateMemoryUsage(cache),
596 trace_event::EstimateMemoryUsage(key));
597}
Denis Yaroshevskiyb900a2122018-01-29 17:57:13598
Daniel Murphy2e5932d2024-09-18 16:23:39599TEST(LRUCacheIndexOrderTest, IndexIteration) {
600 using OrderedCache = LRUCache<int, CachedItem>;
601 using UnorderedCache = HashingLRUCache<int, CachedItem>;
602
603 OrderedCache ordered(OrderedCache::NO_AUTO_EVICT);
604 UnorderedCache unordered(UnorderedCache::NO_AUTO_EVICT);
605
606 // Add items in any order.
607 static const int kItem1Key = 1;
608 CachedItem item1(10);
609 ordered.Put(kItem1Key, item1);
610 unordered.Put(kItem1Key, item1);
611
612 static const int kItem3Key = 3;
613 CachedItem item3(30);
614 ordered.Put(kItem3Key, item3);
615 unordered.Put(kItem3Key, item3);
616
617 static const int kItem2Key = 2;
618 CachedItem item2(20);
619 ordered.Put(kItem2Key, item2);
620 unordered.Put(kItem2Key, item2);
621
622 static const int kItem4Key = 4;
623 CachedItem item4(40);
624 ordered.Put(kItem4Key, item4);
625 unordered.Put(kItem4Key, item4);
626
627 // Ordered should be ordered, and unordered should at least have all elements.
628 std::vector<int> ordered_keys;
629 std::ranges::transform(
630 ordered.index(), std::back_inserter(ordered_keys),
631 [](const auto& key_value_pair) -> int { return key_value_pair.first; });
632 EXPECT_THAT(ordered_keys,
633 testing::ElementsAre(kItem1Key, kItem2Key, kItem3Key, kItem4Key));
634
635 std::vector<int> unordered_keys;
636 std::ranges::transform(
637 unordered.index(), std::back_inserter(unordered_keys),
638 [](const auto& key_value_pair) -> int { return key_value_pair.first; });
639 EXPECT_THAT(unordered_keys, testing::UnorderedElementsAre(
640 kItem1Key, kItem2Key, kItem3Key, kItem4Key));
641}
642
Jan Keitel69411da2025-06-24 05:51:23643TEST(LRUCacheSimpleTest, TransparentLookup) {
644 LRUCache<std::string, int, std::less<>> cache(10);
645 cache.Put("some string", 4);
646 EXPECT_EQ(cache.Get(std::string_view("some string"))->second, 4);
647 EXPECT_EQ(cache.Peek(std::string_view("some string"))->second, 4);
648}
649
dcheng093de9b2016-04-04 21:25:51650} // namespace base