Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / fetch / MemoryCacheTest.cpp
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/fetch/MemoryCache.h"
33
34 #include "core/fetch/MockImageResourceClient.h"
35 #include "core/fetch/RawResource.h"
36 #include "core/fetch/ResourcePtr.h"
37 #include "platform/network/ResourceRequest.h"
38 #include "public/platform/Platform.h"
39 #include "wtf/OwnPtr.h"
40
41 #include <gtest/gtest.h>
42
43 namespace WebCore {
44
45 class MemoryCacheTest : public ::testing::Test {
46 public:
47     class FakeDecodedResource : public WebCore::Resource {
48     public:
49         FakeDecodedResource(const ResourceRequest& request, Type type)
50             : Resource(request, type)
51         {
52         }
53
54         virtual void appendData(const char* data, int len)
55         {
56             Resource::appendData(data, len);
57             setDecodedSize(this->size());
58         }
59
60     protected:
61         virtual void destroyDecodedDataIfPossible() OVERRIDE
62         {
63             setDecodedSize(0);
64         }
65     };
66
67     class FakeResource : public WebCore::Resource {
68     public:
69         FakeResource(const ResourceRequest& request, Type type)
70             : Resource(request, type)
71         {
72         }
73
74         void fakeEncodedSize(size_t size)
75         {
76             setEncodedSize(size);
77         }
78     };
79
80 protected:
81     virtual void SetUp()
82     {
83         // Save the global memory cache to restore it upon teardown.
84         m_globalMemoryCache = adoptPtr(memoryCache());
85         // Create the test memory cache instance and hook it in.
86         m_testingMemoryCache = adoptPtr(new MemoryCache());
87         setMemoryCacheForTesting(m_testingMemoryCache.leakPtr());
88     }
89
90     virtual void TearDown()
91     {
92         // Regain the ownership of testing memory cache, so that it will be
93         // destroyed.
94         m_testingMemoryCache = adoptPtr(memoryCache());
95         // Yield the ownership of the global memory cache back.
96         setMemoryCacheForTesting(m_globalMemoryCache.leakPtr());
97     }
98
99     OwnPtr<MemoryCache> m_testingMemoryCache;
100     OwnPtr<MemoryCache> m_globalMemoryCache;
101 };
102
103 // Verifies that setters and getters for cache capacities work correcty.
104 TEST_F(MemoryCacheTest, CapacityAccounting)
105 {
106     const size_t sizeMax = ~static_cast<size_t>(0);
107     const size_t totalCapacity = sizeMax / 4;
108     const size_t minDeadCapacity = sizeMax / 16;
109     const size_t maxDeadCapacity = sizeMax / 8;
110     memoryCache()->setCapacities(minDeadCapacity, maxDeadCapacity, totalCapacity);
111     ASSERT_EQ(totalCapacity, memoryCache()->capacity());
112     ASSERT_EQ(minDeadCapacity, memoryCache()->minDeadCapacity());
113     ASSERT_EQ(maxDeadCapacity, memoryCache()->maxDeadCapacity());
114 }
115
116 TEST_F(MemoryCacheTest, VeryLargeResourceAccounting)
117 {
118     const size_t sizeMax = ~static_cast<size_t>(0);
119     const size_t totalCapacity = sizeMax / 4;
120     const size_t minDeadCapacity = sizeMax / 16;
121     const size_t maxDeadCapacity = sizeMax / 8;
122     const size_t resourceSize1 = sizeMax / 16;
123     const size_t resourceSize2 = sizeMax / 20;
124     memoryCache()->setCapacities(minDeadCapacity, maxDeadCapacity, totalCapacity);
125     ResourcePtr<FakeResource> cachedResource =
126         new FakeResource(ResourceRequest(""), Resource::Raw);
127     cachedResource->fakeEncodedSize(resourceSize1);
128
129     ASSERT_EQ(0u, memoryCache()->deadSize());
130     ASSERT_EQ(0u, memoryCache()->liveSize());
131     memoryCache()->add(cachedResource.get());
132     ASSERT_EQ(cachedResource->size(), memoryCache()->deadSize());
133     ASSERT_EQ(0u, memoryCache()->liveSize());
134
135     MockImageResourceClient client;
136     cachedResource->addClient(&client);
137     ASSERT_EQ(0u, memoryCache()->deadSize());
138     ASSERT_EQ(cachedResource->size(), memoryCache()->liveSize());
139
140     cachedResource->fakeEncodedSize(resourceSize2);
141     ASSERT_EQ(0u, memoryCache()->deadSize());
142     ASSERT_EQ(cachedResource->size(), memoryCache()->liveSize());
143 }
144
145 // Verifies that dead resources that exceed dead resource capacity are evicted
146 // from cache when pruning.
147 TEST_F(MemoryCacheTest, DeadResourceEviction)
148 {
149     memoryCache()->setDelayBeforeLiveDecodedPrune(0);
150     memoryCache()->setMaxPruneDeferralDelay(0);
151     const unsigned totalCapacity = 1000000;
152     const unsigned minDeadCapacity = 0;
153     const unsigned maxDeadCapacity = 0;
154     memoryCache()->setCapacities(minDeadCapacity, maxDeadCapacity, totalCapacity);
155
156     Resource* cachedResource =
157         new Resource(ResourceRequest(""), Resource::Raw);
158     const char data[5] = "abcd";
159     cachedResource->appendData(data, 3);
160     // The resource size has to be nonzero for this test to be meaningful, but
161     // we do not rely on it having any particular value.
162     ASSERT_GT(cachedResource->size(), 0u);
163
164     ASSERT_EQ(0u, memoryCache()->deadSize());
165     ASSERT_EQ(0u, memoryCache()->liveSize());
166
167     memoryCache()->add(cachedResource);
168     ASSERT_EQ(cachedResource->size(), memoryCache()->deadSize());
169     ASSERT_EQ(0u, memoryCache()->liveSize());
170
171     memoryCache()->prune();
172     ASSERT_EQ(0u, memoryCache()->deadSize());
173     ASSERT_EQ(0u, memoryCache()->liveSize());
174 }
175
176 // Verified that when ordering a prune in a runLoop task, the prune
177 // is deferred to the end of the task.
178 TEST_F(MemoryCacheTest, LiveResourceEvictionAtEndOfTask)
179 {
180     memoryCache()->setDelayBeforeLiveDecodedPrune(0);
181     const unsigned totalCapacity = 1;
182     const unsigned minDeadCapacity = 0;
183     const unsigned maxDeadCapacity = 0;
184     memoryCache()->setCapacities(minDeadCapacity, maxDeadCapacity, totalCapacity);
185     const char data[6] = "abcde";
186     Resource* cachedDeadResource =
187         new Resource(ResourceRequest("hhtp://foo"), Resource::Raw);
188     cachedDeadResource->appendData(data, 3);
189     ResourcePtr<Resource> cachedLiveResource =
190         new FakeDecodedResource(ResourceRequest(""), Resource::Raw);
191     MockImageResourceClient client;
192     cachedLiveResource->addClient(&client);
193     cachedLiveResource->appendData(data, 4);
194
195     class Task1 : public blink::WebThread::Task {
196     public:
197         Task1(const ResourcePtr<Resource>& live, Resource* dead)
198             : m_live(live)
199             , m_dead(dead)
200         { }
201
202         virtual void run() OVERRIDE
203         {
204             // The resource size has to be nonzero for this test to be meaningful, but
205             // we do not rely on it having any particular value.
206             ASSERT_GT(m_live->size(), 0u);
207             ASSERT_GT(m_dead->size(), 0u);
208
209             ASSERT_EQ(0u, memoryCache()->deadSize());
210             ASSERT_EQ(0u, memoryCache()->liveSize());
211
212             memoryCache()->add(m_dead);
213             memoryCache()->add(m_live.get());
214             memoryCache()->updateDecodedResource(m_live.get(), UpdateForPropertyChange);
215             ASSERT_EQ(m_dead->size(), memoryCache()->deadSize());
216             ASSERT_EQ(m_live->size(), memoryCache()->liveSize());
217             ASSERT_GT(m_live->decodedSize(), 0u);
218
219             memoryCache()->prune(); // Dead resources are pruned immediately
220             ASSERT_EQ(m_dead->size(), memoryCache()->deadSize());
221             ASSERT_EQ(m_live->size(), memoryCache()->liveSize());
222             ASSERT_GT(m_live->decodedSize(), 0u);
223         }
224
225     private:
226         ResourcePtr<Resource> m_live;
227         Resource* m_dead;
228     };
229
230     class Task2 : public blink::WebThread::Task {
231     public:
232         Task2(unsigned liveSizeWithoutDecode)
233             : m_liveSizeWithoutDecode(liveSizeWithoutDecode) { }
234
235         virtual void run() OVERRIDE
236         {
237             // Next task: now, the live resource was evicted.
238             ASSERT_EQ(0u, memoryCache()->deadSize());
239             ASSERT_EQ(m_liveSizeWithoutDecode, memoryCache()->liveSize());
240             blink::Platform::current()->currentThread()->exitRunLoop();
241         }
242
243     private:
244         unsigned m_liveSizeWithoutDecode;
245     };
246
247
248     blink::Platform::current()->currentThread()->postTask(new Task1(cachedLiveResource, cachedDeadResource));
249     blink::Platform::current()->currentThread()->postTask(new Task2(cachedLiveResource->encodedSize() + cachedLiveResource->overheadSize()));
250     blink::Platform::current()->currentThread()->enterRunLoop();
251     cachedLiveResource->removeClient(&client);
252 }
253
254 // Verifies that cached resources are evicted immediately after release when
255 // the total dead resource size is more than double the dead resource capacity.
256 TEST_F(MemoryCacheTest, ClientRemoval)
257 {
258     const char data[6] = "abcde";
259     ResourcePtr<Resource> resource1 =
260         new FakeDecodedResource(ResourceRequest("http://foo.com"), Resource::Raw);
261     MockImageResourceClient client1;
262     resource1->addClient(&client1);
263     resource1->appendData(data, 4);
264     ResourcePtr<Resource> resource2 =
265         new FakeDecodedResource(ResourceRequest(""), Resource::Raw);
266     MockImageResourceClient client2;
267     resource2->addClient(&client2);
268     resource2->appendData(data, 4);
269
270     const unsigned minDeadCapacity = 0;
271     const unsigned maxDeadCapacity = ((resource1->size() + resource2->size()) / 2) - 1;
272     const unsigned totalCapacity = maxDeadCapacity;
273     memoryCache()->setCapacities(minDeadCapacity, maxDeadCapacity, totalCapacity);
274     memoryCache()->add(resource1.get());
275     memoryCache()->add(resource2.get());
276     // Call prune. There is nothing to prune, but this will initialize
277     // the prune timestamp, allowing future prunes to be deferred.
278     memoryCache()->prune();
279     ASSERT_GT(resource1->decodedSize(), 0u);
280     ASSERT_GT(resource2->decodedSize(), 0u);
281     ASSERT_EQ(memoryCache()->deadSize(), 0u);
282     ASSERT_EQ(memoryCache()->liveSize(), resource1->size() + resource2->size());
283
284     // Removing the client from resource1 should result in all resources
285     // remaining in cache since the prune is deferred.
286     resource1->removeClient(&client1);
287     ASSERT_GT(resource1->decodedSize(), 0u);
288     ASSERT_GT(resource2->decodedSize(), 0u);
289     ASSERT_EQ(memoryCache()->deadSize(), resource1->size());
290     ASSERT_EQ(memoryCache()->liveSize(), resource2->size());
291     ASSERT_TRUE(memoryCache()->contains(resource1.get()));
292     ASSERT_TRUE(memoryCache()->contains(resource2.get()));
293
294     // Removing the client from resource2 should result in immediate
295     // eviction of resource2 because we are over the prune deferral limit.
296     resource2->removeClient(&client2);
297     ASSERT_GT(resource1->decodedSize(), 0u);
298     ASSERT_GT(resource2->decodedSize(), 0u);
299     ASSERT_EQ(memoryCache()->deadSize(), resource1->size());
300     ASSERT_EQ(memoryCache()->liveSize(), 0u);
301     ASSERT_TRUE(memoryCache()->contains(resource1.get()));
302     ASSERT_FALSE(memoryCache()->contains(resource2.get()));
303 }
304
305 // Verifies that CachedResources are evicted from the decode cache
306 // according to their DecodeCachePriority.
307 TEST_F(MemoryCacheTest, DecodeCacheOrder)
308 {
309     memoryCache()->setDelayBeforeLiveDecodedPrune(0);
310     memoryCache()->setMaxPruneDeferralDelay(0);
311     ResourcePtr<FakeDecodedResource> cachedImageLowPriority =
312         new FakeDecodedResource(ResourceRequest("http://foo.com"), Resource::Raw);
313     ResourcePtr<FakeDecodedResource> cachedImageHighPriority =
314         new FakeDecodedResource(ResourceRequest(""), Resource::Raw);
315
316     MockImageResourceClient clientLowPriority;
317     MockImageResourceClient clientHighPriority;
318     cachedImageLowPriority->addClient(&clientLowPriority);
319     cachedImageHighPriority->addClient(&clientHighPriority);
320
321     const char data[5] = "abcd";
322     cachedImageLowPriority->appendData(data, 1);
323     cachedImageHighPriority->appendData(data, 4);
324     const unsigned lowPrioritySize = cachedImageLowPriority->size();
325     const unsigned highPrioritySize = cachedImageHighPriority->size();
326     const unsigned lowPriorityMockDecodeSize = cachedImageLowPriority->decodedSize();
327     const unsigned highPriorityMockDecodeSize = cachedImageHighPriority->decodedSize();
328     const unsigned totalSize = lowPrioritySize + highPrioritySize;
329
330     // Verify that the sizes are different to ensure that we can test eviction order.
331     ASSERT_GT(lowPrioritySize, 0u);
332     ASSERT_NE(lowPrioritySize, highPrioritySize);
333     ASSERT_GT(lowPriorityMockDecodeSize, 0u);
334     ASSERT_NE(lowPriorityMockDecodeSize, highPriorityMockDecodeSize);
335
336     ASSERT_EQ(memoryCache()->deadSize(), 0u);
337     ASSERT_EQ(memoryCache()->liveSize(), 0u);
338
339     // Add the items. The item added first would normally be evicted first.
340     memoryCache()->add(cachedImageHighPriority.get());
341     ASSERT_EQ(memoryCache()->deadSize(), 0u);
342     ASSERT_EQ(memoryCache()->liveSize(), highPrioritySize);
343
344     memoryCache()->add(cachedImageLowPriority.get());
345     ASSERT_EQ(memoryCache()->deadSize(), 0u);
346     ASSERT_EQ(memoryCache()->liveSize(), highPrioritySize + lowPrioritySize);
347
348     // Insert all items in the decoded items list with the same priority
349     memoryCache()->updateDecodedResource(cachedImageHighPriority.get(), UpdateForPropertyChange);
350     memoryCache()->updateDecodedResource(cachedImageLowPriority.get(), UpdateForPropertyChange);
351     ASSERT_EQ(memoryCache()->deadSize(), 0u);
352     ASSERT_EQ(memoryCache()->liveSize(), totalSize);
353
354     // Now we will assign their priority and make sure they are moved to the correct buckets.
355     memoryCache()->updateDecodedResource(cachedImageLowPriority.get(), UpdateForPropertyChange, MemoryCacheLiveResourcePriorityLow);
356     memoryCache()->updateDecodedResource(cachedImageHighPriority.get(), UpdateForPropertyChange, MemoryCacheLiveResourcePriorityHigh);
357
358     // Should first prune the LowPriority item.
359     memoryCache()->setCapacities(memoryCache()->minDeadCapacity(), memoryCache()->liveSize() - 10, memoryCache()->liveSize() - 10);
360     memoryCache()->prune();
361     ASSERT_EQ(memoryCache()->deadSize(), 0u);
362     ASSERT_EQ(memoryCache()->liveSize(), totalSize - lowPriorityMockDecodeSize);
363
364     // Should prune the HighPriority item.
365     memoryCache()->setCapacities(memoryCache()->minDeadCapacity(), memoryCache()->liveSize() - 10, memoryCache()->liveSize() - 10);
366     memoryCache()->prune();
367     ASSERT_EQ(memoryCache()->deadSize(), 0u);
368     ASSERT_EQ(memoryCache()->liveSize(), totalSize - lowPriorityMockDecodeSize - highPriorityMockDecodeSize);
369 }
370
371 TEST_F(MemoryCacheTest, MultipleReplace)
372 {
373     ResourcePtr<FakeResource> resource1 = new FakeResource(ResourceRequest(""), Resource::Raw);
374     memoryCache()->add(resource1.get());
375
376     ResourcePtr<FakeResource> resource2 = new FakeResource(ResourceRequest(""), Resource::Raw);
377     memoryCache()->replace(resource2.get(), resource1.get());
378     EXPECT_TRUE(memoryCache()->contains(resource2.get()));
379     EXPECT_FALSE(memoryCache()->contains(resource1.get()));
380
381     ResourcePtr<FakeResource> resource3 = new FakeResource(ResourceRequest(""), Resource::Raw);
382     memoryCache()->replace(resource3.get(), resource2.get());
383     EXPECT_TRUE(memoryCache()->contains(resource3.get()));
384     EXPECT_FALSE(memoryCache()->contains(resource2.get()));
385 }
386
387 TEST_F(MemoryCacheTest, RemoveDuringRevalidation)
388 {
389     ResourcePtr<FakeResource> resource1 = new FakeResource(ResourceRequest(""), Resource::Raw);
390     memoryCache()->add(resource1.get());
391
392     ResourcePtr<FakeResource> resource2 = new FakeResource(ResourceRequest(""), Resource::Raw);
393     memoryCache()->remove(resource1.get());
394     memoryCache()->add(resource2.get());
395     EXPECT_TRUE(memoryCache()->contains(resource2.get()));
396     EXPECT_FALSE(memoryCache()->contains(resource1.get()));
397
398     ResourcePtr<FakeResource> resource3 = new FakeResource(ResourceRequest(""), Resource::Raw);
399     memoryCache()->remove(resource2.get());
400     memoryCache()->add(resource3.get());
401     EXPECT_TRUE(memoryCache()->contains(resource3.get()));
402     EXPECT_FALSE(memoryCache()->contains(resource2.get()));
403
404     memoryCache()->replace(resource1.get(), resource2.get());
405     EXPECT_TRUE(memoryCache()->contains(resource1.get()));
406     EXPECT_FALSE(memoryCache()->contains(resource2.get()));
407     EXPECT_FALSE(memoryCache()->contains(resource3.get()));
408 }
409
410 } // namespace