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 index()->SetMaxSize(100); 202 index()->Insert(hashes_.at<2>()); 203 index()->UpdateEntrySize(hashes_.at<2>(), 2); 204 index()->Insert(hashes_.at<3>()); 205 index()->UpdateEntrySize(hashes_.at<3>(), 3); 206 index()->Insert(hashes_.at<4>()); 207 index()->UpdateEntrySize(hashes_.at<4>(), 4); 208 EXPECT_EQ(9U, index()->cache_size_); 209 { 210 scoped_ptr<SimpleIndexLoadResult> result(new SimpleIndexLoadResult()); 211 result->did_load = true; 212 index()->MergeInitializingSet(result.Pass()); 213 } 214 EXPECT_EQ(9U, index()->cache_size_); 215 { 216 scoped_ptr<SimpleIndexLoadResult> result(new SimpleIndexLoadResult()); 217 result->did_load = true; 218 const uint64 new_hash_key = hashes_.at<11>(); 219 result->entries.insert( 220 std::make_pair(new_hash_key, EntryMetadata(base::Time::Now(), 11))); 221 const uint64 redundant_hash_key = hashes_.at<4>(); 222 result->entries.insert(std::make_pair(redundant_hash_key, 223 EntryMetadata(base::Time::Now(), 4))); 224 index()->MergeInitializingSet(result.Pass()); 225 } 226 EXPECT_EQ(2U + 3U + 4U + 11U, index()->cache_size_); 227 } 228 229 // State of index changes as expected with an insert and a remove. 230 TEST_F(SimpleIndexTest, BasicInsertRemove) { 231 // Confirm blank state. 232 EntryMetadata metadata; 233 EXPECT_EQ(base::Time(), metadata.GetLastUsedTime()); 234 EXPECT_EQ(0, metadata.GetEntrySize()); 235 236 // Confirm state after insert. 237 index()->Insert(hashes_.at<1>()); 238 ASSERT_TRUE(GetEntryForTesting(hashes_.at<1>(), &metadata)); 239 base::Time now(base::Time::Now()); 240 EXPECT_LT(now - base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime()); 241 EXPECT_GT(now + base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime()); 242 EXPECT_EQ(0, metadata.GetEntrySize()); 243 244 // Confirm state after remove. 245 metadata = EntryMetadata(); 246 index()->Remove(hashes_.at<1>()); 247 EXPECT_FALSE(GetEntryForTesting(hashes_.at<1>(), &metadata)); 248 EXPECT_EQ(base::Time(), metadata.GetLastUsedTime()); 249 EXPECT_EQ(0, metadata.GetEntrySize()); 250 } 251 252 TEST_F(SimpleIndexTest, Has) { 253 // Confirm the base index has dispatched the request for index entries. 254 EXPECT_TRUE(index_file_.get()); 255 EXPECT_EQ(1, index_file_->load_index_entries_calls()); 256 257 // Confirm "Has()" always returns true before the callback is called. 258 const uint64 kHash1 = hashes_.at<1>(); 259 EXPECT_TRUE(index()->Has(kHash1)); 260 index()->Insert(kHash1); 261 EXPECT_TRUE(index()->Has(kHash1)); 262 index()->Remove(kHash1); 263 // TODO(rdsmith): Maybe return false on explicitly removed entries? 264 EXPECT_TRUE(index()->Has(kHash1)); 265 266 ReturnIndexFile(); 267 268 // Confirm "Has() returns conditionally now. 269 EXPECT_FALSE(index()->Has(kHash1)); 270 index()->Insert(kHash1); 271 EXPECT_TRUE(index()->Has(kHash1)); 272 index()->Remove(kHash1); 273 } 274 275 TEST_F(SimpleIndexTest, UseIfExists) { 276 // Confirm the base index has dispatched the request for index entries. 277 EXPECT_TRUE(index_file_.get()); 278 EXPECT_EQ(1, index_file_->load_index_entries_calls()); 279 280 // Confirm "UseIfExists()" always returns true before the callback is called 281 // and updates mod time if the entry was really there. 282 const uint64 kHash1 = hashes_.at<1>(); 283 EntryMetadata metadata1, metadata2; 284 EXPECT_TRUE(index()->UseIfExists(kHash1)); 285 EXPECT_FALSE(GetEntryForTesting(kHash1, &metadata1)); 286 index()->Insert(kHash1); 287 EXPECT_TRUE(index()->UseIfExists(kHash1)); 288 EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata1)); 289 WaitForTimeChange(); 290 EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata2)); 291 EXPECT_EQ(metadata1.GetLastUsedTime(), metadata2.GetLastUsedTime()); 292 EXPECT_TRUE(index()->UseIfExists(kHash1)); 293 EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata2)); 294 EXPECT_LT(metadata1.GetLastUsedTime(), metadata2.GetLastUsedTime()); 295 index()->Remove(kHash1); 296 EXPECT_TRUE(index()->UseIfExists(kHash1)); 297 298 ReturnIndexFile(); 299 300 // Confirm "UseIfExists() returns conditionally now 301 EXPECT_FALSE(index()->UseIfExists(kHash1)); 302 EXPECT_FALSE(GetEntryForTesting(kHash1, &metadata1)); 303 index()->Insert(kHash1); 304 EXPECT_TRUE(index()->UseIfExists(kHash1)); 305 EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata1)); 306 WaitForTimeChange(); 307 EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata2)); 308 EXPECT_EQ(metadata1.GetLastUsedTime(), metadata2.GetLastUsedTime()); 309 EXPECT_TRUE(index()->UseIfExists(kHash1)); 310 EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata2)); 311 EXPECT_LT(metadata1.GetLastUsedTime(), metadata2.GetLastUsedTime()); 312 index()->Remove(kHash1); 313 EXPECT_FALSE(index()->UseIfExists(kHash1)); 314 } 315 316 TEST_F(SimpleIndexTest, UpdateEntrySize) { 317 base::Time now(base::Time::Now()); 318 319 index()->SetMaxSize(1000); 320 321 const uint64 kHash1 = hashes_.at<1>(); 322 InsertIntoIndexFileReturn(kHash1, now - base::TimeDelta::FromDays(2), 475); 323 ReturnIndexFile(); 324 325 EntryMetadata metadata; 326 EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata)); 327 EXPECT_LT( 328 now - base::TimeDelta::FromDays(2) - base::TimeDelta::FromSeconds(1), 329 metadata.GetLastUsedTime()); 330 EXPECT_GT( 331 now - base::TimeDelta::FromDays(2) + base::TimeDelta::FromSeconds(1), 332 metadata.GetLastUsedTime()); 333 EXPECT_EQ(475, metadata.GetEntrySize()); 334 335 index()->UpdateEntrySize(kHash1, 600u); 336 EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata)); 337 EXPECT_EQ(600, metadata.GetEntrySize()); 338 EXPECT_EQ(1, index()->GetEntryCount()); 339 } 340 341 TEST_F(SimpleIndexTest, GetEntryCount) { 342 EXPECT_EQ(0, index()->GetEntryCount()); 343 index()->Insert(hashes_.at<1>()); 344 EXPECT_EQ(1, index()->GetEntryCount()); 345 index()->Insert(hashes_.at<2>()); 346 EXPECT_EQ(2, index()->GetEntryCount()); 347 index()->Insert(hashes_.at<3>()); 348 EXPECT_EQ(3, index()->GetEntryCount()); 349 index()->Insert(hashes_.at<3>()); 350 EXPECT_EQ(3, index()->GetEntryCount()); 351 index()->Remove(hashes_.at<2>()); 352 EXPECT_EQ(2, index()->GetEntryCount()); 353 index()->Insert(hashes_.at<4>()); 354 EXPECT_EQ(3, index()->GetEntryCount()); 355 index()->Remove(hashes_.at<3>()); 356 EXPECT_EQ(2, index()->GetEntryCount()); 357 index()->Remove(hashes_.at<3>()); 358 EXPECT_EQ(2, index()->GetEntryCount()); 359 index()->Remove(hashes_.at<1>()); 360 EXPECT_EQ(1, index()->GetEntryCount()); 361 index()->Remove(hashes_.at<4>()); 362 EXPECT_EQ(0, index()->GetEntryCount()); 363 } 364 365 // Confirm that we get the results we expect from a simple init. 366 TEST_F(SimpleIndexTest, BasicInit) { 367 base::Time now(base::Time::Now()); 368 369 InsertIntoIndexFileReturn(hashes_.at<1>(), 370 now - base::TimeDelta::FromDays(2), 371 10u); 372 InsertIntoIndexFileReturn(hashes_.at<2>(), 373 now - base::TimeDelta::FromDays(3), 374 100u); 375 376 ReturnIndexFile(); 377 378 EntryMetadata metadata; 379 EXPECT_TRUE(GetEntryForTesting(hashes_.at<1>(), &metadata)); 380 EXPECT_LT( 381 now - base::TimeDelta::FromDays(2) - base::TimeDelta::FromSeconds(1), 382 metadata.GetLastUsedTime()); 383 EXPECT_GT( 384 now - base::TimeDelta::FromDays(2) + base::TimeDelta::FromSeconds(1), 385 metadata.GetLastUsedTime()); 386 EXPECT_EQ(10, metadata.GetEntrySize()); 387 EXPECT_TRUE(GetEntryForTesting(hashes_.at<2>(), &metadata)); 388 EXPECT_LT( 389 now - base::TimeDelta::FromDays(3) - base::TimeDelta::FromSeconds(1), 390 metadata.GetLastUsedTime()); 391 EXPECT_GT( 392 now - base::TimeDelta::FromDays(3) + base::TimeDelta::FromSeconds(1), 393 metadata.GetLastUsedTime()); 394 EXPECT_EQ(100, metadata.GetEntrySize()); 395 } 396 397 // Remove something that's going to come in from the loaded index. 398 TEST_F(SimpleIndexTest, RemoveBeforeInit) { 399 const uint64 kHash1 = hashes_.at<1>(); 400 index()->Remove(kHash1); 401 402 InsertIntoIndexFileReturn(kHash1, 403 base::Time::Now() - base::TimeDelta::FromDays(2), 404 10u); 405 ReturnIndexFile(); 406 407 EXPECT_FALSE(index()->Has(kHash1)); 408 } 409 410 // Insert something that's going to come in from the loaded index; correct 411 // result? 412 TEST_F(SimpleIndexTest, InsertBeforeInit) { 413 const uint64 kHash1 = hashes_.at<1>(); 414 index()->Insert(kHash1); 415 416 InsertIntoIndexFileReturn(kHash1, 417 base::Time::Now() - base::TimeDelta::FromDays(2), 418 10u); 419 ReturnIndexFile(); 420 421 EntryMetadata metadata; 422 EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata)); 423 base::Time now(base::Time::Now()); 424 EXPECT_LT(now - base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime()); 425 EXPECT_GT(now + base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime()); 426 EXPECT_EQ(0, metadata.GetEntrySize()); 427 } 428 429 // Insert and Remove something that's going to come in from the loaded index. 430 TEST_F(SimpleIndexTest, InsertRemoveBeforeInit) { 431 const uint64 kHash1 = hashes_.at<1>(); 432 index()->Insert(kHash1); 433 index()->Remove(kHash1); 434 435 InsertIntoIndexFileReturn(kHash1, 436 base::Time::Now() - base::TimeDelta::FromDays(2), 437 10u); 438 ReturnIndexFile(); 439 440 EXPECT_FALSE(index()->Has(kHash1)); 441 } 442 443 // Insert and Remove something that's going to come in from the loaded index. 444 TEST_F(SimpleIndexTest, RemoveInsertBeforeInit) { 445 const uint64 kHash1 = hashes_.at<1>(); 446 index()->Remove(kHash1); 447 index()->Insert(kHash1); 448 449 InsertIntoIndexFileReturn(kHash1, 450 base::Time::Now() - base::TimeDelta::FromDays(2), 451 10u); 452 ReturnIndexFile(); 453 454 EntryMetadata metadata; 455 EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata)); 456 base::Time now(base::Time::Now()); 457 EXPECT_LT(now - base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime()); 458 EXPECT_GT(now + base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime()); 459 EXPECT_EQ(0, metadata.GetEntrySize()); 460 } 461 462 // Do all above tests at once + a non-conflict to test for cross-key 463 // interactions. 464 TEST_F(SimpleIndexTest, AllInitConflicts) { 465 base::Time now(base::Time::Now()); 466 467 index()->Remove(hashes_.at<1>()); 468 InsertIntoIndexFileReturn(hashes_.at<1>(), 469 now - base::TimeDelta::FromDays(2), 470 10u); 471 index()->Insert(hashes_.at<2>()); 472 InsertIntoIndexFileReturn(hashes_.at<2>(), 473 now - base::TimeDelta::FromDays(3), 474 100u); 475 index()->Insert(hashes_.at<3>()); 476 index()->Remove(hashes_.at<3>()); 477 InsertIntoIndexFileReturn(hashes_.at<3>(), 478 now - base::TimeDelta::FromDays(4), 479 1000u); 480 index()->Remove(hashes_.at<4>()); 481 index()->Insert(hashes_.at<4>()); 482 InsertIntoIndexFileReturn(hashes_.at<4>(), 483 now - base::TimeDelta::FromDays(5), 484 10000u); 485 InsertIntoIndexFileReturn(hashes_.at<5>(), 486 now - base::TimeDelta::FromDays(6), 487 100000u); 488 489 ReturnIndexFile(); 490 491 EXPECT_FALSE(index()->Has(hashes_.at<1>())); 492 493 EntryMetadata metadata; 494 EXPECT_TRUE(GetEntryForTesting(hashes_.at<2>(), &metadata)); 495 EXPECT_LT(now - base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime()); 496 EXPECT_GT(now + base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime()); 497 EXPECT_EQ(0, metadata.GetEntrySize()); 498 499 EXPECT_FALSE(index()->Has(hashes_.at<3>())); 500 501 EXPECT_TRUE(GetEntryForTesting(hashes_.at<4>(), &metadata)); 502 EXPECT_LT(now - base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime()); 503 EXPECT_GT(now + base::TimeDelta::FromMinutes(1), metadata.GetLastUsedTime()); 504 EXPECT_EQ(0, metadata.GetEntrySize()); 505 506 EXPECT_TRUE(GetEntryForTesting(hashes_.at<5>(), &metadata)); 507 508 EXPECT_GT( 509 now - base::TimeDelta::FromDays(6) + base::TimeDelta::FromSeconds(1), 510 metadata.GetLastUsedTime()); 511 EXPECT_LT( 512 now - base::TimeDelta::FromDays(6) - base::TimeDelta::FromSeconds(1), 513 metadata.GetLastUsedTime()); 514 515 EXPECT_EQ(100000, metadata.GetEntrySize()); 516 } 517 518 TEST_F(SimpleIndexTest, BasicEviction) { 519 base::Time now(base::Time::Now()); 520 index()->SetMaxSize(1000); 521 InsertIntoIndexFileReturn(hashes_.at<1>(), 522 now - base::TimeDelta::FromDays(2), 523 475u); 524 index()->Insert(hashes_.at<2>()); 525 index()->UpdateEntrySize(hashes_.at<2>(), 475); 526 ReturnIndexFile(); 527 528 WaitForTimeChange(); 529 530 index()->Insert(hashes_.at<3>()); 531 // Confirm index is as expected: No eviction, everything there. 532 EXPECT_EQ(3, index()->GetEntryCount()); 533 EXPECT_EQ(0, doom_entries_calls()); 534 EXPECT_TRUE(index()->Has(hashes_.at<1>())); 535 EXPECT_TRUE(index()->Has(hashes_.at<2>())); 536 EXPECT_TRUE(index()->Has(hashes_.at<3>())); 537 538 // Trigger an eviction, and make sure the right things are tossed. 539 // TODO(rdsmith): This is dependent on the innards of the implementation 540 // as to at exactly what point we trigger eviction. Not sure how to fix 541 // that. 542 index()->UpdateEntrySize(hashes_.at<3>(), 475); 543 EXPECT_EQ(1, doom_entries_calls()); 544 EXPECT_EQ(1, index()->GetEntryCount()); 545 EXPECT_FALSE(index()->Has(hashes_.at<1>())); 546 EXPECT_FALSE(index()->Has(hashes_.at<2>())); 547 EXPECT_TRUE(index()->Has(hashes_.at<3>())); 548 ASSERT_EQ(2u, last_doom_entry_hashes().size()); 549 } 550 551 // Confirm all the operations queue a disk write at some point in the 552 // future. 553 TEST_F(SimpleIndexTest, DiskWriteQueued) { 554 index()->SetMaxSize(1000); 555 ReturnIndexFile(); 556 557 EXPECT_FALSE(index()->write_to_disk_timer_.IsRunning()); 558 559 const uint64 kHash1 = hashes_.at<1>(); 560 index()->Insert(kHash1); 561 EXPECT_TRUE(index()->write_to_disk_timer_.IsRunning()); 562 index()->write_to_disk_timer_.Stop(); 563 EXPECT_FALSE(index()->write_to_disk_timer_.IsRunning()); 564 565 index()->UseIfExists(kHash1); 566 EXPECT_TRUE(index()->write_to_disk_timer_.IsRunning()); 567 index()->write_to_disk_timer_.Stop(); 568 569 index()->UpdateEntrySize(kHash1, 20); 570 EXPECT_TRUE(index()->write_to_disk_timer_.IsRunning()); 571 index()->write_to_disk_timer_.Stop(); 572 573 index()->Remove(kHash1); 574 EXPECT_TRUE(index()->write_to_disk_timer_.IsRunning()); 575 index()->write_to_disk_timer_.Stop(); 576 } 577 578 TEST_F(SimpleIndexTest, DiskWriteExecuted) { 579 index()->SetMaxSize(1000); 580 ReturnIndexFile(); 581 582 EXPECT_FALSE(index()->write_to_disk_timer_.IsRunning()); 583 584 const uint64 kHash1 = hashes_.at<1>(); 585 index()->Insert(kHash1); 586 index()->UpdateEntrySize(kHash1, 20); 587 EXPECT_TRUE(index()->write_to_disk_timer_.IsRunning()); 588 base::Closure user_task(index()->write_to_disk_timer_.user_task()); 589 index()->write_to_disk_timer_.Stop(); 590 591 EXPECT_EQ(0, index_file_->disk_writes()); 592 user_task.Run(); 593 EXPECT_EQ(1, index_file_->disk_writes()); 594 SimpleIndex::EntrySet entry_set; 595 index_file_->GetAndResetDiskWriteEntrySet(&entry_set); 596 597 uint64 hash_key = kHash1; 598 base::Time now(base::Time::Now()); 599 ASSERT_EQ(1u, entry_set.size()); 600 EXPECT_EQ(hash_key, entry_set.begin()->first); 601 const EntryMetadata& entry1(entry_set.begin()->second); 602 EXPECT_LT(now - base::TimeDelta::FromMinutes(1), entry1.GetLastUsedTime()); 603 EXPECT_GT(now + base::TimeDelta::FromMinutes(1), entry1.GetLastUsedTime()); 604 EXPECT_EQ(20, entry1.GetEntrySize()); 605 } 606 607 TEST_F(SimpleIndexTest, DiskWritePostponed) { 608 index()->SetMaxSize(1000); 609 ReturnIndexFile(); 610 611 EXPECT_FALSE(index()->write_to_disk_timer_.IsRunning()); 612 613 index()->Insert(hashes_.at<1>()); 614 index()->UpdateEntrySize(hashes_.at<1>(), 20); 615 EXPECT_TRUE(index()->write_to_disk_timer_.IsRunning()); 616 base::TimeTicks expected_trigger( 617 index()->write_to_disk_timer_.desired_run_time()); 618 619 WaitForTimeChange(); 620 EXPECT_EQ(expected_trigger, index()->write_to_disk_timer_.desired_run_time()); 621 index()->Insert(hashes_.at<2>()); 622 index()->UpdateEntrySize(hashes_.at<2>(), 40); 623 EXPECT_TRUE(index()->write_to_disk_timer_.IsRunning()); 624 EXPECT_LT(expected_trigger, index()->write_to_disk_timer_.desired_run_time()); 625 index()->write_to_disk_timer_.Stop(); 626 } 627 628 } // namespace disk_cache 629