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:
NewEntryMetadataWithValues()38 EntryMetadata NewEntryMetadataWithValues() {
39 return EntryMetadata(kTestLastUsedTime, kTestEntrySize);
40 }
41
CheckEntryMetadataValues(const EntryMetadata & entry_metadata)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:
MockSimpleIndexFile()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
LoadIndexEntries(base::Time cache_last_modified,const base::Closure & callback,SimpleIndexLoadResult * out_load_result)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
WriteToDisk(const SimpleIndex::EntrySet & entry_set,uint64 cache_size,const base::TimeTicks & start,bool app_on_background)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
GetAndResetDiskWriteEntrySet(SimpleIndex::EntrySet * entry_set)77 void GetAndResetDiskWriteEntrySet(SimpleIndex::EntrySet* entry_set) {
78 entry_set->swap(disk_write_entry_set_);
79 }
80
load_callback() const81 const base::Closure& load_callback() const { return load_callback_; }
load_result() const82 SimpleIndexLoadResult* load_result() const { return load_result_; }
load_index_entries_calls() const83 int load_index_entries_calls() const { return load_index_entries_calls_; }
disk_writes() const84 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:
SimpleIndexTest()96 SimpleIndexTest()
97 : hashes_(base::Bind(&HashesInitializer)),
98 doom_entries_calls_(0) {}
99
HashesInitializer(size_t hash_index)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
SetUp()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
WaitForTimeChange()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:
DoomEntries(std::vector<uint64> * entry_hashes,const net::CompletionCallback & callback)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.
GetEntryForTesting(uint64 key,EntryMetadata * metadata)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
InsertIntoIndexFileReturn(uint64 hash_key,base::Time last_used_time,int entry_size)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
ReturnIndexFile()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.
index()154 SimpleIndex* index() { return index_.get(); }
index_file() const155 const MockSimpleIndexFile* index_file() const { return index_file_.get(); }
156
last_doom_entry_hashes() const157 const std::vector<uint64>& last_doom_entry_hashes() const {
158 return last_doom_entry_hashes_;
159 }
doom_entries_calls() const160 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
TEST_F(EntryMetadataTest,Basics)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
TEST_F(EntryMetadataTest,Serialize)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
TEST_F(SimpleIndexTest,IndexSizeCorrectOnMerge)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.
TEST_F(SimpleIndexTest,BasicInsertRemove)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
TEST_F(SimpleIndexTest,Has)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
TEST_F(SimpleIndexTest,UseIfExists)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
TEST_F(SimpleIndexTest,UpdateEntrySize)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
TEST_F(SimpleIndexTest,GetEntryCount)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.
TEST_F(SimpleIndexTest,BasicInit)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.
TEST_F(SimpleIndexTest,RemoveBeforeInit)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?
TEST_F(SimpleIndexTest,InsertBeforeInit)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.
TEST_F(SimpleIndexTest,InsertRemoveBeforeInit)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.
TEST_F(SimpleIndexTest,RemoveInsertBeforeInit)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.
TEST_F(SimpleIndexTest,AllInitConflicts)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
TEST_F(SimpleIndexTest,BasicEviction)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.
TEST_F(SimpleIndexTest,DiskWriteQueued)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
TEST_F(SimpleIndexTest,DiskWriteExecuted)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
TEST_F(SimpleIndexTest,DiskWritePostponed)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