1/* 2 * Copyright (c) 2013, Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31#include "config.h" 32#include "core/loader/cache/MemoryCache.h" 33 34#include "core/loader/cache/MockImageResourceClient.h" 35#include "core/loader/cache/RawResource.h" 36#include "core/loader/cache/ResourcePtr.h" 37#include "core/platform/network/ResourceRequest.h" 38#include "wtf/OwnPtr.h" 39 40#include <gtest/gtest.h> 41 42namespace WebCore { 43 44class MemoryCacheTest : public ::testing::Test { 45public: 46 class MockImageResource : public WebCore::Resource { 47 public: 48 MockImageResource(const ResourceRequest& request, Type type) 49 : Resource(request, type) 50 { 51 } 52 53 virtual void appendData(const char* data, int len) 54 { 55 Resource::appendData(data, len); 56 setDecodedSize(this->size()); 57 } 58 59 virtual void destroyDecodedData() 60 { 61 setDecodedSize(0); 62 } 63 }; 64 65protected: 66 virtual void SetUp() 67 { 68 // Save the global memory cache to restore it upon teardown. 69 m_globalMemoryCache = adoptPtr(memoryCache()); 70 // Create the test memory cache instance and hook it in. 71 m_testingMemoryCache = adoptPtr(new MemoryCache()); 72 setMemoryCacheForTesting(m_testingMemoryCache.leakPtr()); 73 } 74 75 virtual void TearDown() 76 { 77 // Regain the ownership of testing memory cache, so that it will be 78 // destroyed. 79 m_testingMemoryCache = adoptPtr(memoryCache()); 80 // Yield the ownership of the global memory cache back. 81 setMemoryCacheForTesting(m_globalMemoryCache.leakPtr()); 82 } 83 84 OwnPtr<MemoryCache> m_testingMemoryCache; 85 OwnPtr<MemoryCache> m_globalMemoryCache; 86}; 87 88// Verifies that setters and getters for cache capacities work correcty. 89TEST_F(MemoryCacheTest, CapacityAccounting) 90{ 91 const unsigned totalCapacity = 100; 92 const unsigned minDeadCapacity = 10; 93 const unsigned maxDeadCapacity = 50; 94 memoryCache()->setCapacities(minDeadCapacity, maxDeadCapacity, totalCapacity); 95 96 ASSERT_EQ(totalCapacity, memoryCache()->capacity()); 97 ASSERT_EQ(minDeadCapacity, memoryCache()->minDeadCapacity()); 98 ASSERT_EQ(maxDeadCapacity, memoryCache()->maxDeadCapacity()); 99} 100 101// Verifies that dead resources that exceed dead resource capacity are evicted 102// from cache when pruning. 103TEST_F(MemoryCacheTest, DeadResourceEviction) 104{ 105 const unsigned totalCapacity = 1000000; 106 const unsigned minDeadCapacity = 0; 107 const unsigned maxDeadCapacity = 0; 108 memoryCache()->setCapacities(minDeadCapacity, maxDeadCapacity, totalCapacity); 109 110 ResourcePtr<Resource> cachedResource = 111 new Resource(ResourceRequest(""), Resource::Raw); 112 const char data[5] = "abcd"; 113 cachedResource->appendData(data, 3); 114 // The resource size has to be nonzero for this test to be meaningful, but 115 // we do not rely on it having any particular value. 116 ASSERT_GT(cachedResource->size(), 0u); 117 118 ASSERT_EQ(0u, memoryCache()->deadSize()); 119 ASSERT_EQ(0u, memoryCache()->liveSize()); 120 121 memoryCache()->add(cachedResource.get()); 122 ASSERT_EQ(cachedResource->size(), memoryCache()->deadSize()); 123 ASSERT_EQ(0u, memoryCache()->liveSize()); 124 125 memoryCache()->prune(); 126 ASSERT_EQ(0u, memoryCache()->deadSize()); 127 ASSERT_EQ(0u, memoryCache()->liveSize()); 128} 129 130// Verifies that CachedResources are evicted from the decode cache 131// according to their DecodeCachePriority. 132TEST_F(MemoryCacheTest, DecodeCacheOrder) 133{ 134 memoryCache()->setDelayBeforeLiveDecodedPrune(0); 135 ResourcePtr<MockImageResource> cachedImageLowPriority = 136 new MockImageResource(ResourceRequest(""), Resource::Raw); 137 ResourcePtr<MockImageResource> cachedImageHighPriority = 138 new MockImageResource(ResourceRequest(""), Resource::Raw); 139 140 MockImageResourceClient clientLowPriority; 141 MockImageResourceClient clientHighPriority; 142 cachedImageLowPriority->addClient(&clientLowPriority); 143 cachedImageHighPriority->addClient(&clientHighPriority); 144 145 const char data[5] = "abcd"; 146 cachedImageLowPriority->appendData(data, 1); 147 cachedImageHighPriority->appendData(data, 4); 148 const unsigned lowPrioritySize = cachedImageLowPriority->size(); 149 const unsigned highPrioritySize = cachedImageHighPriority->size(); 150 const unsigned lowPriorityMockDecodeSize = cachedImageLowPriority->decodedSize(); 151 const unsigned highPriorityMockDecodeSize = cachedImageHighPriority->decodedSize(); 152 const unsigned totalSize = lowPrioritySize + highPrioritySize; 153 154 // Verify that the sizes are different to ensure that we can test eviction order. 155 ASSERT_GT(lowPrioritySize, 0u); 156 ASSERT_NE(lowPrioritySize, highPrioritySize); 157 ASSERT_GT(lowPriorityMockDecodeSize, 0u); 158 ASSERT_NE(lowPriorityMockDecodeSize, highPriorityMockDecodeSize); 159 160 ASSERT_EQ(memoryCache()->deadSize(), 0u); 161 ASSERT_EQ(memoryCache()->liveSize(), 0u); 162 163 // Add the items. The item added first would normally be evicted first. 164 memoryCache()->add(cachedImageHighPriority.get()); 165 ASSERT_EQ(memoryCache()->deadSize(), 0u); 166 ASSERT_EQ(memoryCache()->liveSize(), highPrioritySize); 167 168 memoryCache()->add(cachedImageLowPriority.get()); 169 ASSERT_EQ(memoryCache()->deadSize(), 0u); 170 ASSERT_EQ(memoryCache()->liveSize(), highPrioritySize + lowPrioritySize); 171 172 // Insert all items in the decoded items list with the same priority 173 memoryCache()->insertInLiveDecodedResourcesList(cachedImageHighPriority.get()); 174 memoryCache()->insertInLiveDecodedResourcesList(cachedImageLowPriority.get()); 175 ASSERT_EQ(memoryCache()->deadSize(), 0u); 176 ASSERT_EQ(memoryCache()->liveSize(), totalSize); 177 178 // Now we will assign their priority and make sure they are moved to the correct buckets. 179 cachedImageLowPriority->setCacheLiveResourcePriority(Resource::CacheLiveResourcePriorityLow); 180 cachedImageHighPriority->setCacheLiveResourcePriority(Resource::CacheLiveResourcePriorityHigh); 181 182 // Should first prune the LowPriority item. 183 memoryCache()->setCapacities(memoryCache()->minDeadCapacity(), memoryCache()->liveSize() - 10, memoryCache()->liveSize() - 10); 184 memoryCache()->prune(); 185 ASSERT_EQ(memoryCache()->deadSize(), 0u); 186 ASSERT_EQ(memoryCache()->liveSize(), totalSize - lowPriorityMockDecodeSize); 187 188 // Should prune the HighPriority item. 189 memoryCache()->setCapacities(memoryCache()->minDeadCapacity(), memoryCache()->liveSize() - 10, memoryCache()->liveSize() - 10); 190 memoryCache()->prune(); 191 ASSERT_EQ(memoryCache()->deadSize(), 0u); 192 ASSERT_EQ(memoryCache()->liveSize(), totalSize - lowPriorityMockDecodeSize - highPriorityMockDecodeSize); 193} 194} // namespace 195