| Index: chrome/browser/android/thumbnail/lru_expiring_cache_unittest.cc
|
| diff --git a/chrome/browser/android/thumbnail/lru_expiring_cache_unittest.cc b/chrome/browser/android/thumbnail/lru_expiring_cache_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e735e46cf5aac2185517d2249199a4ed3a9137e1
|
| --- /dev/null
|
| +++ b/chrome/browser/android/thumbnail/lru_expiring_cache_unittest.cc
|
| @@ -0,0 +1,161 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include <algorithm>
|
| +#include <vector>
|
| +
|
| +#include "base/memory/ref_counted.h"
|
| +#include "chrome/browser/android/thumbnail/lru_expiring_cache.h"
|
| +#include "testing/gmock/include/gmock/gmock.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +#define MAX_CACHE_SIZE 5u
|
| +
|
| +namespace {
|
| +
|
| +class MockObject {
|
| + public:
|
| + MockObject() : value_(0) {}
|
| + explicit MockObject(int key) : value_((key * 127) % 13 + 23) {}
|
| + bool operator==(const MockObject& object) const {
|
| + return value_ == object.value_;
|
| + }
|
| + bool operator!=(const MockObject& object) const { return !(*this == object); }
|
| +
|
| + private:
|
| + int value_;
|
| +};
|
| +
|
| +const MockObject kInvalidValue;
|
| +
|
| +} // namespace
|
| +
|
| +typedef testing::Test ThumbnailCache_LRUExpiringCacheTest;
|
| +typedef LRUExpiringCache<int, MockObject> TestLRUExpiringCache;
|
| +
|
| +TEST_F(ThumbnailCache_LRUExpiringCacheTest, SimplePutAndGet) {
|
| + TestLRUExpiringCache cache(MAX_CACHE_SIZE);
|
| + EXPECT_EQ(cache.MaximumCacheSize(), MAX_CACHE_SIZE);
|
| + EXPECT_EQ(cache.size(), 0u);
|
| +
|
| + for (unsigned int i = 0; i < MAX_CACHE_SIZE; i++) {
|
| + cache.Put(i, MockObject(i));
|
| + }
|
| +
|
| + EXPECT_EQ(cache.size(), MAX_CACHE_SIZE);
|
| +
|
| + int next_key = MAX_CACHE_SIZE;
|
| +
|
| + // One cache entry should have been evicted.
|
| + cache.Put(next_key, MockObject(next_key));
|
| + EXPECT_EQ(cache.size(), MAX_CACHE_SIZE);
|
| +
|
| + size_t cached_count = 0;
|
| + for (unsigned int i = 0; i < MAX_CACHE_SIZE + 1; i++) {
|
| + if (cache.Get(i) != kInvalidValue) {
|
| + EXPECT_EQ(cache.Get(i), MockObject(i));
|
| + cached_count++;
|
| + }
|
| + }
|
| +
|
| + EXPECT_EQ(cached_count, MAX_CACHE_SIZE);
|
| +
|
| + // Test Contains()
|
| + cached_count = 0;
|
| + for (unsigned int i = 0; i < MAX_CACHE_SIZE + 1; i++) {
|
| + if (cache.Contains(i))
|
| + cached_count++;
|
| + }
|
| + EXPECT_EQ(cached_count, MAX_CACHE_SIZE);
|
| +
|
| + cache.Clear();
|
| + EXPECT_EQ(cache.size(), 0u);
|
| +
|
| + for (unsigned int i = 0; i < MAX_CACHE_SIZE + 1; i++) {
|
| + EXPECT_FALSE(cache.Contains(i));
|
| + EXPECT_EQ(cache.Get(i), kInvalidValue);
|
| + }
|
| +}
|
| +
|
| +// 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(ThumbnailCache_LRUExpiringCacheTest, EvictedEntry) {
|
| + TestLRUExpiringCache cache(MAX_CACHE_SIZE);
|
| + for (unsigned int i = 0; i < MAX_CACHE_SIZE; i++) {
|
| + cache.Put(i, MockObject(i));
|
| + }
|
| +
|
| + int next_key = MAX_CACHE_SIZE;
|
| + cache.Put(next_key, MockObject(next_key));
|
| + EXPECT_EQ(cache.size(), MAX_CACHE_SIZE);
|
| + EXPECT_EQ(cache.Get(next_key), MockObject(next_key));
|
| +
|
| + // The first inserted entry should have been evicted.
|
| + EXPECT_FALSE(cache.Contains(0));
|
| +
|
| + // The rest of the content should be present.
|
| + for (unsigned int i = 1; i < MAX_CACHE_SIZE; i++) {
|
| + EXPECT_TRUE(cache.Contains(i));
|
| + }
|
| +
|
| + next_key++;
|
| +
|
| + // The first candidate to be evicted is the head of the iterator.
|
| + int head_key = cache.begin()->first;
|
| + EXPECT_TRUE(cache.Contains(head_key));
|
| + cache.Put(next_key, MockObject(next_key));
|
| +
|
| + EXPECT_NE(cache.begin()->first, head_key);
|
| + EXPECT_FALSE(cache.Contains(head_key));
|
| +}
|
| +
|
| +TEST_F(ThumbnailCache_LRUExpiringCacheTest, RetainedEntry) {
|
| + TestLRUExpiringCache cache(MAX_CACHE_SIZE);
|
| + for (unsigned int i = 0; i < MAX_CACHE_SIZE; i++) {
|
| + cache.Put(i, MockObject(i));
|
| + }
|
| +
|
| + // Add (cache size - 1)-entries.
|
| + for (unsigned int i = 0; i < MAX_CACHE_SIZE; i++) {
|
| + EXPECT_TRUE(cache.Contains(i));
|
| + }
|
| +
|
| + for (unsigned int i = MAX_CACHE_SIZE; i < 2 * MAX_CACHE_SIZE - 1; i++) {
|
| + cache.Put(i, MockObject(i));
|
| + }
|
| +
|
| + EXPECT_EQ(cache.size(), MAX_CACHE_SIZE);
|
| +
|
| + for (unsigned int i = 0; i < MAX_CACHE_SIZE - 1; i++) {
|
| + EXPECT_FALSE(cache.Contains(i));
|
| + }
|
| +
|
| + // The only retained entry (from the first round of insertion) is the last to
|
| + // be inserted.
|
| + EXPECT_TRUE(cache.Contains(MAX_CACHE_SIZE - 1));
|
| +}
|
| +
|
| +// Test that the iterator order is the insertion order. The first element of
|
| +// the iterator is the oldest entry in the cache.
|
| +TEST_F(ThumbnailCache_LRUExpiringCacheTest, Iterator) {
|
| + TestLRUExpiringCache 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);
|
| + }
|
| + std::random_shuffle(test_keys.begin(), test_keys.end());
|
| +
|
| + for (unsigned int i = 0; i < MAX_CACHE_SIZE; i++) {
|
| + cache.Put(test_keys[i], MockObject(test_keys[i]));
|
| + }
|
| +
|
| + TestLRUExpiringCache::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++;
|
| + }
|
| +}
|
|
|