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 "base/files/scoped_temp_dir.h" 7 #include "base/hash.h" 8 #include "base/logging.h" 9 #include "base/memory/scoped_ptr.h" 10 #include "base/message_loop/message_loop_proxy.h" 11 #include "base/pickle.h" 12 #include "base/run_loop.h" 13 #include "base/strings/stringprintf.h" 14 #include "base/threading/thread.h" 15 #include "base/time/time.h" 16 #include "net/base/cache_type.h" 17 #include "net/base/test_completion_callback.h" 18 #include "net/disk_cache/disk_cache_test_util.h" 19 #include "net/disk_cache/simple/simple_backend_impl.h" 20 #include "net/disk_cache/simple/simple_backend_version.h" 21 #include "net/disk_cache/simple/simple_entry_format.h" 22 #include "net/disk_cache/simple/simple_index.h" 23 #include "net/disk_cache/simple/simple_index_file.h" 24 #include "net/disk_cache/simple/simple_util.h" 25 #include "net/disk_cache/simple/simple_version_upgrade.h" 26 #include "testing/gtest/include/gtest/gtest.h" 27 28 using base::Time; 29 using disk_cache::SimpleIndexFile; 30 using disk_cache::SimpleIndex; 31 32 namespace disk_cache { 33 34 // The Simple Cache backend requires a few guarantees from the filesystem like 35 // atomic renaming of recently open files. Those guarantees are not provided in 36 // general on Windows. 37 #if defined(OS_POSIX) 38 39 TEST(IndexMetadataTest, Basics) { 40 SimpleIndexFile::IndexMetadata index_metadata; 41 42 EXPECT_EQ(disk_cache::kSimpleIndexMagicNumber, index_metadata.magic_number_); 43 EXPECT_EQ(disk_cache::kSimpleVersion, index_metadata.version_); 44 EXPECT_EQ(0U, index_metadata.GetNumberOfEntries()); 45 EXPECT_EQ(0U, index_metadata.cache_size_); 46 47 EXPECT_TRUE(index_metadata.CheckIndexMetadata()); 48 } 49 50 TEST(IndexMetadataTest, Serialize) { 51 SimpleIndexFile::IndexMetadata index_metadata(123, 456); 52 Pickle pickle; 53 index_metadata.Serialize(&pickle); 54 PickleIterator it(pickle); 55 SimpleIndexFile::IndexMetadata new_index_metadata; 56 new_index_metadata.Deserialize(&it); 57 58 EXPECT_EQ(new_index_metadata.magic_number_, index_metadata.magic_number_); 59 EXPECT_EQ(new_index_metadata.version_, index_metadata.version_); 60 EXPECT_EQ(new_index_metadata.GetNumberOfEntries(), 61 index_metadata.GetNumberOfEntries()); 62 EXPECT_EQ(new_index_metadata.cache_size_, index_metadata.cache_size_); 63 64 EXPECT_TRUE(new_index_metadata.CheckIndexMetadata()); 65 } 66 67 // This friend derived class is able to reexport its ancestors private methods 68 // as public, for use in tests. 69 class WrappedSimpleIndexFile : public SimpleIndexFile { 70 public: 71 using SimpleIndexFile::Deserialize; 72 using SimpleIndexFile::LegacyIsIndexFileStale; 73 using SimpleIndexFile::Serialize; 74 using SimpleIndexFile::SerializeFinalData; 75 76 explicit WrappedSimpleIndexFile(const base::FilePath& index_file_directory) 77 : SimpleIndexFile(base::MessageLoopProxy::current().get(), 78 base::MessageLoopProxy::current().get(), 79 net::DISK_CACHE, 80 index_file_directory) {} 81 virtual ~WrappedSimpleIndexFile() { 82 } 83 84 const base::FilePath& GetIndexFilePath() const { 85 return index_file_; 86 } 87 88 bool CreateIndexFileDirectory() const { 89 return base::CreateDirectory(index_file_.DirName()); 90 } 91 }; 92 93 class SimpleIndexFileTest : public testing::Test { 94 public: 95 bool CompareTwoEntryMetadata(const EntryMetadata& a, const EntryMetadata& b) { 96 return 97 a.last_used_time_seconds_since_epoch_ == 98 b.last_used_time_seconds_since_epoch_ && 99 a.entry_size_ == b.entry_size_; 100 } 101 102 protected: 103 SimpleIndexFileTest() : callback_called_(false) {} 104 105 base::Closure GetCallback() { 106 return base::Bind(&SimpleIndexFileTest::LoadIndexEntriesCallback, 107 base::Unretained(this)); 108 } 109 110 bool callback_called() { return callback_called_; } 111 112 private: 113 void LoadIndexEntriesCallback() { 114 EXPECT_FALSE(callback_called_); 115 callback_called_ = true; 116 } 117 118 bool callback_called_; 119 }; 120 121 TEST_F(SimpleIndexFileTest, Serialize) { 122 SimpleIndex::EntrySet entries; 123 static const uint64 kHashes[] = { 11, 22, 33 }; 124 static const size_t kNumHashes = arraysize(kHashes); 125 EntryMetadata metadata_entries[kNumHashes]; 126 127 SimpleIndexFile::IndexMetadata index_metadata(static_cast<uint64>(kNumHashes), 128 456); 129 for (size_t i = 0; i < kNumHashes; ++i) { 130 uint64 hash = kHashes[i]; 131 metadata_entries[i] = EntryMetadata(Time(), hash); 132 SimpleIndex::InsertInEntrySet(hash, metadata_entries[i], &entries); 133 } 134 135 scoped_ptr<Pickle> pickle = WrappedSimpleIndexFile::Serialize( 136 index_metadata, entries); 137 EXPECT_TRUE(pickle.get() != NULL); 138 base::Time now = base::Time::Now(); 139 EXPECT_TRUE(WrappedSimpleIndexFile::SerializeFinalData(now, pickle.get())); 140 base::Time when_index_last_saw_cache; 141 SimpleIndexLoadResult deserialize_result; 142 WrappedSimpleIndexFile::Deserialize(static_cast<const char*>(pickle->data()), 143 pickle->size(), 144 &when_index_last_saw_cache, 145 &deserialize_result); 146 EXPECT_TRUE(deserialize_result.did_load); 147 EXPECT_EQ(now, when_index_last_saw_cache); 148 const SimpleIndex::EntrySet& new_entries = deserialize_result.entries; 149 EXPECT_EQ(entries.size(), new_entries.size()); 150 151 for (size_t i = 0; i < kNumHashes; ++i) { 152 SimpleIndex::EntrySet::const_iterator it = new_entries.find(kHashes[i]); 153 EXPECT_TRUE(new_entries.end() != it); 154 EXPECT_TRUE(CompareTwoEntryMetadata(it->second, metadata_entries[i])); 155 } 156 } 157 158 TEST_F(SimpleIndexFileTest, LegacyIsIndexFileStale) { 159 base::ScopedTempDir cache_dir; 160 ASSERT_TRUE(cache_dir.CreateUniqueTempDir()); 161 base::Time cache_mtime; 162 const base::FilePath cache_path = cache_dir.path(); 163 164 ASSERT_TRUE(simple_util::GetMTime(cache_path, &cache_mtime)); 165 WrappedSimpleIndexFile simple_index_file(cache_path); 166 ASSERT_TRUE(simple_index_file.CreateIndexFileDirectory()); 167 const base::FilePath& index_path = simple_index_file.GetIndexFilePath(); 168 EXPECT_TRUE( 169 WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path)); 170 const std::string kDummyData = "nothing to be seen here"; 171 EXPECT_EQ(static_cast<int>(kDummyData.size()), 172 file_util::WriteFile(index_path, 173 kDummyData.data(), 174 kDummyData.size())); 175 ASSERT_TRUE(simple_util::GetMTime(cache_path, &cache_mtime)); 176 EXPECT_FALSE( 177 WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path)); 178 179 const base::Time past_time = base::Time::Now() - 180 base::TimeDelta::FromSeconds(10); 181 EXPECT_TRUE(base::TouchFile(index_path, past_time, past_time)); 182 EXPECT_TRUE(base::TouchFile(cache_path, past_time, past_time)); 183 ASSERT_TRUE(simple_util::GetMTime(cache_path, &cache_mtime)); 184 EXPECT_FALSE( 185 WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path)); 186 const base::Time even_older = past_time - base::TimeDelta::FromSeconds(10); 187 EXPECT_TRUE(base::TouchFile(index_path, even_older, even_older)); 188 EXPECT_TRUE( 189 WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path)); 190 } 191 192 // This test is flaky, see http://crbug.com/255775. 193 TEST_F(SimpleIndexFileTest, DISABLED_WriteThenLoadIndex) { 194 base::ScopedTempDir cache_dir; 195 ASSERT_TRUE(cache_dir.CreateUniqueTempDir()); 196 197 SimpleIndex::EntrySet entries; 198 static const uint64 kHashes[] = { 11, 22, 33 }; 199 static const size_t kNumHashes = arraysize(kHashes); 200 EntryMetadata metadata_entries[kNumHashes]; 201 for (size_t i = 0; i < kNumHashes; ++i) { 202 uint64 hash = kHashes[i]; 203 metadata_entries[i] = EntryMetadata(Time(), hash); 204 SimpleIndex::InsertInEntrySet(hash, metadata_entries[i], &entries); 205 } 206 207 const uint64 kCacheSize = 456U; 208 { 209 WrappedSimpleIndexFile simple_index_file(cache_dir.path()); 210 simple_index_file.WriteToDisk(entries, kCacheSize, 211 base::TimeTicks(), false); 212 base::RunLoop().RunUntilIdle(); 213 EXPECT_TRUE(base::PathExists(simple_index_file.GetIndexFilePath())); 214 } 215 216 WrappedSimpleIndexFile simple_index_file(cache_dir.path()); 217 base::Time fake_cache_mtime; 218 ASSERT_TRUE(simple_util::GetMTime(simple_index_file.GetIndexFilePath(), 219 &fake_cache_mtime)); 220 SimpleIndexLoadResult load_index_result; 221 simple_index_file.LoadIndexEntries(fake_cache_mtime, 222 GetCallback(), 223 &load_index_result); 224 base::RunLoop().RunUntilIdle(); 225 226 EXPECT_TRUE(base::PathExists(simple_index_file.GetIndexFilePath())); 227 ASSERT_TRUE(callback_called()); 228 EXPECT_TRUE(load_index_result.did_load); 229 EXPECT_FALSE(load_index_result.flush_required); 230 231 EXPECT_EQ(kNumHashes, load_index_result.entries.size()); 232 for (size_t i = 0; i < kNumHashes; ++i) 233 EXPECT_EQ(1U, load_index_result.entries.count(kHashes[i])); 234 } 235 236 TEST_F(SimpleIndexFileTest, LoadCorruptIndex) { 237 base::ScopedTempDir cache_dir; 238 ASSERT_TRUE(cache_dir.CreateUniqueTempDir()); 239 240 WrappedSimpleIndexFile simple_index_file(cache_dir.path()); 241 ASSERT_TRUE(simple_index_file.CreateIndexFileDirectory()); 242 const base::FilePath& index_path = simple_index_file.GetIndexFilePath(); 243 const std::string kDummyData = "nothing to be seen here"; 244 EXPECT_EQ( 245 implicit_cast<int>(kDummyData.size()), 246 file_util::WriteFile(index_path, kDummyData.data(), kDummyData.size())); 247 base::Time fake_cache_mtime; 248 ASSERT_TRUE(simple_util::GetMTime(simple_index_file.GetIndexFilePath(), 249 &fake_cache_mtime)); 250 EXPECT_FALSE(WrappedSimpleIndexFile::LegacyIsIndexFileStale(fake_cache_mtime, 251 index_path)); 252 253 SimpleIndexLoadResult load_index_result; 254 simple_index_file.LoadIndexEntries(fake_cache_mtime, 255 GetCallback(), 256 &load_index_result); 257 base::RunLoop().RunUntilIdle(); 258 259 EXPECT_FALSE(base::PathExists(index_path)); 260 ASSERT_TRUE(callback_called()); 261 EXPECT_TRUE(load_index_result.did_load); 262 EXPECT_TRUE(load_index_result.flush_required); 263 } 264 265 // Tests that after an upgrade the backend has the index file put in place. 266 TEST_F(SimpleIndexFileTest, SimpleCacheUpgrade) { 267 base::ScopedTempDir cache_dir; 268 ASSERT_TRUE(cache_dir.CreateUniqueTempDir()); 269 const base::FilePath cache_path = cache_dir.path(); 270 271 // Write an old fake index file. 272 base::PlatformFileError error; 273 base::PlatformFile file = base::CreatePlatformFile( 274 cache_path.AppendASCII("index"), 275 base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE, 276 NULL, 277 &error); 278 disk_cache::FakeIndexData file_contents; 279 file_contents.initial_magic_number = disk_cache::kSimpleInitialMagicNumber; 280 file_contents.version = 5; 281 int bytes_written = base::WritePlatformFile( 282 file, 0, reinterpret_cast<char*>(&file_contents), sizeof(file_contents)); 283 ASSERT_TRUE(base::ClosePlatformFile(file)); 284 ASSERT_EQ((int)sizeof(file_contents), bytes_written); 285 286 // Write the index file. The format is incorrect, but for transitioning from 287 // v5 it does not matter. 288 const std::string index_file_contents("incorrectly serialized data"); 289 const base::FilePath old_index_file = 290 cache_path.AppendASCII("the-real-index"); 291 ASSERT_EQ(implicit_cast<int>(index_file_contents.size()), 292 file_util::WriteFile(old_index_file, 293 index_file_contents.data(), 294 index_file_contents.size())); 295 296 // Upgrade the cache. 297 ASSERT_TRUE(disk_cache::UpgradeSimpleCacheOnDisk(cache_path)); 298 299 // Create the backend and initiate index flush by destroying the backend. 300 base::Thread cache_thread("CacheThread"); 301 ASSERT_TRUE(cache_thread.StartWithOptions( 302 base::Thread::Options(base::MessageLoop::TYPE_IO, 0))); 303 disk_cache::SimpleBackendImpl* simple_cache = 304 new disk_cache::SimpleBackendImpl(cache_path, 305 0, 306 net::DISK_CACHE, 307 cache_thread.message_loop_proxy().get(), 308 NULL); 309 net::TestCompletionCallback cb; 310 int rv = simple_cache->Init(cb.callback()); 311 EXPECT_EQ(net::OK, cb.GetResult(rv)); 312 rv = simple_cache->index()->ExecuteWhenReady(cb.callback()); 313 EXPECT_EQ(net::OK, cb.GetResult(rv)); 314 delete simple_cache; 315 316 // The backend flushes the index on destruction and does so on the cache 317 // thread, wait for the flushing to finish by posting a callback to the cache 318 // thread after that. 319 MessageLoopHelper helper; 320 CallbackTest cb_shutdown(&helper, false); 321 cache_thread.message_loop_proxy()->PostTask( 322 FROM_HERE, 323 base::Bind(&CallbackTest::Run, base::Unretained(&cb_shutdown), net::OK)); 324 helper.WaitUntilCacheIoFinished(1); 325 326 // Verify that the index file exists. 327 const base::FilePath& index_file_path = 328 cache_path.AppendASCII("index-dir").AppendASCII("the-real-index"); 329 EXPECT_TRUE(base::PathExists(index_file_path)); 330 331 // Verify that the version of the index file is correct. 332 std::string contents; 333 EXPECT_TRUE(base::ReadFileToString(index_file_path, &contents)); 334 base::Time when_index_last_saw_cache; 335 SimpleIndexLoadResult deserialize_result; 336 WrappedSimpleIndexFile::Deserialize(contents.data(), 337 contents.size(), 338 &when_index_last_saw_cache, 339 &deserialize_result); 340 EXPECT_TRUE(deserialize_result.did_load); 341 } 342 343 #endif // defined(OS_POSIX) 344 345 } // namespace disk_cache 346