OLD | NEW |
| (Empty) |
1 // Copyright 2014 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 "net/disk_cache/blockfile/index_table_v3.h" | |
6 | |
7 #include <stdint.h> | |
8 #include <utility> | |
9 | |
10 #include "base/logging.h" | |
11 #include "base/macros.h" | |
12 #include "net/disk_cache/blockfile/addr.h" | |
13 #include "net/disk_cache/blockfile/disk_format_v3.h" | |
14 #include "testing/gtest/include/gtest/gtest.h" | |
15 | |
16 using disk_cache::EntryCell; | |
17 using disk_cache::IndexCell; | |
18 using disk_cache::IndexTable; | |
19 using disk_cache::IndexTableInitData; | |
20 | |
21 namespace { | |
22 | |
23 int GetChecksum(const IndexCell& source) { | |
24 // Only the cell pointer is relevant. | |
25 disk_cache::Addr addr; | |
26 IndexCell* cell = const_cast<IndexCell*>(&source); | |
27 EntryCell entry = EntryCell::GetEntryCellForTest(0, 0, addr, cell, false); | |
28 | |
29 IndexCell result; | |
30 entry.SerializaForTest(&result); | |
31 return result.last_part >> 6; | |
32 } | |
33 | |
34 class MockIndexBackend : public disk_cache::IndexTableBackend { | |
35 public: | |
36 MockIndexBackend() : grow_called_(false), buffer_len_(-1) {} | |
37 ~MockIndexBackend() override {} | |
38 | |
39 bool grow_called() const { return grow_called_; } | |
40 int buffer_len() const { return buffer_len_; } | |
41 | |
42 void GrowIndex() override { grow_called_ = true; } | |
43 void SaveIndex(net::IOBuffer* buffer, int buffer_len) override { | |
44 buffer_len_ = buffer_len; | |
45 } | |
46 void DeleteCell(EntryCell cell) override {} | |
47 void FixCell(EntryCell cell) override {} | |
48 | |
49 private: | |
50 bool grow_called_; | |
51 int buffer_len_; | |
52 }; | |
53 | |
54 class TestCacheTables { | |
55 public: | |
56 // |num_entries| is the capacity of the main table. The extra table is half | |
57 // the size of the main table. | |
58 explicit TestCacheTables(int num_entries); | |
59 ~TestCacheTables() {} | |
60 | |
61 void GetInitData(IndexTableInitData* result); | |
62 void CopyFrom(const TestCacheTables& other); | |
63 base::Time start_time() const { return start_time_; } | |
64 | |
65 private: | |
66 std::unique_ptr<uint64_t[]> main_bitmap_; | |
67 std::unique_ptr<disk_cache::IndexBucket[]> main_table_; | |
68 std::unique_ptr<disk_cache::IndexBucket[]> extra_table_; | |
69 base::Time start_time_; | |
70 int num_bitmap_bytes_; | |
71 | |
72 DISALLOW_COPY_AND_ASSIGN(TestCacheTables); | |
73 }; | |
74 | |
75 TestCacheTables::TestCacheTables(int num_entries) { | |
76 DCHECK_GE(num_entries, 1024); | |
77 DCHECK_EQ(num_entries, num_entries / 1024 * 1024); | |
78 main_table_.reset(new disk_cache::IndexBucket[num_entries]); | |
79 extra_table_.reset(new disk_cache::IndexBucket[num_entries / 2]); | |
80 memset(main_table_.get(), 0, num_entries * sizeof(*main_table_.get())); | |
81 memset(extra_table_.get(), 0, num_entries / 2 * sizeof(*extra_table_.get())); | |
82 | |
83 // We allow IndexBitmap smaller than a page because the code should not really | |
84 // depend on that. | |
85 num_bitmap_bytes_ = (num_entries + num_entries / 2) / 8; | |
86 size_t required_size = sizeof(disk_cache::IndexHeaderV3) + num_bitmap_bytes_; | |
87 main_bitmap_.reset(new uint64_t[required_size / sizeof(uint64_t)]); | |
88 memset(main_bitmap_.get(), 0, required_size); | |
89 | |
90 disk_cache::IndexHeaderV3* header = | |
91 reinterpret_cast<disk_cache::IndexHeaderV3*>(main_bitmap_.get()); | |
92 | |
93 header->magic = disk_cache::kIndexMagicV3; | |
94 header->version = disk_cache::kVersion3; | |
95 header->table_len = num_entries + num_entries / 2; | |
96 header->max_bucket = num_entries / 4 - 1; | |
97 | |
98 start_time_ = base::Time::Now(); | |
99 header->create_time = start_time_.ToInternalValue(); | |
100 header->base_time = | |
101 (start_time_ - base::TimeDelta::FromDays(20)).ToInternalValue(); | |
102 | |
103 if (num_entries < 64 * 1024) | |
104 header->flags = disk_cache::SMALL_CACHE; | |
105 } | |
106 | |
107 void TestCacheTables::GetInitData(IndexTableInitData* result) { | |
108 result->index_bitmap = | |
109 reinterpret_cast<disk_cache::IndexBitmap*>(main_bitmap_.get()); | |
110 | |
111 result->main_table = main_table_.get(); | |
112 result->extra_table = extra_table_.get(); | |
113 | |
114 result->backup_header.reset(new disk_cache::IndexHeaderV3); | |
115 memcpy(result->backup_header.get(), result->index_bitmap, | |
116 sizeof(result->index_bitmap->header)); | |
117 | |
118 result->backup_bitmap.reset( | |
119 new uint32_t[num_bitmap_bytes_ / sizeof(uint32_t)]); | |
120 memcpy(result->backup_bitmap.get(), result->index_bitmap->bitmap, | |
121 num_bitmap_bytes_); | |
122 } | |
123 | |
124 void TestCacheTables::CopyFrom(const TestCacheTables& other) { | |
125 disk_cache::IndexBitmap* this_bitmap = | |
126 reinterpret_cast<disk_cache::IndexBitmap*>(main_bitmap_.get()); | |
127 disk_cache::IndexBitmap* other_bitmap = | |
128 reinterpret_cast<disk_cache::IndexBitmap*>(other.main_bitmap_.get()); | |
129 | |
130 DCHECK_GE(this_bitmap->header.table_len, other_bitmap->header.table_len); | |
131 DCHECK_GE(num_bitmap_bytes_, other.num_bitmap_bytes_); | |
132 | |
133 memcpy(this_bitmap->bitmap, other_bitmap->bitmap, other.num_bitmap_bytes_); | |
134 | |
135 int main_table_buckets = (other_bitmap->header.table_len * 2 / 3) / 4; | |
136 int extra_table_buckets = (other_bitmap->header.table_len * 1 / 3) / 4; | |
137 memcpy(main_table_.get(), other.main_table_.get(), | |
138 main_table_buckets * sizeof(disk_cache::IndexBucket)); | |
139 memcpy(extra_table_.get(), other.extra_table_.get(), | |
140 extra_table_buckets * sizeof(disk_cache::IndexBucket)); | |
141 | |
142 this_bitmap->header.num_entries = other_bitmap->header.num_entries; | |
143 this_bitmap->header.used_cells = other_bitmap->header.used_cells; | |
144 this_bitmap->header.max_bucket = other_bitmap->header.max_bucket; | |
145 this_bitmap->header.create_time = other_bitmap->header.create_time; | |
146 this_bitmap->header.base_time = other_bitmap->header.base_time; | |
147 this_bitmap->header.flags = other_bitmap->header.flags; | |
148 start_time_ = other.start_time_; | |
149 } | |
150 | |
151 } // namespace | |
152 | |
153 TEST(DiskCacheIndexTable, EntryCell) { | |
154 uint32_t hash = 0x55aa6699; | |
155 disk_cache::Addr addr(disk_cache::BLOCK_ENTRIES, 1, 5, 0x4531); | |
156 bool small_table = true; | |
157 int cell_num = 88; | |
158 int reuse = 6; | |
159 int timestamp = 123456; | |
160 disk_cache::EntryState state = disk_cache::ENTRY_MODIFIED; | |
161 disk_cache::EntryGroup group = disk_cache::ENTRY_HIGH_USE; | |
162 | |
163 for (int i = 0; i < 4; i++) { | |
164 SCOPED_TRACE(i); | |
165 EntryCell entry = EntryCell::GetEntryCellForTest(cell_num, hash, addr, NULL, | |
166 small_table); | |
167 EXPECT_EQ(disk_cache::ENTRY_NO_USE, entry.GetGroup()); | |
168 EXPECT_EQ(disk_cache::ENTRY_NEW, entry.GetState()); | |
169 | |
170 entry.SetGroup(group); | |
171 entry.SetState(state); | |
172 entry.SetReuse(reuse); | |
173 entry.SetTimestamp(timestamp); | |
174 | |
175 EXPECT_TRUE(entry.IsValid()); | |
176 EXPECT_EQ(hash, entry.hash()); | |
177 EXPECT_EQ(cell_num, entry.cell_num()); | |
178 EXPECT_EQ(addr.value(), entry.GetAddress().value()); | |
179 | |
180 EXPECT_EQ(group, entry.GetGroup()); | |
181 EXPECT_EQ(state, entry.GetState()); | |
182 EXPECT_EQ(reuse, entry.GetReuse()); | |
183 EXPECT_EQ(timestamp, entry.GetTimestamp()); | |
184 | |
185 // Store the data and read it again. | |
186 IndexCell cell; | |
187 entry.SerializaForTest(&cell); | |
188 | |
189 EntryCell entry2 = EntryCell::GetEntryCellForTest(cell_num, hash, addr, | |
190 &cell, small_table); | |
191 | |
192 EXPECT_EQ(addr.value(), entry2.GetAddress().value()); | |
193 | |
194 EXPECT_EQ(group, entry2.GetGroup()); | |
195 EXPECT_EQ(state, entry2.GetState()); | |
196 EXPECT_EQ(reuse, entry2.GetReuse()); | |
197 EXPECT_EQ(timestamp, entry2.GetTimestamp()); | |
198 | |
199 small_table = !small_table; | |
200 if (i == 1) { | |
201 hash = ~hash; | |
202 cell_num *= 5; | |
203 state = disk_cache::ENTRY_USED; | |
204 group = disk_cache::ENTRY_EVICTED; | |
205 addr = disk_cache::Addr(disk_cache::BLOCK_EVICTED, 1, 6, 0x18a5); | |
206 reuse = 15; // 4 bits | |
207 timestamp = 0xfffff; // 20 bits. | |
208 } | |
209 } | |
210 } | |
211 | |
212 // Goes over some significant values for a cell's sum. | |
213 TEST(DiskCacheIndexTable, EntryCellSum) { | |
214 IndexCell source; | |
215 source.Clear(); | |
216 EXPECT_EQ(0, GetChecksum(source)); | |
217 | |
218 source.first_part++; | |
219 EXPECT_EQ(1, GetChecksum(source)); | |
220 | |
221 source.Clear(); | |
222 source.last_part = 0x80; | |
223 EXPECT_EQ(0, GetChecksum(source)); | |
224 | |
225 source.last_part = 0x55; | |
226 EXPECT_EQ(3, GetChecksum(source)); | |
227 | |
228 source.first_part = 0x555555; | |
229 EXPECT_EQ(2, GetChecksum(source)); | |
230 | |
231 source.last_part = 0; | |
232 EXPECT_EQ(1, GetChecksum(source)); | |
233 | |
234 source.first_part = UINT64_C(0x8000000080000000); | |
235 EXPECT_EQ(0, GetChecksum(source)); | |
236 | |
237 source.first_part = UINT64_C(0x4000000040000000); | |
238 EXPECT_EQ(2, GetChecksum(source)); | |
239 | |
240 source.first_part = UINT64_C(0x200000020000000); | |
241 EXPECT_EQ(1, GetChecksum(source)); | |
242 | |
243 source.first_part = UINT64_C(0x100000010010000); | |
244 EXPECT_EQ(3, GetChecksum(source)); | |
245 | |
246 source.first_part = 0x80008000; | |
247 EXPECT_EQ(0, GetChecksum(source)); | |
248 | |
249 source.first_part = UINT64_C(0x800000008000); | |
250 EXPECT_EQ(1, GetChecksum(source)); | |
251 | |
252 source.first_part = 0x8080; | |
253 EXPECT_EQ(0, GetChecksum(source)); | |
254 | |
255 source.first_part = 0x800080; | |
256 EXPECT_EQ(1, GetChecksum(source)); | |
257 | |
258 source.first_part = 0x88; | |
259 EXPECT_EQ(0, GetChecksum(source)); | |
260 | |
261 source.first_part = 0x808; | |
262 EXPECT_EQ(1, GetChecksum(source)); | |
263 | |
264 source.first_part = 0xA; | |
265 EXPECT_EQ(0, GetChecksum(source)); | |
266 | |
267 source.first_part = 0x22; | |
268 EXPECT_EQ(1, GetChecksum(source)); | |
269 } | |
270 | |
271 TEST(DiskCacheIndexTable, Basics) { | |
272 TestCacheTables cache(1024); | |
273 IndexTableInitData init_data; | |
274 cache.GetInitData(&init_data); | |
275 | |
276 IndexTable index(NULL); | |
277 index.Init(&init_data); | |
278 | |
279 // Write some entries. | |
280 disk_cache::CellList entries; | |
281 for (int i = 0; i < 250; i++) { | |
282 SCOPED_TRACE(i); | |
283 uint32_t hash = i * i * 1111 + i * 11; | |
284 disk_cache::Addr addr(disk_cache::BLOCK_ENTRIES, 1, 5, i * 13 + 1); | |
285 EntryCell entry = index.CreateEntryCell(hash, addr); | |
286 EXPECT_TRUE(entry.IsValid()); | |
287 | |
288 disk_cache::CellInfo info = { hash, addr }; | |
289 entries.push_back(info); | |
290 } | |
291 | |
292 // Read them back. | |
293 for (size_t i = 0; i < entries.size(); i++) { | |
294 SCOPED_TRACE(i); | |
295 uint32_t hash = entries[i].hash; | |
296 disk_cache::Addr addr = entries[i].address; | |
297 | |
298 disk_cache::EntrySet found_entries = index.LookupEntries(hash); | |
299 ASSERT_EQ(1u, found_entries.cells.size()); | |
300 EXPECT_TRUE(found_entries.cells[0].IsValid()); | |
301 EXPECT_EQ(hash, found_entries.cells[0].hash()); | |
302 EXPECT_EQ(addr.value(), found_entries.cells[0].GetAddress().value()); | |
303 | |
304 EntryCell entry = index.FindEntryCell(hash, addr); | |
305 EXPECT_TRUE(entry.IsValid()); | |
306 EXPECT_EQ(hash, entry.hash()); | |
307 EXPECT_EQ(addr.value(), entry.GetAddress().value()); | |
308 | |
309 // Delete the first 100 entries. | |
310 if (i < 100) | |
311 index.SetSate(hash, addr, disk_cache::ENTRY_DELETED); | |
312 } | |
313 | |
314 // See what we have now. | |
315 for (size_t i = 0; i < entries.size(); i++) { | |
316 SCOPED_TRACE(i); | |
317 uint32_t hash = entries[i].hash; | |
318 disk_cache::Addr addr = entries[i].address; | |
319 | |
320 disk_cache::EntrySet found_entries = index.LookupEntries(hash); | |
321 if (i < 100) { | |
322 EXPECT_EQ(0u, found_entries.cells.size()); | |
323 } else { | |
324 ASSERT_EQ(1u, found_entries.cells.size()); | |
325 EXPECT_TRUE(found_entries.cells[0].IsValid()); | |
326 EXPECT_EQ(hash, found_entries.cells[0].hash()); | |
327 EXPECT_EQ(addr.value(), found_entries.cells[0].GetAddress().value()); | |
328 } | |
329 } | |
330 } | |
331 | |
332 // Tests handling of multiple entries with the same hash. | |
333 TEST(DiskCacheIndexTable, SameHash) { | |
334 TestCacheTables cache(1024); | |
335 IndexTableInitData init_data; | |
336 cache.GetInitData(&init_data); | |
337 | |
338 IndexTable index(NULL); | |
339 index.Init(&init_data); | |
340 | |
341 disk_cache::CellList entries; | |
342 uint32_t hash = 0x55aa55bb; | |
343 for (int i = 0; i < 6; i++) { | |
344 SCOPED_TRACE(i); | |
345 disk_cache::Addr addr(disk_cache::BLOCK_ENTRIES, 1, 5, i * 13 + 1); | |
346 EntryCell entry = index.CreateEntryCell(hash, addr); | |
347 EXPECT_TRUE(entry.IsValid()); | |
348 | |
349 disk_cache::CellInfo info = { hash, addr }; | |
350 entries.push_back(info); | |
351 } | |
352 | |
353 disk_cache::EntrySet found_entries = index.LookupEntries(hash); | |
354 EXPECT_EQ(0, found_entries.evicted_count); | |
355 ASSERT_EQ(6u, found_entries.cells.size()); | |
356 | |
357 for (size_t i = 0; i < found_entries.cells.size(); i++) { | |
358 SCOPED_TRACE(i); | |
359 EXPECT_EQ(entries[i].address, found_entries.cells[i].GetAddress()); | |
360 } | |
361 | |
362 // Now verify handling of entries on different states. | |
363 index.SetSate(hash, entries[0].address, disk_cache::ENTRY_DELETED); | |
364 index.SetSate(hash, entries[1].address, disk_cache::ENTRY_DELETED); | |
365 index.SetSate(hash, entries[2].address, disk_cache::ENTRY_USED); | |
366 index.SetSate(hash, entries[3].address, disk_cache::ENTRY_USED); | |
367 index.SetSate(hash, entries[4].address, disk_cache::ENTRY_USED); | |
368 | |
369 found_entries = index.LookupEntries(hash); | |
370 EXPECT_EQ(0, found_entries.evicted_count); | |
371 ASSERT_EQ(4u, found_entries.cells.size()); | |
372 | |
373 index.SetSate(hash, entries[3].address, disk_cache::ENTRY_OPEN); | |
374 index.SetSate(hash, entries[4].address, disk_cache::ENTRY_OPEN); | |
375 | |
376 found_entries = index.LookupEntries(hash); | |
377 EXPECT_EQ(0, found_entries.evicted_count); | |
378 ASSERT_EQ(4u, found_entries.cells.size()); | |
379 | |
380 index.SetSate(hash, entries[4].address, disk_cache::ENTRY_MODIFIED); | |
381 | |
382 found_entries = index.LookupEntries(hash); | |
383 EXPECT_EQ(0, found_entries.evicted_count); | |
384 ASSERT_EQ(4u, found_entries.cells.size()); | |
385 | |
386 index.SetSate(hash, entries[1].address, disk_cache::ENTRY_FREE); | |
387 | |
388 found_entries = index.LookupEntries(hash); | |
389 EXPECT_EQ(0, found_entries.evicted_count); | |
390 ASSERT_EQ(4u, found_entries.cells.size()); | |
391 | |
392 // FindEntryCell should not see deleted entries. | |
393 EntryCell entry = index.FindEntryCell(hash, entries[0].address); | |
394 EXPECT_FALSE(entry.IsValid()); | |
395 | |
396 // A free entry is gone. | |
397 entry = index.FindEntryCell(hash, entries[1].address); | |
398 EXPECT_FALSE(entry.IsValid()); | |
399 | |
400 // Locate a used entry, and evict it. This is not really a correct operation | |
401 // in that an existing cell doesn't transition to evicted; instead a new cell | |
402 // for the evicted entry (on a different block file) should be created. Still, | |
403 // at least evicted_count would be valid. | |
404 entry = index.FindEntryCell(hash, entries[2].address); | |
405 EXPECT_TRUE(entry.IsValid()); | |
406 entry.SetGroup(disk_cache::ENTRY_EVICTED); | |
407 index.Save(&entry); | |
408 | |
409 found_entries = index.LookupEntries(hash); | |
410 EXPECT_EQ(1, found_entries.evicted_count); | |
411 ASSERT_EQ(4u, found_entries.cells.size()); | |
412 | |
413 // Now use the proper way to get an evicted entry. | |
414 disk_cache::Addr addr2(disk_cache::BLOCK_EVICTED, 1, 6, 6); // Any address. | |
415 entry = index.CreateEntryCell(hash, addr2); | |
416 EXPECT_TRUE(entry.IsValid()); | |
417 EXPECT_EQ(disk_cache::ENTRY_EVICTED, entry.GetGroup()); | |
418 | |
419 found_entries = index.LookupEntries(hash); | |
420 EXPECT_EQ(2, found_entries.evicted_count); | |
421 ASSERT_EQ(5u, found_entries.cells.size()); | |
422 } | |
423 | |
424 TEST(DiskCacheIndexTable, Timestamps) { | |
425 TestCacheTables cache(1024); | |
426 IndexTableInitData init_data; | |
427 cache.GetInitData(&init_data); | |
428 | |
429 IndexTable index(NULL); | |
430 index.Init(&init_data); | |
431 | |
432 // The granularity should be 1 minute. | |
433 int timestamp1 = index.CalculateTimestamp(cache.start_time()); | |
434 int timestamp2 = index.CalculateTimestamp(cache.start_time() + | |
435 base::TimeDelta::FromSeconds(59)); | |
436 EXPECT_EQ(timestamp1, timestamp2); | |
437 | |
438 int timestamp3 = index.CalculateTimestamp(cache.start_time() + | |
439 base::TimeDelta::FromSeconds(61)); | |
440 EXPECT_EQ(timestamp1 + 1, timestamp3); | |
441 | |
442 int timestamp4 = index.CalculateTimestamp(cache.start_time() + | |
443 base::TimeDelta::FromSeconds(119)); | |
444 EXPECT_EQ(timestamp1 + 1, timestamp4); | |
445 | |
446 int timestamp5 = index.CalculateTimestamp(cache.start_time() + | |
447 base::TimeDelta::FromSeconds(121)); | |
448 EXPECT_EQ(timestamp1 + 2, timestamp5); | |
449 | |
450 int timestamp6 = index.CalculateTimestamp(cache.start_time() - | |
451 base::TimeDelta::FromSeconds(30)); | |
452 EXPECT_EQ(timestamp1 - 1, timestamp6); | |
453 | |
454 // The base should be 20 days in the past. | |
455 int timestamp7 = index.CalculateTimestamp(cache.start_time() - | |
456 base::TimeDelta::FromDays(20)); | |
457 int timestamp8 = index.CalculateTimestamp(cache.start_time() - | |
458 base::TimeDelta::FromDays(35)); | |
459 EXPECT_EQ(timestamp7, timestamp8); | |
460 EXPECT_EQ(0, timestamp8); | |
461 | |
462 int timestamp9 = index.CalculateTimestamp(cache.start_time() - | |
463 base::TimeDelta::FromDays(19)); | |
464 EXPECT_NE(0, timestamp9); | |
465 } | |
466 | |
467 // Tests GetOldest and GetNextCells. | |
468 TEST(DiskCacheIndexTable, Iterations) { | |
469 TestCacheTables cache(1024); | |
470 IndexTableInitData init_data; | |
471 cache.GetInitData(&init_data); | |
472 | |
473 IndexTable index(NULL); | |
474 index.Init(&init_data); | |
475 | |
476 base::Time time = cache.start_time(); | |
477 | |
478 // Write some entries. | |
479 disk_cache::CellList entries; | |
480 for (int i = 0; i < 44; i++) { | |
481 SCOPED_TRACE(i); | |
482 uint32_t hash = i; // The entries will be ordered on the table. | |
483 disk_cache::Addr addr(disk_cache::BLOCK_ENTRIES, 1, 5, i * 13 + 1); | |
484 if (i < 10 || i == 40) | |
485 addr = disk_cache::Addr(disk_cache::BLOCK_EVICTED, 1, 6, i * 13 + 1); | |
486 | |
487 EntryCell entry = index.CreateEntryCell(hash, addr); | |
488 EXPECT_TRUE(entry.IsValid()); | |
489 | |
490 disk_cache::CellInfo info = { hash, addr }; | |
491 entries.push_back(info); | |
492 | |
493 if (i < 10 || i == 40) { | |
494 // Do nothing. These are ENTRY_EVICTED by default. | |
495 } else if (i < 20 || i == 41) { | |
496 entry.SetGroup(disk_cache::ENTRY_HIGH_USE); | |
497 index.Save(&entry); | |
498 } else if (i < 30 || i == 42) { | |
499 entry.SetGroup(disk_cache::ENTRY_LOW_USE); | |
500 index.Save(&entry); | |
501 } | |
502 | |
503 // Entries [30,39] and 43 are marked as ENTRY_NO_USE (the default). | |
504 | |
505 if (!(i % 10)) | |
506 time += base::TimeDelta::FromMinutes(1); | |
507 | |
508 index.UpdateTime(hash, addr, time); | |
509 } | |
510 | |
511 // Get the oldest entries of each group. | |
512 disk_cache::IndexIterator no_use, low_use, high_use; | |
513 index.GetOldest(&no_use, &low_use, &high_use); | |
514 ASSERT_EQ(10u, no_use.cells.size()); | |
515 ASSERT_EQ(10u, low_use.cells.size()); | |
516 ASSERT_EQ(10u, high_use.cells.size()); | |
517 | |
518 EXPECT_EQ(entries[10].hash, high_use.cells[0].hash); | |
519 EXPECT_EQ(entries[19].hash, high_use.cells[9].hash); | |
520 EXPECT_EQ(entries[20].hash, low_use.cells[0].hash); | |
521 EXPECT_EQ(entries[29].hash, low_use.cells[9].hash); | |
522 EXPECT_EQ(entries[30].hash, no_use.cells[0].hash); | |
523 EXPECT_EQ(entries[39].hash, no_use.cells[9].hash); | |
524 | |
525 // Now start an iteration from the head (most recent entry). | |
526 disk_cache::IndexIterator iterator; | |
527 iterator.timestamp = index.CalculateTimestamp(time) + 1; | |
528 iterator.forward = false; // Back in time. | |
529 | |
530 ASSERT_TRUE(index.GetNextCells(&iterator)); | |
531 ASSERT_EQ(3u, iterator.cells.size()); | |
532 EXPECT_EQ(entries[41].hash, iterator.cells[0].hash); | |
533 EXPECT_EQ(entries[42].hash, iterator.cells[1].hash); | |
534 EXPECT_EQ(entries[43].hash, iterator.cells[2].hash); | |
535 | |
536 ASSERT_TRUE(index.GetNextCells(&iterator)); | |
537 ASSERT_EQ(10u, iterator.cells.size()); | |
538 EXPECT_EQ(entries[30].hash, iterator.cells[0].hash); | |
539 EXPECT_EQ(entries[39].hash, iterator.cells[9].hash); | |
540 | |
541 ASSERT_TRUE(index.GetNextCells(&iterator)); | |
542 ASSERT_EQ(10u, iterator.cells.size()); | |
543 EXPECT_EQ(entries[20].hash, iterator.cells[0].hash); | |
544 EXPECT_EQ(entries[29].hash, iterator.cells[9].hash); | |
545 | |
546 ASSERT_TRUE(index.GetNextCells(&iterator)); | |
547 ASSERT_EQ(10u, iterator.cells.size()); | |
548 EXPECT_EQ(entries[10].hash, iterator.cells[0].hash); | |
549 EXPECT_EQ(entries[19].hash, iterator.cells[9].hash); | |
550 | |
551 ASSERT_FALSE(index.GetNextCells(&iterator)); | |
552 | |
553 // Now start an iteration from the tail (oldest entry). | |
554 iterator.timestamp = 0; | |
555 iterator.forward = true; | |
556 | |
557 ASSERT_TRUE(index.GetNextCells(&iterator)); | |
558 ASSERT_EQ(10u, iterator.cells.size()); | |
559 EXPECT_EQ(entries[10].hash, iterator.cells[0].hash); | |
560 EXPECT_EQ(entries[19].hash, iterator.cells[9].hash); | |
561 | |
562 ASSERT_TRUE(index.GetNextCells(&iterator)); | |
563 ASSERT_EQ(10u, iterator.cells.size()); | |
564 EXPECT_EQ(entries[20].hash, iterator.cells[0].hash); | |
565 EXPECT_EQ(entries[29].hash, iterator.cells[9].hash); | |
566 | |
567 ASSERT_TRUE(index.GetNextCells(&iterator)); | |
568 ASSERT_EQ(10u, iterator.cells.size()); | |
569 EXPECT_EQ(entries[30].hash, iterator.cells[0].hash); | |
570 EXPECT_EQ(entries[39].hash, iterator.cells[9].hash); | |
571 | |
572 ASSERT_TRUE(index.GetNextCells(&iterator)); | |
573 ASSERT_EQ(3u, iterator.cells.size()); | |
574 EXPECT_EQ(entries[41].hash, iterator.cells[0].hash); | |
575 EXPECT_EQ(entries[42].hash, iterator.cells[1].hash); | |
576 EXPECT_EQ(entries[43].hash, iterator.cells[2].hash); | |
577 } | |
578 | |
579 // Tests doubling of the table. | |
580 TEST(DiskCacheIndexTable, Doubling) { | |
581 IndexTable index(NULL); | |
582 int size = 1024; | |
583 std::unique_ptr<TestCacheTables> cache(new TestCacheTables(size)); | |
584 int entry_id = 0; | |
585 disk_cache::CellList entries; | |
586 | |
587 // Go from 1024 to 256k cells. | |
588 for (int resizes = 0; resizes <= 8; resizes++) { | |
589 std::unique_ptr<TestCacheTables> old_cache(std::move(cache)); | |
590 cache.reset(new TestCacheTables(size)); | |
591 cache.get()->CopyFrom(*old_cache.get()); | |
592 | |
593 IndexTableInitData init_data; | |
594 cache.get()->GetInitData(&init_data); | |
595 index.Init(&init_data); | |
596 | |
597 // Write some entries. | |
598 for (int i = 0; i < 250; i++, entry_id++) { | |
599 SCOPED_TRACE(entry_id); | |
600 uint32_t hash = entry_id * i * 321 + entry_id * 13; | |
601 disk_cache::Addr addr(disk_cache::BLOCK_ENTRIES, 1, 5, entry_id * 17 + 1); | |
602 EntryCell entry = index.CreateEntryCell(hash, addr); | |
603 EXPECT_TRUE(entry.IsValid()); | |
604 | |
605 disk_cache::CellInfo info = { hash, addr }; | |
606 entries.push_back(info); | |
607 } | |
608 size *= 2; | |
609 } | |
610 | |
611 // Access all the entries. | |
612 for (size_t i = 0; i < entries.size(); i++) { | |
613 SCOPED_TRACE(i); | |
614 disk_cache::EntrySet found_entries = index.LookupEntries(entries[i].hash); | |
615 ASSERT_EQ(1u, found_entries.cells.size()); | |
616 EXPECT_TRUE(found_entries.cells[0].IsValid()); | |
617 } | |
618 } | |
619 | |
620 // Tests bucket chaining when growing the index. | |
621 TEST(DiskCacheIndexTable, BucketChains) { | |
622 IndexTable index(NULL); | |
623 int size = 1024; | |
624 std::unique_ptr<TestCacheTables> cache(new TestCacheTables(size)); | |
625 disk_cache::CellList entries; | |
626 | |
627 IndexTableInitData init_data; | |
628 cache.get()->GetInitData(&init_data); | |
629 index.Init(&init_data); | |
630 | |
631 // Write some entries. | |
632 for (int i = 0; i < 8; i++) { | |
633 SCOPED_TRACE(i); | |
634 uint32_t hash = i * 256; | |
635 disk_cache::Addr addr(disk_cache::BLOCK_ENTRIES, 1, 5, i * 7 + 1); | |
636 EntryCell entry = index.CreateEntryCell(hash, addr); | |
637 EXPECT_TRUE(entry.IsValid()); | |
638 | |
639 disk_cache::CellInfo info = { hash, addr }; | |
640 entries.push_back(info); | |
641 } | |
642 | |
643 // Double the size. | |
644 std::unique_ptr<TestCacheTables> old_cache(std::move(cache)); | |
645 cache.reset(new TestCacheTables(size * 2)); | |
646 cache.get()->CopyFrom(*old_cache.get()); | |
647 | |
648 cache.get()->GetInitData(&init_data); | |
649 index.Init(&init_data); | |
650 | |
651 // Write more entries, starting with the upper half of the table. | |
652 for (int i = 9; i < 11; i++) { | |
653 SCOPED_TRACE(i); | |
654 uint32_t hash = i * 256; | |
655 disk_cache::Addr addr(disk_cache::BLOCK_ENTRIES, 1, 5, i * 7 + 1); | |
656 EntryCell entry = index.CreateEntryCell(hash, addr); | |
657 EXPECT_TRUE(entry.IsValid()); | |
658 | |
659 disk_cache::CellInfo info = { hash, addr }; | |
660 entries.push_back(info); | |
661 } | |
662 | |
663 // Access all the entries. | |
664 for (size_t i = 0; i < entries.size(); i++) { | |
665 SCOPED_TRACE(i); | |
666 disk_cache::EntrySet found_entries = index.LookupEntries(entries[i].hash); | |
667 ASSERT_EQ(1u, found_entries.cells.size()); | |
668 EXPECT_TRUE(found_entries.cells[0].IsValid()); | |
669 } | |
670 } | |
671 | |
672 // Tests that GrowIndex is called. | |
673 TEST(DiskCacheIndexTable, GrowIndex) { | |
674 TestCacheTables cache(1024); | |
675 IndexTableInitData init_data; | |
676 cache.GetInitData(&init_data); | |
677 MockIndexBackend backend; | |
678 | |
679 IndexTable index(&backend); | |
680 index.Init(&init_data); | |
681 | |
682 // Write some entries. | |
683 for (int i = 0; i < 512; i++) { | |
684 SCOPED_TRACE(i); | |
685 uint32_t hash = 0; | |
686 disk_cache::Addr addr(disk_cache::BLOCK_ENTRIES, 1, 5, i + 1); | |
687 EntryCell entry = index.CreateEntryCell(hash, addr); | |
688 EXPECT_TRUE(entry.IsValid()); | |
689 } | |
690 | |
691 EXPECT_TRUE(backend.grow_called()); | |
692 } | |
693 | |
694 TEST(DiskCacheIndexTable, SaveIndex) { | |
695 TestCacheTables cache(1024); | |
696 IndexTableInitData init_data; | |
697 cache.GetInitData(&init_data); | |
698 MockIndexBackend backend; | |
699 | |
700 IndexTable index(&backend); | |
701 index.Init(&init_data); | |
702 | |
703 uint32_t hash = 0; | |
704 disk_cache::Addr addr(disk_cache::BLOCK_ENTRIES, 1, 5, 6); | |
705 EntryCell entry = index.CreateEntryCell(hash, addr); | |
706 EXPECT_TRUE(entry.IsValid()); | |
707 | |
708 index.OnBackupTimer(); | |
709 int expected = (1024 + 512) / 8 + sizeof(disk_cache::IndexHeaderV3); | |
710 EXPECT_EQ(expected, backend.buffer_len()); | |
711 } | |
OLD | NEW |