xref: /aosp_15_r20/external/cronet/net/disk_cache/simple/simple_file_tracker_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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(static_cast<int>(msg_0.size()),
104               borrow_0->Write(0, msg_0.data(), msg_0.size()));
105     EXPECT_EQ(static_cast<int>(msg_1.size()),
106               borrow_1->Write(0, msg_1.data(), msg_1.size()));
107 
108     // For stream 0 do release/close, for stream 1 do close/release --- where
109     // release happens when borrow_{0,1} go out of scope
110     file_tracker_.Close(entry.get(), SimpleFileTracker::SubFile::FILE_1);
111   }
112   file_tracker_.Close(entry.get(), SimpleFileTracker::SubFile::FILE_0);
113 
114   // Verify contents.
115   std::string verify_0, verify_1;
116   EXPECT_TRUE(ReadFileToString(path_0, &verify_0));
117   EXPECT_TRUE(ReadFileToString(path_1, &verify_1));
118   EXPECT_EQ(msg_0, verify_0);
119   EXPECT_EQ(msg_1, verify_1);
120   EXPECT_TRUE(file_tracker_.IsEmptyForTesting());
121 }
122 
TEST_F(SimpleFileTrackerTest,Collision)123 TEST_F(SimpleFileTrackerTest, Collision) {
124   // Two entries with same key.
125   SyncEntryPointer entry = MakeSyncEntry(1);
126   SyncEntryPointer entry2 = MakeSyncEntry(1);
127   TrivialFileOperations ops;
128 
129   base::FilePath path = cache_path_.AppendASCII("file");
130   base::FilePath path2 = cache_path_.AppendASCII("file2");
131 
132   std::unique_ptr<base::File> file = std::make_unique<base::File>(
133       path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
134   std::unique_ptr<base::File> file2 = std::make_unique<base::File>(
135       path2, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
136   ASSERT_TRUE(file->IsValid());
137   ASSERT_TRUE(file2->IsValid());
138 
139   file_tracker_.Register(entry.get(), SimpleFileTracker::SubFile::FILE_0,
140                          std::move(file));
141   file_tracker_.Register(entry2.get(), SimpleFileTracker::SubFile::FILE_0,
142                          std::move(file2));
143 
144   std::string_view msg = "Alpha";
145   std::string_view msg2 = "Beta";
146 
147   {
148     SimpleFileTracker::FileHandle borrow = file_tracker_.Acquire(
149         &ops, entry.get(), SimpleFileTracker::SubFile::FILE_0);
150     SimpleFileTracker::FileHandle borrow2 = file_tracker_.Acquire(
151         &ops, entry2.get(), SimpleFileTracker::SubFile::FILE_0);
152 
153     EXPECT_EQ(static_cast<int>(msg.size()),
154               borrow->Write(0, msg.data(), msg.size()));
155     EXPECT_EQ(static_cast<int>(msg2.size()),
156               borrow2->Write(0, msg2.data(), msg2.size()));
157   }
158   file_tracker_.Close(entry.get(), SimpleFileTracker::SubFile::FILE_0);
159   file_tracker_.Close(entry2.get(), SimpleFileTracker::SubFile::FILE_0);
160 
161   // Verify contents.
162   std::string verify, verify2;
163   EXPECT_TRUE(ReadFileToString(path, &verify));
164   EXPECT_TRUE(ReadFileToString(path2, &verify2));
165   EXPECT_EQ(msg, verify);
166   EXPECT_EQ(msg2, verify2);
167   EXPECT_TRUE(file_tracker_.IsEmptyForTesting());
168 }
169 
TEST_F(SimpleFileTrackerTest,Reopen)170 TEST_F(SimpleFileTrackerTest, Reopen) {
171   // We may sometimes go Register -> Close -> Register, with info still
172   // alive.
173   SyncEntryPointer entry = MakeSyncEntry(1);
174 
175   base::FilePath path_0 = cache_path_.AppendASCII("file_0");
176   base::FilePath path_1 = cache_path_.AppendASCII("file_1");
177 
178   std::unique_ptr<base::File> file_0 = std::make_unique<base::File>(
179       path_0, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
180   std::unique_ptr<base::File> file_1 = std::make_unique<base::File>(
181       path_1, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
182   ASSERT_TRUE(file_0->IsValid());
183   ASSERT_TRUE(file_1->IsValid());
184 
185   file_tracker_.Register(entry.get(), SimpleFileTracker::SubFile::FILE_0,
186                          std::move(file_0));
187   file_tracker_.Register(entry.get(), SimpleFileTracker::SubFile::FILE_1,
188                          std::move(file_1));
189 
190   file_tracker_.Close(entry.get(), SimpleFileTracker::SubFile::FILE_1);
191   std::unique_ptr<base::File> file_1b = std::make_unique<base::File>(
192       path_1, base::File::FLAG_OPEN | base::File::FLAG_WRITE);
193   ASSERT_TRUE(file_1b->IsValid());
194   file_tracker_.Register(entry.get(), SimpleFileTracker::SubFile::FILE_1,
195                          std::move(file_1b));
196   file_tracker_.Close(entry.get(), SimpleFileTracker::SubFile::FILE_0);
197   file_tracker_.Close(entry.get(), SimpleFileTracker::SubFile::FILE_1);
198   EXPECT_TRUE(file_tracker_.IsEmptyForTesting());
199 }
200 
TEST_F(SimpleFileTrackerTest,PointerStability)201 TEST_F(SimpleFileTrackerTest, PointerStability) {
202   // Make sure the FileHandle lent out doesn't get screwed up as we update
203   // the state (and potentially move the underlying base::File object around).
204   const int kEntries = 8;
205   SyncEntryPointer entries[kEntries] = {
206       MakeSyncEntry(1), MakeSyncEntry(1), MakeSyncEntry(1), MakeSyncEntry(1),
207       MakeSyncEntry(1), MakeSyncEntry(1), MakeSyncEntry(1), MakeSyncEntry(1),
208   };
209   TrivialFileOperations ops;
210   std::unique_ptr<base::File> file_0 = std::make_unique<base::File>(
211       cache_path_.AppendASCII("0"),
212       base::File::FLAG_CREATE | base::File::FLAG_WRITE);
213   ASSERT_TRUE(file_0->IsValid());
214   file_tracker_.Register(entries[0].get(), SimpleFileTracker::SubFile::FILE_0,
215                          std::move(file_0));
216 
217   std::string_view msg = "Message to write";
218   {
219     SimpleFileTracker::FileHandle borrow = file_tracker_.Acquire(
220         &ops, entries[0].get(), SimpleFileTracker::SubFile::FILE_0);
221     for (int i = 1; i < kEntries; ++i) {
222       std::unique_ptr<base::File> file_n = std::make_unique<base::File>(
223           cache_path_.AppendASCII(base::NumberToString(i)),
224           base::File::FLAG_CREATE | base::File::FLAG_WRITE);
225       ASSERT_TRUE(file_n->IsValid());
226       file_tracker_.Register(entries[i].get(),
227                              SimpleFileTracker::SubFile::FILE_0,
228                              std::move(file_n));
229     }
230 
231     EXPECT_EQ(static_cast<int>(msg.size()),
232               borrow->Write(0, msg.data(), msg.size()));
233   }
234 
235   for (const auto& entry : entries)
236     file_tracker_.Close(entry.get(), SimpleFileTracker::SubFile::FILE_0);
237 
238   // Verify the file.
239   std::string verify;
240   EXPECT_TRUE(ReadFileToString(cache_path_.AppendASCII("0"), &verify));
241   EXPECT_EQ(msg, verify);
242   EXPECT_TRUE(file_tracker_.IsEmptyForTesting());
243 }
244 
TEST_F(SimpleFileTrackerTest,Doom)245 TEST_F(SimpleFileTrackerTest, Doom) {
246   SyncEntryPointer entry1 = MakeSyncEntry(1);
247   base::FilePath path1 = cache_path_.AppendASCII("file1");
248   std::unique_ptr<base::File> file1 = std::make_unique<base::File>(
249       path1, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
250   ASSERT_TRUE(file1->IsValid());
251 
252   file_tracker_.Register(entry1.get(), SimpleFileTracker::SubFile::FILE_0,
253                          std::move(file1));
254   SimpleFileTracker::EntryFileKey key1 = entry1->entry_file_key();
255   file_tracker_.Doom(entry1.get(), &key1);
256   EXPECT_NE(0u, key1.doom_generation);
257 
258   // Other entry with same key.
259   SyncEntryPointer entry2 = MakeSyncEntry(1);
260   base::FilePath path2 = cache_path_.AppendASCII("file2");
261   std::unique_ptr<base::File> file2 = std::make_unique<base::File>(
262       path2, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
263   ASSERT_TRUE(file2->IsValid());
264 
265   file_tracker_.Register(entry2.get(), SimpleFileTracker::SubFile::FILE_0,
266                          std::move(file2));
267   SimpleFileTracker::EntryFileKey key2 = entry2->entry_file_key();
268   file_tracker_.Doom(entry2.get(), &key2);
269   EXPECT_NE(0u, key2.doom_generation);
270   EXPECT_NE(key1.doom_generation, key2.doom_generation);
271 
272   file_tracker_.Close(entry1.get(), SimpleFileTracker::SubFile::FILE_0);
273   file_tracker_.Close(entry2.get(), SimpleFileTracker::SubFile::FILE_0);
274 }
275 
TEST_F(SimpleFileTrackerTest,OverLimit)276 TEST_F(SimpleFileTrackerTest, OverLimit) {
277   base::HistogramTester histogram_tester;
278 
279   const int kEntries = 10;  // want more than FD limit in fixture.
280   std::vector<SyncEntryPointer> entries;
281   std::vector<base::FilePath> names;
282   TrivialFileOperations ops;
283   for (int i = 0; i < kEntries; ++i) {
284     SyncEntryPointer entry = MakeSyncEntry(i);
285     base::FilePath name =
286         entry->GetFilenameForSubfile(SimpleFileTracker::SubFile::FILE_0);
287     std::unique_ptr<base::File> file = std::make_unique<base::File>(
288         name, base::File::FLAG_CREATE | base::File::FLAG_WRITE |
289                   base::File::FLAG_READ);
290     ASSERT_TRUE(file->IsValid());
291     file_tracker_.Register(entry.get(), SimpleFileTracker::SubFile::FILE_0,
292                            std::move(file));
293     entries.push_back(std::move(entry));
294     names.push_back(name);
295   }
296 
297   histogram_tester.ExpectBucketCount("SimpleCache.FileDescriptorLimiterAction",
298                                      disk_cache::FD_LIMIT_CLOSE_FILE,
299                                      kEntries - kFileLimit);
300   histogram_tester.ExpectBucketCount("SimpleCache.FileDescriptorLimiterAction",
301                                      disk_cache::FD_LIMIT_REOPEN_FILE, 0);
302   histogram_tester.ExpectBucketCount("SimpleCache.FileDescriptorLimiterAction",
303                                      disk_cache::FD_LIMIT_FAIL_REOPEN_FILE, 0);
304 
305   // Grab the last one; we will hold it open till the end of the test. It's
306   // still open, so no change in stats after.
307   SimpleFileTracker::FileHandle borrow_last = file_tracker_.Acquire(
308       &ops, entries[kEntries - 1].get(), SimpleFileTracker::SubFile::FILE_0);
309   EXPECT_EQ(1, borrow_last->Write(0, "L", 1));
310 
311   histogram_tester.ExpectBucketCount("SimpleCache.FileDescriptorLimiterAction",
312                                      disk_cache::FD_LIMIT_CLOSE_FILE,
313                                      kEntries - kFileLimit);
314   histogram_tester.ExpectBucketCount("SimpleCache.FileDescriptorLimiterAction",
315                                      disk_cache::FD_LIMIT_REOPEN_FILE, 0);
316   histogram_tester.ExpectBucketCount("SimpleCache.FileDescriptorLimiterAction",
317                                      disk_cache::FD_LIMIT_FAIL_REOPEN_FILE, 0);
318 
319   // Delete file for [2], to cause error on its re-open.
320   EXPECT_TRUE(base::DeleteFile(names[2])) << names[2];
321 
322   // Reacquire all the other files.
323   for (int i = 0; i < kEntries - 1; ++i) {
324     SimpleFileTracker::FileHandle borrow = file_tracker_.Acquire(
325         &ops, entries[i].get(), SimpleFileTracker::SubFile::FILE_0);
326     if (i != 2) {
327       EXPECT_TRUE(borrow.IsOK());
328       char c = static_cast<char>(i);
329       EXPECT_EQ(1, borrow->Write(0, &c, 1));
330     } else {
331       EXPECT_FALSE(borrow.IsOK());
332     }
333   }
334 
335   histogram_tester.ExpectBucketCount("SimpleCache.FileDescriptorLimiterAction",
336                                      disk_cache::FD_LIMIT_CLOSE_FILE,
337                                      kEntries - kFileLimit + kEntries - 2);
338   histogram_tester.ExpectBucketCount("SimpleCache.FileDescriptorLimiterAction",
339                                      disk_cache::FD_LIMIT_REOPEN_FILE,
340                                      kEntries - 2);
341   histogram_tester.ExpectBucketCount("SimpleCache.FileDescriptorLimiterAction",
342                                      disk_cache::FD_LIMIT_FAIL_REOPEN_FILE, 1);
343 
344   // Doom file for [1].
345   SimpleFileTracker::EntryFileKey key = entries[1]->entry_file_key();
346   file_tracker_.Doom(entries[1].get(), &key);
347   base::FilePath old_path = names[1];
348   UpdateEntryFileKey(entries[1].get(), key);
349   base::FilePath new_path =
350       entries[1]->GetFilenameForSubfile(SimpleFileTracker::SubFile::FILE_0);
351   EXPECT_TRUE(new_path.BaseName().MaybeAsASCII().starts_with("todelete_"));
352   EXPECT_TRUE(base::Move(old_path, new_path));
353 
354   // Now re-acquire everything again; this time reading.
355   for (int i = 0; i < kEntries - 1; ++i) {
356     SimpleFileTracker::FileHandle borrow = file_tracker_.Acquire(
357         &ops, entries[i].get(), SimpleFileTracker::SubFile::FILE_0);
358     char read;
359     char expected = static_cast<char>(i);
360     if (i != 2) {
361       EXPECT_TRUE(borrow.IsOK());
362       EXPECT_EQ(1, borrow->Read(0, &read, 1));
363       EXPECT_EQ(expected, read);
364     } else {
365       EXPECT_FALSE(borrow.IsOK());
366     }
367   }
368 
369   histogram_tester.ExpectBucketCount(
370       "SimpleCache.FileDescriptorLimiterAction",
371       disk_cache::FD_LIMIT_CLOSE_FILE,
372       kEntries - kFileLimit + 2 * (kEntries - 2));
373   histogram_tester.ExpectBucketCount("SimpleCache.FileDescriptorLimiterAction",
374                                      disk_cache::FD_LIMIT_REOPEN_FILE,
375                                      2 * (kEntries - 2));
376   histogram_tester.ExpectBucketCount("SimpleCache.FileDescriptorLimiterAction",
377                                      disk_cache::FD_LIMIT_FAIL_REOPEN_FILE, 2);
378 
379   // Read from the last one, too. Should still be fine.
380   char read;
381   EXPECT_EQ(1, borrow_last->Read(0, &read, 1));
382   EXPECT_EQ('L', read);
383 
384   for (const auto& entry : entries)
385     file_tracker_.Close(entry.get(), SimpleFileTracker::SubFile::FILE_0);
386 }
387 
388 }  // namespace disk_cache
389