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