1 // Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
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 <string>
6
7 #include "base/basictypes.h"
8 #include "base/file_path.h"
9 #include "base/file_util.h"
10 #include "base/perftimer.h"
11 #include "base/string_util.h"
12 #include "base/threading/thread.h"
13 #include "base/test/test_file_util.h"
14 #include "base/timer.h"
15 #include "net/base/io_buffer.h"
16 #include "net/base/net_errors.h"
17 #include "net/base/test_completion_callback.h"
18 #include "net/disk_cache/block_files.h"
19 #include "net/disk_cache/disk_cache.h"
20 #include "net/disk_cache/disk_cache_test_util.h"
21 #include "net/disk_cache/hash.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "testing/platform_test.h"
24
25 using base::Time;
26
27 extern volatile int g_cache_tests_received;
28 extern volatile bool g_cache_tests_error;
29
30 typedef PlatformTest DiskCacheTest;
31
32 namespace {
33
34 struct TestEntry {
35 std::string key;
36 int data_len;
37 };
38 typedef std::vector<TestEntry> TestEntries;
39
40 const int kMaxSize = 16 * 1024 - 1;
41
42 // Creates num_entries on the cache, and writes 200 bytes of metadata and up
43 // to kMaxSize of data to each entry.
TimeWrite(int num_entries,disk_cache::Backend * cache,TestEntries * entries)44 int TimeWrite(int num_entries, disk_cache::Backend* cache,
45 TestEntries* entries) {
46 const int kSize1 = 200;
47 scoped_refptr<net::IOBuffer> buffer1(new net::IOBuffer(kSize1));
48 scoped_refptr<net::IOBuffer> buffer2(new net::IOBuffer(kMaxSize));
49
50 CacheTestFillBuffer(buffer1->data(), kSize1, false);
51 CacheTestFillBuffer(buffer2->data(), kMaxSize, false);
52
53 CallbackTest callback(true);
54 g_cache_tests_error = false;
55 g_cache_tests_received = 0;
56 int expected = 0;
57
58 MessageLoopHelper helper;
59
60 PerfTimeLogger timer("Write disk cache entries");
61
62 for (int i = 0; i < num_entries; i++) {
63 TestEntry entry;
64 entry.key = GenerateKey(true);
65 entry.data_len = rand() % kMaxSize;
66 entries->push_back(entry);
67
68 disk_cache::Entry* cache_entry;
69 TestCompletionCallback cb;
70 int rv = cache->CreateEntry(entry.key, &cache_entry, &cb);
71 if (net::OK != cb.GetResult(rv))
72 break;
73 int ret = cache_entry->WriteData(0, 0, buffer1, kSize1, &callback, false);
74 if (net::ERR_IO_PENDING == ret)
75 expected++;
76 else if (kSize1 != ret)
77 break;
78
79 ret = cache_entry->WriteData(1, 0, buffer2, entry.data_len, &callback,
80 false);
81 if (net::ERR_IO_PENDING == ret)
82 expected++;
83 else if (entry.data_len != ret)
84 break;
85 cache_entry->Close();
86 }
87
88 helper.WaitUntilCacheIoFinished(expected);
89 timer.Done();
90
91 return expected;
92 }
93
94 // Reads the data and metadata from each entry listed on |entries|.
TimeRead(int num_entries,disk_cache::Backend * cache,const TestEntries & entries,bool cold)95 int TimeRead(int num_entries, disk_cache::Backend* cache,
96 const TestEntries& entries, bool cold) {
97 const int kSize1 = 200;
98 scoped_refptr<net::IOBuffer> buffer1(new net::IOBuffer(kSize1));
99 scoped_refptr<net::IOBuffer> buffer2(new net::IOBuffer(kMaxSize));
100
101 CacheTestFillBuffer(buffer1->data(), kSize1, false);
102 CacheTestFillBuffer(buffer2->data(), kMaxSize, false);
103
104 CallbackTest callback(true);
105 g_cache_tests_error = false;
106 g_cache_tests_received = 0;
107 int expected = 0;
108
109 MessageLoopHelper helper;
110
111 const char* message = cold ? "Read disk cache entries (cold)" :
112 "Read disk cache entries (warm)";
113 PerfTimeLogger timer(message);
114
115 for (int i = 0; i < num_entries; i++) {
116 disk_cache::Entry* cache_entry;
117 TestCompletionCallback cb;
118 int rv = cache->OpenEntry(entries[i].key, &cache_entry, &cb);
119 if (net::OK != cb.GetResult(rv))
120 break;
121 int ret = cache_entry->ReadData(0, 0, buffer1, kSize1, &callback);
122 if (net::ERR_IO_PENDING == ret)
123 expected++;
124 else if (kSize1 != ret)
125 break;
126
127 ret = cache_entry->ReadData(1, 0, buffer2, entries[i].data_len, &callback);
128 if (net::ERR_IO_PENDING == ret)
129 expected++;
130 else if (entries[i].data_len != ret)
131 break;
132 cache_entry->Close();
133 }
134
135 helper.WaitUntilCacheIoFinished(expected);
136 timer.Done();
137
138 return expected;
139 }
140
BlockSize()141 int BlockSize() {
142 // We can use form 1 to 4 blocks.
143 return (rand() & 0x3) + 1;
144 }
145
146 } // namespace
147
TEST_F(DiskCacheTest,Hash)148 TEST_F(DiskCacheTest, Hash) {
149 int seed = static_cast<int>(Time::Now().ToInternalValue());
150 srand(seed);
151
152 PerfTimeLogger timer("Hash disk cache keys");
153 for (int i = 0; i < 300000; i++) {
154 std::string key = GenerateKey(true);
155 disk_cache::Hash(key);
156 }
157 timer.Done();
158 }
159
TEST_F(DiskCacheTest,CacheBackendPerformance)160 TEST_F(DiskCacheTest, CacheBackendPerformance) {
161 MessageLoopForIO message_loop;
162
163 base::Thread cache_thread("CacheThread");
164 ASSERT_TRUE(cache_thread.StartWithOptions(
165 base::Thread::Options(MessageLoop::TYPE_IO, 0)));
166
167 ScopedTestCache test_cache;
168 TestCompletionCallback cb;
169 disk_cache::Backend* cache;
170 int rv = disk_cache::CreateCacheBackend(
171 net::DISK_CACHE, test_cache.path(), 0, false,
172 cache_thread.message_loop_proxy(), NULL, &cache, &cb);
173
174 ASSERT_EQ(net::OK, cb.GetResult(rv));
175
176 int seed = static_cast<int>(Time::Now().ToInternalValue());
177 srand(seed);
178
179 TestEntries entries;
180 int num_entries = 1000;
181
182 int ret = TimeWrite(num_entries, cache, &entries);
183 EXPECT_EQ(ret, g_cache_tests_received);
184
185 MessageLoop::current()->RunAllPending();
186 delete cache;
187
188 ASSERT_TRUE(file_util::EvictFileFromSystemCache(
189 test_cache.path().AppendASCII("index")));
190 ASSERT_TRUE(file_util::EvictFileFromSystemCache(
191 test_cache.path().AppendASCII("data_0")));
192 ASSERT_TRUE(file_util::EvictFileFromSystemCache(
193 test_cache.path().AppendASCII("data_1")));
194 ASSERT_TRUE(file_util::EvictFileFromSystemCache(
195 test_cache.path().AppendASCII("data_2")));
196 ASSERT_TRUE(file_util::EvictFileFromSystemCache(
197 test_cache.path().AppendASCII("data_3")));
198
199 rv = disk_cache::CreateCacheBackend(net::DISK_CACHE, test_cache.path(), 0,
200 false, cache_thread.message_loop_proxy(),
201 NULL, &cache, &cb);
202 ASSERT_EQ(net::OK, cb.GetResult(rv));
203
204 ret = TimeRead(num_entries, cache, entries, true);
205 EXPECT_EQ(ret, g_cache_tests_received);
206
207 ret = TimeRead(num_entries, cache, entries, false);
208 EXPECT_EQ(ret, g_cache_tests_received);
209
210 MessageLoop::current()->RunAllPending();
211 delete cache;
212 }
213
214 // Creating and deleting "entries" on a block-file is something quite frequent
215 // (after all, almost everything is stored on block files). The operation is
216 // almost free when the file is empty, but can be expensive if the file gets
217 // fragmented, or if we have multiple files. This test measures that scenario,
218 // by using multiple, highly fragmented files.
TEST_F(DiskCacheTest,BlockFilesPerformance)219 TEST_F(DiskCacheTest, BlockFilesPerformance) {
220 MessageLoopForIO message_loop;
221
222 ScopedTestCache test_cache;
223
224 disk_cache::BlockFiles files(test_cache.path());
225 ASSERT_TRUE(files.Init(true));
226
227 int seed = static_cast<int>(Time::Now().ToInternalValue());
228 srand(seed);
229
230 const int kNumEntries = 60000;
231 disk_cache::Addr* address = new disk_cache::Addr[kNumEntries];
232
233 PerfTimeLogger timer1("Fill three block-files");
234
235 // Fill up the 32-byte block file (use three files).
236 for (int i = 0; i < kNumEntries; i++) {
237 EXPECT_TRUE(files.CreateBlock(disk_cache::RANKINGS, BlockSize(),
238 &address[i]));
239 }
240
241 timer1.Done();
242 PerfTimeLogger timer2("Create and delete blocks");
243
244 for (int i = 0; i < 200000; i++) {
245 int entry = rand() * (kNumEntries / RAND_MAX + 1);
246 if (entry >= kNumEntries)
247 entry = 0;
248
249 files.DeleteBlock(address[entry], false);
250 EXPECT_TRUE(files.CreateBlock(disk_cache::RANKINGS, BlockSize(),
251 &address[entry]));
252 }
253
254 timer2.Done();
255 MessageLoop::current()->RunAllPending();
256 delete[] address;
257 }
258