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