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.
5 #include "base/basictypes.h"
6 #include "base/logging.h"
7 #include "net/disk_cache/blockfile/addr.h"
8 #include "net/disk_cache/blockfile/disk_format_v3.h"
9 #include "net/disk_cache/blockfile/index_table_v3.h"
10 #include "testing/gtest/include/gtest/gtest.h"
12 using disk_cache::EntryCell
;
13 using disk_cache::IndexCell
;
14 using disk_cache::IndexTable
;
15 using disk_cache::IndexTableInitData
;
19 int GetChecksum(const IndexCell
& source
) {
20 // Only the cell pointer is relevant.
21 disk_cache::Addr addr
;
22 IndexCell
* cell
= const_cast<IndexCell
*>(&source
);
23 EntryCell entry
= EntryCell::GetEntryCellForTest(0, 0, addr
, cell
, false);
26 entry
.SerializaForTest(&result
);
27 return result
.last_part
>> 6;
30 class MockIndexBackend
: public disk_cache::IndexTableBackend
{
32 MockIndexBackend() : grow_called_(false), buffer_len_(-1) {}
33 ~MockIndexBackend() override
{}
35 bool grow_called() const { return grow_called_
; }
36 int buffer_len() const { return buffer_len_
; }
38 void GrowIndex() override
{ grow_called_
= true; }
39 void SaveIndex(net::IOBuffer
* buffer
, int buffer_len
) override
{
40 buffer_len_
= buffer_len
;
42 void DeleteCell(EntryCell cell
) override
{}
43 void FixCell(EntryCell cell
) override
{}
50 class TestCacheTables
{
52 // |num_entries| is the capacity of the main table. The extra table is half
53 // the size of the main table.
54 explicit TestCacheTables(int num_entries
);
57 void GetInitData(IndexTableInitData
* result
);
58 void CopyFrom(const TestCacheTables
& other
);
59 base::Time
start_time() const { return start_time_
; }
62 scoped_ptr
<uint64
[]> main_bitmap_
;
63 scoped_ptr
<disk_cache::IndexBucket
[]> main_table_
;
64 scoped_ptr
<disk_cache::IndexBucket
[]> extra_table_
;
65 base::Time start_time_
;
66 int num_bitmap_bytes_
;
68 DISALLOW_COPY_AND_ASSIGN(TestCacheTables
);
71 TestCacheTables::TestCacheTables(int num_entries
) {
72 DCHECK_GE(num_entries
, 1024);
73 DCHECK_EQ(num_entries
, num_entries
/ 1024 * 1024);
74 main_table_
.reset(new disk_cache::IndexBucket
[num_entries
]);
75 extra_table_
.reset(new disk_cache::IndexBucket
[num_entries
/ 2]);
76 memset(main_table_
.get(), 0, num_entries
* sizeof(*main_table_
.get()));
77 memset(extra_table_
.get(), 0, num_entries
/ 2 * sizeof(*extra_table_
.get()));
79 // We allow IndexBitmap smaller than a page because the code should not really
81 num_bitmap_bytes_
= (num_entries
+ num_entries
/ 2) / 8;
82 size_t required_size
= sizeof(disk_cache::IndexHeaderV3
) + num_bitmap_bytes_
;
83 main_bitmap_
.reset(new uint64
[required_size
/ sizeof(uint64
)]);
84 memset(main_bitmap_
.get(), 0, required_size
);
86 disk_cache::IndexHeaderV3
* header
=
87 reinterpret_cast<disk_cache::IndexHeaderV3
*>(main_bitmap_
.get());
89 header
->magic
= disk_cache::kIndexMagicV3
;
90 header
->version
= disk_cache::kVersion3
;
91 header
->table_len
= num_entries
+ num_entries
/ 2;
92 header
->max_bucket
= num_entries
/ 4 - 1;
94 start_time_
= base::Time::Now();
95 header
->create_time
= start_time_
.ToInternalValue();
97 (start_time_
- base::TimeDelta::FromDays(20)).ToInternalValue();
99 if (num_entries
< 64 * 1024)
100 header
->flags
= disk_cache::SMALL_CACHE
;
103 void TestCacheTables::GetInitData(IndexTableInitData
* result
) {
104 result
->index_bitmap
=
105 reinterpret_cast<disk_cache::IndexBitmap
*>(main_bitmap_
.get());
107 result
->main_table
= main_table_
.get();
108 result
->extra_table
= extra_table_
.get();
110 result
->backup_header
.reset(new disk_cache::IndexHeaderV3
);
111 memcpy(result
->backup_header
.get(), result
->index_bitmap
,
112 sizeof(result
->index_bitmap
->header
));
114 result
->backup_bitmap
.reset(new uint32
[num_bitmap_bytes_
/ sizeof(uint32
)]);
115 memcpy(result
->backup_bitmap
.get(), result
->index_bitmap
->bitmap
,
119 void TestCacheTables::CopyFrom(const TestCacheTables
& other
) {
120 disk_cache::IndexBitmap
* this_bitmap
=
121 reinterpret_cast<disk_cache::IndexBitmap
*>(main_bitmap_
.get());
122 disk_cache::IndexBitmap
* other_bitmap
=
123 reinterpret_cast<disk_cache::IndexBitmap
*>(other
.main_bitmap_
.get());
125 DCHECK_GE(this_bitmap
->header
.table_len
, other_bitmap
->header
.table_len
);
126 DCHECK_GE(num_bitmap_bytes_
, other
.num_bitmap_bytes_
);
128 memcpy(this_bitmap
->bitmap
, other_bitmap
->bitmap
, other
.num_bitmap_bytes_
);
130 int main_table_buckets
= (other_bitmap
->header
.table_len
* 2 / 3) / 4;
131 int extra_table_buckets
= (other_bitmap
->header
.table_len
* 1 / 3) / 4;
132 memcpy(main_table_
.get(), other
.main_table_
.get(),
133 main_table_buckets
* sizeof(disk_cache::IndexBucket
));
134 memcpy(extra_table_
.get(), other
.extra_table_
.get(),
135 extra_table_buckets
* sizeof(disk_cache::IndexBucket
));
137 this_bitmap
->header
.num_entries
= other_bitmap
->header
.num_entries
;
138 this_bitmap
->header
.used_cells
= other_bitmap
->header
.used_cells
;
139 this_bitmap
->header
.max_bucket
= other_bitmap
->header
.max_bucket
;
140 this_bitmap
->header
.create_time
= other_bitmap
->header
.create_time
;
141 this_bitmap
->header
.base_time
= other_bitmap
->header
.base_time
;
142 this_bitmap
->header
.flags
= other_bitmap
->header
.flags
;
143 start_time_
= other
.start_time_
;
148 TEST(DiskCacheIndexTable
, EntryCell
) {
149 uint32 hash
= 0x55aa6699;
150 disk_cache::Addr
addr(disk_cache::BLOCK_ENTRIES
, 1, 5, 0x4531);
151 bool small_table
= true;
154 int timestamp
= 123456;
155 disk_cache::EntryState state
= disk_cache::ENTRY_MODIFIED
;
156 disk_cache::EntryGroup group
= disk_cache::ENTRY_HIGH_USE
;
158 for (int i
= 0; i
< 4; i
++) {
160 EntryCell entry
= EntryCell::GetEntryCellForTest(cell_num
, hash
, addr
, NULL
,
162 EXPECT_EQ(disk_cache::ENTRY_NO_USE
, entry
.GetGroup());
163 EXPECT_EQ(disk_cache::ENTRY_NEW
, entry
.GetState());
165 entry
.SetGroup(group
);
166 entry
.SetState(state
);
167 entry
.SetReuse(reuse
);
168 entry
.SetTimestamp(timestamp
);
170 EXPECT_TRUE(entry
.IsValid());
171 EXPECT_EQ(hash
, entry
.hash());
172 EXPECT_EQ(cell_num
, entry
.cell_num());
173 EXPECT_EQ(addr
.value(), entry
.GetAddress().value());
175 EXPECT_EQ(group
, entry
.GetGroup());
176 EXPECT_EQ(state
, entry
.GetState());
177 EXPECT_EQ(reuse
, entry
.GetReuse());
178 EXPECT_EQ(timestamp
, entry
.GetTimestamp());
180 // Store the data and read it again.
182 entry
.SerializaForTest(&cell
);
184 EntryCell entry2
= EntryCell::GetEntryCellForTest(cell_num
, hash
, addr
,
187 EXPECT_EQ(addr
.value(), entry2
.GetAddress().value());
189 EXPECT_EQ(group
, entry2
.GetGroup());
190 EXPECT_EQ(state
, entry2
.GetState());
191 EXPECT_EQ(reuse
, entry2
.GetReuse());
192 EXPECT_EQ(timestamp
, entry2
.GetTimestamp());
194 small_table
= !small_table
;
198 state
= disk_cache::ENTRY_USED
;
199 group
= disk_cache::ENTRY_EVICTED
;
200 addr
= disk_cache::Addr(disk_cache::BLOCK_EVICTED
, 1, 6, 0x18a5);
201 reuse
= 15; // 4 bits
202 timestamp
= 0xfffff; // 20 bits.
207 // Goes over some significant values for a cell's sum.
208 TEST(DiskCacheIndexTable
, EntryCellSum
) {
211 EXPECT_EQ(0, GetChecksum(source
));
214 EXPECT_EQ(1, GetChecksum(source
));
217 source
.last_part
= 0x80;
218 EXPECT_EQ(0, GetChecksum(source
));
220 source
.last_part
= 0x55;
221 EXPECT_EQ(3, GetChecksum(source
));
223 source
.first_part
= 0x555555;
224 EXPECT_EQ(2, GetChecksum(source
));
226 source
.last_part
= 0;
227 EXPECT_EQ(1, GetChecksum(source
));
229 source
.first_part
= GG_UINT64_C(0x8000000080000000);
230 EXPECT_EQ(0, GetChecksum(source
));
232 source
.first_part
= GG_UINT64_C(0x4000000040000000);
233 EXPECT_EQ(2, GetChecksum(source
));
235 source
.first_part
= GG_UINT64_C(0x200000020000000);
236 EXPECT_EQ(1, GetChecksum(source
));
238 source
.first_part
= GG_UINT64_C(0x100000010010000);
239 EXPECT_EQ(3, GetChecksum(source
));
241 source
.first_part
= 0x80008000;
242 EXPECT_EQ(0, GetChecksum(source
));
244 source
.first_part
= GG_UINT64_C(0x800000008000);
245 EXPECT_EQ(1, GetChecksum(source
));
247 source
.first_part
= 0x8080;
248 EXPECT_EQ(0, GetChecksum(source
));
250 source
.first_part
= 0x800080;
251 EXPECT_EQ(1, GetChecksum(source
));
253 source
.first_part
= 0x88;
254 EXPECT_EQ(0, GetChecksum(source
));
256 source
.first_part
= 0x808;
257 EXPECT_EQ(1, GetChecksum(source
));
259 source
.first_part
= 0xA;
260 EXPECT_EQ(0, GetChecksum(source
));
262 source
.first_part
= 0x22;
263 EXPECT_EQ(1, GetChecksum(source
));
266 TEST(DiskCacheIndexTable
, Basics
) {
267 TestCacheTables
cache(1024);
268 IndexTableInitData init_data
;
269 cache
.GetInitData(&init_data
);
271 IndexTable
index(NULL
);
272 index
.Init(&init_data
);
274 // Write some entries.
275 disk_cache::CellList entries
;
276 for (int i
= 0; i
< 250; i
++) {
278 uint32 hash
= i
* i
* 1111 + i
* 11;
279 disk_cache::Addr
addr(disk_cache::BLOCK_ENTRIES
, 1, 5, i
* 13 + 1);
280 EntryCell entry
= index
.CreateEntryCell(hash
, addr
);
281 EXPECT_TRUE(entry
.IsValid());
283 disk_cache::CellInfo info
= { hash
, addr
};
284 entries
.push_back(info
);
288 for (size_t i
= 0; i
< entries
.size(); i
++) {
290 uint32 hash
= entries
[i
].hash
;
291 disk_cache::Addr addr
= entries
[i
].address
;
293 disk_cache::EntrySet found_entries
= index
.LookupEntries(hash
);
294 ASSERT_EQ(1u, found_entries
.cells
.size());
295 EXPECT_TRUE(found_entries
.cells
[0].IsValid());
296 EXPECT_EQ(hash
, found_entries
.cells
[0].hash());
297 EXPECT_EQ(addr
.value(), found_entries
.cells
[0].GetAddress().value());
299 EntryCell entry
= index
.FindEntryCell(hash
, addr
);
300 EXPECT_TRUE(entry
.IsValid());
301 EXPECT_EQ(hash
, entry
.hash());
302 EXPECT_EQ(addr
.value(), entry
.GetAddress().value());
304 // Delete the first 100 entries.
306 index
.SetSate(hash
, addr
, disk_cache::ENTRY_DELETED
);
309 // See what we have now.
310 for (size_t i
= 0; i
< entries
.size(); i
++) {
312 uint32 hash
= entries
[i
].hash
;
313 disk_cache::Addr addr
= entries
[i
].address
;
315 disk_cache::EntrySet found_entries
= index
.LookupEntries(hash
);
317 EXPECT_EQ(0u, found_entries
.cells
.size());
319 ASSERT_EQ(1u, found_entries
.cells
.size());
320 EXPECT_TRUE(found_entries
.cells
[0].IsValid());
321 EXPECT_EQ(hash
, found_entries
.cells
[0].hash());
322 EXPECT_EQ(addr
.value(), found_entries
.cells
[0].GetAddress().value());
327 // Tests handling of multiple entries with the same hash.
328 TEST(DiskCacheIndexTable
, SameHash
) {
329 TestCacheTables
cache(1024);
330 IndexTableInitData init_data
;
331 cache
.GetInitData(&init_data
);
333 IndexTable
index(NULL
);
334 index
.Init(&init_data
);
336 disk_cache::CellList entries
;
337 uint32 hash
= 0x55aa55bb;
338 for (int i
= 0; i
< 6; i
++) {
340 disk_cache::Addr
addr(disk_cache::BLOCK_ENTRIES
, 1, 5, i
* 13 + 1);
341 EntryCell entry
= index
.CreateEntryCell(hash
, addr
);
342 EXPECT_TRUE(entry
.IsValid());
344 disk_cache::CellInfo info
= { hash
, addr
};
345 entries
.push_back(info
);
348 disk_cache::EntrySet found_entries
= index
.LookupEntries(hash
);
349 EXPECT_EQ(0, found_entries
.evicted_count
);
350 ASSERT_EQ(6u, found_entries
.cells
.size());
352 for (size_t i
= 0; i
< found_entries
.cells
.size(); i
++) {
354 EXPECT_EQ(entries
[i
].address
, found_entries
.cells
[i
].GetAddress());
357 // Now verify handling of entries on different states.
358 index
.SetSate(hash
, entries
[0].address
, disk_cache::ENTRY_DELETED
);
359 index
.SetSate(hash
, entries
[1].address
, disk_cache::ENTRY_DELETED
);
360 index
.SetSate(hash
, entries
[2].address
, disk_cache::ENTRY_USED
);
361 index
.SetSate(hash
, entries
[3].address
, disk_cache::ENTRY_USED
);
362 index
.SetSate(hash
, entries
[4].address
, disk_cache::ENTRY_USED
);
364 found_entries
= index
.LookupEntries(hash
);
365 EXPECT_EQ(0, found_entries
.evicted_count
);
366 ASSERT_EQ(4u, found_entries
.cells
.size());
368 index
.SetSate(hash
, entries
[3].address
, disk_cache::ENTRY_OPEN
);
369 index
.SetSate(hash
, entries
[4].address
, disk_cache::ENTRY_OPEN
);
371 found_entries
= index
.LookupEntries(hash
);
372 EXPECT_EQ(0, found_entries
.evicted_count
);
373 ASSERT_EQ(4u, found_entries
.cells
.size());
375 index
.SetSate(hash
, entries
[4].address
, disk_cache::ENTRY_MODIFIED
);
377 found_entries
= index
.LookupEntries(hash
);
378 EXPECT_EQ(0, found_entries
.evicted_count
);
379 ASSERT_EQ(4u, found_entries
.cells
.size());
381 index
.SetSate(hash
, entries
[1].address
, disk_cache::ENTRY_FREE
);
383 found_entries
= index
.LookupEntries(hash
);
384 EXPECT_EQ(0, found_entries
.evicted_count
);
385 ASSERT_EQ(4u, found_entries
.cells
.size());
387 // FindEntryCell should not see deleted entries.
388 EntryCell entry
= index
.FindEntryCell(hash
, entries
[0].address
);
389 EXPECT_FALSE(entry
.IsValid());
391 // A free entry is gone.
392 entry
= index
.FindEntryCell(hash
, entries
[1].address
);
393 EXPECT_FALSE(entry
.IsValid());
395 // Locate a used entry, and evict it. This is not really a correct operation
396 // in that an existing cell doesn't transition to evicted; instead a new cell
397 // for the evicted entry (on a different block file) should be created. Still,
398 // at least evicted_count would be valid.
399 entry
= index
.FindEntryCell(hash
, entries
[2].address
);
400 EXPECT_TRUE(entry
.IsValid());
401 entry
.SetGroup(disk_cache::ENTRY_EVICTED
);
404 found_entries
= index
.LookupEntries(hash
);
405 EXPECT_EQ(1, found_entries
.evicted_count
);
406 ASSERT_EQ(4u, found_entries
.cells
.size());
408 // Now use the proper way to get an evicted entry.
409 disk_cache::Addr
addr2(disk_cache::BLOCK_EVICTED
, 1, 6, 6); // Any address.
410 entry
= index
.CreateEntryCell(hash
, addr2
);
411 EXPECT_TRUE(entry
.IsValid());
412 EXPECT_EQ(disk_cache::ENTRY_EVICTED
, entry
.GetGroup());
414 found_entries
= index
.LookupEntries(hash
);
415 EXPECT_EQ(2, found_entries
.evicted_count
);
416 ASSERT_EQ(5u, found_entries
.cells
.size());
419 TEST(DiskCacheIndexTable
, Timestamps
) {
420 TestCacheTables
cache(1024);
421 IndexTableInitData init_data
;
422 cache
.GetInitData(&init_data
);
424 IndexTable
index(NULL
);
425 index
.Init(&init_data
);
427 // The granularity should be 1 minute.
428 int timestamp1
= index
.CalculateTimestamp(cache
.start_time());
429 int timestamp2
= index
.CalculateTimestamp(cache
.start_time() +
430 base::TimeDelta::FromSeconds(59));
431 EXPECT_EQ(timestamp1
, timestamp2
);
433 int timestamp3
= index
.CalculateTimestamp(cache
.start_time() +
434 base::TimeDelta::FromSeconds(61));
435 EXPECT_EQ(timestamp1
+ 1, timestamp3
);
437 int timestamp4
= index
.CalculateTimestamp(cache
.start_time() +
438 base::TimeDelta::FromSeconds(119));
439 EXPECT_EQ(timestamp1
+ 1, timestamp4
);
441 int timestamp5
= index
.CalculateTimestamp(cache
.start_time() +
442 base::TimeDelta::FromSeconds(121));
443 EXPECT_EQ(timestamp1
+ 2, timestamp5
);
445 int timestamp6
= index
.CalculateTimestamp(cache
.start_time() -
446 base::TimeDelta::FromSeconds(30));
447 EXPECT_EQ(timestamp1
- 1, timestamp6
);
449 // The base should be 20 days in the past.
450 int timestamp7
= index
.CalculateTimestamp(cache
.start_time() -
451 base::TimeDelta::FromDays(20));
452 int timestamp8
= index
.CalculateTimestamp(cache
.start_time() -
453 base::TimeDelta::FromDays(35));
454 EXPECT_EQ(timestamp7
, timestamp8
);
455 EXPECT_EQ(0, timestamp8
);
457 int timestamp9
= index
.CalculateTimestamp(cache
.start_time() -
458 base::TimeDelta::FromDays(19));
459 EXPECT_NE(0, timestamp9
);
462 // Tests GetOldest and GetNextCells.
463 TEST(DiskCacheIndexTable
, Iterations
) {
464 TestCacheTables
cache(1024);
465 IndexTableInitData init_data
;
466 cache
.GetInitData(&init_data
);
468 IndexTable
index(NULL
);
469 index
.Init(&init_data
);
471 base::Time time
= cache
.start_time();
473 // Write some entries.
474 disk_cache::CellList entries
;
475 for (int i
= 0; i
< 44; i
++) {
477 uint32 hash
= i
; // The entries will be ordered on the table.
478 disk_cache::Addr
addr(disk_cache::BLOCK_ENTRIES
, 1, 5, i
* 13 + 1);
479 if (i
< 10 || i
== 40)
480 addr
= disk_cache::Addr(disk_cache::BLOCK_EVICTED
, 1, 6, i
* 13 + 1);
482 EntryCell entry
= index
.CreateEntryCell(hash
, addr
);
483 EXPECT_TRUE(entry
.IsValid());
485 disk_cache::CellInfo info
= { hash
, addr
};
486 entries
.push_back(info
);
488 if (i
< 10 || i
== 40) {
489 // Do nothing. These are ENTRY_EVICTED by default.
490 } else if (i
< 20 || i
== 41) {
491 entry
.SetGroup(disk_cache::ENTRY_HIGH_USE
);
493 } else if (i
< 30 || i
== 42) {
494 entry
.SetGroup(disk_cache::ENTRY_LOW_USE
);
498 // Entries [30,39] and 43 are marked as ENTRY_NO_USE (the default).
501 time
+= base::TimeDelta::FromMinutes(1);
503 index
.UpdateTime(hash
, addr
, time
);
506 // Get the oldest entries of each group.
507 disk_cache::IndexIterator no_use
, low_use
, high_use
;
508 index
.GetOldest(&no_use
, &low_use
, &high_use
);
509 ASSERT_EQ(10u, no_use
.cells
.size());
510 ASSERT_EQ(10u, low_use
.cells
.size());
511 ASSERT_EQ(10u, high_use
.cells
.size());
513 EXPECT_EQ(entries
[10].hash
, high_use
.cells
[0].hash
);
514 EXPECT_EQ(entries
[19].hash
, high_use
.cells
[9].hash
);
515 EXPECT_EQ(entries
[20].hash
, low_use
.cells
[0].hash
);
516 EXPECT_EQ(entries
[29].hash
, low_use
.cells
[9].hash
);
517 EXPECT_EQ(entries
[30].hash
, no_use
.cells
[0].hash
);
518 EXPECT_EQ(entries
[39].hash
, no_use
.cells
[9].hash
);
520 // Now start an iteration from the head (most recent entry).
521 disk_cache::IndexIterator iterator
;
522 iterator
.timestamp
= index
.CalculateTimestamp(time
) + 1;
523 iterator
.forward
= false; // Back in time.
525 ASSERT_TRUE(index
.GetNextCells(&iterator
));
526 ASSERT_EQ(3u, iterator
.cells
.size());
527 EXPECT_EQ(entries
[41].hash
, iterator
.cells
[0].hash
);
528 EXPECT_EQ(entries
[42].hash
, iterator
.cells
[1].hash
);
529 EXPECT_EQ(entries
[43].hash
, iterator
.cells
[2].hash
);
531 ASSERT_TRUE(index
.GetNextCells(&iterator
));
532 ASSERT_EQ(10u, iterator
.cells
.size());
533 EXPECT_EQ(entries
[30].hash
, iterator
.cells
[0].hash
);
534 EXPECT_EQ(entries
[39].hash
, iterator
.cells
[9].hash
);
536 ASSERT_TRUE(index
.GetNextCells(&iterator
));
537 ASSERT_EQ(10u, iterator
.cells
.size());
538 EXPECT_EQ(entries
[20].hash
, iterator
.cells
[0].hash
);
539 EXPECT_EQ(entries
[29].hash
, iterator
.cells
[9].hash
);
541 ASSERT_TRUE(index
.GetNextCells(&iterator
));
542 ASSERT_EQ(10u, iterator
.cells
.size());
543 EXPECT_EQ(entries
[10].hash
, iterator
.cells
[0].hash
);
544 EXPECT_EQ(entries
[19].hash
, iterator
.cells
[9].hash
);
546 ASSERT_FALSE(index
.GetNextCells(&iterator
));
548 // Now start an iteration from the tail (oldest entry).
549 iterator
.timestamp
= 0;
550 iterator
.forward
= true;
552 ASSERT_TRUE(index
.GetNextCells(&iterator
));
553 ASSERT_EQ(10u, iterator
.cells
.size());
554 EXPECT_EQ(entries
[10].hash
, iterator
.cells
[0].hash
);
555 EXPECT_EQ(entries
[19].hash
, iterator
.cells
[9].hash
);
557 ASSERT_TRUE(index
.GetNextCells(&iterator
));
558 ASSERT_EQ(10u, iterator
.cells
.size());
559 EXPECT_EQ(entries
[20].hash
, iterator
.cells
[0].hash
);
560 EXPECT_EQ(entries
[29].hash
, iterator
.cells
[9].hash
);
562 ASSERT_TRUE(index
.GetNextCells(&iterator
));
563 ASSERT_EQ(10u, iterator
.cells
.size());
564 EXPECT_EQ(entries
[30].hash
, iterator
.cells
[0].hash
);
565 EXPECT_EQ(entries
[39].hash
, iterator
.cells
[9].hash
);
567 ASSERT_TRUE(index
.GetNextCells(&iterator
));
568 ASSERT_EQ(3u, iterator
.cells
.size());
569 EXPECT_EQ(entries
[41].hash
, iterator
.cells
[0].hash
);
570 EXPECT_EQ(entries
[42].hash
, iterator
.cells
[1].hash
);
571 EXPECT_EQ(entries
[43].hash
, iterator
.cells
[2].hash
);
574 // Tests doubling of the table.
575 TEST(DiskCacheIndexTable
, Doubling
) {
576 IndexTable
index(NULL
);
578 scoped_ptr
<TestCacheTables
> cache(new TestCacheTables(size
));
580 disk_cache::CellList entries
;
582 // Go from 1024 to 256k cells.
583 for (int resizes
= 0; resizes
<= 8; resizes
++) {
584 scoped_ptr
<TestCacheTables
> old_cache(cache
.Pass());
585 cache
.reset(new TestCacheTables(size
));
586 cache
.get()->CopyFrom(*old_cache
.get());
588 IndexTableInitData init_data
;
589 cache
.get()->GetInitData(&init_data
);
590 index
.Init(&init_data
);
592 // Write some entries.
593 for (int i
= 0; i
< 250; i
++, entry_id
++) {
594 SCOPED_TRACE(entry_id
);
595 uint32 hash
= entry_id
* i
* 321 + entry_id
* 13;
596 disk_cache::Addr
addr(disk_cache::BLOCK_ENTRIES
, 1, 5, entry_id
* 17 + 1);
597 EntryCell entry
= index
.CreateEntryCell(hash
, addr
);
598 EXPECT_TRUE(entry
.IsValid());
600 disk_cache::CellInfo info
= { hash
, addr
};
601 entries
.push_back(info
);
606 // Access all the entries.
607 for (size_t i
= 0; i
< entries
.size(); i
++) {
609 disk_cache::EntrySet found_entries
= index
.LookupEntries(entries
[i
].hash
);
610 ASSERT_EQ(1u, found_entries
.cells
.size());
611 EXPECT_TRUE(found_entries
.cells
[0].IsValid());
615 // Tests bucket chaining when growing the index.
616 TEST(DiskCacheIndexTable
, BucketChains
) {
617 IndexTable
index(NULL
);
619 scoped_ptr
<TestCacheTables
> cache(new TestCacheTables(size
));
620 disk_cache::CellList entries
;
622 IndexTableInitData init_data
;
623 cache
.get()->GetInitData(&init_data
);
624 index
.Init(&init_data
);
626 // Write some entries.
627 for (int i
= 0; i
< 8; i
++) {
629 uint32 hash
= i
* 256;
630 disk_cache::Addr
addr(disk_cache::BLOCK_ENTRIES
, 1, 5, i
* 7 + 1);
631 EntryCell entry
= index
.CreateEntryCell(hash
, addr
);
632 EXPECT_TRUE(entry
.IsValid());
634 disk_cache::CellInfo info
= { hash
, addr
};
635 entries
.push_back(info
);
639 scoped_ptr
<TestCacheTables
> old_cache(cache
.Pass());
640 cache
.reset(new TestCacheTables(size
* 2));
641 cache
.get()->CopyFrom(*old_cache
.get());
643 cache
.get()->GetInitData(&init_data
);
644 index
.Init(&init_data
);
646 // Write more entries, starting with the upper half of the table.
647 for (int i
= 9; i
< 11; i
++) {
649 uint32 hash
= i
* 256;
650 disk_cache::Addr
addr(disk_cache::BLOCK_ENTRIES
, 1, 5, i
* 7 + 1);
651 EntryCell entry
= index
.CreateEntryCell(hash
, addr
);
652 EXPECT_TRUE(entry
.IsValid());
654 disk_cache::CellInfo info
= { hash
, addr
};
655 entries
.push_back(info
);
658 // Access all the entries.
659 for (size_t i
= 0; i
< entries
.size(); i
++) {
661 disk_cache::EntrySet found_entries
= index
.LookupEntries(entries
[i
].hash
);
662 ASSERT_EQ(1u, found_entries
.cells
.size());
663 EXPECT_TRUE(found_entries
.cells
[0].IsValid());
667 // Tests that GrowIndex is called.
668 TEST(DiskCacheIndexTable
, GrowIndex
) {
669 TestCacheTables
cache(1024);
670 IndexTableInitData init_data
;
671 cache
.GetInitData(&init_data
);
672 MockIndexBackend backend
;
674 IndexTable
index(&backend
);
675 index
.Init(&init_data
);
677 // Write some entries.
678 for (int i
= 0; i
< 512; i
++) {
681 disk_cache::Addr
addr(disk_cache::BLOCK_ENTRIES
, 1, 5, i
+ 1);
682 EntryCell entry
= index
.CreateEntryCell(hash
, addr
);
683 EXPECT_TRUE(entry
.IsValid());
686 EXPECT_TRUE(backend
.grow_called());
689 TEST(DiskCacheIndexTable
, SaveIndex
) {
690 TestCacheTables
cache(1024);
691 IndexTableInitData init_data
;
692 cache
.GetInitData(&init_data
);
693 MockIndexBackend backend
;
695 IndexTable
index(&backend
);
696 index
.Init(&init_data
);
699 disk_cache::Addr
addr(disk_cache::BLOCK_ENTRIES
, 1, 5, 6);
700 EntryCell entry
= index
.CreateEntryCell(hash
, addr
);
701 EXPECT_TRUE(entry
.IsValid());
703 index
.OnBackupTimer();
704 int expected
= (1024 + 512) / 8 + sizeof(disk_cache::IndexHeaderV3
);
705 EXPECT_EQ(expected
, backend
.buffer_len());