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 <algorithm> 6 #include <functional> 7 8 #include "base/files/scoped_temp_dir.h" 9 #include "base/hash.h" 10 #include "base/logging.h" 11 #include "base/memory/scoped_ptr.h" 12 #include "base/pickle.h" 13 #include "base/sha1.h" 14 #include "base/strings/stringprintf.h" 15 #include "base/task_runner.h" 16 #include "base/threading/platform_thread.h" 17 #include "base/time/time.h" 18 #include "net/base/cache_type.h" 19 #include "net/disk_cache/simple/simple_index.h" 20 #include "net/disk_cache/simple/simple_index_delegate.h" 21 #include "net/disk_cache/simple/simple_index_file.h" 22 #include "net/disk_cache/simple/simple_test_util.h" 23 #include "net/disk_cache/simple/simple_util.h" 24 #include "testing/gtest/include/gtest/gtest.h" 25 26 namespace disk_cache { 27 namespace { 28 29 const base::Time kTestLastUsedTime = 30 base::Time::UnixEpoch() + base::TimeDelta::FromDays(20); 31 const int kTestEntrySize = 789; 32 33 } // namespace 34 35 36 class EntryMetadataTest : public testing::Test { 37 public: 38 EntryMetadata NewEntryMetadataWithValues() { 39 return EntryMetadata(kTestLastUsedTime, kTestEntrySize); 40 } 41 42 void CheckEntryMetadataValues(const EntryMetadata& entry_metadata) { 43 EXPECT_LT(kTestLastUsedTime - base::TimeDelta::FromSeconds(2), 44 entry_metadata.GetLastUsedTime()); 45 EXPECT_GT(kTestLastUsedTime + base::TimeDelta::FromSeconds(2), 46 entry_metadata.GetLastUsedTime()); 47 EXPECT_EQ(kTestEntrySize, entry_metadata.GetEntrySize()); 48 } 49 }; 50 51 class MockSimpleIndexFile : public SimpleIndexFile, 52 public base::SupportsWeakPtr<MockSimpleIndexFile> { 53 public: 54 MockSimpleIndexFile() 55 : SimpleIndexFile(NULL, NULL, net::DISK_CACHE, base::FilePath()), 56 load_result_(NULL), 57 load_index_entries_calls_(0), 58 disk_writes_(0) {} 59 60 virtual void LoadIndexEntries( 61 base::Time cache_last_modified, 62 const base::Closure& callback, 63 SimpleIndexLoadResult* out_load_result) OVERRIDE { 64 load_callback_ = callback; 65 load_result_ = out_load_result; 66 ++load_index_entries_calls_; 67 } 68 69 virtual void WriteToDisk(const SimpleIndex::EntrySet& entry_set, 70 uint64 cache_size, 71 const base::TimeTicks& start, 72 bool app_on_background) OVERRIDE { 73 disk_writes_++; 74 disk_write_entry_set_ = entry_set; 75 } 76 77 void GetAndResetDiskWriteEntrySet(SimpleIndex::EntrySet* entry_set) { 78 entry_set->swap(disk_write_entry_set_); 79 } 80 81 const base::Closure& load_callback() const { return load_callback_; } 82 SimpleIndexLoadResult* load_result() const { return load_result_; } 83 int load_index_entries_calls() const { return load_index_entries_calls_; } 84 int disk_writes() const { return disk_writes_; } 85 86 private: 87 base::Closure load_callback_; 88 SimpleIndexLoadResult* load_result_; 89 int load_index_entries_calls_; 90 int disk_writes_; 91 SimpleIndex::EntrySet disk_write_entry_set_; 92 }; 93 94 class SimpleIndexTest : public testing::Test, public SimpleIndexDelegate { 95 protected: 96 SimpleIndexTest() 97 : hashes_(base::Bind(&HashesInitializer)), 98 doom_entries_calls_(0) {} 99 100 static uint64 HashesInitializer(size_t hash_index) { 101 return disk_cache::simple_util::GetEntryHashKey( 102 base::StringPrintf("key%d", static_cast<int>(hash_index))); 103 } 104 105 virtual void SetUp() OVERRIDE { 106 scoped_ptr<MockSimpleIndexFile> index_file(new MockSimpleIndexFile()); 107 index_file_ = index_file->AsWeakPtr(); 108 index_.reset(new SimpleIndex(NULL, this, net::DISK_CACHE, 109 index_file.PassAs<SimpleIndexFile>())); 110 111 index_->Initialize(base::Time()); 112 } 113 114 void WaitForTimeChange() { 115 const base::Time initial_time = base::Time::Now(); 116 do { 117 base::PlatformThread::YieldCurrentThread(); 118 } while (base::Time::Now() - 119 initial_time < base::TimeDelta::FromSeconds(1)); 120 } 121 122 // From SimpleIndexDelegate: 123 virtual void DoomEntries(std::vector<uint64>* entry_hashes, 124 const net::CompletionCallback& callback) OVERRIDE { 125 std::for_each(entry_hashes->begin(), entry_hashes->end(), 126 std::bind1st(std::mem_fun(&SimpleIndex::Remove), 127 index_.get())); 128 last_doom_entry_hashes_ = *entry_hashes; 129 ++doom_entries_calls_; 130 } 131 132 // Redirect to allow single "friend" declaration in base class. 133 bool GetEntryForTesting(uint64 key, EntryMetadata* metadata) { 134 SimpleIndex::EntrySet::iterator it = index_->entries_set_.find(key); 135 if (index_->entries_set_.end() == it) 136 return false; 137 *metadata = it->second; 138 return true; 139 } 140 141 void InsertIntoIndexFileReturn(uint64 hash_key, 142 base::Time last_used_time, 143 int entry_size) { 144 index_file_->load_result()->entries.insert(std::make_pair( 145 hash_key, EntryMetadata(last_used_time, entry_size))); 146 } 147 148 void ReturnIndexFile() { 149 index_file_->load_result()->did_load = true; 150 index_file_->load_callback().Run(); 151 } 152 153 // Non-const for timer manipulation. 154 SimpleIndex* index() { return index_.get(); } 155 const MockSimpleIndexFile* index_file() const { return index_file_.get(); } 156 157 const std::vector<uint64>& last_doom_entry_hashes() const { 158 return last_doom_entry_hashes_; 159 } 160 int doom_entries_calls() const { return doom_entries_calls_; } 161 162 163 const simple_util::ImmutableArray<uint64, 16> hashes_; 164 scoped_ptr<SimpleIndex> index_; 165 base::WeakPtr<MockSimpleIndexFile> index_file_; 166 167 std::vector<uint64> last_doom_entry_hashes_; 168 int doom_entries_calls_; 169 }; 170 171 TEST_F(EntryMetadataTest, Basics) { 172 EntryMetadata entry_metadata; 173 EXPECT_EQ(base::Time(), entry_metadata.GetLastUsedTime()); 174 EXPECT_EQ(0, entry_metadata.GetEntrySize()); 175 176 entry_metadata = NewEntryMetadataWithValues(); 177 CheckEntryMetadataValues(entry_metadata); 178 179 const base::Time new_time = base::Time::Now(); 180 entry_metadata.SetLastUsedTime(new_time); 181 182 EXPECT_LT(new_time - base::TimeDelta::FromSeconds(2), 183 entry_metadata.GetLastUsedTime()); 184 EXPECT_GT(new_time + base::TimeDelta::FromSeconds(2), 185 entry_metadata.GetLastUsedTime()); 186 } 187 188 TEST_F(EntryMetadataTest, Serialize) { 189 EntryMetadata entry_metadata = NewEntryMetadataWithValues(); 190 191 Pickle pickle; 192 entry_metadata.Serialize(&pickle); 193 194 PickleIterator it(pickle); 195 EntryMetadata new_entry_metadata; 196 new_entry_metadata.Deserialize(&it); 197 CheckEntryMetadataValues(new_entry_metadata); 198 } 199 200 TEST_F(SimpleIndexTest, IndexSizeCorrectOnMerge) { 201 typedef disk_cache::SimpleIndex::EntrySet EntrySet; 202 index()->SetMaxSize(100); 203 index()->Insert(hashes_.at<2>()); 204 index()->UpdateEntrySize(hashes_.at<2>(), 2); 205 index()->Insert(hashes_.at<3>()); 206 index()->UpdateEntrySize(hashes_.at<3>(), 3); 207 index()->Insert(hashes_.at<4>()); 208 index()->UpdateEntrySize(hashes_.at<4>(), 4); 209 EXPECT_EQ(9U, index()->cache_size_); 210 { 211 scoped_ptr<SimpleIndexLoadResult> result(new SimpleIndexLoadResult()); 212 result->did_load = true; 213 index()->MergeInitializingSet(result.Pass()); 214 } 215 EXPECT_EQ(9U, index()->cache_size_); 216 { 217 scoped_ptr<SimpleIndexLoadResult> result(new SimpleIndexLoadResult()); 218 result->did_load = true; 219 const uint64 new_hash_key = hashes_.at<11>(); 220 result->entries.insert( 221 std::make_pair(new_hash_key, EntryMetadata(base::Time::Now(), 11))); 222 const uint64 redundant_hash_key = hashes_.at<4>(); 223 result->entries.insert(std::make_pair(redundant_hash_key, 224 EntryMetadata(base::Time::Now(), 4))); 225 index()->MergeInitializingSet(result.Pass()); 226 } 227 EXPECT_EQ(2U + 3U + 4U + 11U, index()->cache_size_); 228 } 229 230 // State of index changes as expected with an insert and a remove. 231 TEST_F(SimpleIndexTest, BasicInsertRemove) { 232 // Confirm blank state. 233 EntryMetadata metadata; 234 EXPECT_EQ(base::Time(), metadata.GetLastUsedTime()); 235 EXPECT_EQ(0, metadata.GetEntrySize()); 236 237 // Confirm state after insert. 238 index()->Insert(hashes_.at<1>()); 239 ASSERT_TRUE(GetEntryForTesting(hashes_.at<1>(), &metadata)); 240 base::Time now(base::Time::Now()); 241 EXPECT_LT(now - base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime()); 242 EXPECT_GT(now + base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime()); 243 EXPECT_EQ(0, metadata.GetEntrySize()); 244 245 // Confirm state after remove. 246 metadata = EntryMetadata(); 247 index()->Remove(hashes_.at<1>()); 248 EXPECT_FALSE(GetEntryForTesting(hashes_.at<1>(), &metadata)); 249 EXPECT_EQ(base::Time(), metadata.GetLastUsedTime()); 250 EXPECT_EQ(0, metadata.GetEntrySize()); 251 } 252 253 TEST_F(SimpleIndexTest, Has) { 254 // Confirm the base index has dispatched the request for index entries. 255 EXPECT_TRUE(index_file_.get()); 256 EXPECT_EQ(1, index_file_->load_index_entries_calls()); 257 258 // Confirm "Has()" always returns true before the callback is called. 259 const uint64 kHash1 = hashes_.at<1>(); 260 EXPECT_TRUE(index()->Has(kHash1)); 261 index()->Insert(kHash1); 262 EXPECT_TRUE(index()->Has(kHash1)); 263 index()->Remove(kHash1); 264 // TODO(rdsmith): Maybe return false on explicitly removed entries? 265 EXPECT_TRUE(index()->Has(kHash1)); 266 267 ReturnIndexFile(); 268 269 // Confirm "Has() returns conditionally now. 270 EXPECT_FALSE(index()->Has(kHash1)); 271 index()->Insert(kHash1); 272 EXPECT_TRUE(index()->Has(kHash1)); 273 index()->Remove(kHash1); 274 } 275 276 TEST_F(SimpleIndexTest, UseIfExists) { 277 // Confirm the base index has dispatched the request for index entries. 278 EXPECT_TRUE(index_file_.get()); 279 EXPECT_EQ(1, index_file_->load_index_entries_calls()); 280 281 // Confirm "UseIfExists()" always returns true before the callback is called 282 // and updates mod time if the entry was really there. 283 const uint64 kHash1 = hashes_.at<1>(); 284 EntryMetadata metadata1, metadata2; 285 EXPECT_TRUE(index()->UseIfExists(kHash1)); 286 EXPECT_FALSE(GetEntryForTesting(kHash1, &metadata1)); 287 index()->Insert(kHash1); 288 EXPECT_TRUE(index()->UseIfExists(kHash1)); 289 EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata1)); 290 WaitForTimeChange(); 291 EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata2)); 292 EXPECT_EQ(metadata1.GetLastUsedTime(), metadata2.GetLastUsedTime()); 293 EXPECT_TRUE(index()->UseIfExists(kHash1)); 294 EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata2)); 295 EXPECT_LT(metadata1.GetLastUsedTime(), metadata2.GetLastUsedTime()); 296 index()->Remove(kHash1); 297 EXPECT_TRUE(index()->UseIfExists(kHash1)); 298 299 ReturnIndexFile(); 300 301 // Confirm "UseIfExists() returns conditionally now 302 EXPECT_FALSE(index()->UseIfExists(kHash1)); 303 EXPECT_FALSE(GetEntryForTesting(kHash1, &metadata1)); 304 index()->Insert(kHash1); 305 EXPECT_TRUE(index()->UseIfExists(kHash1)); 306 EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata1)); 307 WaitForTimeChange(); 308 EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata2)); 309 EXPECT_EQ(metadata1.GetLastUsedTime(), metadata2.GetLastUsedTime()); 310 EXPECT_TRUE(index()->UseIfExists(kHash1)); 311 EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata2)); 312 EXPECT_LT(metadata1.GetLastUsedTime(), metadata2.GetLastUsedTime()); 313 index()->Remove(kHash1); 314 EXPECT_FALSE(index()->UseIfExists(kHash1)); 315 } 316 317 TEST_F(SimpleIndexTest, UpdateEntrySize) { 318 base::Time now(base::Time::Now()); 319 320 index()->SetMaxSize(1000); 321 322 const uint64 kHash1 = hashes_.at<1>(); 323 InsertIntoIndexFileReturn(kHash1, now - base::TimeDelta::FromDays(2), 475); 324 ReturnIndexFile(); 325 326 EntryMetadata metadata; 327 EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata)); 328 EXPECT_LT( 329 now - base::TimeDelta::FromDays(2) - base::TimeDelta::FromSeconds(1), 330 metadata.GetLastUsedTime()); 331 EXPECT_GT( 332 now - base::TimeDelta::FromDays(2) + base::TimeDelta::FromSeconds(1), 333 metadata.GetLastUsedTime()); 334 EXPECT_EQ(475, metadata.GetEntrySize()); 335 336 index()->UpdateEntrySize(kHash1, 600u); 337 EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata)); 338 EXPECT_EQ(600, metadata.GetEntrySize()); 339 EXPECT_EQ(1, index()->GetEntryCount()); 340 } 341 342 TEST_F(SimpleIndexTest, GetEntryCount) { 343 EXPECT_EQ(0, index()->GetEntryCount()); 344 index()->Insert(hashes_.at<1>()); 345 EXPECT_EQ(1, index()->GetEntryCount()); 346 index()->Insert(hashes_.at<2>()); 347 EXPECT_EQ(2, index()->GetEntryCount()); 348 index()->Insert(hashes_.at<3>()); 349 EXPECT_EQ(3, index()->GetEntryCount()); 350 index()->Insert(hashes_.at<3>()); 351 EXPECT_EQ(3, index()->GetEntryCount()); 352 index()->Remove(hashes_.at<2>()); 353 EXPECT_EQ(2, index()->GetEntryCount()); 354 index()->Insert(hashes_.at<4>()); 355 EXPECT_EQ(3, index()->GetEntryCount()); 356 index()->Remove(hashes_.at<3>()); 357 EXPECT_EQ(2, index()->GetEntryCount()); 358 index()->Remove(hashes_.at<3>()); 359 EXPECT_EQ(2, index()->GetEntryCount()); 360 index()->Remove(hashes_.at<1>()); 361 EXPECT_EQ(1, index()->GetEntryCount()); 362 index()->Remove(hashes_.at<4>()); 363 EXPECT_EQ(0, index()->GetEntryCount()); 364 } 365 366 // Confirm that we get the results we expect from a simple init. 367 TEST_F(SimpleIndexTest, BasicInit) { 368 base::Time now(base::Time::Now()); 369 370 InsertIntoIndexFileReturn(hashes_.at<1>(), 371 now - base::TimeDelta::FromDays(2), 372 10u); 373 InsertIntoIndexFileReturn(hashes_.at<2>(), 374 now - base::TimeDelta::FromDays(3), 375 100u); 376 377 ReturnIndexFile(); 378 379 EntryMetadata metadata; 380 EXPECT_TRUE(GetEntryForTesting(hashes_.at<1>(), &metadata)); 381 EXPECT_LT( 382 now - base::TimeDelta::FromDays(2) - base::TimeDelta::FromSeconds(1), 383 metadata.GetLastUsedTime()); 384 EXPECT_GT( 385 now - base::TimeDelta::FromDays(2) + base::TimeDelta::FromSeconds(1), 386 metadata.GetLastUsedTime()); 387 EXPECT_EQ(10, metadata.GetEntrySize()); 388 EXPECT_TRUE(GetEntryForTesting(hashes_.at<2>(), &metadata)); 389 EXPECT_LT( 390 now - base::TimeDelta::FromDays(3) - base::TimeDelta::FromSeconds(1), 391 metadata.GetLastUsedTime()); 392 EXPECT_GT( 393 now - base::TimeDelta::FromDays(3) + base::TimeDelta::FromSeconds(1), 394 metadata.GetLastUsedTime()); 395 EXPECT_EQ(100, metadata.GetEntrySize()); 396 } 397 398 // Remove something that's going to come in from the loaded index. 399 TEST_F(SimpleIndexTest, RemoveBeforeInit) { 400 const uint64 kHash1 = hashes_.at<1>(); 401 index()->Remove(kHash1); 402 403 InsertIntoIndexFileReturn(kHash1, 404 base::Time::Now() - base::TimeDelta::FromDays(2), 405 10u); 406 ReturnIndexFile(); 407 408 EXPECT_FALSE(index()->Has(kHash1)); 409 } 410 411 // Insert something that's going to come in from the loaded index; correct 412 // result? 413 TEST_F(SimpleIndexTest, InsertBeforeInit) { 414 const uint64 kHash1 = hashes_.at<1>(); 415 index()->Insert(kHash1); 416 417 InsertIntoIndexFileReturn(kHash1, 418 base::Time::Now() - base::TimeDelta::FromDays(2), 419 10u); 420 ReturnIndexFile(); 421 422 EntryMetadata metadata; 423 EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata)); 424 base::Time now(base::Time::Now()); 425 EXPECT_LT(now - base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime()); 426 EXPECT_GT(now + base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime()); 427 EXPECT_EQ(0, metadata.GetEntrySize()); 428 } 429 430 // Insert and Remove something that's going to come in from the loaded index. 431 TEST_F(SimpleIndexTest, InsertRemoveBeforeInit) { 432 const uint64 kHash1 = hashes_.at<1>(); 433 index()->Insert(kHash1); 434 index()->Remove(kHash1); 435 436 InsertIntoIndexFileReturn(kHash1, 437 base::Time::Now() - base::TimeDelta::FromDays(2), 438 10u); 439 ReturnIndexFile(); 440 441 EXPECT_FALSE(index()->Has(kHash1)); 442 } 443 444 // Insert and Remove something that's going to come in from the loaded index. 445 TEST_F(SimpleIndexTest, RemoveInsertBeforeInit) { 446 const uint64 kHash1 = hashes_.at<1>(); 447 index()->Remove(kHash1); 448 index()->Insert(kHash1); 449 450 InsertIntoIndexFileReturn(kHash1, 451 base::Time::Now() - base::TimeDelta::FromDays(2), 452 10u); 453 ReturnIndexFile(); 454 455 EntryMetadata metadata; 456 EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata)); 457 base::Time now(base::Time::Now()); 458 EXPECT_LT(now - base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime()); 459 EXPECT_GT(now + base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime()); 460 EXPECT_EQ(0, metadata.GetEntrySize()); 461 } 462 463 // Do all above tests at once + a non-conflict to test for cross-key 464 // interactions. 465 TEST_F(SimpleIndexTest, AllInitConflicts) { 466 base::Time now(base::Time::Now()); 467 468 index()->Remove(hashes_.at<1>()); 469 InsertIntoIndexFileReturn(hashes_.at<1>(), 470 now - base::TimeDelta::FromDays(2), 471 10u); 472 index()->Insert(hashes_.at<2>()); 473 InsertIntoIndexFileReturn(hashes_.at<2>(), 474 now - base::TimeDelta::FromDays(3), 475 100u); 476 index()->Insert(hashes_.at<3>()); 477 index()->Remove(hashes_.at<3>()); 478 InsertIntoIndexFileReturn(hashes_.at<3>(), 479 now - base::TimeDelta::FromDays(4), 480 1000u); 481 index()->Remove(hashes_.at<4>()); 482 index()->Insert(hashes_.at<4>()); 483 InsertIntoIndexFileReturn(hashes_.at<4>(), 484 now - base::TimeDelta::FromDays(5), 485 10000u); 486 InsertIntoIndexFileReturn(hashes_.at<5>(), 487 now - base::TimeDelta::FromDays(6), 488 100000u); 489 490 ReturnIndexFile(); 491 492 EXPECT_FALSE(index()->Has(hashes_.at<1>())); 493 494 EntryMetadata metadata; 495 EXPECT_TRUE(GetEntryForTesting(hashes_.at<2>(), &metadata)); 496 EXPECT_LT(now - base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime()); 497 EXPECT_GT(now + base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime()); 498 EXPECT_EQ(0, metadata.GetEntrySize()); 499 500 EXPECT_FALSE(index()->Has(hashes_.at<3>())); 501 502 EXPECT_TRUE(GetEntryForTesting(hashes_.at<4>(), &metadata)); 503 EXPECT_LT(now - base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime()); 504 EXPECT_GT(now + base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime()); 505 EXPECT_EQ(0, metadata.GetEntrySize()); 506 507 EXPECT_TRUE(GetEntryForTesting(hashes_.at<5>(), &metadata)); 508 509 EXPECT_GT( 510 now - base::TimeDelta::FromDays(6) + base::TimeDelta::FromSeconds(1), 511 metadata.GetLastUsedTime()); 512 EXPECT_LT( 513 now - base::TimeDelta::FromDays(6) - base::TimeDelta::FromSeconds(1), 514 metadata.GetLastUsedTime()); 515 516 EXPECT_EQ(100000, metadata.GetEntrySize()); 517 } 518 519 TEST_F(SimpleIndexTest, BasicEviction) { 520 base::Time now(base::Time::Now()); 521 index()->SetMaxSize(1000); 522 InsertIntoIndexFileReturn(hashes_.at<1>(), 523 now - base::TimeDelta::FromDays(2), 524 475u); 525 index()->Insert(hashes_.at<2>()); 526 index()->UpdateEntrySize(hashes_.at<2>(), 475); 527 ReturnIndexFile(); 528 529 WaitForTimeChange(); 530 531 index()->Insert(hashes_.at<3>()); 532 // Confirm index is as expected: No eviction, everything there. 533 EXPECT_EQ(3, index()->GetEntryCount()); 534 EXPECT_EQ(0, doom_entries_calls()); 535 EXPECT_TRUE(index()->Has(hashes_.at<1>())); 536 EXPECT_TRUE(index()->Has(hashes_.at<2>())); 537 EXPECT_TRUE(index()->Has(hashes_.at<3>())); 538 539 // Trigger an eviction, and make sure the right things are tossed. 540 // TODO(rdsmith): This is dependent on the innards of the implementation 541 // as to at exactly what point we trigger eviction. Not sure how to fix 542 // that. 543 index()->UpdateEntrySize(hashes_.at<3>(), 475); 544 EXPECT_EQ(1, doom_entries_calls()); 545 EXPECT_EQ(1, index()->GetEntryCount()); 546 EXPECT_FALSE(index()->Has(hashes_.at<1>())); 547 EXPECT_FALSE(index()->Has(hashes_.at<2>())); 548 EXPECT_TRUE(index()->Has(hashes_.at<3>())); 549 ASSERT_EQ(2u, last_doom_entry_hashes().size()); 550 } 551 552 // Confirm all the operations queue a disk write at some point in the 553 // future. 554 TEST_F(SimpleIndexTest, DiskWriteQueued) { 555 index()->SetMaxSize(1000); 556 ReturnIndexFile(); 557 558 EXPECT_FALSE(index()->write_to_disk_timer_.IsRunning()); 559 560 const uint64 kHash1 = hashes_.at<1>(); 561 index()->Insert(kHash1); 562 EXPECT_TRUE(index()->write_to_disk_timer_.IsRunning()); 563 index()->write_to_disk_timer_.Stop(); 564 EXPECT_FALSE(index()->write_to_disk_timer_.IsRunning()); 565 566 index()->UseIfExists(kHash1); 567 EXPECT_TRUE(index()->write_to_disk_timer_.IsRunning()); 568 index()->write_to_disk_timer_.Stop(); 569 570 index()->UpdateEntrySize(kHash1, 20); 571 EXPECT_TRUE(index()->write_to_disk_timer_.IsRunning()); 572 index()->write_to_disk_timer_.Stop(); 573 574 index()->Remove(kHash1); 575 EXPECT_TRUE(index()->write_to_disk_timer_.IsRunning()); 576 index()->write_to_disk_timer_.Stop(); 577 } 578 579 TEST_F(SimpleIndexTest, DiskWriteExecuted) { 580 index()->SetMaxSize(1000); 581 ReturnIndexFile(); 582 583 EXPECT_FALSE(index()->write_to_disk_timer_.IsRunning()); 584 585 const uint64 kHash1 = hashes_.at<1>(); 586 index()->Insert(kHash1); 587 index()->UpdateEntrySize(kHash1, 20); 588 EXPECT_TRUE(index()->write_to_disk_timer_.IsRunning()); 589 base::Closure user_task(index()->write_to_disk_timer_.user_task()); 590 index()->write_to_disk_timer_.Stop(); 591 592 EXPECT_EQ(0, index_file_->disk_writes()); 593 user_task.Run(); 594 EXPECT_EQ(1, index_file_->disk_writes()); 595 SimpleIndex::EntrySet entry_set; 596 index_file_->GetAndResetDiskWriteEntrySet(&entry_set); 597 598 uint64 hash_key = kHash1; 599 base::Time now(base::Time::Now()); 600 ASSERT_EQ(1u, entry_set.size()); 601 EXPECT_EQ(hash_key, entry_set.begin()->first); 602 const EntryMetadata& entry1(entry_set.begin()->second); 603 EXPECT_LT(now - base::TimeDelta::FromMinutes(1), entry1.GetLastUsedTime()); 604 EXPECT_GT(now + base::TimeDelta::FromMinutes(1), entry1.GetLastUsedTime()); 605 EXPECT_EQ(20, entry1.GetEntrySize()); 606 } 607 608 TEST_F(SimpleIndexTest, DiskWritePostponed) { 609 index()->SetMaxSize(1000); 610 ReturnIndexFile(); 611 612 EXPECT_FALSE(index()->write_to_disk_timer_.IsRunning()); 613 614 index()->Insert(hashes_.at<1>()); 615 index()->UpdateEntrySize(hashes_.at<1>(), 20); 616 EXPECT_TRUE(index()->write_to_disk_timer_.IsRunning()); 617 base::TimeTicks expected_trigger( 618 index()->write_to_disk_timer_.desired_run_time()); 619 620 WaitForTimeChange(); 621 EXPECT_EQ(expected_trigger, index()->write_to_disk_timer_.desired_run_time()); 622 index()->Insert(hashes_.at<2>()); 623 index()->UpdateEntrySize(hashes_.at<2>(), 40); 624 EXPECT_TRUE(index()->write_to_disk_timer_.IsRunning()); 625 EXPECT_LT(expected_trigger, index()->write_to_disk_timer_.desired_run_time()); 626 index()->write_to_disk_timer_.Stop(); 627 } 628 629 } // namespace disk_cache 630