• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 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 <memory>
6 #include <string>
7 #include <string_view>
8 
9 #include "base/files/file.h"
10 #include "base/files/file_path.h"
11 #include "base/files/file_util.h"
12 #include "base/memory/raw_ptr.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_util.h"
15 #include "base/test/metrics/histogram_tester.h"
16 #include "net/base/cache_type.h"
17 #include "net/disk_cache/disk_cache.h"
18 #include "net/disk_cache/disk_cache_test_base.h"
19 #include "net/disk_cache/simple/simple_file_tracker.h"
20 #include "net/disk_cache/simple/simple_histogram_enums.h"
21 #include "net/disk_cache/simple/simple_synchronous_entry.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 
24 namespace disk_cache {
25 
26 class SimpleFileTrackerTest : public DiskCacheTest {
27  public:
DeleteSyncEntry(SimpleSynchronousEntry * entry)28   void DeleteSyncEntry(SimpleSynchronousEntry* entry) { delete entry; }
29 
30   // We limit open files to 4 for the fixture, as this is large enough
31   // that simple tests don't have to worry about naming files normally,
32   // but small enough to test with easily.
33   static const int kFileLimit = 4;
34 
35  protected:
SimpleFileTrackerTest()36   SimpleFileTrackerTest() : file_tracker_(kFileLimit) {}
37 
38   // A bit of messiness since we rely on friendship of the fixture to be able to
39   // create/delete SimpleSynchronousEntry objects.
40   class SyncEntryDeleter {
41    public:
SyncEntryDeleter(SimpleFileTrackerTest * fixture)42     explicit SyncEntryDeleter(SimpleFileTrackerTest* fixture)
43         : fixture_(fixture) {}
operator ()(SimpleSynchronousEntry * entry)44     void operator()(SimpleSynchronousEntry* entry) {
45       fixture_->DeleteSyncEntry(entry);
46     }
47 
48    private:
49     raw_ptr<SimpleFileTrackerTest> fixture_;
50   };
51 
52   using SyncEntryPointer =
53       std::unique_ptr<SimpleSynchronousEntry, SyncEntryDeleter>;
54 
MakeSyncEntry(uint64_t hash)55   SyncEntryPointer MakeSyncEntry(uint64_t hash) {
56     return SyncEntryPointer(
57         new SimpleSynchronousEntry(
58             net::DISK_CACHE, cache_path_, "dummy", hash, &file_tracker_,
59             base::MakeRefCounted<disk_cache::TrivialFileOperationsFactory>()
60                 ->CreateUnbound(),
61             /*stream_0_size=*/-1),
62         SyncEntryDeleter(this));
63   }
64 
UpdateEntryFileKey(SimpleSynchronousEntry * sync_entry,SimpleFileTracker::EntryFileKey file_key)65   void UpdateEntryFileKey(SimpleSynchronousEntry* sync_entry,
66                           SimpleFileTracker::EntryFileKey file_key) {
67     sync_entry->entry_file_key_ = file_key;
68   }
69 
70   SimpleFileTracker file_tracker_;
71 };
72 
TEST_F(SimpleFileTrackerTest,Basic)73 TEST_F(SimpleFileTrackerTest, Basic) {
74   SyncEntryPointer entry = MakeSyncEntry(1);
75   TrivialFileOperations ops;
76 
77   // Just transfer some files to the tracker, and then do some I/O on getting
78   // them back.
79   base::FilePath path_0 = cache_path_.AppendASCII("file_0");
80   base::FilePath path_1 = cache_path_.AppendASCII("file_1");
81 
82   std::unique_ptr<base::File> file_0 = std::make_unique<base::File>(
83       path_0, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
84   std::unique_ptr<base::File> file_1 = std::make_unique<base::File>(
85       path_1, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
86   ASSERT_TRUE(file_0->IsValid());
87   ASSERT_TRUE(file_1->IsValid());
88 
89   file_tracker_.Register(entry.get(), SimpleFileTracker::SubFile::FILE_0,
90                          std::move(file_0));
91   file_tracker_.Register(entry.get(), SimpleFileTracker::SubFile::FILE_1,
92                          std::move(file_1));
93 
94   std::string_view msg_0 = "Hello";
95   std::string_view msg_1 = "Worldish Place";
96 
97   {
98     SimpleFileTracker::FileHandle borrow_0 = file_tracker_.Acquire(
99         &ops, entry.get(), SimpleFileTracker::SubFile::FILE_0);
100     SimpleFileTracker::FileHandle borrow_1 = file_tracker_.Acquire(
101         &ops, entry.get(), SimpleFileTracker::SubFile::FILE_1);
102 
103     EXPECT_EQ(msg_0.size(), borrow_0->Write(0, base::as_byte_span(msg_0)));
104     EXPECT_EQ(msg_1.size(), borrow_1->Write(0, base::as_byte_span(msg_1)));
105 
106     // For stream 0 do release/close, for stream 1 do close/release --- where
107     // release happens when borrow_{0,1} go out of scope
108     file_tracker_.Close(entry.get(), SimpleFileTracker::SubFile::FILE_1);
109   }
110   file_tracker_.Close(entry.get(), SimpleFileTracker::SubFile::FILE_0);
111 
112   // Verify contents.
113   std::string verify_0, verify_1;
114   EXPECT_TRUE(ReadFileToString(path_0, &verify_0));
115   EXPECT_TRUE(ReadFileToString(path_1, &verify_1));
116   EXPECT_EQ(msg_0, verify_0);
117   EXPECT_EQ(msg_1, verify_1);
118   EXPECT_TRUE(file_tracker_.IsEmptyForTesting());
119 }
120 
TEST_F(SimpleFileTrackerTest,Collision)121 TEST_F(SimpleFileTrackerTest, Collision) {
122   // Two entries with same key.
123   SyncEntryPointer entry = MakeSyncEntry(1);
124   SyncEntryPointer entry2 = MakeSyncEntry(1);
125   TrivialFileOperations ops;
126 
127   base::FilePath path = cache_path_.AppendASCII("file");
128   base::FilePath path2 = cache_path_.AppendASCII("file2");
129 
130   std::unique_ptr<base::File> file = std::make_unique<base::File>(
131       path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
132   std::unique_ptr<base::File> file2 = std::make_unique<base::File>(
133       path2, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
134   ASSERT_TRUE(file->IsValid());
135   ASSERT_TRUE(file2->IsValid());
136 
137   file_tracker_.Register(entry.get(), SimpleFileTracker::SubFile::FILE_0,
138                          std::move(file));
139   file_tracker_.Register(entry2.get(), SimpleFileTracker::SubFile::FILE_0,
140                          std::move(file2));
141 
142   std::string_view msg = "Alpha";
143   std::string_view msg2 = "Beta";
144 
145   {
146     SimpleFileTracker::FileHandle borrow = file_tracker_.Acquire(
147         &ops, entry.get(), SimpleFileTracker::SubFile::FILE_0);
148     SimpleFileTracker::FileHandle borrow2 = file_tracker_.Acquire(
149         &ops, entry2.get(), SimpleFileTracker::SubFile::FILE_0);
150 
151     EXPECT_EQ(msg.size(), borrow->Write(0, base::as_byte_span(msg)));
152     EXPECT_EQ(msg2.size(), borrow2->Write(0, base::as_byte_span(msg2)));
153   }
154   file_tracker_.Close(entry.get(), SimpleFileTracker::SubFile::FILE_0);
155   file_tracker_.Close(entry2.get(), SimpleFileTracker::SubFile::FILE_0);
156 
157   // Verify contents.
158   std::string verify, verify2;
159   EXPECT_TRUE(ReadFileToString(path, &verify));
160   EXPECT_TRUE(ReadFileToString(path2, &verify2));
161   EXPECT_EQ(msg, verify);
162   EXPECT_EQ(msg2, verify2);
163   EXPECT_TRUE(file_tracker_.IsEmptyForTesting());
164 }
165 
TEST_F(SimpleFileTrackerTest,Reopen)166 TEST_F(SimpleFileTrackerTest, Reopen) {
167   // We may sometimes go Register -> Close -> Register, with info still
168   // alive.
169   SyncEntryPointer entry = MakeSyncEntry(1);
170 
171   base::FilePath path_0 = cache_path_.AppendASCII("file_0");
172   base::FilePath path_1 = cache_path_.AppendASCII("file_1");
173 
174   std::unique_ptr<base::File> file_0 = std::make_unique<base::File>(
175       path_0, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
176   std::unique_ptr<base::File> file_1 = std::make_unique<base::File>(
177       path_1, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
178   ASSERT_TRUE(file_0->IsValid());
179   ASSERT_TRUE(file_1->IsValid());
180 
181   file_tracker_.Register(entry.get(), SimpleFileTracker::SubFile::FILE_0,
182                          std::move(file_0));
183   file_tracker_.Register(entry.get(), SimpleFileTracker::SubFile::FILE_1,
184                          std::move(file_1));
185 
186   file_tracker_.Close(entry.get(), SimpleFileTracker::SubFile::FILE_1);
187   std::unique_ptr<base::File> file_1b = std::make_unique<base::File>(
188       path_1, base::File::FLAG_OPEN | base::File::FLAG_WRITE);
189   ASSERT_TRUE(file_1b->IsValid());
190   file_tracker_.Register(entry.get(), SimpleFileTracker::SubFile::FILE_1,
191                          std::move(file_1b));
192   file_tracker_.Close(entry.get(), SimpleFileTracker::SubFile::FILE_0);
193   file_tracker_.Close(entry.get(), SimpleFileTracker::SubFile::FILE_1);
194   EXPECT_TRUE(file_tracker_.IsEmptyForTesting());
195 }
196 
TEST_F(SimpleFileTrackerTest,PointerStability)197 TEST_F(SimpleFileTrackerTest, PointerStability) {
198   // Make sure the FileHandle lent out doesn't get screwed up as we update
199   // the state (and potentially move the underlying base::File object around).
200   const int kEntries = 8;
201   std::array<SyncEntryPointer, kEntries> entries = {
202       MakeSyncEntry(1), MakeSyncEntry(1), MakeSyncEntry(1), MakeSyncEntry(1),
203       MakeSyncEntry(1), MakeSyncEntry(1), MakeSyncEntry(1), MakeSyncEntry(1),
204   };
205   TrivialFileOperations ops;
206   std::unique_ptr<base::File> file_0 = std::make_unique<base::File>(
207       cache_path_.AppendASCII("0"),
208       base::File::FLAG_CREATE | base::File::FLAG_WRITE);
209   ASSERT_TRUE(file_0->IsValid());
210   file_tracker_.Register(entries[0].get(), SimpleFileTracker::SubFile::FILE_0,
211                          std::move(file_0));
212 
213   std::string_view msg = "Message to write";
214   {
215     SimpleFileTracker::FileHandle borrow = file_tracker_.Acquire(
216         &ops, entries[0].get(), SimpleFileTracker::SubFile::FILE_0);
217     for (int i = 1; i < kEntries; ++i) {
218       std::unique_ptr<base::File> file_n = std::make_unique<base::File>(
219           cache_path_.AppendASCII(base::NumberToString(i)),
220           base::File::FLAG_CREATE | base::File::FLAG_WRITE);
221       ASSERT_TRUE(file_n->IsValid());
222       file_tracker_.Register(entries[i].get(),
223                              SimpleFileTracker::SubFile::FILE_0,
224                              std::move(file_n));
225     }
226 
227     EXPECT_EQ(msg.size(), borrow->Write(0, base::as_byte_span(msg)));
228   }
229 
230   for (const auto& entry : entries)
231     file_tracker_.Close(entry.get(), SimpleFileTracker::SubFile::FILE_0);
232 
233   // Verify the file.
234   std::string verify;
235   EXPECT_TRUE(ReadFileToString(cache_path_.AppendASCII("0"), &verify));
236   EXPECT_EQ(msg, verify);
237   EXPECT_TRUE(file_tracker_.IsEmptyForTesting());
238 }
239 
TEST_F(SimpleFileTrackerTest,Doom)240 TEST_F(SimpleFileTrackerTest, Doom) {
241   SyncEntryPointer entry1 = MakeSyncEntry(1);
242   base::FilePath path1 = cache_path_.AppendASCII("file1");
243   std::unique_ptr<base::File> file1 = std::make_unique<base::File>(
244       path1, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
245   ASSERT_TRUE(file1->IsValid());
246 
247   file_tracker_.Register(entry1.get(), SimpleFileTracker::SubFile::FILE_0,
248                          std::move(file1));
249   SimpleFileTracker::EntryFileKey key1 = entry1->entry_file_key();
250   file_tracker_.Doom(entry1.get(), &key1);
251   EXPECT_NE(0u, key1.doom_generation);
252 
253   // Other entry with same key.
254   SyncEntryPointer entry2 = MakeSyncEntry(1);
255   base::FilePath path2 = cache_path_.AppendASCII("file2");
256   std::unique_ptr<base::File> file2 = std::make_unique<base::File>(
257       path2, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
258   ASSERT_TRUE(file2->IsValid());
259 
260   file_tracker_.Register(entry2.get(), SimpleFileTracker::SubFile::FILE_0,
261                          std::move(file2));
262   SimpleFileTracker::EntryFileKey key2 = entry2->entry_file_key();
263   file_tracker_.Doom(entry2.get(), &key2);
264   EXPECT_NE(0u, key2.doom_generation);
265   EXPECT_NE(key1.doom_generation, key2.doom_generation);
266 
267   file_tracker_.Close(entry1.get(), SimpleFileTracker::SubFile::FILE_0);
268   file_tracker_.Close(entry2.get(), SimpleFileTracker::SubFile::FILE_0);
269 }
270 
TEST_F(SimpleFileTrackerTest,OverLimit)271 TEST_F(SimpleFileTrackerTest, OverLimit) {
272   base::HistogramTester histogram_tester;
273 
274   const int kEntries = 10;  // want more than FD limit in fixture.
275   std::vector<SyncEntryPointer> entries;
276   std::vector<base::FilePath> names;
277   TrivialFileOperations ops;
278   for (int i = 0; i < kEntries; ++i) {
279     SyncEntryPointer entry = MakeSyncEntry(i);
280     base::FilePath name =
281         entry->GetFilenameForSubfile(SimpleFileTracker::SubFile::FILE_0);
282     std::unique_ptr<base::File> file = std::make_unique<base::File>(
283         name, base::File::FLAG_CREATE | base::File::FLAG_WRITE |
284                   base::File::FLAG_READ);
285     ASSERT_TRUE(file->IsValid());
286     file_tracker_.Register(entry.get(), SimpleFileTracker::SubFile::FILE_0,
287                            std::move(file));
288     entries.push_back(std::move(entry));
289     names.push_back(name);
290   }
291 
292   histogram_tester.ExpectBucketCount("SimpleCache.FileDescriptorLimiterAction",
293                                      disk_cache::FD_LIMIT_CLOSE_FILE,
294                                      kEntries - kFileLimit);
295   histogram_tester.ExpectBucketCount("SimpleCache.FileDescriptorLimiterAction",
296                                      disk_cache::FD_LIMIT_REOPEN_FILE, 0);
297   histogram_tester.ExpectBucketCount("SimpleCache.FileDescriptorLimiterAction",
298                                      disk_cache::FD_LIMIT_FAIL_REOPEN_FILE, 0);
299 
300   // Grab the last one; we will hold it open till the end of the test. It's
301   // still open, so no change in stats after.
302   SimpleFileTracker::FileHandle borrow_last = file_tracker_.Acquire(
303       &ops, entries[kEntries - 1].get(), SimpleFileTracker::SubFile::FILE_0);
304   EXPECT_EQ(1u, borrow_last->Write(0, base::byte_span_from_cstring("L")));
305 
306   histogram_tester.ExpectBucketCount("SimpleCache.FileDescriptorLimiterAction",
307                                      disk_cache::FD_LIMIT_CLOSE_FILE,
308                                      kEntries - kFileLimit);
309   histogram_tester.ExpectBucketCount("SimpleCache.FileDescriptorLimiterAction",
310                                      disk_cache::FD_LIMIT_REOPEN_FILE, 0);
311   histogram_tester.ExpectBucketCount("SimpleCache.FileDescriptorLimiterAction",
312                                      disk_cache::FD_LIMIT_FAIL_REOPEN_FILE, 0);
313 
314   // Delete file for [2], to cause error on its re-open.
315   EXPECT_TRUE(base::DeleteFile(names[2])) << names[2];
316 
317   // Reacquire all the other files.
318   for (int i = 0; i < kEntries - 1; ++i) {
319     SimpleFileTracker::FileHandle borrow = file_tracker_.Acquire(
320         &ops, entries[i].get(), SimpleFileTracker::SubFile::FILE_0);
321     if (i != 2) {
322       EXPECT_TRUE(borrow.IsOK());
323       char c = static_cast<char>(i);
324       EXPECT_EQ(1u, borrow->Write(0, base::byte_span_from_ref(c)));
325     } else {
326       EXPECT_FALSE(borrow.IsOK());
327     }
328   }
329 
330   histogram_tester.ExpectBucketCount("SimpleCache.FileDescriptorLimiterAction",
331                                      disk_cache::FD_LIMIT_CLOSE_FILE,
332                                      kEntries - kFileLimit + kEntries - 2);
333   histogram_tester.ExpectBucketCount("SimpleCache.FileDescriptorLimiterAction",
334                                      disk_cache::FD_LIMIT_REOPEN_FILE,
335                                      kEntries - 2);
336   histogram_tester.ExpectBucketCount("SimpleCache.FileDescriptorLimiterAction",
337                                      disk_cache::FD_LIMIT_FAIL_REOPEN_FILE, 1);
338 
339   // Doom file for [1].
340   SimpleFileTracker::EntryFileKey key = entries[1]->entry_file_key();
341   file_tracker_.Doom(entries[1].get(), &key);
342   base::FilePath old_path = names[1];
343   UpdateEntryFileKey(entries[1].get(), key);
344   base::FilePath new_path =
345       entries[1]->GetFilenameForSubfile(SimpleFileTracker::SubFile::FILE_0);
346   EXPECT_TRUE(new_path.BaseName().MaybeAsASCII().starts_with("todelete_"));
347   EXPECT_TRUE(base::Move(old_path, new_path));
348 
349   // Now re-acquire everything again; this time reading.
350   for (int i = 0; i < kEntries - 1; ++i) {
351     SimpleFileTracker::FileHandle borrow = file_tracker_.Acquire(
352         &ops, entries[i].get(), SimpleFileTracker::SubFile::FILE_0);
353     char read;
354     char expected = static_cast<char>(i);
355     if (i != 2) {
356       EXPECT_TRUE(borrow.IsOK());
357       EXPECT_EQ(1, borrow->Read(0, base::byte_span_from_ref(read)));
358       EXPECT_EQ(expected, read);
359     } else {
360       EXPECT_FALSE(borrow.IsOK());
361     }
362   }
363 
364   histogram_tester.ExpectBucketCount(
365       "SimpleCache.FileDescriptorLimiterAction",
366       disk_cache::FD_LIMIT_CLOSE_FILE,
367       kEntries - kFileLimit + 2 * (kEntries - 2));
368   histogram_tester.ExpectBucketCount("SimpleCache.FileDescriptorLimiterAction",
369                                      disk_cache::FD_LIMIT_REOPEN_FILE,
370                                      2 * (kEntries - 2));
371   histogram_tester.ExpectBucketCount("SimpleCache.FileDescriptorLimiterAction",
372                                      disk_cache::FD_LIMIT_FAIL_REOPEN_FILE, 2);
373 
374   // Read from the last one, too. Should still be fine.
375   char read;
376   EXPECT_EQ(1, borrow_last->Read(0, base::byte_span_from_ref(read)));
377   EXPECT_EQ('L', read);
378 
379   for (const auto& entry : entries)
380     file_tracker_.Close(entry.get(), SimpleFileTracker::SubFile::FILE_0);
381 }
382 
383 }  // namespace disk_cache
384