• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors
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 "net/disk_cache/simple/simple_index.h"
6 
7 #include <algorithm>
8 #include <functional>
9 #include <memory>
10 #include <utility>
11 
12 #include "base/files/scoped_temp_dir.h"
13 #include "base/functional/bind.h"
14 #include "base/hash/hash.h"
15 #include "base/memory/raw_ptr.h"
16 #include "base/pickle.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/task/task_runner.h"
19 #include "base/test/mock_entropy_provider.h"
20 #include "base/threading/platform_thread.h"
21 #include "base/time/time.h"
22 #include "net/base/cache_type.h"
23 #include "net/disk_cache/backend_cleanup_tracker.h"
24 #include "net/disk_cache/disk_cache.h"
25 #include "net/disk_cache/simple/simple_index_delegate.h"
26 #include "net/disk_cache/simple/simple_index_file.h"
27 #include "net/disk_cache/simple/simple_test_util.h"
28 #include "net/disk_cache/simple/simple_util.h"
29 #include "net/test/test_with_task_environment.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31 
32 namespace disk_cache {
33 namespace {
34 
35 const base::Time kTestLastUsedTime = base::Time::UnixEpoch() + base::Days(20);
36 const uint32_t kTestEntrySize = 789;
37 const uint8_t kTestEntryMemoryData = 123;
38 
RoundSize(uint32_t in)39 uint32_t RoundSize(uint32_t in) {
40   return (in + 0xFFu) & 0xFFFFFF00u;
41 }
42 
43 }  // namespace
44 
45 class EntryMetadataTest : public testing::Test {
46  public:
NewEntryMetadataWithValues()47   EntryMetadata NewEntryMetadataWithValues() {
48     EntryMetadata entry(kTestLastUsedTime, kTestEntrySize);
49     entry.SetInMemoryData(kTestEntryMemoryData);
50     return entry;
51   }
52 
CheckEntryMetadataValues(const EntryMetadata & entry_metadata)53   void CheckEntryMetadataValues(const EntryMetadata& entry_metadata) {
54     EXPECT_LT(kTestLastUsedTime - base::Seconds(2),
55               entry_metadata.GetLastUsedTime());
56     EXPECT_GT(kTestLastUsedTime + base::Seconds(2),
57               entry_metadata.GetLastUsedTime());
58     EXPECT_EQ(RoundSize(kTestEntrySize), entry_metadata.GetEntrySize());
59     EXPECT_EQ(kTestEntryMemoryData, entry_metadata.GetInMemoryData());
60   }
61 };
62 
63 class MockSimpleIndexFile : public SimpleIndexFile,
64                             public base::SupportsWeakPtr<MockSimpleIndexFile> {
65  public:
MockSimpleIndexFile(net::CacheType cache_type)66   explicit MockSimpleIndexFile(net::CacheType cache_type)
67       : SimpleIndexFile(nullptr,
68                         base::MakeRefCounted<TrivialFileOperationsFactory>(),
69                         cache_type,
70                         base::FilePath()) {}
71 
LoadIndexEntries(base::Time cache_last_modified,base::OnceClosure callback,SimpleIndexLoadResult * out_load_result)72   void LoadIndexEntries(base::Time cache_last_modified,
73                         base::OnceClosure callback,
74                         SimpleIndexLoadResult* out_load_result) override {
75     load_callback_ = std::move(callback);
76     load_result_ = out_load_result;
77     ++load_index_entries_calls_;
78   }
79 
WriteToDisk(net::CacheType cache_type,SimpleIndex::IndexWriteToDiskReason reason,const SimpleIndex::EntrySet & entry_set,uint64_t cache_size,base::OnceClosure callback)80   void WriteToDisk(net::CacheType cache_type,
81                    SimpleIndex::IndexWriteToDiskReason reason,
82                    const SimpleIndex::EntrySet& entry_set,
83                    uint64_t cache_size,
84                    base::OnceClosure callback) override {
85     disk_writes_++;
86     disk_write_entry_set_ = entry_set;
87   }
88 
GetAndResetDiskWriteEntrySet(SimpleIndex::EntrySet * entry_set)89   void GetAndResetDiskWriteEntrySet(SimpleIndex::EntrySet* entry_set) {
90     entry_set->swap(disk_write_entry_set_);
91   }
92 
RunLoadCallback()93   void RunLoadCallback() {
94     // Clear dangling reference since callback may destroy `load_result_`.
95     load_result_ = nullptr;
96     std::move(load_callback_).Run();
97   }
load_result() const98   SimpleIndexLoadResult* load_result() const { return load_result_; }
load_index_entries_calls() const99   int load_index_entries_calls() const { return load_index_entries_calls_; }
disk_writes() const100   int disk_writes() const { return disk_writes_; }
101 
102  private:
103   base::OnceClosure load_callback_;
104   raw_ptr<SimpleIndexLoadResult> load_result_ = nullptr;
105   int load_index_entries_calls_ = 0;
106   int disk_writes_ = 0;
107   SimpleIndex::EntrySet disk_write_entry_set_;
108 };
109 
110 class SimpleIndexTest : public net::TestWithTaskEnvironment,
111                         public SimpleIndexDelegate {
112  protected:
SimpleIndexTest()113   SimpleIndexTest() : hashes_(base::BindRepeating(&HashesInitializer)) {}
114 
HashesInitializer(size_t hash_index)115   static uint64_t HashesInitializer(size_t hash_index) {
116     return disk_cache::simple_util::GetEntryHashKey(
117         base::StringPrintf("key%d", static_cast<int>(hash_index)));
118   }
119 
SetUp()120   void SetUp() override {
121     auto index_file = std::make_unique<MockSimpleIndexFile>(CacheType());
122     index_file_ = index_file->AsWeakPtr();
123     index_ =
124         std::make_unique<SimpleIndex>(/* io_thread = */ nullptr,
125                                       /* cleanup_tracker = */ nullptr, this,
126                                       CacheType(), std::move(index_file));
127 
128     index_->Initialize(base::Time());
129   }
130 
WaitForTimeChange()131   void WaitForTimeChange() {
132     const base::Time initial_time = base::Time::Now();
133     do {
134       base::PlatformThread::YieldCurrentThread();
135     } while (base::Time::Now() - initial_time < base::Seconds(1));
136   }
137 
138   // From SimpleIndexDelegate:
DoomEntries(std::vector<uint64_t> * entry_hashes,net::CompletionOnceCallback callback)139   void DoomEntries(std::vector<uint64_t>* entry_hashes,
140                    net::CompletionOnceCallback callback) override {
141     for (const uint64_t& entry_hash : *entry_hashes)
142       index_->Remove(entry_hash);
143     last_doom_entry_hashes_ = *entry_hashes;
144     ++doom_entries_calls_;
145   }
146 
147   // Redirect to allow single "friend" declaration in base class.
GetEntryForTesting(uint64_t key,EntryMetadata * metadata)148   bool GetEntryForTesting(uint64_t key, EntryMetadata* metadata) {
149     auto it = index_->entries_set_.find(key);
150     if (index_->entries_set_.end() == it)
151       return false;
152     *metadata = it->second;
153     return true;
154   }
155 
InsertIntoIndexFileReturn(uint64_t hash_key,base::Time last_used_time,int entry_size)156   void InsertIntoIndexFileReturn(uint64_t hash_key,
157                                  base::Time last_used_time,
158                                  int entry_size) {
159     index_file_->load_result()->entries.insert(std::make_pair(
160         hash_key, EntryMetadata(last_used_time,
161                                 base::checked_cast<uint32_t>(entry_size))));
162   }
163 
ReturnIndexFile()164   void ReturnIndexFile() {
165     index_file_->load_result()->did_load = true;
166     index_file_->RunLoadCallback();
167   }
168 
169   // Non-const for timer manipulation.
index()170   SimpleIndex* index() { return index_.get(); }
index_file() const171   const MockSimpleIndexFile* index_file() const { return index_file_.get(); }
172 
last_doom_entry_hashes() const173   const std::vector<uint64_t>& last_doom_entry_hashes() const {
174     return last_doom_entry_hashes_;
175   }
doom_entries_calls() const176   int doom_entries_calls() const { return doom_entries_calls_; }
177 
CacheType() const178   virtual net::CacheType CacheType() const { return net::DISK_CACHE; }
179 
180   const simple_util::ImmutableArray<uint64_t, 16> hashes_;
181   std::unique_ptr<SimpleIndex> index_;
182   base::WeakPtr<MockSimpleIndexFile> index_file_;
183 
184   std::vector<uint64_t> last_doom_entry_hashes_;
185   int doom_entries_calls_ = 0;
186 };
187 
188 class SimpleIndexAppCacheTest : public SimpleIndexTest {
189  protected:
CacheType() const190   net::CacheType CacheType() const override { return net::APP_CACHE; }
191 };
192 
193 class SimpleIndexCodeCacheTest : public SimpleIndexTest {
194  protected:
CacheType() const195   net::CacheType CacheType() const override {
196     return net::GENERATED_BYTE_CODE_CACHE;
197   }
198 };
199 
TEST_F(EntryMetadataTest,Basics)200 TEST_F(EntryMetadataTest, Basics) {
201   EntryMetadata entry_metadata;
202   EXPECT_EQ(base::Time(), entry_metadata.GetLastUsedTime());
203   EXPECT_EQ(0u, entry_metadata.GetEntrySize());
204   EXPECT_EQ(0u, entry_metadata.GetInMemoryData());
205 
206   entry_metadata = NewEntryMetadataWithValues();
207   CheckEntryMetadataValues(entry_metadata);
208 
209   const base::Time new_time = base::Time::Now();
210   entry_metadata.SetLastUsedTime(new_time);
211 
212   EXPECT_LT(new_time - base::Seconds(2), entry_metadata.GetLastUsedTime());
213   EXPECT_GT(new_time + base::Seconds(2), entry_metadata.GetLastUsedTime());
214 }
215 
216 // Tests that setting an unusually small/large last used time results in
217 // truncation (rather than crashing).
TEST_F(EntryMetadataTest,SaturatedLastUsedTime)218 TEST_F(EntryMetadataTest, SaturatedLastUsedTime) {
219   EntryMetadata entry_metadata;
220 
221   // Set a time that is too large to be represented internally as 32-bit unix
222   // timestamp. Will saturate to a large timestamp (in year 2106).
223   entry_metadata.SetLastUsedTime(base::Time::Max());
224   EXPECT_EQ(INT64_C(15939440895000000),
225             entry_metadata.GetLastUsedTime().ToInternalValue());
226 
227   // Set a time that is too small to be represented by a unix timestamp (before
228   // 1970).
229   entry_metadata.SetLastUsedTime(
230       base::Time::FromInternalValue(7u));  // This is a date in 1601.
231   EXPECT_EQ(base::Time::UnixEpoch() + base::Seconds(1),
232             entry_metadata.GetLastUsedTime());
233 }
234 
TEST_F(EntryMetadataTest,Serialize)235 TEST_F(EntryMetadataTest, Serialize) {
236   EntryMetadata entry_metadata = NewEntryMetadataWithValues();
237 
238   base::Pickle pickle;
239   entry_metadata.Serialize(net::DISK_CACHE, &pickle);
240 
241   base::PickleIterator it(pickle);
242   EntryMetadata new_entry_metadata;
243   new_entry_metadata.Deserialize(net::DISK_CACHE, &it, true, true);
244   CheckEntryMetadataValues(new_entry_metadata);
245 
246   // Test reading of old format --- the modern serialization of above entry
247   // corresponds, in older format, to an entry with size =
248   //   RoundSize(kTestEntrySize) | kTestEntryMemoryData, which then gets
249   // rounded again when stored by EntryMetadata.
250   base::PickleIterator it2(pickle);
251   EntryMetadata new_entry_metadata2;
252   new_entry_metadata2.Deserialize(net::DISK_CACHE, &it2, false, false);
253   EXPECT_EQ(RoundSize(RoundSize(kTestEntrySize) | kTestEntryMemoryData),
254             new_entry_metadata2.GetEntrySize());
255   EXPECT_EQ(0, new_entry_metadata2.GetInMemoryData());
256 }
257 
TEST_F(SimpleIndexTest,IndexSizeCorrectOnMerge)258 TEST_F(SimpleIndexTest, IndexSizeCorrectOnMerge) {
259   const unsigned int kSizeResolution = 256u;
260   index()->SetMaxSize(100 * kSizeResolution);
261   index()->Insert(hashes_.at<2>());
262   index()->UpdateEntrySize(hashes_.at<2>(), 2u * kSizeResolution);
263   index()->Insert(hashes_.at<3>());
264   index()->UpdateEntrySize(hashes_.at<3>(), 3u * kSizeResolution);
265   index()->Insert(hashes_.at<4>());
266   index()->UpdateEntrySize(hashes_.at<4>(), 4u * kSizeResolution);
267   EXPECT_EQ(9u * kSizeResolution, index()->cache_size_);
268   {
269     auto result = std::make_unique<SimpleIndexLoadResult>();
270     result->did_load = true;
271     index()->MergeInitializingSet(std::move(result));
272   }
273   EXPECT_EQ(9u * kSizeResolution, index()->cache_size_);
274   {
275     auto result = std::make_unique<SimpleIndexLoadResult>();
276     result->did_load = true;
277     const uint64_t new_hash_key = hashes_.at<11>();
278     result->entries.insert(std::make_pair(
279         new_hash_key, EntryMetadata(base::Time::Now(), 11u * kSizeResolution)));
280     const uint64_t redundant_hash_key = hashes_.at<4>();
281     result->entries.insert(
282         std::make_pair(redundant_hash_key,
283                        EntryMetadata(base::Time::Now(), 4u * kSizeResolution)));
284     index()->MergeInitializingSet(std::move(result));
285   }
286   EXPECT_EQ((2u + 3u + 4u + 11u) * kSizeResolution, index()->cache_size_);
287 }
288 
289 // State of index changes as expected with an insert and a remove.
TEST_F(SimpleIndexTest,BasicInsertRemove)290 TEST_F(SimpleIndexTest, BasicInsertRemove) {
291   // Confirm blank state.
292   EntryMetadata metadata;
293   EXPECT_EQ(base::Time(), metadata.GetLastUsedTime());
294   EXPECT_EQ(0U, metadata.GetEntrySize());
295 
296   // Confirm state after insert.
297   index()->Insert(hashes_.at<1>());
298   ASSERT_TRUE(GetEntryForTesting(hashes_.at<1>(), &metadata));
299   base::Time now(base::Time::Now());
300   EXPECT_LT(now - base::Minutes(1), metadata.GetLastUsedTime());
301   EXPECT_GT(now + base::Minutes(1), metadata.GetLastUsedTime());
302   EXPECT_EQ(0U, metadata.GetEntrySize());
303 
304   // Confirm state after remove.
305   metadata = EntryMetadata();
306   index()->Remove(hashes_.at<1>());
307   EXPECT_FALSE(GetEntryForTesting(hashes_.at<1>(), &metadata));
308   EXPECT_EQ(base::Time(), metadata.GetLastUsedTime());
309   EXPECT_EQ(0U, metadata.GetEntrySize());
310 }
311 
TEST_F(SimpleIndexTest,Has)312 TEST_F(SimpleIndexTest, Has) {
313   // Confirm the base index has dispatched the request for index entries.
314   EXPECT_TRUE(index_file_.get());
315   EXPECT_EQ(1, index_file_->load_index_entries_calls());
316 
317   // Confirm "Has()" always returns true before the callback is called.
318   const uint64_t kHash1 = hashes_.at<1>();
319   EXPECT_TRUE(index()->Has(kHash1));
320   index()->Insert(kHash1);
321   EXPECT_TRUE(index()->Has(kHash1));
322   index()->Remove(kHash1);
323   // TODO(morlovich): Maybe return false on explicitly removed entries?
324   EXPECT_TRUE(index()->Has(kHash1));
325 
326   ReturnIndexFile();
327 
328   // Confirm "Has() returns conditionally now.
329   EXPECT_FALSE(index()->Has(kHash1));
330   index()->Insert(kHash1);
331   EXPECT_TRUE(index()->Has(kHash1));
332   index()->Remove(kHash1);
333 }
334 
TEST_F(SimpleIndexTest,UseIfExists)335 TEST_F(SimpleIndexTest, UseIfExists) {
336   // Confirm the base index has dispatched the request for index entries.
337   EXPECT_TRUE(index_file_.get());
338   EXPECT_EQ(1, index_file_->load_index_entries_calls());
339 
340   // Confirm "UseIfExists()" always returns true before the callback is called
341   // and updates mod time if the entry was really there.
342   const uint64_t kHash1 = hashes_.at<1>();
343   EntryMetadata metadata1, metadata2;
344   EXPECT_TRUE(index()->UseIfExists(kHash1));
345   EXPECT_FALSE(GetEntryForTesting(kHash1, &metadata1));
346   index()->Insert(kHash1);
347   EXPECT_TRUE(index()->UseIfExists(kHash1));
348   EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata1));
349   WaitForTimeChange();
350   EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata2));
351   EXPECT_EQ(metadata1.GetLastUsedTime(), metadata2.GetLastUsedTime());
352   EXPECT_TRUE(index()->UseIfExists(kHash1));
353   EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata2));
354   EXPECT_LT(metadata1.GetLastUsedTime(), metadata2.GetLastUsedTime());
355   index()->Remove(kHash1);
356   EXPECT_TRUE(index()->UseIfExists(kHash1));
357 
358   ReturnIndexFile();
359 
360   // Confirm "UseIfExists() returns conditionally now
361   EXPECT_FALSE(index()->UseIfExists(kHash1));
362   EXPECT_FALSE(GetEntryForTesting(kHash1, &metadata1));
363   index()->Insert(kHash1);
364   EXPECT_TRUE(index()->UseIfExists(kHash1));
365   EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata1));
366   WaitForTimeChange();
367   EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata2));
368   EXPECT_EQ(metadata1.GetLastUsedTime(), metadata2.GetLastUsedTime());
369   EXPECT_TRUE(index()->UseIfExists(kHash1));
370   EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata2));
371   EXPECT_LT(metadata1.GetLastUsedTime(), metadata2.GetLastUsedTime());
372   index()->Remove(kHash1);
373   EXPECT_FALSE(index()->UseIfExists(kHash1));
374 }
375 
TEST_F(SimpleIndexTest,UpdateEntrySize)376 TEST_F(SimpleIndexTest, UpdateEntrySize) {
377   base::Time now(base::Time::Now());
378 
379   index()->SetMaxSize(1000);
380 
381   const uint64_t kHash1 = hashes_.at<1>();
382   InsertIntoIndexFileReturn(kHash1, now - base::Days(2), 475);
383   ReturnIndexFile();
384 
385   EntryMetadata metadata;
386   EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata));
387   EXPECT_LT(now - base::Days(2) - base::Seconds(1), metadata.GetLastUsedTime());
388   EXPECT_GT(now - base::Days(2) + base::Seconds(1), metadata.GetLastUsedTime());
389   EXPECT_EQ(RoundSize(475u), metadata.GetEntrySize());
390 
391   index()->UpdateEntrySize(kHash1, 600u);
392   EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata));
393   EXPECT_EQ(RoundSize(600u), metadata.GetEntrySize());
394   EXPECT_EQ(1, index()->GetEntryCount());
395 }
396 
TEST_F(SimpleIndexTest,GetEntryCount)397 TEST_F(SimpleIndexTest, GetEntryCount) {
398   EXPECT_EQ(0, index()->GetEntryCount());
399   index()->Insert(hashes_.at<1>());
400   EXPECT_EQ(1, index()->GetEntryCount());
401   index()->Insert(hashes_.at<2>());
402   EXPECT_EQ(2, index()->GetEntryCount());
403   index()->Insert(hashes_.at<3>());
404   EXPECT_EQ(3, index()->GetEntryCount());
405   index()->Insert(hashes_.at<3>());
406   EXPECT_EQ(3, index()->GetEntryCount());
407   index()->Remove(hashes_.at<2>());
408   EXPECT_EQ(2, index()->GetEntryCount());
409   index()->Insert(hashes_.at<4>());
410   EXPECT_EQ(3, index()->GetEntryCount());
411   index()->Remove(hashes_.at<3>());
412   EXPECT_EQ(2, index()->GetEntryCount());
413   index()->Remove(hashes_.at<3>());
414   EXPECT_EQ(2, index()->GetEntryCount());
415   index()->Remove(hashes_.at<1>());
416   EXPECT_EQ(1, index()->GetEntryCount());
417   index()->Remove(hashes_.at<4>());
418   EXPECT_EQ(0, index()->GetEntryCount());
419 }
420 
421 // Confirm that we get the results we expect from a simple init.
TEST_F(SimpleIndexTest,BasicInit)422 TEST_F(SimpleIndexTest, BasicInit) {
423   base::Time now(base::Time::Now());
424 
425   InsertIntoIndexFileReturn(hashes_.at<1>(), now - base::Days(2), 10u);
426   InsertIntoIndexFileReturn(hashes_.at<2>(), now - base::Days(3), 1000u);
427 
428   ReturnIndexFile();
429 
430   EntryMetadata metadata;
431   EXPECT_TRUE(GetEntryForTesting(hashes_.at<1>(), &metadata));
432   EXPECT_EQ(metadata.GetLastUsedTime(),
433             index()->GetLastUsedTime(hashes_.at<1>()));
434   EXPECT_LT(now - base::Days(2) - base::Seconds(1), metadata.GetLastUsedTime());
435   EXPECT_GT(now - base::Days(2) + base::Seconds(1), metadata.GetLastUsedTime());
436   EXPECT_EQ(RoundSize(10u), metadata.GetEntrySize());
437   EXPECT_TRUE(GetEntryForTesting(hashes_.at<2>(), &metadata));
438   EXPECT_EQ(metadata.GetLastUsedTime(),
439             index()->GetLastUsedTime(hashes_.at<2>()));
440   EXPECT_LT(now - base::Days(3) - base::Seconds(1), metadata.GetLastUsedTime());
441   EXPECT_GT(now - base::Days(3) + base::Seconds(1), metadata.GetLastUsedTime());
442   EXPECT_EQ(RoundSize(1000u), metadata.GetEntrySize());
443   EXPECT_EQ(base::Time(), index()->GetLastUsedTime(hashes_.at<3>()));
444 }
445 
446 // Remove something that's going to come in from the loaded index.
TEST_F(SimpleIndexTest,RemoveBeforeInit)447 TEST_F(SimpleIndexTest, RemoveBeforeInit) {
448   const uint64_t kHash1 = hashes_.at<1>();
449   index()->Remove(kHash1);
450 
451   InsertIntoIndexFileReturn(kHash1, base::Time::Now() - base::Days(2), 10u);
452   ReturnIndexFile();
453 
454   EXPECT_FALSE(index()->Has(kHash1));
455 }
456 
457 // Insert something that's going to come in from the loaded index; correct
458 // result?
TEST_F(SimpleIndexTest,InsertBeforeInit)459 TEST_F(SimpleIndexTest, InsertBeforeInit) {
460   const uint64_t kHash1 = hashes_.at<1>();
461   index()->Insert(kHash1);
462 
463   InsertIntoIndexFileReturn(kHash1, base::Time::Now() - base::Days(2), 10u);
464   ReturnIndexFile();
465 
466   EntryMetadata metadata;
467   EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata));
468   base::Time now(base::Time::Now());
469   EXPECT_LT(now - base::Minutes(1), metadata.GetLastUsedTime());
470   EXPECT_GT(now + base::Minutes(1), metadata.GetLastUsedTime());
471   EXPECT_EQ(0U, metadata.GetEntrySize());
472 }
473 
474 // Insert and Remove something that's going to come in from the loaded index.
TEST_F(SimpleIndexTest,InsertRemoveBeforeInit)475 TEST_F(SimpleIndexTest, InsertRemoveBeforeInit) {
476   const uint64_t kHash1 = hashes_.at<1>();
477   index()->Insert(kHash1);
478   index()->Remove(kHash1);
479 
480   InsertIntoIndexFileReturn(kHash1, base::Time::Now() - base::Days(2), 10u);
481   ReturnIndexFile();
482 
483   EXPECT_FALSE(index()->Has(kHash1));
484 }
485 
486 // Insert and Remove something that's going to come in from the loaded index.
TEST_F(SimpleIndexTest,RemoveInsertBeforeInit)487 TEST_F(SimpleIndexTest, RemoveInsertBeforeInit) {
488   const uint64_t kHash1 = hashes_.at<1>();
489   index()->Remove(kHash1);
490   index()->Insert(kHash1);
491 
492   InsertIntoIndexFileReturn(kHash1, base::Time::Now() - base::Days(2), 10u);
493   ReturnIndexFile();
494 
495   EntryMetadata metadata;
496   EXPECT_TRUE(GetEntryForTesting(kHash1, &metadata));
497   base::Time now(base::Time::Now());
498   EXPECT_LT(now - base::Minutes(1), metadata.GetLastUsedTime());
499   EXPECT_GT(now + base::Minutes(1), metadata.GetLastUsedTime());
500   EXPECT_EQ(0U, metadata.GetEntrySize());
501 }
502 
503 // Do all above tests at once + a non-conflict to test for cross-key
504 // interactions.
TEST_F(SimpleIndexTest,AllInitConflicts)505 TEST_F(SimpleIndexTest, AllInitConflicts) {
506   base::Time now(base::Time::Now());
507 
508   index()->Remove(hashes_.at<1>());
509   InsertIntoIndexFileReturn(hashes_.at<1>(), now - base::Days(2), 10u);
510   index()->Insert(hashes_.at<2>());
511   InsertIntoIndexFileReturn(hashes_.at<2>(), now - base::Days(3), 100u);
512   index()->Insert(hashes_.at<3>());
513   index()->Remove(hashes_.at<3>());
514   InsertIntoIndexFileReturn(hashes_.at<3>(), now - base::Days(4), 1000u);
515   index()->Remove(hashes_.at<4>());
516   index()->Insert(hashes_.at<4>());
517   InsertIntoIndexFileReturn(hashes_.at<4>(), now - base::Days(5), 10000u);
518   InsertIntoIndexFileReturn(hashes_.at<5>(), now - base::Days(6), 100000u);
519 
520   ReturnIndexFile();
521 
522   EXPECT_FALSE(index()->Has(hashes_.at<1>()));
523 
524   EntryMetadata metadata;
525   EXPECT_TRUE(GetEntryForTesting(hashes_.at<2>(), &metadata));
526   EXPECT_LT(now - base::Minutes(1), metadata.GetLastUsedTime());
527   EXPECT_GT(now + base::Minutes(1), metadata.GetLastUsedTime());
528   EXPECT_EQ(0U, metadata.GetEntrySize());
529 
530   EXPECT_FALSE(index()->Has(hashes_.at<3>()));
531 
532   EXPECT_TRUE(GetEntryForTesting(hashes_.at<4>(), &metadata));
533   EXPECT_LT(now - base::Minutes(1), metadata.GetLastUsedTime());
534   EXPECT_GT(now + base::Minutes(1), metadata.GetLastUsedTime());
535   EXPECT_EQ(0U, metadata.GetEntrySize());
536 
537   EXPECT_TRUE(GetEntryForTesting(hashes_.at<5>(), &metadata));
538 
539   EXPECT_GT(now - base::Days(6) + base::Seconds(1), metadata.GetLastUsedTime());
540   EXPECT_LT(now - base::Days(6) - base::Seconds(1), metadata.GetLastUsedTime());
541 
542   EXPECT_EQ(RoundSize(100000u), metadata.GetEntrySize());
543 }
544 
TEST_F(SimpleIndexTest,BasicEviction)545 TEST_F(SimpleIndexTest, BasicEviction) {
546   base::Time now(base::Time::Now());
547   index()->SetMaxSize(1000);
548   InsertIntoIndexFileReturn(hashes_.at<1>(), now - base::Days(2), 475u);
549   index()->Insert(hashes_.at<2>());
550   index()->UpdateEntrySize(hashes_.at<2>(), 475u);
551   ReturnIndexFile();
552 
553   WaitForTimeChange();
554 
555   index()->Insert(hashes_.at<3>());
556   // Confirm index is as expected: No eviction, everything there.
557   EXPECT_EQ(3, index()->GetEntryCount());
558   EXPECT_EQ(0, doom_entries_calls());
559   EXPECT_TRUE(index()->Has(hashes_.at<1>()));
560   EXPECT_TRUE(index()->Has(hashes_.at<2>()));
561   EXPECT_TRUE(index()->Has(hashes_.at<3>()));
562 
563   // Trigger an eviction, and make sure the right things are tossed.
564   // TODO(morlovich): This is dependent on the innards of the implementation
565   // as to at exactly what point we trigger eviction. Not sure how to fix
566   // that.
567   index()->UpdateEntrySize(hashes_.at<3>(), 475u);
568   EXPECT_EQ(1, doom_entries_calls());
569   EXPECT_EQ(1, index()->GetEntryCount());
570   EXPECT_FALSE(index()->Has(hashes_.at<1>()));
571   EXPECT_FALSE(index()->Has(hashes_.at<2>()));
572   EXPECT_TRUE(index()->Has(hashes_.at<3>()));
573   ASSERT_EQ(2u, last_doom_entry_hashes().size());
574 }
575 
TEST_F(SimpleIndexTest,EvictBySize)576 TEST_F(SimpleIndexTest, EvictBySize) {
577   base::Time now(base::Time::Now());
578   index()->SetMaxSize(50000);
579   InsertIntoIndexFileReturn(hashes_.at<1>(), now - base::Days(2), 475u);
580   InsertIntoIndexFileReturn(hashes_.at<2>(), now - base::Days(1), 40000u);
581   ReturnIndexFile();
582   WaitForTimeChange();
583 
584   index()->Insert(hashes_.at<3>());
585   // Confirm index is as expected: No eviction, everything there.
586   EXPECT_EQ(3, index()->GetEntryCount());
587   EXPECT_EQ(0, doom_entries_calls());
588   EXPECT_TRUE(index()->Has(hashes_.at<1>()));
589   EXPECT_TRUE(index()->Has(hashes_.at<2>()));
590   EXPECT_TRUE(index()->Has(hashes_.at<3>()));
591 
592   // Trigger an eviction, and make sure the right things are tossed.
593   // TODO(morlovich): This is dependent on the innards of the implementation
594   // as to at exactly what point we trigger eviction. Not sure how to fix
595   // that.
596   index()->UpdateEntrySize(hashes_.at<3>(), 40000u);
597   EXPECT_EQ(1, doom_entries_calls());
598   EXPECT_EQ(2, index()->GetEntryCount());
599   EXPECT_TRUE(index()->Has(hashes_.at<1>()));
600   EXPECT_FALSE(index()->Has(hashes_.at<2>()));
601   EXPECT_TRUE(index()->Has(hashes_.at<3>()));
602   ASSERT_EQ(1u, last_doom_entry_hashes().size());
603 }
604 
TEST_F(SimpleIndexCodeCacheTest,DisableEvictBySize)605 TEST_F(SimpleIndexCodeCacheTest, DisableEvictBySize) {
606   base::Time now(base::Time::Now());
607   index()->SetMaxSize(50000);
608   InsertIntoIndexFileReturn(hashes_.at<1>(), now - base::Days(2), 475u);
609   InsertIntoIndexFileReturn(hashes_.at<2>(), now - base::Days(1), 40000u);
610   ReturnIndexFile();
611   WaitForTimeChange();
612 
613   index()->Insert(hashes_.at<3>());
614   // Confirm index is as expected: No eviction, everything there.
615   EXPECT_EQ(3, index()->GetEntryCount());
616   EXPECT_EQ(0, doom_entries_calls());
617   EXPECT_TRUE(index()->Has(hashes_.at<1>()));
618   EXPECT_TRUE(index()->Has(hashes_.at<2>()));
619   EXPECT_TRUE(index()->Has(hashes_.at<3>()));
620 
621   // Trigger an eviction, and make sure the right things are tossed.
622   // Since evict by size is supposed to be disabled, it evicts in LRU order,
623   // so entries 1 and 2 are both kicked out.
624   index()->UpdateEntrySize(hashes_.at<3>(), 40000u);
625   EXPECT_EQ(1, doom_entries_calls());
626   EXPECT_EQ(1, index()->GetEntryCount());
627   EXPECT_FALSE(index()->Has(hashes_.at<1>()));
628   EXPECT_FALSE(index()->Has(hashes_.at<2>()));
629   EXPECT_TRUE(index()->Has(hashes_.at<3>()));
630   ASSERT_EQ(2u, last_doom_entry_hashes().size());
631 }
632 
633 // Same as test above, but using much older entries to make sure that small
634 // things eventually get evictied.
TEST_F(SimpleIndexTest,EvictBySize2)635 TEST_F(SimpleIndexTest, EvictBySize2) {
636   base::Time now(base::Time::Now());
637   index()->SetMaxSize(50000);
638   InsertIntoIndexFileReturn(hashes_.at<1>(), now - base::Days(200), 475u);
639   InsertIntoIndexFileReturn(hashes_.at<2>(), now - base::Days(1), 40000u);
640   ReturnIndexFile();
641   WaitForTimeChange();
642 
643   index()->Insert(hashes_.at<3>());
644   // Confirm index is as expected: No eviction, everything there.
645   EXPECT_EQ(3, index()->GetEntryCount());
646   EXPECT_EQ(0, doom_entries_calls());
647   EXPECT_TRUE(index()->Has(hashes_.at<1>()));
648   EXPECT_TRUE(index()->Has(hashes_.at<2>()));
649   EXPECT_TRUE(index()->Has(hashes_.at<3>()));
650 
651   // Trigger an eviction, and make sure the right things are tossed.
652   // TODO(morlovich): This is dependent on the innards of the implementation
653   // as to at exactly what point we trigger eviction. Not sure how to fix
654   // that.
655   index()->UpdateEntrySize(hashes_.at<3>(), 40000u);
656   EXPECT_EQ(1, doom_entries_calls());
657   EXPECT_EQ(1, index()->GetEntryCount());
658   EXPECT_FALSE(index()->Has(hashes_.at<1>()));
659   EXPECT_FALSE(index()->Has(hashes_.at<2>()));
660   EXPECT_TRUE(index()->Has(hashes_.at<3>()));
661   ASSERT_EQ(2u, last_doom_entry_hashes().size());
662 }
663 
664 // Confirm all the operations queue a disk write at some point in the
665 // future.
TEST_F(SimpleIndexTest,DiskWriteQueued)666 TEST_F(SimpleIndexTest, DiskWriteQueued) {
667   index()->SetMaxSize(1000);
668   ReturnIndexFile();
669 
670   EXPECT_FALSE(index()->HasPendingWrite());
671 
672   const uint64_t kHash1 = hashes_.at<1>();
673   index()->Insert(kHash1);
674   EXPECT_TRUE(index()->HasPendingWrite());
675   index()->write_to_disk_timer_.Stop();
676   EXPECT_FALSE(index()->HasPendingWrite());
677 
678   // Attempting to insert a hash that already exists should not queue the
679   // write timer.
680   index()->Insert(kHash1);
681   EXPECT_FALSE(index()->HasPendingWrite());
682 
683   index()->UseIfExists(kHash1);
684   EXPECT_TRUE(index()->HasPendingWrite());
685   index()->write_to_disk_timer_.Stop();
686 
687   index()->UpdateEntrySize(kHash1, 20u);
688   EXPECT_TRUE(index()->HasPendingWrite());
689   index()->write_to_disk_timer_.Stop();
690 
691   // Updating to the same size should not queue the write timer.
692   index()->UpdateEntrySize(kHash1, 20u);
693   EXPECT_FALSE(index()->HasPendingWrite());
694 
695   index()->Remove(kHash1);
696   EXPECT_TRUE(index()->HasPendingWrite());
697   index()->write_to_disk_timer_.Stop();
698 
699   // Removing a non-existent hash should not queue the write timer.
700   index()->Remove(kHash1);
701   EXPECT_FALSE(index()->HasPendingWrite());
702 }
703 
TEST_F(SimpleIndexTest,DiskWriteExecuted)704 TEST_F(SimpleIndexTest, DiskWriteExecuted) {
705   index()->SetMaxSize(1000);
706   ReturnIndexFile();
707 
708   EXPECT_FALSE(index()->HasPendingWrite());
709 
710   const uint64_t kHash1 = hashes_.at<1>();
711   index()->Insert(kHash1);
712   index()->UpdateEntrySize(kHash1, 20u);
713   EXPECT_TRUE(index()->HasPendingWrite());
714 
715   EXPECT_EQ(0, index_file_->disk_writes());
716   index()->write_to_disk_timer_.FireNow();
717   EXPECT_EQ(1, index_file_->disk_writes());
718   SimpleIndex::EntrySet entry_set;
719   index_file_->GetAndResetDiskWriteEntrySet(&entry_set);
720 
721   uint64_t hash_key = kHash1;
722   base::Time now(base::Time::Now());
723   ASSERT_EQ(1u, entry_set.size());
724   EXPECT_EQ(hash_key, entry_set.begin()->first);
725   const EntryMetadata& entry1(entry_set.begin()->second);
726   EXPECT_LT(now - base::Minutes(1), entry1.GetLastUsedTime());
727   EXPECT_GT(now + base::Minutes(1), entry1.GetLastUsedTime());
728   EXPECT_EQ(RoundSize(20u), entry1.GetEntrySize());
729 }
730 
TEST_F(SimpleIndexTest,DiskWritePostponed)731 TEST_F(SimpleIndexTest, DiskWritePostponed) {
732   index()->SetMaxSize(1000);
733   ReturnIndexFile();
734 
735   EXPECT_FALSE(index()->HasPendingWrite());
736 
737   index()->Insert(hashes_.at<1>());
738   index()->UpdateEntrySize(hashes_.at<1>(), 20u);
739   EXPECT_TRUE(index()->HasPendingWrite());
740   base::TimeTicks expected_trigger(
741       index()->write_to_disk_timer_.desired_run_time());
742 
743   WaitForTimeChange();
744   EXPECT_EQ(expected_trigger, index()->write_to_disk_timer_.desired_run_time());
745   index()->Insert(hashes_.at<2>());
746   index()->UpdateEntrySize(hashes_.at<2>(), 40u);
747   EXPECT_TRUE(index()->HasPendingWrite());
748   EXPECT_LT(expected_trigger, index()->write_to_disk_timer_.desired_run_time());
749   index()->write_to_disk_timer_.Stop();
750 }
751 
752 // net::APP_CACHE mode should not need to queue disk writes in as many places
753 // as the default net::DISK_CACHE mode.
TEST_F(SimpleIndexAppCacheTest,DiskWriteQueued)754 TEST_F(SimpleIndexAppCacheTest, DiskWriteQueued) {
755   index()->SetMaxSize(1000);
756   ReturnIndexFile();
757 
758   EXPECT_FALSE(index()->HasPendingWrite());
759 
760   const uint64_t kHash1 = hashes_.at<1>();
761   index()->Insert(kHash1);
762   EXPECT_TRUE(index()->HasPendingWrite());
763   index()->write_to_disk_timer_.Stop();
764   EXPECT_FALSE(index()->HasPendingWrite());
765 
766   // Attempting to insert a hash that already exists should not queue the
767   // write timer.
768   index()->Insert(kHash1);
769   EXPECT_FALSE(index()->HasPendingWrite());
770 
771   // Since net::APP_CACHE does not evict or track access times using an
772   // entry should not queue the write timer.
773   index()->UseIfExists(kHash1);
774   EXPECT_FALSE(index()->HasPendingWrite());
775 
776   index()->UpdateEntrySize(kHash1, 20u);
777   EXPECT_TRUE(index()->HasPendingWrite());
778   index()->write_to_disk_timer_.Stop();
779 
780   // Updating to the same size should not queue the write timer.
781   index()->UpdateEntrySize(kHash1, 20u);
782   EXPECT_FALSE(index()->HasPendingWrite());
783 
784   index()->Remove(kHash1);
785   EXPECT_TRUE(index()->HasPendingWrite());
786   index()->write_to_disk_timer_.Stop();
787 
788   // Removing a non-existent hash should not queue the write timer.
789   index()->Remove(kHash1);
790   EXPECT_FALSE(index()->HasPendingWrite());
791 }
792 
793 }  // namespace disk_cache
794