| OLD | NEW |
| (Empty) |
| 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 ~WrappedSimpleIndexFile() override {} | |
| 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 | |
| 103 TEST_F(SimpleIndexFileTest, Serialize) { | |
| 104 SimpleIndex::EntrySet entries; | |
| 105 static const uint64 kHashes[] = { 11, 22, 33 }; | |
| 106 static const size_t kNumHashes = arraysize(kHashes); | |
| 107 EntryMetadata metadata_entries[kNumHashes]; | |
| 108 | |
| 109 SimpleIndexFile::IndexMetadata index_metadata(static_cast<uint64>(kNumHashes), | |
| 110 456); | |
| 111 for (size_t i = 0; i < kNumHashes; ++i) { | |
| 112 uint64 hash = kHashes[i]; | |
| 113 metadata_entries[i] = EntryMetadata(Time(), hash); | |
| 114 SimpleIndex::InsertInEntrySet(hash, metadata_entries[i], &entries); | |
| 115 } | |
| 116 | |
| 117 scoped_ptr<Pickle> pickle = WrappedSimpleIndexFile::Serialize( | |
| 118 index_metadata, entries); | |
| 119 EXPECT_TRUE(pickle.get() != NULL); | |
| 120 base::Time now = base::Time::Now(); | |
| 121 EXPECT_TRUE(WrappedSimpleIndexFile::SerializeFinalData(now, pickle.get())); | |
| 122 base::Time when_index_last_saw_cache; | |
| 123 SimpleIndexLoadResult deserialize_result; | |
| 124 WrappedSimpleIndexFile::Deserialize(static_cast<const char*>(pickle->data()), | |
| 125 pickle->size(), | |
| 126 &when_index_last_saw_cache, | |
| 127 &deserialize_result); | |
| 128 EXPECT_TRUE(deserialize_result.did_load); | |
| 129 EXPECT_EQ(now, when_index_last_saw_cache); | |
| 130 const SimpleIndex::EntrySet& new_entries = deserialize_result.entries; | |
| 131 EXPECT_EQ(entries.size(), new_entries.size()); | |
| 132 | |
| 133 for (size_t i = 0; i < kNumHashes; ++i) { | |
| 134 SimpleIndex::EntrySet::const_iterator it = new_entries.find(kHashes[i]); | |
| 135 EXPECT_TRUE(new_entries.end() != it); | |
| 136 EXPECT_TRUE(CompareTwoEntryMetadata(it->second, metadata_entries[i])); | |
| 137 } | |
| 138 } | |
| 139 | |
| 140 TEST_F(SimpleIndexFileTest, LegacyIsIndexFileStale) { | |
| 141 base::ScopedTempDir cache_dir; | |
| 142 ASSERT_TRUE(cache_dir.CreateUniqueTempDir()); | |
| 143 base::Time cache_mtime; | |
| 144 const base::FilePath cache_path = cache_dir.path(); | |
| 145 | |
| 146 ASSERT_TRUE(simple_util::GetMTime(cache_path, &cache_mtime)); | |
| 147 WrappedSimpleIndexFile simple_index_file(cache_path); | |
| 148 ASSERT_TRUE(simple_index_file.CreateIndexFileDirectory()); | |
| 149 const base::FilePath& index_path = simple_index_file.GetIndexFilePath(); | |
| 150 EXPECT_TRUE( | |
| 151 WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path)); | |
| 152 const std::string kDummyData = "nothing to be seen here"; | |
| 153 EXPECT_EQ(static_cast<int>(kDummyData.size()), | |
| 154 base::WriteFile(index_path, | |
| 155 kDummyData.data(), kDummyData.size())); | |
| 156 ASSERT_TRUE(simple_util::GetMTime(cache_path, &cache_mtime)); | |
| 157 EXPECT_FALSE( | |
| 158 WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path)); | |
| 159 | |
| 160 const base::Time past_time = base::Time::Now() - | |
| 161 base::TimeDelta::FromSeconds(10); | |
| 162 EXPECT_TRUE(base::TouchFile(index_path, past_time, past_time)); | |
| 163 EXPECT_TRUE(base::TouchFile(cache_path, past_time, past_time)); | |
| 164 ASSERT_TRUE(simple_util::GetMTime(cache_path, &cache_mtime)); | |
| 165 EXPECT_FALSE( | |
| 166 WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path)); | |
| 167 const base::Time even_older = past_time - base::TimeDelta::FromSeconds(10); | |
| 168 EXPECT_TRUE(base::TouchFile(index_path, even_older, even_older)); | |
| 169 EXPECT_TRUE( | |
| 170 WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path)); | |
| 171 } | |
| 172 | |
| 173 TEST_F(SimpleIndexFileTest, WriteThenLoadIndex) { | |
| 174 base::ScopedTempDir cache_dir; | |
| 175 ASSERT_TRUE(cache_dir.CreateUniqueTempDir()); | |
| 176 | |
| 177 SimpleIndex::EntrySet entries; | |
| 178 static const uint64 kHashes[] = { 11, 22, 33 }; | |
| 179 static const size_t kNumHashes = arraysize(kHashes); | |
| 180 EntryMetadata metadata_entries[kNumHashes]; | |
| 181 for (size_t i = 0; i < kNumHashes; ++i) { | |
| 182 uint64 hash = kHashes[i]; | |
| 183 metadata_entries[i] = EntryMetadata(Time(), hash); | |
| 184 SimpleIndex::InsertInEntrySet(hash, metadata_entries[i], &entries); | |
| 185 } | |
| 186 | |
| 187 const uint64 kCacheSize = 456U; | |
| 188 net::TestClosure closure; | |
| 189 { | |
| 190 WrappedSimpleIndexFile simple_index_file(cache_dir.path()); | |
| 191 simple_index_file.WriteToDisk(entries, kCacheSize, base::TimeTicks(), | |
| 192 false, closure.closure()); | |
| 193 closure.WaitForResult(); | |
| 194 EXPECT_TRUE(base::PathExists(simple_index_file.GetIndexFilePath())); | |
| 195 } | |
| 196 | |
| 197 WrappedSimpleIndexFile simple_index_file(cache_dir.path()); | |
| 198 base::Time fake_cache_mtime; | |
| 199 ASSERT_TRUE(simple_util::GetMTime(cache_dir.path(), &fake_cache_mtime)); | |
| 200 SimpleIndexLoadResult load_index_result; | |
| 201 simple_index_file.LoadIndexEntries(fake_cache_mtime, closure.closure(), | |
| 202 &load_index_result); | |
| 203 closure.WaitForResult(); | |
| 204 | |
| 205 EXPECT_TRUE(base::PathExists(simple_index_file.GetIndexFilePath())); | |
| 206 EXPECT_TRUE(load_index_result.did_load); | |
| 207 EXPECT_FALSE(load_index_result.flush_required); | |
| 208 | |
| 209 EXPECT_EQ(kNumHashes, load_index_result.entries.size()); | |
| 210 for (size_t i = 0; i < kNumHashes; ++i) | |
| 211 EXPECT_EQ(1U, load_index_result.entries.count(kHashes[i])); | |
| 212 } | |
| 213 | |
| 214 TEST_F(SimpleIndexFileTest, LoadCorruptIndex) { | |
| 215 base::ScopedTempDir cache_dir; | |
| 216 ASSERT_TRUE(cache_dir.CreateUniqueTempDir()); | |
| 217 | |
| 218 WrappedSimpleIndexFile simple_index_file(cache_dir.path()); | |
| 219 ASSERT_TRUE(simple_index_file.CreateIndexFileDirectory()); | |
| 220 const base::FilePath& index_path = simple_index_file.GetIndexFilePath(); | |
| 221 const std::string kDummyData = "nothing to be seen here"; | |
| 222 EXPECT_EQ( | |
| 223 implicit_cast<int>(kDummyData.size()), | |
| 224 base::WriteFile(index_path, kDummyData.data(), kDummyData.size())); | |
| 225 base::Time fake_cache_mtime; | |
| 226 ASSERT_TRUE(simple_util::GetMTime(simple_index_file.GetIndexFilePath(), | |
| 227 &fake_cache_mtime)); | |
| 228 EXPECT_FALSE(WrappedSimpleIndexFile::LegacyIsIndexFileStale(fake_cache_mtime, | |
| 229 index_path)); | |
| 230 SimpleIndexLoadResult load_index_result; | |
| 231 net::TestClosure closure; | |
| 232 simple_index_file.LoadIndexEntries(fake_cache_mtime, closure.closure(), | |
| 233 &load_index_result); | |
| 234 closure.WaitForResult(); | |
| 235 | |
| 236 EXPECT_FALSE(base::PathExists(index_path)); | |
| 237 EXPECT_TRUE(load_index_result.did_load); | |
| 238 EXPECT_TRUE(load_index_result.flush_required); | |
| 239 } | |
| 240 | |
| 241 // Tests that after an upgrade the backend has the index file put in place. | |
| 242 TEST_F(SimpleIndexFileTest, SimpleCacheUpgrade) { | |
| 243 base::ScopedTempDir cache_dir; | |
| 244 ASSERT_TRUE(cache_dir.CreateUniqueTempDir()); | |
| 245 const base::FilePath cache_path = cache_dir.path(); | |
| 246 | |
| 247 // Write an old fake index file. | |
| 248 base::File file(cache_path.AppendASCII("index"), | |
| 249 base::File::FLAG_CREATE | base::File::FLAG_WRITE); | |
| 250 ASSERT_TRUE(file.IsValid()); | |
| 251 disk_cache::FakeIndexData file_contents; | |
| 252 file_contents.initial_magic_number = disk_cache::kSimpleInitialMagicNumber; | |
| 253 file_contents.version = 5; | |
| 254 int bytes_written = file.Write(0, reinterpret_cast<char*>(&file_contents), | |
| 255 sizeof(file_contents)); | |
| 256 ASSERT_EQ((int)sizeof(file_contents), bytes_written); | |
| 257 file.Close(); | |
| 258 | |
| 259 // Write the index file. The format is incorrect, but for transitioning from | |
| 260 // v5 it does not matter. | |
| 261 const std::string index_file_contents("incorrectly serialized data"); | |
| 262 const base::FilePath old_index_file = | |
| 263 cache_path.AppendASCII("the-real-index"); | |
| 264 ASSERT_EQ(implicit_cast<int>(index_file_contents.size()), | |
| 265 base::WriteFile(old_index_file, | |
| 266 index_file_contents.data(), | |
| 267 index_file_contents.size())); | |
| 268 | |
| 269 // Upgrade the cache. | |
| 270 ASSERT_TRUE(disk_cache::UpgradeSimpleCacheOnDisk(cache_path)); | |
| 271 | |
| 272 // Create the backend and initiate index flush by destroying the backend. | |
| 273 base::Thread cache_thread("CacheThread"); | |
| 274 ASSERT_TRUE(cache_thread.StartWithOptions( | |
| 275 base::Thread::Options(base::MessageLoop::TYPE_IO, 0))); | |
| 276 disk_cache::SimpleBackendImpl* simple_cache = | |
| 277 new disk_cache::SimpleBackendImpl(cache_path, | |
| 278 0, | |
| 279 net::DISK_CACHE, | |
| 280 cache_thread.message_loop_proxy().get(), | |
| 281 NULL); | |
| 282 net::TestCompletionCallback cb; | |
| 283 int rv = simple_cache->Init(cb.callback()); | |
| 284 EXPECT_EQ(net::OK, cb.GetResult(rv)); | |
| 285 rv = simple_cache->index()->ExecuteWhenReady(cb.callback()); | |
| 286 EXPECT_EQ(net::OK, cb.GetResult(rv)); | |
| 287 delete simple_cache; | |
| 288 | |
| 289 // The backend flushes the index on destruction and does so on the cache | |
| 290 // thread, wait for the flushing to finish by posting a callback to the cache | |
| 291 // thread after that. | |
| 292 MessageLoopHelper helper; | |
| 293 CallbackTest cb_shutdown(&helper, false); | |
| 294 cache_thread.message_loop_proxy()->PostTask( | |
| 295 FROM_HERE, | |
| 296 base::Bind(&CallbackTest::Run, base::Unretained(&cb_shutdown), net::OK)); | |
| 297 helper.WaitUntilCacheIoFinished(1); | |
| 298 | |
| 299 // Verify that the index file exists. | |
| 300 const base::FilePath& index_file_path = | |
| 301 cache_path.AppendASCII("index-dir").AppendASCII("the-real-index"); | |
| 302 EXPECT_TRUE(base::PathExists(index_file_path)); | |
| 303 | |
| 304 // Verify that the version of the index file is correct. | |
| 305 std::string contents; | |
| 306 EXPECT_TRUE(base::ReadFileToString(index_file_path, &contents)); | |
| 307 base::Time when_index_last_saw_cache; | |
| 308 SimpleIndexLoadResult deserialize_result; | |
| 309 WrappedSimpleIndexFile::Deserialize(contents.data(), | |
| 310 contents.size(), | |
| 311 &when_index_last_saw_cache, | |
| 312 &deserialize_result); | |
| 313 EXPECT_TRUE(deserialize_result.did_load); | |
| 314 } | |
| 315 | |
| 316 #endif // defined(OS_POSIX) | |
| 317 | |
| 318 } // namespace disk_cache | |
| OLD | NEW |