OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2013 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/addr.h" |
| 8 #include "net/disk_cache/v3/disk_format_v3.h" |
| 9 #include "net/disk_cache/v3/index_table.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_id = 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_id, 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_id, entry.cell_id()); |
| 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_id, 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_id *= 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 that GrowIndex is called. |
| 616 TEST(DiskCacheIndexTable, GrowIndex) { |
| 617 TestCacheTables cache(1024); |
| 618 IndexTableInitData init_data; |
| 619 cache.GetInitData(&init_data); |
| 620 MockIndexBackend backend; |
| 621 |
| 622 IndexTable index(&backend); |
| 623 index.Init(&init_data); |
| 624 |
| 625 // Write some entries. |
| 626 for (int i = 0; i < 512; i++) { |
| 627 SCOPED_TRACE(i); |
| 628 uint32 hash = 0; |
| 629 disk_cache::Addr addr(disk_cache::BLOCK_ENTRIES, 1, 5, i + 1); |
| 630 EntryCell entry = index.CreateEntryCell(hash, addr); |
| 631 EXPECT_TRUE(entry.IsValid()); |
| 632 } |
| 633 |
| 634 EXPECT_TRUE(backend.grow_called()); |
| 635 } |
| 636 |
| 637 TEST(DiskCacheIndexTable, SaveIndex) { |
| 638 TestCacheTables cache(1024); |
| 639 IndexTableInitData init_data; |
| 640 cache.GetInitData(&init_data); |
| 641 MockIndexBackend backend; |
| 642 |
| 643 IndexTable index(&backend); |
| 644 index.Init(&init_data); |
| 645 |
| 646 uint32 hash = 0; |
| 647 disk_cache::Addr addr(disk_cache::BLOCK_ENTRIES, 1, 5, 6); |
| 648 EntryCell entry = index.CreateEntryCell(hash, addr); |
| 649 EXPECT_TRUE(entry.IsValid()); |
| 650 |
| 651 index.OnBackupTimer(); |
| 652 int expected = (1024 + 512) / 8 + sizeof(disk_cache::IndexHeaderV3); |
| 653 EXPECT_EQ(expected, backend.buffer_len()); |
| 654 } |
OLD | NEW |