• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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