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.
5 #include "base/file_util.h"
6 #include "base/files/file.h"
7 #include "base/files/scoped_temp_dir.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"
30 using disk_cache::SimpleIndexFile;
31 using disk_cache::SimpleIndex;
33 namespace disk_cache {
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.
40 TEST(IndexMetadataTest, Basics) {
41 SimpleIndexFile::IndexMetadata index_metadata;
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_);
48 EXPECT_TRUE(index_metadata.CheckIndexMetadata());
51 TEST(IndexMetadataTest, Serialize) {
52 SimpleIndexFile::IndexMetadata index_metadata(123, 456);
54 index_metadata.Serialize(&pickle);
55 PickleIterator it(pickle);
56 SimpleIndexFile::IndexMetadata new_index_metadata;
57 new_index_metadata.Deserialize(&it);
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_);
65 EXPECT_TRUE(new_index_metadata.CheckIndexMetadata());
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 {
72 using SimpleIndexFile::Deserialize;
73 using SimpleIndexFile::LegacyIsIndexFileStale;
74 using SimpleIndexFile::Serialize;
75 using SimpleIndexFile::SerializeFinalData;
77 explicit WrappedSimpleIndexFile(const base::FilePath& index_file_directory)
78 : SimpleIndexFile(base::MessageLoopProxy::current().get(),
79 base::MessageLoopProxy::current().get(),
81 index_file_directory) {}
82 virtual ~WrappedSimpleIndexFile() {
85 const base::FilePath& GetIndexFilePath() const {
89 bool CreateIndexFileDirectory() const {
90 return base::CreateDirectory(index_file_.DirName());
94 class SimpleIndexFileTest : public testing::Test {
96 bool CompareTwoEntryMetadata(const EntryMetadata& a, const EntryMetadata& b) {
98 a.last_used_time_seconds_since_epoch_ ==
99 b.last_used_time_seconds_since_epoch_ &&
100 a.entry_size_ == b.entry_size_;
104 SimpleIndexFileTest() : callback_called_(false) {}
106 base::Closure GetCallback() {
107 return base::Bind(&SimpleIndexFileTest::LoadIndexEntriesCallback,
108 base::Unretained(this));
111 bool callback_called() { return callback_called_; }
114 void LoadIndexEntriesCallback() {
115 EXPECT_FALSE(callback_called_);
116 callback_called_ = true;
119 bool callback_called_;
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];
128 SimpleIndexFile::IndexMetadata index_metadata(static_cast<uint64>(kNumHashes),
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);
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()),
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());
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]));
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();
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();
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,
176 ASSERT_TRUE(simple_util::GetMTime(cache_path, &cache_mtime));
178 WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path));
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));
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));
190 WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path));
193 // This test is flaky, see http://crbug.com/255775.
194 TEST_F(SimpleIndexFileTest, DISABLED_WriteThenLoadIndex) {
195 base::ScopedTempDir cache_dir;
196 ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
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);
208 const uint64 kCacheSize = 456U;
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()));
217 WrappedSimpleIndexFile simple_index_file(cache_dir.path());
218 base::Time fake_cache_mtime;
219 ASSERT_TRUE(simple_util::GetMTime(simple_index_file.GetIndexFilePath(),
221 SimpleIndexLoadResult load_index_result;
222 simple_index_file.LoadIndexEntries(fake_cache_mtime,
225 base::RunLoop().RunUntilIdle();
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);
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]));
237 TEST_F(SimpleIndexFileTest, LoadCorruptIndex) {
238 base::ScopedTempDir cache_dir;
239 ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
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";
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(),
251 EXPECT_FALSE(WrappedSimpleIndexFile::LegacyIsIndexFileStale(fake_cache_mtime,
254 SimpleIndexLoadResult load_index_result;
255 simple_index_file.LoadIndexEntries(fake_cache_mtime,
258 base::RunLoop().RunUntilIdle();
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);
266 // Tests that after an upgrade the backend has the index file put in place.
267 TEST_F(SimpleIndexFileTest, SimpleCacheUpgrade) {
268 base::ScopedTempDir cache_dir;
269 ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
270 const base::FilePath cache_path = cache_dir.path();
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);
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()));
294 // Upgrade the cache.
295 ASSERT_TRUE(disk_cache::UpgradeSimpleCacheOnDisk(cache_path));
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,
305 cache_thread.message_loop_proxy().get(),
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));
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(
321 base::Bind(&CallbackTest::Run, base::Unretained(&cb_shutdown), net::OK));
322 helper.WaitUntilCacheIoFinished(1);
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));
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(),
336 &when_index_last_saw_cache,
337 &deserialize_result);
338 EXPECT_TRUE(deserialize_result.did_load);
341 #endif // defined(OS_POSIX)
343 } // namespace disk_cache