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 "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" 11 12 using disk_cache::EntryCell; 13 using disk_cache::IndexCell; 14 using disk_cache::IndexTable; 15 using disk_cache::IndexTableInitData; 16 17 namespace { 18 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); 24 25 IndexCell result; 26 entry.SerializaForTest(&result); 27 return result.last_part >> 6; 28 } 29 30 class MockIndexBackend : public disk_cache::IndexTableBackend { 31 public: 32 MockIndexBackend() : grow_called_(false), buffer_len_(-1) {} 33 virtual ~MockIndexBackend() {} 34 35 bool grow_called() const { return grow_called_; } 36 int buffer_len() const { return buffer_len_; } 37 38 virtual void GrowIndex() OVERRIDE { grow_called_ = true; } 39 virtual void SaveIndex(net::IOBuffer* buffer, int buffer_len) OVERRIDE { 40 buffer_len_ = buffer_len; 41 } 42 virtual void DeleteCell(EntryCell cell) OVERRIDE {} 43 virtual void FixCell(EntryCell cell) OVERRIDE {} 44 45 private: 46 bool grow_called_; 47 int buffer_len_; 48 }; 49 50 class TestCacheTables { 51 public: 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); 55 ~TestCacheTables() {} 56 57 void GetInitData(IndexTableInitData* result); 58 void CopyFrom(const TestCacheTables& other); 59 base::Time start_time() const { return start_time_; } 60 61 private: 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_; 67 68 DISALLOW_COPY_AND_ASSIGN(TestCacheTables); 69 }; 70 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())); 78 79 // We allow IndexBitmap smaller than a page because the code should not really 80 // depend on that. 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); 85 86 disk_cache::IndexHeaderV3* header = 87 reinterpret_cast<disk_cache::IndexHeaderV3*>(main_bitmap_.get()); 88 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; 93 94 start_time_ = base::Time::Now(); 95 header->create_time = start_time_.ToInternalValue(); 96 header->base_time = 97 (start_time_ - base::TimeDelta::FromDays(20)).ToInternalValue(); 98 99 if (num_entries < 64 * 1024) 100 header->flags = disk_cache::SMALL_CACHE; 101 } 102 103 void TestCacheTables::GetInitData(IndexTableInitData* result) { 104 result->index_bitmap = 105 reinterpret_cast<disk_cache::IndexBitmap*>(main_bitmap_.get()); 106 107 result->main_table = main_table_.get(); 108 result->extra_table = extra_table_.get(); 109 110 result->backup_header.reset(new disk_cache::IndexHeaderV3); 111 memcpy(result->backup_header.get(), result->index_bitmap, 112 sizeof(result->index_bitmap->header)); 113 114 result->backup_bitmap.reset(new uint32[num_bitmap_bytes_ / sizeof(uint32)]); 115 memcpy(result->backup_bitmap.get(), result->index_bitmap->bitmap, 116 num_bitmap_bytes_); 117 } 118 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()); 124 125 DCHECK_GE(this_bitmap->header.table_len, other_bitmap->header.table_len); 126 DCHECK_GE(num_bitmap_bytes_, other.num_bitmap_bytes_); 127 128 memcpy(this_bitmap->bitmap, other_bitmap->bitmap, other.num_bitmap_bytes_); 129 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)); 136 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_; 144 } 145 146 } // namespace 147 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; 152 int cell_num = 88; 153 int reuse = 6; 154 int timestamp = 123456; 155 disk_cache::EntryState state = disk_cache::ENTRY_MODIFIED; 156 disk_cache::EntryGroup group = disk_cache::ENTRY_HIGH_USE; 157 158 for (int i = 0; i < 4; i++) { 159 SCOPED_TRACE(i); 160 EntryCell entry = EntryCell::GetEntryCellForTest(cell_num, hash, addr, NULL, 161 small_table); 162 EXPECT_EQ(disk_cache::ENTRY_NO_USE, entry.GetGroup()); 163 EXPECT_EQ(disk_cache::ENTRY_NEW, entry.GetState()); 164 165 entry.SetGroup(group); 166 entry.SetState(state); 167 entry.SetReuse(reuse); 168 entry.SetTimestamp(timestamp); 169 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()); 174 175 EXPECT_EQ(group, entry.GetGroup()); 176 EXPECT_EQ(state, entry.GetState()); 177 EXPECT_EQ(reuse, entry.GetReuse()); 178 EXPECT_EQ(timestamp, entry.GetTimestamp()); 179 180 // Store the data and read it again. 181 IndexCell cell; 182 entry.SerializaForTest(&cell); 183 184 EntryCell entry2 = EntryCell::GetEntryCellForTest(cell_num, hash, addr, 185 &cell, small_table); 186 187 EXPECT_EQ(addr.value(), entry2.GetAddress().value()); 188 189 EXPECT_EQ(group, entry2.GetGroup()); 190 EXPECT_EQ(state, entry2.GetState()); 191 EXPECT_EQ(reuse, entry2.GetReuse()); 192 EXPECT_EQ(timestamp, entry2.GetTimestamp()); 193 194 small_table = !small_table; 195 if (i == 1) { 196 hash = ~hash; 197 cell_num *= 5; 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. 203 } 204 } 205 } 206 207 // Goes over some significant values for a cell's sum. 208 TEST(DiskCacheIndexTable, EntryCellSum) { 209 IndexCell source; 210 source.Clear(); 211 EXPECT_EQ(0, GetChecksum(source)); 212 213 source.first_part++; 214 EXPECT_EQ(1, GetChecksum(source)); 215 216 source.Clear(); 217 source.last_part = 0x80; 218 EXPECT_EQ(0, GetChecksum(source)); 219 220 source.last_part = 0x55; 221 EXPECT_EQ(3, GetChecksum(source)); 222 223 source.first_part = 0x555555; 224 EXPECT_EQ(2, GetChecksum(source)); 225 226 source.last_part = 0; 227 EXPECT_EQ(1, GetChecksum(source)); 228 229 source.first_part = GG_UINT64_C(0x8000000080000000); 230 EXPECT_EQ(0, GetChecksum(source)); 231 232 source.first_part = GG_UINT64_C(0x4000000040000000); 233 EXPECT_EQ(2, GetChecksum(source)); 234 235 source.first_part = GG_UINT64_C(0x200000020000000); 236 EXPECT_EQ(1, GetChecksum(source)); 237 238 source.first_part = GG_UINT64_C(0x100000010010000); 239 EXPECT_EQ(3, GetChecksum(source)); 240 241 source.first_part = 0x80008000; 242 EXPECT_EQ(0, GetChecksum(source)); 243 244 source.first_part = GG_UINT64_C(0x800000008000); 245 EXPECT_EQ(1, GetChecksum(source)); 246 247 source.first_part = 0x8080; 248 EXPECT_EQ(0, GetChecksum(source)); 249 250 source.first_part = 0x800080; 251 EXPECT_EQ(1, GetChecksum(source)); 252 253 source.first_part = 0x88; 254 EXPECT_EQ(0, GetChecksum(source)); 255 256 source.first_part = 0x808; 257 EXPECT_EQ(1, GetChecksum(source)); 258 259 source.first_part = 0xA; 260 EXPECT_EQ(0, GetChecksum(source)); 261 262 source.first_part = 0x22; 263 EXPECT_EQ(1, GetChecksum(source)); 264 } 265 266 TEST(DiskCacheIndexTable, Basics) { 267 TestCacheTables cache(1024); 268 IndexTableInitData init_data; 269 cache.GetInitData(&init_data); 270 271 IndexTable index(NULL); 272 index.Init(&init_data); 273 274 // Write some entries. 275 disk_cache::CellList entries; 276 for (int i = 0; i < 250; i++) { 277 SCOPED_TRACE(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()); 282 283 disk_cache::CellInfo info = { hash, addr }; 284 entries.push_back(info); 285 } 286 287 // Read them back. 288 for (size_t i = 0; i < entries.size(); i++) { 289 SCOPED_TRACE(i); 290 uint32 hash = entries[i].hash; 291 disk_cache::Addr addr = entries[i].address; 292 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()); 298 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()); 303 304 // Delete the first 100 entries. 305 if (i < 100) 306 index.SetSate(hash, addr, disk_cache::ENTRY_DELETED); 307 } 308 309 // See what we have now. 310 for (size_t i = 0; i < entries.size(); i++) { 311 SCOPED_TRACE(i); 312 uint32 hash = entries[i].hash; 313 disk_cache::Addr addr = entries[i].address; 314 315 disk_cache::EntrySet found_entries = index.LookupEntries(hash); 316 if (i < 100) { 317 EXPECT_EQ(0u, found_entries.cells.size()); 318 } else { 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()); 323 } 324 } 325 } 326 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); 332 333 IndexTable index(NULL); 334 index.Init(&init_data); 335 336 disk_cache::CellList entries; 337 uint32 hash = 0x55aa55bb; 338 for (int i = 0; i < 6; i++) { 339 SCOPED_TRACE(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()); 343 344 disk_cache::CellInfo info = { hash, addr }; 345 entries.push_back(info); 346 } 347 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()); 351 352 for (size_t i = 0; i < found_entries.cells.size(); i++) { 353 SCOPED_TRACE(i); 354 EXPECT_EQ(entries[i].address, found_entries.cells[i].GetAddress()); 355 } 356 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); 363 364 found_entries = index.LookupEntries(hash); 365 EXPECT_EQ(0, found_entries.evicted_count); 366 ASSERT_EQ(4u, found_entries.cells.size()); 367 368 index.SetSate(hash, entries[3].address, disk_cache::ENTRY_OPEN); 369 index.SetSate(hash, entries[4].address, disk_cache::ENTRY_OPEN); 370 371 found_entries = index.LookupEntries(hash); 372 EXPECT_EQ(0, found_entries.evicted_count); 373 ASSERT_EQ(4u, found_entries.cells.size()); 374 375 index.SetSate(hash, entries[4].address, disk_cache::ENTRY_MODIFIED); 376 377 found_entries = index.LookupEntries(hash); 378 EXPECT_EQ(0, found_entries.evicted_count); 379 ASSERT_EQ(4u, found_entries.cells.size()); 380 381 index.SetSate(hash, entries[1].address, disk_cache::ENTRY_FREE); 382 383 found_entries = index.LookupEntries(hash); 384 EXPECT_EQ(0, found_entries.evicted_count); 385 ASSERT_EQ(4u, found_entries.cells.size()); 386 387 // FindEntryCell should not see deleted entries. 388 EntryCell entry = index.FindEntryCell(hash, entries[0].address); 389 EXPECT_FALSE(entry.IsValid()); 390 391 // A free entry is gone. 392 entry = index.FindEntryCell(hash, entries[1].address); 393 EXPECT_FALSE(entry.IsValid()); 394 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); 402 index.Save(&entry); 403 404 found_entries = index.LookupEntries(hash); 405 EXPECT_EQ(1, found_entries.evicted_count); 406 ASSERT_EQ(4u, found_entries.cells.size()); 407 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()); 413 414 found_entries = index.LookupEntries(hash); 415 EXPECT_EQ(2, found_entries.evicted_count); 416 ASSERT_EQ(5u, found_entries.cells.size()); 417 } 418 419 TEST(DiskCacheIndexTable, Timestamps) { 420 TestCacheTables cache(1024); 421 IndexTableInitData init_data; 422 cache.GetInitData(&init_data); 423 424 IndexTable index(NULL); 425 index.Init(&init_data); 426 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); 432 433 int timestamp3 = index.CalculateTimestamp(cache.start_time() + 434 base::TimeDelta::FromSeconds(61)); 435 EXPECT_EQ(timestamp1 + 1, timestamp3); 436 437 int timestamp4 = index.CalculateTimestamp(cache.start_time() + 438 base::TimeDelta::FromSeconds(119)); 439 EXPECT_EQ(timestamp1 + 1, timestamp4); 440 441 int timestamp5 = index.CalculateTimestamp(cache.start_time() + 442 base::TimeDelta::FromSeconds(121)); 443 EXPECT_EQ(timestamp1 + 2, timestamp5); 444 445 int timestamp6 = index.CalculateTimestamp(cache.start_time() - 446 base::TimeDelta::FromSeconds(30)); 447 EXPECT_EQ(timestamp1 - 1, timestamp6); 448 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); 456 457 int timestamp9 = index.CalculateTimestamp(cache.start_time() - 458 base::TimeDelta::FromDays(19)); 459 EXPECT_NE(0, timestamp9); 460 } 461 462 // Tests GetOldest and GetNextCells. 463 TEST(DiskCacheIndexTable, Iterations) { 464 TestCacheTables cache(1024); 465 IndexTableInitData init_data; 466 cache.GetInitData(&init_data); 467 468 IndexTable index(NULL); 469 index.Init(&init_data); 470 471 base::Time time = cache.start_time(); 472 473 // Write some entries. 474 disk_cache::CellList entries; 475 for (int i = 0; i < 44; i++) { 476 SCOPED_TRACE(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); 481 482 EntryCell entry = index.CreateEntryCell(hash, addr); 483 EXPECT_TRUE(entry.IsValid()); 484 485 disk_cache::CellInfo info = { hash, addr }; 486 entries.push_back(info); 487 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); 492 index.Save(&entry); 493 } else if (i < 30 || i == 42) { 494 entry.SetGroup(disk_cache::ENTRY_LOW_USE); 495 index.Save(&entry); 496 } 497 498 // Entries [30,39] and 43 are marked as ENTRY_NO_USE (the default). 499 500 if (!(i % 10)) 501 time += base::TimeDelta::FromMinutes(1); 502 503 index.UpdateTime(hash, addr, time); 504 } 505 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()); 512 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); 519 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. 524 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); 530 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); 535 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); 540 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); 545 546 ASSERT_FALSE(index.GetNextCells(&iterator)); 547 548 // Now start an iteration from the tail (oldest entry). 549 iterator.timestamp = 0; 550 iterator.forward = true; 551 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); 556 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); 561 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); 566 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); 572 } 573 574 // Tests doubling of the table. 575 TEST(DiskCacheIndexTable, Doubling) { 576 IndexTable index(NULL); 577 int size = 1024; 578 scoped_ptr<TestCacheTables> cache(new TestCacheTables(size)); 579 int entry_id = 0; 580 disk_cache::CellList entries; 581 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()); 587 588 IndexTableInitData init_data; 589 cache.get()->GetInitData(&init_data); 590 index.Init(&init_data); 591 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()); 599 600 disk_cache::CellInfo info = { hash, addr }; 601 entries.push_back(info); 602 } 603 size *= 2; 604 } 605 606 // Access all the entries. 607 for (size_t i = 0; i < entries.size(); i++) { 608 SCOPED_TRACE(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()); 612 } 613 } 614 615 // Tests bucket chaining when growing the index. 616 TEST(DiskCacheIndexTable, BucketChains) { 617 IndexTable index(NULL); 618 int size = 1024; 619 scoped_ptr<TestCacheTables> cache(new TestCacheTables(size)); 620 disk_cache::CellList entries; 621 622 IndexTableInitData init_data; 623 cache.get()->GetInitData(&init_data); 624 index.Init(&init_data); 625 626 // Write some entries. 627 for (int i = 0; i < 8; i++) { 628 SCOPED_TRACE(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()); 633 634 disk_cache::CellInfo info = { hash, addr }; 635 entries.push_back(info); 636 } 637 638 // Double the size. 639 scoped_ptr<TestCacheTables> old_cache(cache.Pass()); 640 cache.reset(new TestCacheTables(size * 2)); 641 cache.get()->CopyFrom(*old_cache.get()); 642 643 cache.get()->GetInitData(&init_data); 644 index.Init(&init_data); 645 646 // Write more entries, starting with the upper half of the table. 647 for (int i = 9; i < 11; i++) { 648 SCOPED_TRACE(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()); 653 654 disk_cache::CellInfo info = { hash, addr }; 655 entries.push_back(info); 656 } 657 658 // Access all the entries. 659 for (size_t i = 0; i < entries.size(); i++) { 660 SCOPED_TRACE(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()); 664 } 665 } 666 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; 673 674 IndexTable index(&backend); 675 index.Init(&init_data); 676 677 // Write some entries. 678 for (int i = 0; i < 512; i++) { 679 SCOPED_TRACE(i); 680 uint32 hash = 0; 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()); 684 } 685 686 EXPECT_TRUE(backend.grow_called()); 687 } 688 689 TEST(DiskCacheIndexTable, SaveIndex) { 690 TestCacheTables cache(1024); 691 IndexTableInitData init_data; 692 cache.GetInitData(&init_data); 693 MockIndexBackend backend; 694 695 IndexTable index(&backend); 696 index.Init(&init_data); 697 698 uint32 hash = 0; 699 disk_cache::Addr addr(disk_cache::BLOCK_ENTRIES, 1, 5, 6); 700 EntryCell entry = index.CreateEntryCell(hash, addr); 701 EXPECT_TRUE(entry.IsValid()); 702 703 index.OnBackupTimer(); 704 int expected = (1024 + 512) / 8 + sizeof(disk_cache::IndexHeaderV3); 705 EXPECT_EQ(expected, backend.buffer_len()); 706 } 707