xref: /aosp_15_r20/external/cronet/net/disk_cache/blockfile/block_files_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 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 "base/files/file_enumerator.h"
6 #include "base/files/file_util.h"
7 #include "build/chromeos_buildflags.h"
8 #include "net/disk_cache/blockfile/block_files.h"
9 #include "net/disk_cache/disk_cache.h"
10 #include "net/disk_cache/disk_cache_test_base.h"
11 #include "net/disk_cache/disk_cache_test_util.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 
14 using base::Time;
15 
16 namespace {
17 
18 // Returns the number of files in this folder.
NumberOfFiles(const base::FilePath & path)19 int NumberOfFiles(const base::FilePath& path) {
20   base::FileEnumerator iter(path, false, base::FileEnumerator::FILES);
21   int count = 0;
22   for (base::FilePath file = iter.Next(); !file.value().empty();
23        file = iter.Next()) {
24     count++;
25   }
26   return count;
27 }
28 
29 }  // namespace
30 
31 namespace disk_cache {
32 
33 #if BUILDFLAG(IS_CHROMEOS_ASH)
34 // Flaky on ChromeOS: https://crbug.com/1156795
35 #define MAYBE_BlockFiles_Grow DISABLED_BlockFiles_Grow
36 #else
37 #define MAYBE_BlockFiles_Grow BlockFiles_Grow
38 #endif
TEST_F(DiskCacheTest,MAYBE_BlockFiles_Grow)39 TEST_F(DiskCacheTest, MAYBE_BlockFiles_Grow) {
40   ASSERT_TRUE(CleanupCacheDir());
41   ASSERT_TRUE(base::CreateDirectory(cache_path_));
42 
43   BlockFiles files(cache_path_);
44   ASSERT_TRUE(files.Init(true));
45 
46 #if BUILDFLAG(IS_FUCHSIA)
47   // Too slow on Fuchsia: https://crbug.com/1354793
48   const int kMaxSize = 3500;
49   const int kNumberOfFiles = 4;
50 #else
51   const int kMaxSize = 35000;
52   const int kNumberOfFiles = 6;
53 #endif
54   Addr address[kMaxSize];
55 
56   // Fill up the 32-byte block file (use three files).
57   for (auto& addr : address) {
58     EXPECT_TRUE(files.CreateBlock(RANKINGS, 4, &addr));
59   }
60   EXPECT_EQ(kNumberOfFiles, NumberOfFiles(cache_path_));
61 
62   // Make sure we don't keep adding files.
63   for (int i = 0; i < kMaxSize * 4; i += 2) {
64     int target = i % kMaxSize;
65     files.DeleteBlock(address[target], false);
66     EXPECT_TRUE(files.CreateBlock(RANKINGS, 4, &address[target]));
67   }
68   EXPECT_EQ(kNumberOfFiles, NumberOfFiles(cache_path_));
69 }
70 
71 // We should be able to delete empty block files.
TEST_F(DiskCacheTest,BlockFiles_Shrink)72 TEST_F(DiskCacheTest, BlockFiles_Shrink) {
73   ASSERT_TRUE(CleanupCacheDir());
74   ASSERT_TRUE(base::CreateDirectory(cache_path_));
75 
76   BlockFiles files(cache_path_);
77   ASSERT_TRUE(files.Init(true));
78 
79   const int kMaxSize = 35000;
80   Addr address[kMaxSize];
81 
82   // Fill up the 32-byte block file (use three files).
83   for (auto& addr : address) {
84     EXPECT_TRUE(files.CreateBlock(RANKINGS, 4, &addr));
85   }
86 
87   // Now delete all the blocks, so that we can delete the two extra files.
88   for (const auto& addr : address) {
89     files.DeleteBlock(addr, false);
90   }
91   EXPECT_EQ(4, NumberOfFiles(cache_path_));
92 }
93 
94 // Handling of block files not properly closed.
TEST_F(DiskCacheTest,BlockFiles_Recover)95 TEST_F(DiskCacheTest, BlockFiles_Recover) {
96   ASSERT_TRUE(CleanupCacheDir());
97   ASSERT_TRUE(base::CreateDirectory(cache_path_));
98 
99   BlockFiles files(cache_path_);
100   ASSERT_TRUE(files.Init(true));
101 
102   const int kNumEntries = 2000;
103   CacheAddr entries[kNumEntries];
104 
105   int seed = static_cast<int>(Time::Now().ToInternalValue());
106   srand(seed);
107   for (auto& entry : entries) {
108     Addr address(0);
109     int size = (rand() % 4) + 1;
110     EXPECT_TRUE(files.CreateBlock(RANKINGS, size, &address));
111     entry = address.value();
112   }
113 
114   for (int i = 0; i < kNumEntries; i++) {
115     int source1 = rand() % kNumEntries;
116     int source2 = rand() % kNumEntries;
117     CacheAddr temp = entries[source1];
118     entries[source1] = entries[source2];
119     entries[source2] = temp;
120   }
121 
122   for (int i = 0; i < kNumEntries / 2; i++) {
123     Addr address(entries[i]);
124     files.DeleteBlock(address, false);
125   }
126 
127   // At this point, there are kNumEntries / 2 entries on the file, randomly
128   // distributed both on location and size.
129 
130   Addr address(entries[kNumEntries / 2]);
131   MappedFile* file = files.GetFile(address);
132   ASSERT_TRUE(nullptr != file);
133 
134   BlockFileHeader* header =
135       reinterpret_cast<BlockFileHeader*>(file->buffer());
136   ASSERT_TRUE(nullptr != header);
137 
138   ASSERT_EQ(0, header->updating);
139 
140   int max_entries = header->max_entries;
141   int empty_1 = header->empty[0];
142   int empty_2 = header->empty[1];
143   int empty_3 = header->empty[2];
144   int empty_4 = header->empty[3];
145 
146   // Corrupt the file.
147   header->max_entries = header->empty[0] = 0;
148   header->empty[1] = header->empty[2] = header->empty[3] = 0;
149   header->updating = -1;
150 
151   files.CloseFiles();
152 
153   ASSERT_TRUE(files.Init(false));
154 
155   // The file must have been fixed.
156   file = files.GetFile(address);
157   ASSERT_TRUE(nullptr != file);
158 
159   header = reinterpret_cast<BlockFileHeader*>(file->buffer());
160   ASSERT_TRUE(nullptr != header);
161 
162   ASSERT_EQ(0, header->updating);
163 
164   EXPECT_EQ(max_entries, header->max_entries);
165   EXPECT_EQ(empty_1, header->empty[0]);
166   EXPECT_EQ(empty_2, header->empty[1]);
167   EXPECT_EQ(empty_3, header->empty[2]);
168   EXPECT_EQ(empty_4, header->empty[3]);
169 }
170 
171 // Handling of truncated files.
TEST_F(DiskCacheTest,BlockFiles_ZeroSizeFile)172 TEST_F(DiskCacheTest, BlockFiles_ZeroSizeFile) {
173   ASSERT_TRUE(CleanupCacheDir());
174   ASSERT_TRUE(base::CreateDirectory(cache_path_));
175 
176   BlockFiles files(cache_path_);
177   ASSERT_TRUE(files.Init(true));
178 
179   base::FilePath filename = files.Name(0);
180   files.CloseFiles();
181   // Truncate one of the files.
182   {
183     auto file = base::MakeRefCounted<File>();
184     ASSERT_TRUE(file->Init(filename));
185     EXPECT_TRUE(file->SetLength(0));
186   }
187 
188   // Initializing should fail, not crash.
189   ASSERT_FALSE(files.Init(false));
190 }
191 
192 // Handling of truncated files (non empty).
TEST_F(DiskCacheTest,BlockFiles_TruncatedFile)193 TEST_F(DiskCacheTest, BlockFiles_TruncatedFile) {
194   ASSERT_TRUE(CleanupCacheDir());
195   ASSERT_TRUE(base::CreateDirectory(cache_path_));
196 
197   BlockFiles files(cache_path_);
198   ASSERT_TRUE(files.Init(true));
199   Addr address;
200   EXPECT_TRUE(files.CreateBlock(RANKINGS, 2, &address));
201 
202   base::FilePath filename = files.Name(0);
203   files.CloseFiles();
204   // Truncate one of the files.
205   {
206     auto file = base::MakeRefCounted<File>();
207     ASSERT_TRUE(file->Init(filename));
208     EXPECT_TRUE(file->SetLength(15000));
209   }
210 
211   // Initializing should fail, not crash.
212   ASSERT_FALSE(files.Init(false));
213 }
214 
215 // Tests detection of out of sync counters.
TEST_F(DiskCacheTest,BlockFiles_Counters)216 TEST_F(DiskCacheTest, BlockFiles_Counters) {
217   ASSERT_TRUE(CleanupCacheDir());
218   ASSERT_TRUE(base::CreateDirectory(cache_path_));
219 
220   BlockFiles files(cache_path_);
221   ASSERT_TRUE(files.Init(true));
222 
223   // Create a block of size 2.
224   Addr address(0);
225   EXPECT_TRUE(files.CreateBlock(RANKINGS, 2, &address));
226 
227   MappedFile* file = files.GetFile(address);
228   ASSERT_TRUE(nullptr != file);
229 
230   BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer());
231   ASSERT_TRUE(nullptr != header);
232   ASSERT_EQ(0, header->updating);
233 
234   // Alter the counters so that the free space doesn't add up.
235   header->empty[2] = 50;  // 50 free blocks of size 3.
236   files.CloseFiles();
237 
238   ASSERT_TRUE(files.Init(false));
239   file = files.GetFile(address);
240   ASSERT_TRUE(nullptr != file);
241   header = reinterpret_cast<BlockFileHeader*>(file->buffer());
242   ASSERT_TRUE(nullptr != header);
243 
244   // The file must have been fixed.
245   ASSERT_EQ(0, header->empty[2]);
246 
247   // Change the number of entries.
248   header->num_entries = 3;
249   header->updating = 1;
250   files.CloseFiles();
251 
252   ASSERT_TRUE(files.Init(false));
253   file = files.GetFile(address);
254   ASSERT_TRUE(nullptr != file);
255   header = reinterpret_cast<BlockFileHeader*>(file->buffer());
256   ASSERT_TRUE(nullptr != header);
257 
258   // The file must have been "fixed".
259   ASSERT_EQ(2, header->num_entries);
260 
261   // Change the number of entries.
262   header->num_entries = -1;
263   header->updating = 1;
264   files.CloseFiles();
265 
266   // Detect the error.
267   ASSERT_FALSE(files.Init(false));
268 }
269 
270 // An invalid file can be detected after init.
TEST_F(DiskCacheTest,BlockFiles_InvalidFile)271 TEST_F(DiskCacheTest, BlockFiles_InvalidFile) {
272   ASSERT_TRUE(CleanupCacheDir());
273   ASSERT_TRUE(base::CreateDirectory(cache_path_));
274 
275   BlockFiles files(cache_path_);
276   ASSERT_TRUE(files.Init(true));
277 
278   // Let's access block 10 of file 5. (There is no file).
279   Addr addr(BLOCK_256, 1, 5, 10);
280   EXPECT_TRUE(nullptr == files.GetFile(addr));
281 
282   // Let's create an invalid file.
283   base::FilePath filename(files.Name(5));
284   char header[kBlockHeaderSize];
285   memset(header, 'a', kBlockHeaderSize);
286   EXPECT_TRUE(base::WriteFile(filename, {header, kBlockHeaderSize}));
287 
288   EXPECT_TRUE(nullptr == files.GetFile(addr));
289 
290   // The file should not have been changed (it is still invalid).
291   EXPECT_TRUE(nullptr == files.GetFile(addr));
292 }
293 
294 // Tests that we add and remove blocks correctly.
TEST_F(DiskCacheTest,AllocationMap)295 TEST_F(DiskCacheTest, AllocationMap) {
296   ASSERT_TRUE(CleanupCacheDir());
297   ASSERT_TRUE(base::CreateDirectory(cache_path_));
298 
299   BlockFiles files(cache_path_);
300   ASSERT_TRUE(files.Init(true));
301 
302   // Create a bunch of entries.
303   const int kSize = 100;
304   Addr address[kSize];
305   for (int i = 0; i < kSize; i++) {
306     SCOPED_TRACE(i);
307     int block_size = i % 4 + 1;
308     EXPECT_TRUE(files.CreateBlock(BLOCK_1K, block_size, &address[i]));
309     EXPECT_EQ(BLOCK_1K, address[i].file_type());
310     EXPECT_EQ(block_size, address[i].num_blocks());
311     int start = address[i].start_block();
312     EXPECT_EQ(start / 4, (start + block_size - 1) / 4);
313   }
314 
315   for (int i = 0; i < kSize; i++) {
316     SCOPED_TRACE(i);
317     EXPECT_TRUE(files.IsValid(address[i]));
318   }
319 
320   // The first part of the allocation map should be completely filled. We used
321   // 10 bits per each four entries, so 250 bits total.
322   BlockFileHeader* header =
323       reinterpret_cast<BlockFileHeader*>(files.GetFile(address[0])->buffer());
324   uint8_t* buffer = reinterpret_cast<uint8_t*>(&header->allocation_map);
325   for (int i =0; i < 29; i++) {
326     SCOPED_TRACE(i);
327     EXPECT_EQ(0xff, buffer[i]);
328   }
329 
330   for (int i = 0; i < kSize; i++) {
331     SCOPED_TRACE(i);
332     files.DeleteBlock(address[i], false);
333   }
334 
335   // The allocation map should be empty.
336   for (int i =0; i < 50; i++) {
337     SCOPED_TRACE(i);
338     EXPECT_EQ(0, buffer[i]);
339   }
340 }
341 
342 }  // namespace disk_cache
343