1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
|
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/thumbnail/cc/scoped_ptr_expiring_cache.h"
#include <stddef.h>
#include <algorithm>
#include <vector>
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/rand_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#define MAX_CACHE_SIZE 5u
namespace thumbnail {
namespace {
unsigned int GenerateValue(unsigned int key) {
return (key * key * 127) % 133 + key * 23;
}
class MockObject {
public:
static std::unique_ptr<MockObject> Create(unsigned int key) {
return base::WrapUnique(new MockObject(key));
}
MockObject(const MockObject&) = delete;
MockObject& operator=(const MockObject&) = delete;
unsigned int value() const { return value_; }
private:
explicit MockObject(unsigned int key) : value_(GenerateValue(key)) {}
unsigned int value_;
};
} // namespace
typedef testing::Test ScopedPtrExpiringCacheTest;
typedef ScopedPtrExpiringCache<unsigned int, MockObject>
TestScopedPtrExpiringCache;
TEST_F(ScopedPtrExpiringCacheTest, SimplePutAndGet) {
TestScopedPtrExpiringCache cache(MAX_CACHE_SIZE);
EXPECT_EQ(MAX_CACHE_SIZE, cache.MaximumCacheSize());
EXPECT_EQ(0u, cache.size());
for (unsigned int i = 0; i < MAX_CACHE_SIZE; i++) {
cache.Put(i, MockObject::Create(i));
}
EXPECT_EQ(MAX_CACHE_SIZE, cache.size());
unsigned int next_key = MAX_CACHE_SIZE;
// One cache entry should have been evicted.
cache.Put(next_key, MockObject::Create(next_key));
EXPECT_EQ(MAX_CACHE_SIZE, cache.size());
size_t cached_count = 0;
for (unsigned int i = 0; i < MAX_CACHE_SIZE + 1; i++) {
if (cache.Get(i)) {
EXPECT_EQ(GenerateValue(i), cache.Get(i)->value());
cached_count++;
}
}
EXPECT_EQ(MAX_CACHE_SIZE, cached_count);
// Test Get as membership test.
cached_count = 0;
for (unsigned int i = 0; i < MAX_CACHE_SIZE + 1; i++) {
if (cache.Get(i)) {
cached_count++;
}
}
EXPECT_EQ(MAX_CACHE_SIZE, cached_count);
cache.Clear();
EXPECT_EQ(0u, cache.size());
for (unsigned int i = 0; i < MAX_CACHE_SIZE + 1; i++) {
EXPECT_EQ(nullptr, cache.Get(i));
}
}
// The eviction policy is least-recently-used, where we define used as insertion
// into the cache. We test that the first to be evicted is the first entry
// inserted into the cache.
TEST_F(ScopedPtrExpiringCacheTest, EvictedEntry) {
TestScopedPtrExpiringCache cache(MAX_CACHE_SIZE);
for (unsigned int i = 0; i < MAX_CACHE_SIZE; i++) {
cache.Put(i, MockObject::Create(i));
}
unsigned int next_key = MAX_CACHE_SIZE;
cache.Put(next_key, MockObject::Create(next_key));
EXPECT_EQ(MAX_CACHE_SIZE, cache.size());
EXPECT_EQ(GenerateValue(next_key), cache.Get(next_key)->value());
// The first inserted entry should have been evicted.
EXPECT_EQ(nullptr, cache.Get(0));
// The rest of the content should be present.
for (unsigned int i = 1; i < MAX_CACHE_SIZE; i++) {
EXPECT_TRUE(cache.Get(i) != nullptr);
}
next_key++;
// The first candidate to be evicted is the head of the iterator.
unsigned int head_key = cache.begin()->first;
EXPECT_TRUE(cache.Get(head_key) != nullptr);
cache.Put(next_key, MockObject::Create(next_key));
EXPECT_NE(cache.begin()->first, head_key);
EXPECT_EQ(nullptr, cache.Get(head_key));
}
TEST_F(ScopedPtrExpiringCacheTest, RetainedEntry) {
TestScopedPtrExpiringCache cache(MAX_CACHE_SIZE);
for (unsigned int i = 0; i < MAX_CACHE_SIZE; i++) {
cache.Put(i, MockObject::Create(i));
}
// Add (cache size - 1)-entries.
for (unsigned int i = 0; i < MAX_CACHE_SIZE; i++) {
EXPECT_TRUE(cache.Get(i) != nullptr);
}
for (unsigned int i = MAX_CACHE_SIZE; i < 2 * MAX_CACHE_SIZE - 1; i++) {
cache.Put(i, MockObject::Create(i));
}
EXPECT_EQ(MAX_CACHE_SIZE, cache.size());
for (unsigned int i = 0; i < MAX_CACHE_SIZE - 1; i++) {
EXPECT_EQ(nullptr, cache.Get(i));
}
// The only retained entry (from the first round of insertion) is the last to
// be inserted.
EXPECT_TRUE(cache.Get(MAX_CACHE_SIZE - 1) != nullptr);
}
// Test that the iterator order is the insertion order. The first element of
// the iterator is the oldest entry in the cache.
TEST_F(ScopedPtrExpiringCacheTest, Iterator) {
TestScopedPtrExpiringCache cache(MAX_CACHE_SIZE);
std::vector<unsigned int> test_keys;
for (unsigned int i = 0; i < MAX_CACHE_SIZE; i++) {
test_keys.push_back(i);
}
base::RandomShuffle(test_keys.begin(), test_keys.end());
for (unsigned int i = 0; i < MAX_CACHE_SIZE; i++) {
cache.Put(test_keys[i], MockObject::Create(test_keys[i]));
}
TestScopedPtrExpiringCache::iterator cache_iter = cache.begin();
std::vector<unsigned int>::iterator key_iter = test_keys.begin();
while (cache_iter != cache.end() && key_iter != test_keys.end()) {
EXPECT_EQ(cache_iter->first, *key_iter);
cache_iter++;
key_iter++;
}
}
} // namespace thumbnail
|