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/files/file.h" 6 #include "base/files/file_util.h" 7 #include "base/files/scoped_temp_dir.h" 8 #include "base/hash.h" 9 #include "base/logging.h" 10 #include "base/memory/scoped_ptr.h" 11 #include "base/pickle.h" 12 #include "base/run_loop.h" 13 #include "base/strings/stringprintf.h" 14 #include "base/thread_task_runner_handle.h" 15 #include "base/threading/thread.h" 16 #include "base/time/time.h" 17 #include "net/base/cache_type.h" 18 #include "net/base/test_completion_callback.h" 19 #include "net/disk_cache/disk_cache_test_util.h" 20 #include "net/disk_cache/simple/simple_backend_impl.h" 21 #include "net/disk_cache/simple/simple_backend_version.h" 22 #include "net/disk_cache/simple/simple_entry_format.h" 23 #include "net/disk_cache/simple/simple_index.h" 24 #include "net/disk_cache/simple/simple_index_file.h" 25 #include "net/disk_cache/simple/simple_util.h" 26 #include "net/disk_cache/simple/simple_version_upgrade.h" 27 #include "testing/gtest/include/gtest/gtest.h" 28 29 using base::Time; 30 using disk_cache::SimpleIndexFile; 31 using disk_cache::SimpleIndex; 32 33 namespace disk_cache { 34 35 // The Simple Cache backend requires a few guarantees from the filesystem like 36 // atomic renaming of recently open files. Those guarantees are not provided in 37 // general on Windows. 38 #if defined(OS_POSIX) 39 40 TEST(IndexMetadataTest, Basics) { 41 SimpleIndexFile::IndexMetadata index_metadata; 42 43 EXPECT_EQ(disk_cache::kSimpleIndexMagicNumber, index_metadata.magic_number_); 44 EXPECT_EQ(disk_cache::kSimpleVersion, index_metadata.version_); 45 EXPECT_EQ(0U, index_metadata.GetNumberOfEntries()); 46 EXPECT_EQ(0U, index_metadata.cache_size_); 47 48 EXPECT_TRUE(index_metadata.CheckIndexMetadata()); 49 } 50 51 TEST(IndexMetadataTest, Serialize) { 52 SimpleIndexFile::IndexMetadata index_metadata(123, 456); 53 Pickle pickle; 54 index_metadata.Serialize(&pickle); 55 PickleIterator it(pickle); 56 SimpleIndexFile::IndexMetadata new_index_metadata; 57 new_index_metadata.Deserialize(&it); 58 59 EXPECT_EQ(new_index_metadata.magic_number_, index_metadata.magic_number_); 60 EXPECT_EQ(new_index_metadata.version_, index_metadata.version_); 61 EXPECT_EQ(new_index_metadata.GetNumberOfEntries(), 62 index_metadata.GetNumberOfEntries()); 63 EXPECT_EQ(new_index_metadata.cache_size_, index_metadata.cache_size_); 64 65 EXPECT_TRUE(new_index_metadata.CheckIndexMetadata()); 66 } 67 68 // This friend derived class is able to reexport its ancestors private methods 69 // as public, for use in tests. 70 class WrappedSimpleIndexFile : public SimpleIndexFile { 71 public: 72 using SimpleIndexFile::Deserialize; 73 using SimpleIndexFile::LegacyIsIndexFileStale; 74 using SimpleIndexFile::Serialize; 75 using SimpleIndexFile::SerializeFinalData; 76 77 explicit WrappedSimpleIndexFile(const base::FilePath& index_file_directory) 78 : SimpleIndexFile(base::ThreadTaskRunnerHandle::Get(), 79 base::ThreadTaskRunnerHandle::Get(), 80 net::DISK_CACHE, 81 index_file_directory) {} 82 virtual ~WrappedSimpleIndexFile() { 83 } 84 85 const base::FilePath& GetIndexFilePath() const { 86 return index_file_; 87 } 88 89 bool CreateIndexFileDirectory() const { 90 return base::CreateDirectory(index_file_.DirName()); 91 } 92 }; 93 94 class SimpleIndexFileTest : public testing::Test { 95 public: 96 bool CompareTwoEntryMetadata(const EntryMetadata& a, const EntryMetadata& b) { 97 return 98 a.last_used_time_seconds_since_epoch_ == 99 b.last_used_time_seconds_since_epoch_ && 100 a.entry_size_ == b.entry_size_; 101 } 102 103 protected: 104 SimpleIndexFileTest() : callback_called_(false) {} 105 106 base::Closure GetCallback() { 107 return base::Bind(&SimpleIndexFileTest::LoadIndexEntriesCallback, 108 base::Unretained(this)); 109 } 110 111 bool callback_called() { return callback_called_; } 112 113 private: 114 void LoadIndexEntriesCallback() { 115 EXPECT_FALSE(callback_called_); 116 callback_called_ = true; 117 } 118 119 bool callback_called_; 120 }; 121 122 TEST_F(SimpleIndexFileTest, Serialize) { 123 SimpleIndex::EntrySet entries; 124 static const uint64 kHashes[] = { 11, 22, 33 }; 125 static const size_t kNumHashes = arraysize(kHashes); 126 EntryMetadata metadata_entries[kNumHashes]; 127 128 SimpleIndexFile::IndexMetadata index_metadata(static_cast<uint64>(kNumHashes), 129 456); 130 for (size_t i = 0; i < kNumHashes; ++i) { 131 uint64 hash = kHashes[i]; 132 metadata_entries[i] = EntryMetadata(Time(), hash); 133 SimpleIndex::InsertInEntrySet(hash, metadata_entries[i], &entries); 134 } 135 136 scoped_ptr<Pickle> pickle = WrappedSimpleIndexFile::Serialize( 137 index_metadata, entries); 138 EXPECT_TRUE(pickle.get() != NULL); 139 base::Time now = base::Time::Now(); 140 EXPECT_TRUE(WrappedSimpleIndexFile::SerializeFinalData(now, pickle.get())); 141 base::Time when_index_last_saw_cache; 142 SimpleIndexLoadResult deserialize_result; 143 WrappedSimpleIndexFile::Deserialize(static_cast<const char*>(pickle->data()), 144 pickle->size(), 145 &when_index_last_saw_cache, 146 &deserialize_result); 147 EXPECT_TRUE(deserialize_result.did_load); 148 EXPECT_EQ(now, when_index_last_saw_cache); 149 const SimpleIndex::EntrySet& new_entries = deserialize_result.entries; 150 EXPECT_EQ(entries.size(), new_entries.size()); 151 152 for (size_t i = 0; i < kNumHashes; ++i) { 153 SimpleIndex::EntrySet::const_iterator it = new_entries.find(kHashes[i]); 154 EXPECT_TRUE(new_entries.end() != it); 155 EXPECT_TRUE(CompareTwoEntryMetadata(it->second, metadata_entries[i])); 156 } 157 } 158 159 TEST_F(SimpleIndexFileTest, LegacyIsIndexFileStale) { 160 base::ScopedTempDir cache_dir; 161 ASSERT_TRUE(cache_dir.CreateUniqueTempDir()); 162 base::Time cache_mtime; 163 const base::FilePath cache_path = cache_dir.path(); 164 165 ASSERT_TRUE(simple_util::GetMTime(cache_path, &cache_mtime)); 166 WrappedSimpleIndexFile simple_index_file(cache_path); 167 ASSERT_TRUE(simple_index_file.CreateIndexFileDirectory()); 168 const base::FilePath& index_path = simple_index_file.GetIndexFilePath(); 169 EXPECT_TRUE( 170 WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path)); 171 const std::string kDummyData = "nothing to be seen here"; 172 EXPECT_EQ(static_cast<int>(kDummyData.size()), 173 base::WriteFile(index_path, 174 kDummyData.data(), 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 base::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::File file(cache_path.AppendASCII("index"), 273 base::File::FLAG_CREATE | base::File::FLAG_WRITE); 274 ASSERT_TRUE(file.IsValid()); 275 disk_cache::FakeIndexData file_contents; 276 file_contents.initial_magic_number = disk_cache::kSimpleInitialMagicNumber; 277 file_contents.version = 5; 278 int bytes_written = file.Write(0, reinterpret_cast<char*>(&file_contents), 279 sizeof(file_contents)); 280 ASSERT_EQ((int)sizeof(file_contents), bytes_written); 281 file.Close(); 282 283 // Write the index file. The format is incorrect, but for transitioning from 284 // v5 it does not matter. 285 const std::string index_file_contents("incorrectly serialized data"); 286 const base::FilePath old_index_file = 287 cache_path.AppendASCII("the-real-index"); 288 ASSERT_EQ(implicit_cast<int>(index_file_contents.size()), 289 base::WriteFile(old_index_file, 290 index_file_contents.data(), 291 index_file_contents.size())); 292 293 // Upgrade the cache. 294 ASSERT_TRUE(disk_cache::UpgradeSimpleCacheOnDisk(cache_path)); 295 296 // Create the backend and initiate index flush by destroying the backend. 297 base::Thread cache_thread("CacheThread"); 298 ASSERT_TRUE(cache_thread.StartWithOptions( 299 base::Thread::Options(base::MessageLoop::TYPE_IO, 0))); 300 disk_cache::SimpleBackendImpl* simple_cache = 301 new disk_cache::SimpleBackendImpl(cache_path, 302 0, 303 net::DISK_CACHE, 304 cache_thread.message_loop_proxy().get(), 305 NULL); 306 net::TestCompletionCallback cb; 307 int rv = simple_cache->Init(cb.callback()); 308 EXPECT_EQ(net::OK, cb.GetResult(rv)); 309 rv = simple_cache->index()->ExecuteWhenReady(cb.callback()); 310 EXPECT_EQ(net::OK, cb.GetResult(rv)); 311 delete simple_cache; 312 313 // The backend flushes the index on destruction and does so on the cache 314 // thread, wait for the flushing to finish by posting a callback to the cache 315 // thread after that. 316 MessageLoopHelper helper; 317 CallbackTest cb_shutdown(&helper, false); 318 cache_thread.message_loop_proxy()->PostTask( 319 FROM_HERE, 320 base::Bind(&CallbackTest::Run, base::Unretained(&cb_shutdown), net::OK)); 321 helper.WaitUntilCacheIoFinished(1); 322 323 // Verify that the index file exists. 324 const base::FilePath& index_file_path = 325 cache_path.AppendASCII("index-dir").AppendASCII("the-real-index"); 326 EXPECT_TRUE(base::PathExists(index_file_path)); 327 328 // Verify that the version of the index file is correct. 329 std::string contents; 330 EXPECT_TRUE(base::ReadFileToString(index_file_path, &contents)); 331 base::Time when_index_last_saw_cache; 332 SimpleIndexLoadResult deserialize_result; 333 WrappedSimpleIndexFile::Deserialize(contents.data(), 334 contents.size(), 335 &when_index_last_saw_cache, 336 &deserialize_result); 337 EXPECT_TRUE(deserialize_result.did_load); 338 } 339 340 #endif // defined(OS_POSIX) 341 342 } // namespace disk_cache 343