• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2011 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/containers/lru_cache.h"
6 
7 #include <algorithm>
8 #include <cstddef>
9 #include <iterator>
10 #include <memory>
11 #include <string>
12 
13 #include "base/memory/ref_counted.h"
14 #include "base/memory/scoped_refptr.h"
15 #include "base/tracing_buildflags.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 
19 #if BUILDFLAG(ENABLE_BASE_TRACING)
20 #include "base/trace_event/memory_usage_estimator.h"  // no-presubmit-check
21 #endif  // BUILDFLAG(ENABLE_BASE_TRACING)
22 
23 namespace base {
24 
25 namespace {
26 
27 using testing::_;
28 using testing::Pair;
29 
30 int cached_item_live_count = 0;
31 
32 struct CachedItem {
CachedItembase::__anon5919a8af0111::CachedItem33   CachedItem() : value(0) { cached_item_live_count++; }
34 
CachedItembase::__anon5919a8af0111::CachedItem35   explicit CachedItem(int new_value) : value(new_value) {
36     cached_item_live_count++;
37   }
38 
CachedItembase::__anon5919a8af0111::CachedItem39   CachedItem(const CachedItem& other) : value(other.value) {
40     cached_item_live_count++;
41   }
42 
~CachedItembase::__anon5919a8af0111::CachedItem43   ~CachedItem() { cached_item_live_count--; }
44 
45   int value;
46 };
47 
48 }  // namespace
49 
50 template <typename LRUCacheTemplate>
51 class LRUCacheTest : public testing::Test {};
52 
53 struct LRUCacheTemplate {
54   template <class Key, class Value, class KeyCompare = std::less<Key>>
55   using Type = base::LRUCache<Key, Value, KeyCompare>;
56 };
57 
58 struct HashingLRUCacheTemplate {
59   template <class Key,
60             class Value,
61             class KeyHash = std::hash<Key>,
62             class KeyEqual = std::equal_to<Key>>
63   using Type = base::HashingLRUCache<Key, Value, KeyHash, KeyEqual>;
64 };
65 
66 using LRUCacheTemplates =
67     testing::Types<LRUCacheTemplate, HashingLRUCacheTemplate>;
68 TYPED_TEST_SUITE(LRUCacheTest, LRUCacheTemplates);
69 
70 template <typename LRUCacheSetTemplate>
71 class LRUCacheSetTest : public testing::Test {};
72 
73 struct LRUCacheSetTemplate {
74   template <class Value, class Compare = std::less<Value>>
75   using Type = base::LRUCacheSet<Value, Compare>;
76 };
77 
78 struct HashingLRUCacheSetTemplate {
79   template <class Value,
80             class Hash = std::hash<Value>,
81             class Equal = std::equal_to<Value>>
82   using Type = base::HashingLRUCacheSet<Value, Hash, Equal>;
83 };
84 
85 using LRUCacheSetTemplates =
86     testing::Types<LRUCacheSetTemplate, HashingLRUCacheSetTemplate>;
87 TYPED_TEST_SUITE(LRUCacheSetTest, LRUCacheSetTemplates);
88 
TYPED_TEST(LRUCacheTest,Basic)89 TYPED_TEST(LRUCacheTest, Basic) {
90   typedef typename TypeParam::template Type<int, CachedItem> Cache;
91   Cache cache(Cache::NO_AUTO_EVICT);
92 
93   // Check failure conditions
94   {
95     CachedItem test_item;
96     EXPECT_TRUE(cache.Get(0) == cache.end());
97     EXPECT_TRUE(cache.Peek(0) == cache.end());
98   }
99 
100   static const int kItem1Key = 5;
101   CachedItem item1(10);
102   auto inserted_item = cache.Put(kItem1Key, item1);
103   EXPECT_EQ(1U, cache.size());
104 
105   // Check that item1 was properly inserted.
106   {
107     auto found = cache.Get(kItem1Key);
108     EXPECT_TRUE(inserted_item == cache.begin());
109     EXPECT_TRUE(found != cache.end());
110 
111     found = cache.Peek(kItem1Key);
112     EXPECT_TRUE(found != cache.end());
113 
114     EXPECT_EQ(kItem1Key, found->first);
115     EXPECT_EQ(item1.value, found->second.value);
116   }
117 
118   static const int kItem2Key = 7;
119   CachedItem item2(12);
120   cache.Put(kItem2Key, item2);
121   EXPECT_EQ(2U, cache.size());
122 
123   // Check that item1 is the oldest since item2 was added afterwards.
124   {
125     auto oldest = cache.rbegin();
126     ASSERT_TRUE(oldest != cache.rend());
127     EXPECT_EQ(kItem1Key, oldest->first);
128     EXPECT_EQ(item1.value, oldest->second.value);
129   }
130 
131   // Check that item1 is still accessible by key.
132   {
133     auto test_item = cache.Get(kItem1Key);
134     ASSERT_TRUE(test_item != cache.end());
135     EXPECT_EQ(kItem1Key, test_item->first);
136     EXPECT_EQ(item1.value, test_item->second.value);
137   }
138 
139   // Check that retrieving item1 pushed item2 to oldest.
140   {
141     auto oldest = cache.rbegin();
142     ASSERT_TRUE(oldest != cache.rend());
143     EXPECT_EQ(kItem2Key, oldest->first);
144     EXPECT_EQ(item2.value, oldest->second.value);
145   }
146 
147   // Remove the oldest item and check that item1 is now the only member.
148   {
149     auto next = cache.Erase(cache.rbegin());
150 
151     EXPECT_EQ(1U, cache.size());
152 
153     EXPECT_TRUE(next == cache.rbegin());
154     EXPECT_EQ(kItem1Key, next->first);
155     EXPECT_EQ(item1.value, next->second.value);
156 
157     cache.Erase(cache.begin());
158     EXPECT_EQ(0U, cache.size());
159   }
160 
161   // Check that Clear() works properly.
162   cache.Put(kItem1Key, item1);
163   cache.Put(kItem2Key, item2);
164   EXPECT_EQ(2U, cache.size());
165   cache.Clear();
166   EXPECT_EQ(0U, cache.size());
167 }
168 
TYPED_TEST(LRUCacheTest,GetVsPeek)169 TYPED_TEST(LRUCacheTest, GetVsPeek) {
170   typedef typename TypeParam::template Type<int, CachedItem> Cache;
171   Cache cache(Cache::NO_AUTO_EVICT);
172 
173   static const int kItem1Key = 1;
174   CachedItem item1(10);
175   cache.Put(kItem1Key, item1);
176 
177   static const int kItem2Key = 2;
178   CachedItem item2(20);
179   cache.Put(kItem2Key, item2);
180 
181   // This should do nothing since the size is bigger than the number of items.
182   cache.ShrinkToSize(100);
183 
184   // Check that item1 starts out as oldest
185   {
186     auto iter = cache.rbegin();
187     ASSERT_TRUE(iter != cache.rend());
188     EXPECT_EQ(kItem1Key, iter->first);
189     EXPECT_EQ(item1.value, iter->second.value);
190   }
191 
192   // Check that Peek doesn't change ordering
193   {
194     auto peekiter = cache.Peek(kItem1Key);
195     ASSERT_TRUE(peekiter != cache.end());
196 
197     auto iter = cache.rbegin();
198     ASSERT_TRUE(iter != cache.rend());
199     EXPECT_EQ(kItem1Key, iter->first);
200     EXPECT_EQ(item1.value, iter->second.value);
201   }
202 }
203 
TYPED_TEST(LRUCacheTest,KeyReplacement)204 TYPED_TEST(LRUCacheTest, KeyReplacement) {
205   typedef typename TypeParam::template Type<int, CachedItem> Cache;
206   Cache cache(Cache::NO_AUTO_EVICT);
207 
208   static const int kItem1Key = 1;
209   CachedItem item1(10);
210   cache.Put(kItem1Key, item1);
211 
212   static const int kItem2Key = 2;
213   CachedItem item2(20);
214   cache.Put(kItem2Key, item2);
215 
216   static const int kItem3Key = 3;
217   CachedItem item3(30);
218   cache.Put(kItem3Key, item3);
219 
220   static const int kItem4Key = 4;
221   CachedItem item4(40);
222   cache.Put(kItem4Key, item4);
223 
224   CachedItem item5(50);
225   cache.Put(kItem3Key, item5);
226 
227   EXPECT_EQ(4U, cache.size());
228   for (int i = 0; i < 3; ++i) {
229     auto iter = cache.rbegin();
230     ASSERT_TRUE(iter != cache.rend());
231   }
232 
233   // Make it so only the most important element is there.
234   cache.ShrinkToSize(1);
235 
236   auto iter = cache.begin();
237   EXPECT_EQ(kItem3Key, iter->first);
238   EXPECT_EQ(item5.value, iter->second.value);
239 }
240 
241 // Make sure that the cache release its pointers properly.
TYPED_TEST(LRUCacheTest,Owning)242 TYPED_TEST(LRUCacheTest, Owning) {
243   using Cache =
244       typename TypeParam::template Type<int, std::unique_ptr<CachedItem>>;
245   Cache cache(Cache::NO_AUTO_EVICT);
246 
247   int initial_count = cached_item_live_count;
248 
249   // First insert and item and then overwrite it.
250   static const int kItem1Key = 1;
251   cache.Put(kItem1Key, std::make_unique<CachedItem>(20));
252   cache.Put(kItem1Key, std::make_unique<CachedItem>(22));
253 
254   // There should still be one item, and one extra live item.
255   auto iter = cache.Get(kItem1Key);
256   EXPECT_EQ(1U, cache.size());
257   EXPECT_TRUE(iter != cache.end());
258   EXPECT_EQ(initial_count + 1, cached_item_live_count);
259 
260   // Now remove it.
261   cache.Erase(cache.begin());
262   EXPECT_EQ(initial_count, cached_item_live_count);
263 
264   // Now try another cache that goes out of scope to make sure its pointers
265   // go away.
266   {
267     Cache cache2(Cache::NO_AUTO_EVICT);
268     cache2.Put(1, std::make_unique<CachedItem>(20));
269     cache2.Put(2, std::make_unique<CachedItem>(20));
270   }
271 
272   // There should be no objects leaked.
273   EXPECT_EQ(initial_count, cached_item_live_count);
274 
275   // Check that Clear() also frees things correctly.
276   {
277     Cache cache2(Cache::NO_AUTO_EVICT);
278     cache2.Put(1, std::make_unique<CachedItem>(20));
279     cache2.Put(2, std::make_unique<CachedItem>(20));
280     EXPECT_EQ(initial_count + 2, cached_item_live_count);
281     cache2.Clear();
282     EXPECT_EQ(initial_count, cached_item_live_count);
283   }
284 }
285 
TYPED_TEST(LRUCacheTest,AutoEvict)286 TYPED_TEST(LRUCacheTest, AutoEvict) {
287   using Cache =
288       typename TypeParam::template Type<int, std::unique_ptr<CachedItem>>;
289   static const typename Cache::size_type kMaxSize = 3;
290 
291   int initial_count = cached_item_live_count;
292 
293   {
294     Cache cache(kMaxSize);
295 
296     static const int kItem1Key = 1, kItem2Key = 2, kItem3Key = 3, kItem4Key = 4;
297     cache.Put(kItem1Key, std::make_unique<CachedItem>(20));
298     cache.Put(kItem2Key, std::make_unique<CachedItem>(21));
299     cache.Put(kItem3Key, std::make_unique<CachedItem>(22));
300     cache.Put(kItem4Key, std::make_unique<CachedItem>(23));
301 
302     // The cache should only have kMaxSize items in it even though we inserted
303     // more.
304     EXPECT_EQ(kMaxSize, cache.size());
305   }
306 
307   // There should be no objects leaked.
308   EXPECT_EQ(initial_count, cached_item_live_count);
309 }
310 
TYPED_TEST(LRUCacheTest,HashingLRUCache)311 TYPED_TEST(LRUCacheTest, HashingLRUCache) {
312   // Very simple test to make sure that the hashing cache works correctly.
313   typedef typename TypeParam::template Type<std::string, CachedItem> Cache;
314   Cache cache(Cache::NO_AUTO_EVICT);
315 
316   CachedItem one(1);
317   cache.Put("First", one);
318 
319   CachedItem two(2);
320   cache.Put("Second", two);
321 
322   EXPECT_EQ(one.value, cache.Get("First")->second.value);
323   EXPECT_EQ(two.value, cache.Get("Second")->second.value);
324   cache.ShrinkToSize(1);
325   EXPECT_EQ(two.value, cache.Get("Second")->second.value);
326   EXPECT_TRUE(cache.Get("First") == cache.end());
327 }
328 
TYPED_TEST(LRUCacheTest,Swap)329 TYPED_TEST(LRUCacheTest, Swap) {
330   typedef typename TypeParam::template Type<int, CachedItem> Cache;
331   Cache cache1(Cache::NO_AUTO_EVICT);
332 
333   // Insert two items into cache1.
334   static const int kItem1Key = 1;
335   CachedItem item1(2);
336   auto inserted_item = cache1.Put(kItem1Key, item1);
337   EXPECT_EQ(1U, cache1.size());
338 
339   static const int kItem2Key = 3;
340   CachedItem item2(4);
341   cache1.Put(kItem2Key, item2);
342   EXPECT_EQ(2U, cache1.size());
343 
344   // Verify cache1's elements.
345   {
346     auto iter = cache1.begin();
347     ASSERT_TRUE(iter != cache1.end());
348     EXPECT_EQ(kItem2Key, iter->first);
349     EXPECT_EQ(item2.value, iter->second.value);
350 
351     ++iter;
352     ASSERT_TRUE(iter != cache1.end());
353     EXPECT_EQ(kItem1Key, iter->first);
354     EXPECT_EQ(item1.value, iter->second.value);
355   }
356 
357   // Create another cache2.
358   Cache cache2(Cache::NO_AUTO_EVICT);
359 
360   // Insert three items into cache2.
361   static const int kItem3Key = 5;
362   CachedItem item3(6);
363   inserted_item = cache2.Put(kItem3Key, item3);
364   EXPECT_EQ(1U, cache2.size());
365 
366   static const int kItem4Key = 7;
367   CachedItem item4(8);
368   cache2.Put(kItem4Key, item4);
369   EXPECT_EQ(2U, cache2.size());
370 
371   static const int kItem5Key = 9;
372   CachedItem item5(10);
373   cache2.Put(kItem5Key, item5);
374   EXPECT_EQ(3U, cache2.size());
375 
376   // Verify cache2's elements.
377   {
378     auto iter = cache2.begin();
379     ASSERT_TRUE(iter != cache2.end());
380     EXPECT_EQ(kItem5Key, iter->first);
381     EXPECT_EQ(item5.value, iter->second.value);
382 
383     ++iter;
384     ASSERT_TRUE(iter != cache2.end());
385     EXPECT_EQ(kItem4Key, iter->first);
386     EXPECT_EQ(item4.value, iter->second.value);
387 
388     ++iter;
389     ASSERT_TRUE(iter != cache2.end());
390     EXPECT_EQ(kItem3Key, iter->first);
391     EXPECT_EQ(item3.value, iter->second.value);
392   }
393 
394   // Swap cache1 and cache2 and verify cache2 has cache1's elements and cache1
395   // has cache2's elements.
396   cache2.Swap(cache1);
397 
398   EXPECT_EQ(3U, cache1.size());
399   EXPECT_EQ(2U, cache2.size());
400 
401   // Verify cache1's elements.
402   {
403     auto iter = cache1.begin();
404     ASSERT_TRUE(iter != cache1.end());
405     EXPECT_EQ(kItem5Key, iter->first);
406     EXPECT_EQ(item5.value, iter->second.value);
407 
408     ++iter;
409     ASSERT_TRUE(iter != cache1.end());
410     EXPECT_EQ(kItem4Key, iter->first);
411     EXPECT_EQ(item4.value, iter->second.value);
412 
413     ++iter;
414     ASSERT_TRUE(iter != cache1.end());
415     EXPECT_EQ(kItem3Key, iter->first);
416     EXPECT_EQ(item3.value, iter->second.value);
417   }
418 
419   // Verify cache2's elements.
420   {
421     auto iter = cache2.begin();
422     ASSERT_TRUE(iter != cache2.end());
423     EXPECT_EQ(kItem2Key, iter->first);
424     EXPECT_EQ(item2.value, iter->second.value);
425 
426     ++iter;
427     ASSERT_TRUE(iter != cache2.end());
428     EXPECT_EQ(kItem1Key, iter->first);
429     EXPECT_EQ(item1.value, iter->second.value);
430   }
431 }
432 
TYPED_TEST(LRUCacheSetTest,SetTest)433 TYPED_TEST(LRUCacheSetTest, SetTest) {
434   typedef typename TypeParam::template Type<std::string> Cache;
435   Cache cache(Cache::NO_AUTO_EVICT);
436 
437   cache.Put("Hello");
438   cache.Put("world");
439   cache.Put("foo");
440   cache.Put("bar");
441 
442   // Insert a duplicate element
443   cache.Put("foo");
444 
445   // Iterate from oldest to newest
446   auto r_iter = cache.rbegin();
447   EXPECT_EQ(*r_iter, "Hello");
448   ++r_iter;
449   EXPECT_EQ(*r_iter, "world");
450   ++r_iter;
451   EXPECT_EQ(*r_iter, "bar");
452   ++r_iter;
453   EXPECT_EQ(*r_iter, "foo");
454   ++r_iter;
455   EXPECT_EQ(r_iter, cache.rend());
456 
457   // Iterate from newest to oldest
458   auto iter = cache.begin();
459   EXPECT_EQ(*iter, "foo");
460   ++iter;
461   EXPECT_EQ(*iter, "bar");
462   ++iter;
463   EXPECT_EQ(*iter, "world");
464   ++iter;
465   EXPECT_EQ(*iter, "Hello");
466   ++iter;
467   EXPECT_EQ(iter, cache.end());
468 }
469 
470 // Generalized dereference function. For the base case, this is the identity
471 // function.
472 template <typename T>
473 struct Deref {
474   using Target = T;
derefbase::Deref475   static const Target& deref(const T& x) { return x; }
476 };
477 
478 // `RefCountedData` wraps a type in an interface that supports refcounting.
479 // Deref this as the wrapped type.
480 template <typename T>
481 struct Deref<RefCountedData<T>> {
482   using Target = typename Deref<T>::Target;
derefbase::Deref483   static const Target& deref(const RefCountedData<T>& x) {
484     return Deref<T>::deref(x.data);
485   }
486 };
487 
488 // `scoped_refptr` is a smart pointer that implements reference counting.
489 // Deref this as the pointee.
490 template <typename T>
491 struct Deref<scoped_refptr<T>> {
492   using Target = typename Deref<T>::Target;
derefbase::Deref493   static const Target& deref(const scoped_refptr<T>& x) {
494     return Deref<T>::deref(*x);
495   }
496 };
497 
498 // Implementation of a `std::less`-like type that dereferences the given values
499 // before comparison.
500 template <typename T>
501 struct DerefCompare {
operator ()base::DerefCompare502   bool operator()(const T& lhs, const T& rhs) const {
503     return Deref<T>::deref(lhs) < Deref<T>::deref(rhs);
504   }
505 };
506 
507 // Implementation of a `std::equal_to`-like type that dereferences the given
508 // values before comparison.
509 template <typename T>
510 struct DerefEqual {
operator ()base::DerefEqual511   bool operator()(const T& lhs, const T& rhs) const {
512     return Deref<T>::deref(lhs) == Deref<T>::deref(rhs);
513   }
514 };
515 
516 // Implementation of a `std::hash`-like type that dereferences the given value
517 // before calculating the hash.
518 template <typename T, template <class> typename HashT = std::hash>
519 struct DerefHash {
operator ()base::DerefHash520   size_t operator()(const T& x) const {
521     return HashT<typename Deref<T>::Target>()(Deref<T>::deref(x));
522   }
523 };
524 
525 // This tests that upon replacing a duplicate element in the cache with `Put`,
526 // the element's identity is replaced as well.
TYPED_TEST(LRUCacheSetTest,ReplacementIdentity)527 TYPED_TEST(LRUCacheSetTest, ReplacementIdentity) {
528   using Item = RefCountedData<std::string>;
529   using Ptr = scoped_refptr<Item>;
530 
531   // Helper to create the correct type of base::*LRUCacheSet, since they have
532   // different template arguments.
533   constexpr auto kCreateCache = [] {
534     if constexpr (std::is_same_v<TypeParam, LRUCacheSetTemplate>) {
535       using Cache = typename TypeParam::template Type<Ptr, DerefCompare<Ptr>>;
536       return Cache(Cache::NO_AUTO_EVICT);
537     } else if constexpr (std::is_same_v<TypeParam,
538                                         HashingLRUCacheSetTemplate>) {
539       using Cache = typename TypeParam::template Type<Ptr, DerefHash<Ptr>,
540                                                       DerefEqual<Ptr>>;
541       return Cache(Cache::NO_AUTO_EVICT);
542     } else {
543       static_assert(!sizeof(TypeParam),
544                     "This test was only written to support "
545                     "`LRUCacheSetTemplate` and `HashingLRUCacheSetTemplate`");
546     }
547   };
548 
549   auto cache = kCreateCache();
550   cache.Put(MakeRefCounted<Item>("Hello"));
551   cache.Put(MakeRefCounted<Item>("world"));
552   cache.Put(MakeRefCounted<Item>("foo"));
553   cache.Put(MakeRefCounted<Item>("bar"));
554 
555   // Insert a duplicate element
556   {
557     auto foo = MakeRefCounted<Item>("foo");
558     const auto* new_foo_addr = foo.get();
559     const auto* old_foo_addr = cache.Peek(foo)->get();
560     auto iter = cache.Put(std::move(foo));
561     EXPECT_EQ(iter->get(), new_foo_addr);
562     EXPECT_NE(iter->get(), old_foo_addr);
563   }
564 
565   // Iterate from oldest to newest
566   auto r_iter = cache.rbegin();
567   EXPECT_EQ((*r_iter)->data, "Hello");
568   ++r_iter;
569   EXPECT_EQ((*r_iter)->data, "world");
570   ++r_iter;
571   EXPECT_EQ((*r_iter)->data, "bar");
572   ++r_iter;
573   EXPECT_EQ((*r_iter)->data, "foo");
574   ++r_iter;
575   EXPECT_EQ(r_iter, cache.rend());
576 
577   // Iterate from newest to oldest
578   auto iter = cache.begin();
579   EXPECT_EQ((*iter)->data, "foo");
580   ++iter;
581   EXPECT_EQ((*iter)->data, "bar");
582   ++iter;
583   EXPECT_EQ((*iter)->data, "world");
584   ++iter;
585   EXPECT_EQ((*iter)->data, "Hello");
586   ++iter;
587   EXPECT_EQ(iter, cache.end());
588 }
589 
590 #if BUILDFLAG(ENABLE_BASE_TRACING)
TYPED_TEST(LRUCacheTest,EstimateMemory)591 TYPED_TEST(LRUCacheTest, EstimateMemory) {
592   typedef typename TypeParam::template Type<std::string, int> Cache;
593   Cache cache(10);
594 
595   const std::string key(100u, 'a');
596   cache.Put(key, 1);
597 
598   EXPECT_GT(trace_event::EstimateMemoryUsage(cache),
599             trace_event::EstimateMemoryUsage(key));
600 }
601 #endif  // BUILDFLAG(ENABLE_BASE_TRACING)
602 
TEST(LRUCacheIndexOrderTest,IndexIteration)603 TEST(LRUCacheIndexOrderTest, IndexIteration) {
604   using OrderedCache = LRUCache<int, CachedItem>;
605   using UnorderedCache = HashingLRUCache<int, CachedItem>;
606 
607   OrderedCache ordered(OrderedCache::NO_AUTO_EVICT);
608   UnorderedCache unordered(UnorderedCache::NO_AUTO_EVICT);
609 
610   // Add items in any order.
611   static const int kItem1Key = 1;
612   CachedItem item1(10);
613   ordered.Put(kItem1Key, item1);
614   unordered.Put(kItem1Key, item1);
615 
616   static const int kItem3Key = 3;
617   CachedItem item3(30);
618   ordered.Put(kItem3Key, item3);
619   unordered.Put(kItem3Key, item3);
620 
621   static const int kItem2Key = 2;
622   CachedItem item2(20);
623   ordered.Put(kItem2Key, item2);
624   unordered.Put(kItem2Key, item2);
625 
626   static const int kItem4Key = 4;
627   CachedItem item4(40);
628   ordered.Put(kItem4Key, item4);
629   unordered.Put(kItem4Key, item4);
630 
631   // Ordered should be ordered, and unordered should at least have all elements.
632   std::vector<int> ordered_keys;
633   std::ranges::transform(
634       ordered.index(), std::back_inserter(ordered_keys),
635       [](const auto& key_value_pair) -> int { return key_value_pair.first; });
636   EXPECT_THAT(ordered_keys,
637               testing::ElementsAre(kItem1Key, kItem2Key, kItem3Key, kItem4Key));
638 
639   std::vector<int> unordered_keys;
640   std::ranges::transform(
641       unordered.index(), std::back_inserter(unordered_keys),
642       [](const auto& key_value_pair) -> int { return key_value_pair.first; });
643   EXPECT_THAT(unordered_keys, testing::UnorderedElementsAre(
644                                   kItem1Key, kItem2Key, kItem3Key, kItem4Key));
645 }
646 
647 }  // namespace base
648