simple_index_file_unittest.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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/file.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/message_loop/message_loop_proxy.h" 12#include "base/pickle.h" 13#include "base/run_loop.h" 14#include "base/strings/stringprintf.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 29using base::Time; 30using disk_cache::SimpleIndexFile; 31using disk_cache::SimpleIndex; 32 33namespace 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 40TEST(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 51TEST(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. 70class 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::MessageLoopProxy::current().get(), 79 base::MessageLoopProxy::current().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 94class 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 122TEST_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 159TEST_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 file_util::WriteFile(index_path, 174 kDummyData.data(), 175 kDummyData.size())); 176 ASSERT_TRUE(simple_util::GetMTime(cache_path, &cache_mtime)); 177 EXPECT_FALSE( 178 WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path)); 179 180 const base::Time past_time = base::Time::Now() - 181 base::TimeDelta::FromSeconds(10); 182 EXPECT_TRUE(base::TouchFile(index_path, past_time, past_time)); 183 EXPECT_TRUE(base::TouchFile(cache_path, past_time, past_time)); 184 ASSERT_TRUE(simple_util::GetMTime(cache_path, &cache_mtime)); 185 EXPECT_FALSE( 186 WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path)); 187 const base::Time even_older = past_time - base::TimeDelta::FromSeconds(10); 188 EXPECT_TRUE(base::TouchFile(index_path, even_older, even_older)); 189 EXPECT_TRUE( 190 WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path)); 191} 192 193// This test is flaky, see http://crbug.com/255775. 194TEST_F(SimpleIndexFileTest, DISABLED_WriteThenLoadIndex) { 195 base::ScopedTempDir cache_dir; 196 ASSERT_TRUE(cache_dir.CreateUniqueTempDir()); 197 198 SimpleIndex::EntrySet entries; 199 static const uint64 kHashes[] = { 11, 22, 33 }; 200 static const size_t kNumHashes = arraysize(kHashes); 201 EntryMetadata metadata_entries[kNumHashes]; 202 for (size_t i = 0; i < kNumHashes; ++i) { 203 uint64 hash = kHashes[i]; 204 metadata_entries[i] = EntryMetadata(Time(), hash); 205 SimpleIndex::InsertInEntrySet(hash, metadata_entries[i], &entries); 206 } 207 208 const uint64 kCacheSize = 456U; 209 { 210 WrappedSimpleIndexFile simple_index_file(cache_dir.path()); 211 simple_index_file.WriteToDisk(entries, kCacheSize, 212 base::TimeTicks(), false); 213 base::RunLoop().RunUntilIdle(); 214 EXPECT_TRUE(base::PathExists(simple_index_file.GetIndexFilePath())); 215 } 216 217 WrappedSimpleIndexFile simple_index_file(cache_dir.path()); 218 base::Time fake_cache_mtime; 219 ASSERT_TRUE(simple_util::GetMTime(simple_index_file.GetIndexFilePath(), 220 &fake_cache_mtime)); 221 SimpleIndexLoadResult load_index_result; 222 simple_index_file.LoadIndexEntries(fake_cache_mtime, 223 GetCallback(), 224 &load_index_result); 225 base::RunLoop().RunUntilIdle(); 226 227 EXPECT_TRUE(base::PathExists(simple_index_file.GetIndexFilePath())); 228 ASSERT_TRUE(callback_called()); 229 EXPECT_TRUE(load_index_result.did_load); 230 EXPECT_FALSE(load_index_result.flush_required); 231 232 EXPECT_EQ(kNumHashes, load_index_result.entries.size()); 233 for (size_t i = 0; i < kNumHashes; ++i) 234 EXPECT_EQ(1U, load_index_result.entries.count(kHashes[i])); 235} 236 237TEST_F(SimpleIndexFileTest, LoadCorruptIndex) { 238 base::ScopedTempDir cache_dir; 239 ASSERT_TRUE(cache_dir.CreateUniqueTempDir()); 240 241 WrappedSimpleIndexFile simple_index_file(cache_dir.path()); 242 ASSERT_TRUE(simple_index_file.CreateIndexFileDirectory()); 243 const base::FilePath& index_path = simple_index_file.GetIndexFilePath(); 244 const std::string kDummyData = "nothing to be seen here"; 245 EXPECT_EQ( 246 implicit_cast<int>(kDummyData.size()), 247 file_util::WriteFile(index_path, kDummyData.data(), kDummyData.size())); 248 base::Time fake_cache_mtime; 249 ASSERT_TRUE(simple_util::GetMTime(simple_index_file.GetIndexFilePath(), 250 &fake_cache_mtime)); 251 EXPECT_FALSE(WrappedSimpleIndexFile::LegacyIsIndexFileStale(fake_cache_mtime, 252 index_path)); 253 254 SimpleIndexLoadResult load_index_result; 255 simple_index_file.LoadIndexEntries(fake_cache_mtime, 256 GetCallback(), 257 &load_index_result); 258 base::RunLoop().RunUntilIdle(); 259 260 EXPECT_FALSE(base::PathExists(index_path)); 261 ASSERT_TRUE(callback_called()); 262 EXPECT_TRUE(load_index_result.did_load); 263 EXPECT_TRUE(load_index_result.flush_required); 264} 265 266// Tests that after an upgrade the backend has the index file put in place. 267TEST_F(SimpleIndexFileTest, SimpleCacheUpgrade) { 268 base::ScopedTempDir cache_dir; 269 ASSERT_TRUE(cache_dir.CreateUniqueTempDir()); 270 const base::FilePath cache_path = cache_dir.path(); 271 272 // Write an old fake index file. 273 base::File file(cache_path.AppendASCII("index"), 274 base::File::FLAG_CREATE | base::File::FLAG_WRITE); 275 ASSERT_TRUE(file.IsValid()); 276 disk_cache::FakeIndexData file_contents; 277 file_contents.initial_magic_number = disk_cache::kSimpleInitialMagicNumber; 278 file_contents.version = 5; 279 int bytes_written = file.Write(0, reinterpret_cast<char*>(&file_contents), 280 sizeof(file_contents)); 281 ASSERT_EQ((int)sizeof(file_contents), bytes_written); 282 file.Close(); 283 284 // Write the index file. The format is incorrect, but for transitioning from 285 // v5 it does not matter. 286 const std::string index_file_contents("incorrectly serialized data"); 287 const base::FilePath old_index_file = 288 cache_path.AppendASCII("the-real-index"); 289 ASSERT_EQ(implicit_cast<int>(index_file_contents.size()), 290 file_util::WriteFile(old_index_file, 291 index_file_contents.data(), 292 index_file_contents.size())); 293 294 // Upgrade the cache. 295 ASSERT_TRUE(disk_cache::UpgradeSimpleCacheOnDisk(cache_path)); 296 297 // Create the backend and initiate index flush by destroying the backend. 298 base::Thread cache_thread("CacheThread"); 299 ASSERT_TRUE(cache_thread.StartWithOptions( 300 base::Thread::Options(base::MessageLoop::TYPE_IO, 0))); 301 disk_cache::SimpleBackendImpl* simple_cache = 302 new disk_cache::SimpleBackendImpl(cache_path, 303 0, 304 net::DISK_CACHE, 305 cache_thread.message_loop_proxy().get(), 306 NULL); 307 net::TestCompletionCallback cb; 308 int rv = simple_cache->Init(cb.callback()); 309 EXPECT_EQ(net::OK, cb.GetResult(rv)); 310 rv = simple_cache->index()->ExecuteWhenReady(cb.callback()); 311 EXPECT_EQ(net::OK, cb.GetResult(rv)); 312 delete simple_cache; 313 314 // The backend flushes the index on destruction and does so on the cache 315 // thread, wait for the flushing to finish by posting a callback to the cache 316 // thread after that. 317 MessageLoopHelper helper; 318 CallbackTest cb_shutdown(&helper, false); 319 cache_thread.message_loop_proxy()->PostTask( 320 FROM_HERE, 321 base::Bind(&CallbackTest::Run, base::Unretained(&cb_shutdown), net::OK)); 322 helper.WaitUntilCacheIoFinished(1); 323 324 // Verify that the index file exists. 325 const base::FilePath& index_file_path = 326 cache_path.AppendASCII("index-dir").AppendASCII("the-real-index"); 327 EXPECT_TRUE(base::PathExists(index_file_path)); 328 329 // Verify that the version of the index file is correct. 330 std::string contents; 331 EXPECT_TRUE(base::ReadFileToString(index_file_path, &contents)); 332 base::Time when_index_last_saw_cache; 333 SimpleIndexLoadResult deserialize_result; 334 WrappedSimpleIndexFile::Deserialize(contents.data(), 335 contents.size(), 336 &when_index_last_saw_cache, 337 &deserialize_result); 338 EXPECT_TRUE(deserialize_result.did_load); 339} 340 341#endif // defined(OS_POSIX) 342 343} // namespace disk_cache 344