• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2011 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_file.h"
6 
7 #include <memory>
8 
9 #include "base/check.h"
10 #include "base/files/file.h"
11 #include "base/files/file_util.h"
12 #include "base/files/scoped_temp_dir.h"
13 #include "base/functional/callback.h"
14 #include "base/hash/hash.h"
15 #include "base/location.h"
16 #include "base/pickle.h"
17 #include "base/run_loop.h"
18 #include "base/task/single_thread_task_runner.h"
19 #include "base/threading/thread.h"
20 #include "base/time/time.h"
21 #include "net/base/cache_type.h"
22 #include "net/base/test_completion_callback.h"
23 #include "net/disk_cache/backend_cleanup_tracker.h"
24 #include "net/disk_cache/disk_cache.h"
25 #include "net/disk_cache/disk_cache_test_util.h"
26 #include "net/disk_cache/simple/simple_backend_impl.h"
27 #include "net/disk_cache/simple/simple_backend_version.h"
28 #include "net/disk_cache/simple/simple_entry_format.h"
29 #include "net/disk_cache/simple/simple_index.h"
30 #include "net/disk_cache/simple/simple_util.h"
31 #include "net/disk_cache/simple/simple_version_upgrade.h"
32 #include "net/test/gtest_util.h"
33 #include "net/test/test_with_task_environment.h"
34 #include "testing/gmock/include/gmock/gmock.h"
35 #include "testing/gtest/include/gtest/gtest.h"
36 
37 using net::test::IsOk;
38 
39 using base::Time;
40 using disk_cache::SimpleIndexFile;
41 using disk_cache::SimpleIndex;
42 
43 namespace disk_cache {
44 
45 namespace {
46 
RoundSize(uint32_t in)47 uint32_t RoundSize(uint32_t in) {
48   return (in + 0xFFu) & 0xFFFFFF00u;
49 }
50 
51 }  // namespace
52 
TEST(IndexMetadataTest,Basics)53 TEST(IndexMetadataTest, Basics) {
54   SimpleIndexFile::IndexMetadata index_metadata;
55 
56   EXPECT_EQ(disk_cache::kSimpleIndexMagicNumber, index_metadata.magic_number_);
57   EXPECT_EQ(disk_cache::kSimpleVersion, index_metadata.version_);
58   EXPECT_EQ(0U, index_metadata.entry_count());
59   EXPECT_EQ(0U, index_metadata.cache_size_);
60 
61   // Without setting a |reason_|, the index metadata isn't valid.
62   index_metadata.reason_ = SimpleIndex::INDEX_WRITE_REASON_SHUTDOWN;
63 
64   EXPECT_TRUE(index_metadata.CheckIndexMetadata());
65 }
66 
TEST(IndexMetadataTest,Serialize)67 TEST(IndexMetadataTest, Serialize) {
68   SimpleIndexFile::IndexMetadata index_metadata(
69       SimpleIndex::INDEX_WRITE_REASON_SHUTDOWN, 123, 456);
70   base::Pickle pickle;
71   index_metadata.Serialize(&pickle);
72   base::PickleIterator it(pickle);
73   SimpleIndexFile::IndexMetadata new_index_metadata;
74   new_index_metadata.Deserialize(&it);
75 
76   EXPECT_EQ(new_index_metadata.magic_number_, index_metadata.magic_number_);
77   EXPECT_EQ(new_index_metadata.version_, index_metadata.version_);
78   EXPECT_EQ(new_index_metadata.reason_, index_metadata.reason_);
79   EXPECT_EQ(new_index_metadata.entry_count(), index_metadata.entry_count());
80   EXPECT_EQ(new_index_metadata.cache_size_, index_metadata.cache_size_);
81 
82   EXPECT_TRUE(new_index_metadata.CheckIndexMetadata());
83 }
84 
85 // This derived index metadata class allows us to serialize the older V6 format
86 // of the index metadata, thus allowing us to test deserializing the old format.
87 class V6IndexMetadataForTest : public SimpleIndexFile::IndexMetadata {
88  public:
89   // Do not default to |SimpleIndex::INDEX_WRITE_REASON_MAX|, because we want to
90   // ensure we don't serialize that value and then deserialize it and have a
91   // false positive result.
V6IndexMetadataForTest(uint64_t entry_count,uint64_t cache_size)92   V6IndexMetadataForTest(uint64_t entry_count, uint64_t cache_size)
93       : SimpleIndexFile::IndexMetadata(SimpleIndex::INDEX_WRITE_REASON_SHUTDOWN,
94                                        entry_count,
95                                        cache_size) {
96     version_ = 6;
97   }
98 
99   // Copied and pasted from the V6 implementation of
100   // |SimpleIndexFile::IndexMetadata()| (removing DCHECKs).
Serialize(base::Pickle * pickle) const101   void Serialize(base::Pickle* pickle) const override {
102     pickle->WriteUInt64(magic_number_);
103     pickle->WriteUInt32(version_);
104     pickle->WriteUInt64(entry_count_);
105     pickle->WriteUInt64(cache_size_);
106   }
107 };
108 
TEST(IndexMetadataTest,ReadV6Format)109 TEST(IndexMetadataTest, ReadV6Format) {
110   V6IndexMetadataForTest v6_index_metadata(123, 456);
111   EXPECT_EQ(6U, v6_index_metadata.version_);
112   base::Pickle pickle;
113   v6_index_metadata.Serialize(&pickle);
114   base::PickleIterator it(pickle);
115   SimpleIndexFile::IndexMetadata new_index_metadata;
116   new_index_metadata.Deserialize(&it);
117 
118   EXPECT_EQ(new_index_metadata.magic_number_, v6_index_metadata.magic_number_);
119   EXPECT_EQ(new_index_metadata.version_, v6_index_metadata.version_);
120 
121   EXPECT_EQ(new_index_metadata.reason_, SimpleIndex::INDEX_WRITE_REASON_MAX);
122   EXPECT_EQ(new_index_metadata.entry_count(), v6_index_metadata.entry_count());
123   EXPECT_EQ(new_index_metadata.cache_size_, v6_index_metadata.cache_size_);
124 
125   EXPECT_TRUE(new_index_metadata.CheckIndexMetadata());
126 }
127 
128 // This derived index metadata class allows us to serialize the older V7 format
129 // of the index metadata, thus allowing us to test deserializing the old format.
130 class V7IndexMetadataForTest : public SimpleIndexFile::IndexMetadata {
131  public:
V7IndexMetadataForTest(uint64_t entry_count,uint64_t cache_size)132   V7IndexMetadataForTest(uint64_t entry_count, uint64_t cache_size)
133       : SimpleIndexFile::IndexMetadata(SimpleIndex::INDEX_WRITE_REASON_SHUTDOWN,
134                                        entry_count,
135                                        cache_size) {
136     version_ = 7;
137   }
138 };
139 
140 class V8IndexMetadataForTest : public SimpleIndexFile::IndexMetadata {
141  public:
V8IndexMetadataForTest(uint64_t entry_count,uint64_t cache_size)142   V8IndexMetadataForTest(uint64_t entry_count, uint64_t cache_size)
143       : SimpleIndexFile::IndexMetadata(SimpleIndex::INDEX_WRITE_REASON_SHUTDOWN,
144                                        entry_count,
145                                        cache_size) {
146     version_ = 8;
147   }
148 };
149 
150 // This friend derived class is able to reexport its ancestors private methods
151 // as public, for use in tests.
152 class WrappedSimpleIndexFile : public SimpleIndexFile {
153  public:
154   using SimpleIndexFile::Deserialize;
155   using SimpleIndexFile::Serialize;
156   using SimpleIndexFile::SerializeFinalData;
157 
WrappedSimpleIndexFile(const base::FilePath & index_file_directory)158   explicit WrappedSimpleIndexFile(const base::FilePath& index_file_directory)
159       : SimpleIndexFile(base::SingleThreadTaskRunner::GetCurrentDefault(),
160                         base::MakeRefCounted<TrivialFileOperationsFactory>(),
161                         net::DISK_CACHE,
162                         index_file_directory) {}
163   ~WrappedSimpleIndexFile() override = default;
164 
GetIndexFilePath() const165   const base::FilePath& GetIndexFilePath() const {
166     return index_file_;
167   }
168 
GetTempIndexFilePath() const169   const base::FilePath& GetTempIndexFilePath() const {
170     return temp_index_file_;
171   }
172 
CreateIndexFileDirectory() const173   bool CreateIndexFileDirectory() const {
174     return base::CreateDirectory(index_file_.DirName());
175   }
176 
LegacyIsIndexFileStale(base::Time cache_last_modified,const base::FilePath & index_file_path)177   static bool LegacyIsIndexFileStale(base::Time cache_last_modified,
178                                      const base::FilePath& index_file_path) {
179     TrivialFileOperations ops;
180     return SimpleIndexFile::LegacyIsIndexFileStale(&ops, cache_last_modified,
181                                                    index_file_path);
182   }
183 };
184 
185 class SimpleIndexFileTest : public net::TestWithTaskEnvironment {
186  public:
CompareTwoEntryMetadata(const EntryMetadata & a,const EntryMetadata & b)187   bool CompareTwoEntryMetadata(const EntryMetadata& a, const EntryMetadata& b) {
188     return a.last_used_time_seconds_since_epoch_ ==
189                b.last_used_time_seconds_since_epoch_ &&
190            a.entry_size_256b_chunks_ == b.entry_size_256b_chunks_ &&
191            a.in_memory_data_ == b.in_memory_data_;
192   }
193 
CompareTwoAppCacheEntryMetadata(const EntryMetadata & a,const EntryMetadata & b)194   bool CompareTwoAppCacheEntryMetadata(const EntryMetadata& a,
195                                        const EntryMetadata& b) {
196     return a.trailer_prefetch_size_ == b.trailer_prefetch_size_ &&
197            a.entry_size_256b_chunks_ == b.entry_size_256b_chunks_ &&
198            a.in_memory_data_ == b.in_memory_data_;
199   }
200 };
201 
TEST_F(SimpleIndexFileTest,Serialize)202 TEST_F(SimpleIndexFileTest, Serialize) {
203   SimpleIndex::EntrySet entries;
204   static const size_t kNumHashes = 3u;
205   const std::array<uint64_t, kNumHashes> kHashes = {11, 22, 33};
206   std::array<EntryMetadata, kNumHashes> metadata_entries;
207 
208   SimpleIndexFile::IndexMetadata index_metadata(
209       SimpleIndex::INDEX_WRITE_REASON_SHUTDOWN,
210       static_cast<uint64_t>(kNumHashes), 456);
211   for (size_t i = 0; i < kNumHashes; ++i) {
212     uint64_t hash = kHashes[i];
213     // TODO(eroman): Should restructure the test so no casting here (and same
214     //               elsewhere where a hash is cast to an entry size).
215     metadata_entries[i] = EntryMetadata(Time(), static_cast<uint32_t>(hash));
216     metadata_entries[i].SetInMemoryData(static_cast<uint8_t>(i));
217     SimpleIndex::InsertInEntrySet(hash, metadata_entries[i], &entries);
218   }
219 
220   std::unique_ptr<base::Pickle> pickle = WrappedSimpleIndexFile::Serialize(
221       net::DISK_CACHE, index_metadata, entries);
222   EXPECT_TRUE(pickle.get() != nullptr);
223   base::Time now = base::Time::Now();
224   WrappedSimpleIndexFile::SerializeFinalData(now, pickle.get());
225   base::Time when_index_last_saw_cache;
226   SimpleIndexLoadResult deserialize_result;
227   WrappedSimpleIndexFile::Deserialize(net::DISK_CACHE, *pickle,
228                                       &when_index_last_saw_cache,
229                                       &deserialize_result);
230   EXPECT_TRUE(deserialize_result.did_load);
231   EXPECT_EQ(now, when_index_last_saw_cache);
232   const SimpleIndex::EntrySet& new_entries = deserialize_result.entries;
233   EXPECT_EQ(entries.size(), new_entries.size());
234 
235   for (size_t i = 0; i < kNumHashes; ++i) {
236     auto it = new_entries.find(kHashes[i]);
237     EXPECT_TRUE(new_entries.end() != it);
238     EXPECT_TRUE(CompareTwoEntryMetadata(it->second, metadata_entries[i]));
239   }
240 }
241 
TEST_F(SimpleIndexFileTest,SerializeAppCache)242 TEST_F(SimpleIndexFileTest, SerializeAppCache) {
243   SimpleIndex::EntrySet entries;
244 
245   static const size_t kNumHashes = 3u;
246   const std::array<uint64_t, kNumHashes> kHashes = {11, 22, 33};
247   const std::array<int32_t, kNumHashes> kTrailerPrefetches = {123, -1, 987};
248   std::array<EntryMetadata, kNumHashes> metadata_entries;
249 
250   SimpleIndexFile::IndexMetadata index_metadata(
251       SimpleIndex::INDEX_WRITE_REASON_SHUTDOWN,
252       static_cast<uint64_t>(kNumHashes), 456);
253   for (size_t i = 0; i < kNumHashes; ++i) {
254     uint64_t hash = kHashes[i];
255     metadata_entries[i] =
256         EntryMetadata(kTrailerPrefetches[i], static_cast<uint32_t>(hash));
257     metadata_entries[i].SetInMemoryData(static_cast<uint8_t>(i));
258     SimpleIndex::InsertInEntrySet(hash, metadata_entries[i], &entries);
259   }
260 
261   std::unique_ptr<base::Pickle> pickle = WrappedSimpleIndexFile::Serialize(
262       net::APP_CACHE, index_metadata, entries);
263   EXPECT_TRUE(pickle.get() != nullptr);
264   base::Time now = base::Time::Now();
265   WrappedSimpleIndexFile::SerializeFinalData(now, pickle.get());
266   base::Time when_index_last_saw_cache;
267   SimpleIndexLoadResult deserialize_result;
268   WrappedSimpleIndexFile::Deserialize(
269       net::APP_CACHE, *pickle, &when_index_last_saw_cache, &deserialize_result);
270   EXPECT_TRUE(deserialize_result.did_load);
271   EXPECT_EQ(now, when_index_last_saw_cache);
272   const SimpleIndex::EntrySet& new_entries = deserialize_result.entries;
273   EXPECT_EQ(entries.size(), new_entries.size());
274 
275   for (size_t i = 0; i < kNumHashes; ++i) {
276     auto it = new_entries.find(kHashes[i]);
277     EXPECT_TRUE(new_entries.end() != it);
278     EXPECT_TRUE(
279         CompareTwoAppCacheEntryMetadata(it->second, metadata_entries[i]));
280   }
281 }
282 
TEST_F(SimpleIndexFileTest,ReadV7Format)283 TEST_F(SimpleIndexFileTest, ReadV7Format) {
284   static const size_t kNumHashes = 3u;
285   const std::array<uint64_t, kNumHashes> kHashes = {11, 22, 33};
286   const std::array<uint32_t, kNumHashes> kSizes = {394, 594, 495940};
287 
288   V7IndexMetadataForTest v7_metadata(kNumHashes, 100 * 1024 * 1024);
289 
290   // We don't have a convenient way of serializing the actual entries in the
291   // V7 format, but we can cheat a bit by using the implementation details: if
292   // we set the 8 lower bits of size as the memory data, and upper bits
293   // as the size, the new serialization will produce what we want.
294   SimpleIndex::EntrySet entries;
295   for (size_t i = 0; i < kNumHashes; ++i) {
296     EntryMetadata entry(Time(), kSizes[i] & 0xFFFFFF00u);
297     entry.SetInMemoryData(static_cast<uint8_t>(kSizes[i] & 0xFFu));
298     SimpleIndex::InsertInEntrySet(kHashes[i], entry, &entries);
299   }
300   std::unique_ptr<base::Pickle> pickle =
301       WrappedSimpleIndexFile::Serialize(net::DISK_CACHE, v7_metadata, entries);
302   ASSERT_TRUE(pickle.get() != nullptr);
303   base::Time now = base::Time::Now();
304   WrappedSimpleIndexFile::SerializeFinalData(now, pickle.get());
305 
306   // Now read it back. We should get the sizes rounded, and 0 for mem entries.
307   base::Time when_index_last_saw_cache;
308   SimpleIndexLoadResult deserialize_result;
309   WrappedSimpleIndexFile::Deserialize(net::DISK_CACHE, *pickle,
310                                       &when_index_last_saw_cache,
311                                       &deserialize_result);
312   EXPECT_TRUE(deserialize_result.did_load);
313   EXPECT_EQ(now, when_index_last_saw_cache);
314   const SimpleIndex::EntrySet& new_entries = deserialize_result.entries;
315   ASSERT_EQ(entries.size(), new_entries.size());
316   for (size_t i = 0; i < kNumHashes; ++i) {
317     auto it = new_entries.find(kHashes[i]);
318     ASSERT_TRUE(new_entries.end() != it);
319     EXPECT_EQ(RoundSize(kSizes[i]), it->second.GetEntrySize());
320     EXPECT_EQ(0u, it->second.GetInMemoryData());
321   }
322 }
323 
TEST_F(SimpleIndexFileTest,ReadV8Format)324 TEST_F(SimpleIndexFileTest, ReadV8Format) {
325   static const size_t kNumHashes = 3u;
326   const std::array<uint64_t, kNumHashes> kHashes = {11, 22, 33};
327   std::array<EntryMetadata, kNumHashes> metadata_entries;
328 
329   // V8 to V9 should not make any modifications for non-APP_CACHE modes.
330   // Verify that the data is preserved through the migration.
331   V8IndexMetadataForTest v8_metadata(kNumHashes, 100 * 1024 * 1024);
332 
333   SimpleIndex::EntrySet entries;
334   for (size_t i = 0; i < kNumHashes; ++i) {
335     metadata_entries[i] =
336         EntryMetadata(base::Time::Now(), static_cast<uint32_t>(kHashes[i]));
337     metadata_entries[i].SetInMemoryData(static_cast<uint8_t>(i));
338     SimpleIndex::InsertInEntrySet(kHashes[i], metadata_entries[i], &entries);
339   }
340   std::unique_ptr<base::Pickle> pickle =
341       WrappedSimpleIndexFile::Serialize(net::DISK_CACHE, v8_metadata, entries);
342   ASSERT_TRUE(pickle.get() != nullptr);
343   base::Time now = base::Time::Now();
344   WrappedSimpleIndexFile::SerializeFinalData(now, pickle.get());
345 
346   base::Time when_index_last_saw_cache;
347   SimpleIndexLoadResult deserialize_result;
348   WrappedSimpleIndexFile::Deserialize(net::DISK_CACHE, *pickle,
349                                       &when_index_last_saw_cache,
350                                       &deserialize_result);
351   EXPECT_TRUE(deserialize_result.did_load);
352   EXPECT_EQ(now, when_index_last_saw_cache);
353   const SimpleIndex::EntrySet& new_entries = deserialize_result.entries;
354   ASSERT_EQ(entries.size(), new_entries.size());
355   for (size_t i = 0; i < kNumHashes; ++i) {
356     auto it = new_entries.find(kHashes[i]);
357     ASSERT_TRUE(new_entries.end() != it);
358     EXPECT_TRUE(CompareTwoEntryMetadata(it->second, metadata_entries[i]));
359   }
360 }
361 
TEST_F(SimpleIndexFileTest,ReadV8FormatAppCache)362 TEST_F(SimpleIndexFileTest, ReadV8FormatAppCache) {
363   static const size_t kNumHashes = 3u;
364   const std::array<uint64_t, kNumHashes> kHashes = {11, 22, 33};
365   std::array<EntryMetadata, kNumHashes> metadata_entries;
366 
367   // To simulate an upgrade from v8 to v9 write out the v8 schema
368   // using DISK_CACHE mode.  The read it back in in APP_CACHE mode.
369   // The entry access time data should be zeroed to reset it as the
370   // new trailer prefetch size.
371   V8IndexMetadataForTest v8_metadata(kNumHashes, 100 * 1024 * 1024);
372 
373   SimpleIndex::EntrySet entries;
374   for (size_t i = 0; i < kNumHashes; ++i) {
375     metadata_entries[i] =
376         EntryMetadata(base::Time::Now(), static_cast<uint32_t>(kHashes[i]));
377     metadata_entries[i].SetInMemoryData(static_cast<uint8_t>(i));
378     SimpleIndex::InsertInEntrySet(kHashes[i], metadata_entries[i], &entries);
379   }
380   std::unique_ptr<base::Pickle> pickle =
381       WrappedSimpleIndexFile::Serialize(net::DISK_CACHE, v8_metadata, entries);
382   ASSERT_TRUE(pickle.get() != nullptr);
383   base::Time now = base::Time::Now();
384   WrappedSimpleIndexFile::SerializeFinalData(now, pickle.get());
385 
386   // Deserialize using APP_CACHE mode.  This should zero out the
387   // trailer_prefetch_size_ instead of using the time bits written
388   // out previously.
389   base::Time when_index_last_saw_cache;
390   SimpleIndexLoadResult deserialize_result;
391   WrappedSimpleIndexFile::Deserialize(
392       net::APP_CACHE, *pickle, &when_index_last_saw_cache, &deserialize_result);
393   EXPECT_TRUE(deserialize_result.did_load);
394   EXPECT_EQ(now, when_index_last_saw_cache);
395   const SimpleIndex::EntrySet& new_entries = deserialize_result.entries;
396   ASSERT_EQ(entries.size(), new_entries.size());
397   for (size_t i = 0; i < kNumHashes; ++i) {
398     auto it = new_entries.find(kHashes[i]);
399     ASSERT_TRUE(new_entries.end() != it);
400     // The trailer prefetch size should be zeroed.
401     EXPECT_NE(metadata_entries[i].trailer_prefetch_size_,
402               it->second.trailer_prefetch_size_);
403     EXPECT_EQ(0, it->second.trailer_prefetch_size_);
404     // Other data should be unaffected.
405     EXPECT_EQ(metadata_entries[i].entry_size_256b_chunks_,
406               it->second.entry_size_256b_chunks_);
407     EXPECT_EQ(metadata_entries[i].in_memory_data_, it->second.in_memory_data_);
408   }
409 }
410 
TEST_F(SimpleIndexFileTest,LegacyIsIndexFileStale)411 TEST_F(SimpleIndexFileTest, LegacyIsIndexFileStale) {
412   base::ScopedTempDir cache_dir;
413   ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
414   base::File::Info file_info;
415   base::Time cache_mtime;
416   const base::FilePath cache_path = cache_dir.GetPath();
417 
418   ASSERT_TRUE(base::GetFileInfo(cache_path, &file_info));
419   cache_mtime = file_info.last_modified;
420   WrappedSimpleIndexFile simple_index_file(cache_path);
421   ASSERT_TRUE(simple_index_file.CreateIndexFileDirectory());
422   const base::FilePath& index_path = simple_index_file.GetIndexFilePath();
423   EXPECT_TRUE(
424       WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path));
425   const std::string kDummyData = "nothing to be seen here";
426   EXPECT_TRUE(base::WriteFile(index_path, kDummyData));
427   ASSERT_TRUE(base::GetFileInfo(cache_path, &file_info));
428   cache_mtime = file_info.last_modified;
429   EXPECT_FALSE(
430       WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path));
431 
432   const base::Time past_time = base::Time::Now() - base::Seconds(10);
433   EXPECT_TRUE(base::TouchFile(index_path, past_time, past_time));
434   EXPECT_TRUE(base::TouchFile(cache_path, past_time, past_time));
435   ASSERT_TRUE(base::GetFileInfo(cache_path, &file_info));
436   cache_mtime = file_info.last_modified;
437   EXPECT_FALSE(
438       WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path));
439   const base::Time even_older = past_time - base::Seconds(10);
440   EXPECT_TRUE(base::TouchFile(index_path, even_older, even_older));
441   EXPECT_TRUE(
442       WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path));
443 }
444 
TEST_F(SimpleIndexFileTest,WriteThenLoadIndex)445 TEST_F(SimpleIndexFileTest, WriteThenLoadIndex) {
446   base::ScopedTempDir cache_dir;
447   ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
448 
449   SimpleIndex::EntrySet entries;
450 
451   static const size_t kNumHashes = 3u;
452   const std::array<uint64_t, kNumHashes> kHashes = {11, 22, 33};
453   std::array<EntryMetadata, kNumHashes> metadata_entries;
454   for (size_t i = 0; i < kNumHashes; ++i) {
455     uint64_t hash = kHashes[i];
456     metadata_entries[i] = EntryMetadata(Time(), static_cast<uint32_t>(hash));
457     SimpleIndex::InsertInEntrySet(hash, metadata_entries[i], &entries);
458   }
459 
460   const uint64_t kCacheSize = 456U;
461   net::TestClosure closure;
462   {
463     WrappedSimpleIndexFile simple_index_file(cache_dir.GetPath());
464     simple_index_file.WriteToDisk(net::DISK_CACHE,
465                                   SimpleIndex::INDEX_WRITE_REASON_SHUTDOWN,
466                                   entries, kCacheSize, closure.closure());
467     closure.WaitForResult();
468     EXPECT_TRUE(base::PathExists(simple_index_file.GetIndexFilePath()));
469   }
470 
471   WrappedSimpleIndexFile simple_index_file(cache_dir.GetPath());
472   base::File::Info file_info;
473   ASSERT_TRUE(base::GetFileInfo(cache_dir.GetPath(), &file_info));
474   base::Time fake_cache_mtime = file_info.last_modified;
475   SimpleIndexLoadResult load_index_result;
476   simple_index_file.LoadIndexEntries(fake_cache_mtime, closure.closure(),
477                                      &load_index_result);
478   closure.WaitForResult();
479 
480   EXPECT_TRUE(base::PathExists(simple_index_file.GetIndexFilePath()));
481   EXPECT_TRUE(load_index_result.did_load);
482   EXPECT_FALSE(load_index_result.flush_required);
483 
484   EXPECT_EQ(kNumHashes, load_index_result.entries.size());
485   for (uint64_t hash : kHashes)
486     EXPECT_EQ(1U, load_index_result.entries.count(hash));
487 }
488 
TEST_F(SimpleIndexFileTest,LoadCorruptIndex)489 TEST_F(SimpleIndexFileTest, LoadCorruptIndex) {
490   base::ScopedTempDir cache_dir;
491   ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
492 
493   WrappedSimpleIndexFile simple_index_file(cache_dir.GetPath());
494   ASSERT_TRUE(simple_index_file.CreateIndexFileDirectory());
495   const base::FilePath& index_path = simple_index_file.GetIndexFilePath();
496   const std::string kDummyData = "nothing to be seen here";
497   EXPECT_TRUE(base::WriteFile(index_path, kDummyData));
498   base::File::Info file_info;
499   ASSERT_TRUE(
500       base::GetFileInfo(simple_index_file.GetIndexFilePath(), &file_info));
501   base::Time fake_cache_mtime = file_info.last_modified;
502   EXPECT_FALSE(WrappedSimpleIndexFile::LegacyIsIndexFileStale(fake_cache_mtime,
503                                                               index_path));
504   SimpleIndexLoadResult load_index_result;
505   net::TestClosure closure;
506   simple_index_file.LoadIndexEntries(fake_cache_mtime, closure.closure(),
507                                      &load_index_result);
508   closure.WaitForResult();
509 
510   EXPECT_FALSE(base::PathExists(index_path));
511   EXPECT_TRUE(load_index_result.did_load);
512   EXPECT_TRUE(load_index_result.flush_required);
513 }
514 
TEST_F(SimpleIndexFileTest,LoadCorruptIndex2)515 TEST_F(SimpleIndexFileTest, LoadCorruptIndex2) {
516   // Variant where the index looks like a pickle, but not one with right
517   // header size --- that used to hit a DCHECK on debug builds.
518   base::ScopedTempDir cache_dir;
519   ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
520 
521   WrappedSimpleIndexFile simple_index_file(cache_dir.GetPath());
522   ASSERT_TRUE(simple_index_file.CreateIndexFileDirectory());
523   const base::FilePath& index_path = simple_index_file.GetIndexFilePath();
524   base::Pickle bad_payload;
525   bad_payload.WriteString("nothing to be seen here");
526 
527   EXPECT_TRUE(base::WriteFile(index_path, bad_payload));
528   base::File::Info file_info;
529   ASSERT_TRUE(
530       base::GetFileInfo(simple_index_file.GetIndexFilePath(), &file_info));
531   base::Time fake_cache_mtime = file_info.last_modified;
532   EXPECT_FALSE(WrappedSimpleIndexFile::LegacyIsIndexFileStale(fake_cache_mtime,
533                                                               index_path));
534   SimpleIndexLoadResult load_index_result;
535   net::TestClosure closure;
536   simple_index_file.LoadIndexEntries(fake_cache_mtime, closure.closure(),
537                                      &load_index_result);
538   closure.WaitForResult();
539 
540   EXPECT_FALSE(base::PathExists(index_path));
541   EXPECT_TRUE(load_index_result.did_load);
542   EXPECT_TRUE(load_index_result.flush_required);
543 }
544 
545 // Tests that after an upgrade the backend has the index file put in place.
TEST_F(SimpleIndexFileTest,SimpleCacheUpgrade)546 TEST_F(SimpleIndexFileTest, SimpleCacheUpgrade) {
547   base::ScopedTempDir cache_dir;
548   ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
549   const base::FilePath cache_path = cache_dir.GetPath();
550 
551   // Write an old fake index file.
552   base::File file(cache_path.AppendASCII("index"),
553                   base::File::FLAG_CREATE | base::File::FLAG_WRITE);
554   ASSERT_TRUE(file.IsValid());
555   disk_cache::FakeIndexData file_contents;
556   file_contents.initial_magic_number = disk_cache::kSimpleInitialMagicNumber;
557   file_contents.version = 5;
558   auto bytes_written = file.Write(0, base::byte_span_from_ref(file_contents));
559   ASSERT_EQ(sizeof(file_contents), bytes_written);
560   file.Close();
561 
562   // Write the index file. The format is incorrect, but for transitioning from
563   // v5 it does not matter.
564   const std::string index_file_contents("incorrectly serialized data");
565   const base::FilePath old_index_file =
566       cache_path.AppendASCII("the-real-index");
567   ASSERT_TRUE(base::WriteFile(old_index_file, index_file_contents));
568 
569   TrivialFileOperations file_operations;
570   // Upgrade the cache.
571   ASSERT_EQ(disk_cache::UpgradeSimpleCacheOnDisk(&file_operations, cache_path),
572             SimpleCacheConsistencyResult::kOK);
573 
574   // Create the backend and initiate index flush by destroying the backend.
575   scoped_refptr<disk_cache::BackendCleanupTracker> cleanup_tracker =
576       disk_cache::BackendCleanupTracker::TryCreate(cache_path,
577                                                    base::OnceClosure());
578   ASSERT_TRUE(cleanup_tracker != nullptr);
579 
580   net::TestClosure post_cleanup;
581   cleanup_tracker->AddPostCleanupCallback(post_cleanup.closure());
582 
583   auto simple_cache = std::make_unique<disk_cache::SimpleBackendImpl>(
584       /*file_operations_factory=*/nullptr, cache_path, cleanup_tracker,
585       /*file_tracker=*/nullptr, 0, net::DISK_CACHE,
586       /*net_log=*/nullptr);
587   net::TestCompletionCallback cb;
588   simple_cache->Init(cb.callback());
589   EXPECT_THAT(cb.WaitForResult(), IsOk());
590   simple_cache->index()->ExecuteWhenReady(cb.callback());
591   int rv = cb.WaitForResult();
592   EXPECT_THAT(rv, IsOk());
593   simple_cache.reset();
594   cleanup_tracker = nullptr;
595 
596   // The backend flushes the index on destruction; it will run the post-cleanup
597   // callback set on the cleanup_tracker once that finishes.
598   post_cleanup.WaitForResult();
599 
600   // Verify that the index file exists.
601   const base::FilePath& index_file_path =
602       cache_path.AppendASCII("index-dir").AppendASCII("the-real-index");
603   EXPECT_TRUE(base::PathExists(index_file_path));
604 
605   // Verify that the version of the index file is correct.
606   std::string contents;
607   EXPECT_TRUE(base::ReadFileToString(index_file_path, &contents));
608   base::Time when_index_last_saw_cache;
609   SimpleIndexLoadResult deserialize_result;
610   WrappedSimpleIndexFile::Deserialize(
611       net::DISK_CACHE, base::as_byte_span(contents), &when_index_last_saw_cache,
612       &deserialize_result);
613   EXPECT_TRUE(deserialize_result.did_load);
614 }
615 
TEST_F(SimpleIndexFileTest,OverwritesStaleTempFile)616 TEST_F(SimpleIndexFileTest, OverwritesStaleTempFile) {
617   base::ScopedTempDir cache_dir;
618   ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
619   const base::FilePath cache_path = cache_dir.GetPath();
620   WrappedSimpleIndexFile simple_index_file(cache_path);
621   ASSERT_TRUE(simple_index_file.CreateIndexFileDirectory());
622 
623   // Create an temporary index file.
624   const base::FilePath& temp_index_path =
625       simple_index_file.GetTempIndexFilePath();
626   const std::string kDummyData = "nothing to be seen here";
627   EXPECT_TRUE(base::WriteFile(temp_index_path, kDummyData));
628   ASSERT_TRUE(base::PathExists(simple_index_file.GetTempIndexFilePath()));
629 
630   // Write the index file.
631   SimpleIndex::EntrySet entries;
632   SimpleIndex::InsertInEntrySet(11, EntryMetadata(Time(), 11u), &entries);
633   net::TestClosure closure;
634   simple_index_file.WriteToDisk(net::DISK_CACHE,
635                                 SimpleIndex::INDEX_WRITE_REASON_SHUTDOWN,
636                                 entries, 120U, closure.closure());
637   closure.WaitForResult();
638 
639   // Check that the temporary file was deleted and the index file was created.
640   EXPECT_FALSE(base::PathExists(simple_index_file.GetTempIndexFilePath()));
641   EXPECT_TRUE(base::PathExists(simple_index_file.GetIndexFilePath()));
642 }
643 
644 }  // namespace disk_cache
645