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/files/file.h"
6 #include "base/files/file_util.h"
7 #include "base/files/scoped_temp_dir.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"
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::ThreadTaskRunnerHandle::Get(),
79 base::ThreadTaskRunnerHandle::Get(),
81 index_file_directory
) {}
82 ~WrappedSimpleIndexFile() override
{}
84 const base::FilePath
& GetIndexFilePath() const {
88 bool CreateIndexFileDirectory() const {
89 return base::CreateDirectory(index_file_
.DirName());
93 class SimpleIndexFileTest
: public testing::Test
{
95 bool CompareTwoEntryMetadata(const EntryMetadata
& a
, const EntryMetadata
& b
) {
97 a
.last_used_time_seconds_since_epoch_
==
98 b
.last_used_time_seconds_since_epoch_
&&
99 a
.entry_size_
== b
.entry_size_
;
103 SimpleIndexFileTest() : callback_called_(false) {}
105 base::Closure
GetCallback() {
106 return base::Bind(&SimpleIndexFileTest::LoadIndexEntriesCallback
,
107 base::Unretained(this));
110 bool callback_called() { return callback_called_
; }
113 void LoadIndexEntriesCallback() {
114 EXPECT_FALSE(callback_called_
);
115 callback_called_
= true;
118 bool callback_called_
;
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
];
127 SimpleIndexFile::IndexMetadata
index_metadata(static_cast<uint64
>(kNumHashes
),
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
);
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()),
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());
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
]));
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();
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();
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 base::WriteFile(index_path
,
173 kDummyData
.data(), kDummyData
.size()));
174 ASSERT_TRUE(simple_util::GetMTime(cache_path
, &cache_mtime
));
176 WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime
, index_path
));
178 const base::Time past_time
= base::Time::Now() -
179 base::TimeDelta::FromSeconds(10);
180 EXPECT_TRUE(base::TouchFile(index_path
, past_time
, past_time
));
181 EXPECT_TRUE(base::TouchFile(cache_path
, past_time
, past_time
));
182 ASSERT_TRUE(simple_util::GetMTime(cache_path
, &cache_mtime
));
184 WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime
, index_path
));
185 const base::Time even_older
= past_time
- base::TimeDelta::FromSeconds(10);
186 EXPECT_TRUE(base::TouchFile(index_path
, even_older
, even_older
));
188 WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime
, index_path
));
191 // This test is flaky, see http://crbug.com/255775.
192 TEST_F(SimpleIndexFileTest
, DISABLED_WriteThenLoadIndex
) {
193 base::ScopedTempDir cache_dir
;
194 ASSERT_TRUE(cache_dir
.CreateUniqueTempDir());
196 SimpleIndex::EntrySet entries
;
197 static const uint64 kHashes
[] = { 11, 22, 33 };
198 static const size_t kNumHashes
= arraysize(kHashes
);
199 EntryMetadata metadata_entries
[kNumHashes
];
200 for (size_t i
= 0; i
< kNumHashes
; ++i
) {
201 uint64 hash
= kHashes
[i
];
202 metadata_entries
[i
] = EntryMetadata(Time(), hash
);
203 SimpleIndex::InsertInEntrySet(hash
, metadata_entries
[i
], &entries
);
206 const uint64 kCacheSize
= 456U;
208 WrappedSimpleIndexFile
simple_index_file(cache_dir
.path());
209 simple_index_file
.WriteToDisk(entries
, kCacheSize
,
210 base::TimeTicks(), false);
211 base::RunLoop().RunUntilIdle();
212 EXPECT_TRUE(base::PathExists(simple_index_file
.GetIndexFilePath()));
215 WrappedSimpleIndexFile
simple_index_file(cache_dir
.path());
216 base::Time fake_cache_mtime
;
217 ASSERT_TRUE(simple_util::GetMTime(simple_index_file
.GetIndexFilePath(),
219 SimpleIndexLoadResult load_index_result
;
220 simple_index_file
.LoadIndexEntries(fake_cache_mtime
,
223 base::RunLoop().RunUntilIdle();
225 EXPECT_TRUE(base::PathExists(simple_index_file
.GetIndexFilePath()));
226 ASSERT_TRUE(callback_called());
227 EXPECT_TRUE(load_index_result
.did_load
);
228 EXPECT_FALSE(load_index_result
.flush_required
);
230 EXPECT_EQ(kNumHashes
, load_index_result
.entries
.size());
231 for (size_t i
= 0; i
< kNumHashes
; ++i
)
232 EXPECT_EQ(1U, load_index_result
.entries
.count(kHashes
[i
]));
235 TEST_F(SimpleIndexFileTest
, LoadCorruptIndex
) {
236 base::ScopedTempDir cache_dir
;
237 ASSERT_TRUE(cache_dir
.CreateUniqueTempDir());
239 WrappedSimpleIndexFile
simple_index_file(cache_dir
.path());
240 ASSERT_TRUE(simple_index_file
.CreateIndexFileDirectory());
241 const base::FilePath
& index_path
= simple_index_file
.GetIndexFilePath();
242 const std::string kDummyData
= "nothing to be seen here";
244 implicit_cast
<int>(kDummyData
.size()),
245 base::WriteFile(index_path
, kDummyData
.data(), kDummyData
.size()));
246 base::Time fake_cache_mtime
;
247 ASSERT_TRUE(simple_util::GetMTime(simple_index_file
.GetIndexFilePath(),
249 EXPECT_FALSE(WrappedSimpleIndexFile::LegacyIsIndexFileStale(fake_cache_mtime
,
252 SimpleIndexLoadResult load_index_result
;
253 simple_index_file
.LoadIndexEntries(fake_cache_mtime
,
256 base::RunLoop().RunUntilIdle();
258 EXPECT_FALSE(base::PathExists(index_path
));
259 ASSERT_TRUE(callback_called());
260 EXPECT_TRUE(load_index_result
.did_load
);
261 EXPECT_TRUE(load_index_result
.flush_required
);
264 // Tests that after an upgrade the backend has the index file put in place.
265 TEST_F(SimpleIndexFileTest
, SimpleCacheUpgrade
) {
266 base::ScopedTempDir cache_dir
;
267 ASSERT_TRUE(cache_dir
.CreateUniqueTempDir());
268 const base::FilePath cache_path
= cache_dir
.path();
270 // Write an old fake index file.
271 base::File
file(cache_path
.AppendASCII("index"),
272 base::File::FLAG_CREATE
| base::File::FLAG_WRITE
);
273 ASSERT_TRUE(file
.IsValid());
274 disk_cache::FakeIndexData file_contents
;
275 file_contents
.initial_magic_number
= disk_cache::kSimpleInitialMagicNumber
;
276 file_contents
.version
= 5;
277 int bytes_written
= file
.Write(0, reinterpret_cast<char*>(&file_contents
),
278 sizeof(file_contents
));
279 ASSERT_EQ((int)sizeof(file_contents
), bytes_written
);
282 // Write the index file. The format is incorrect, but for transitioning from
283 // v5 it does not matter.
284 const std::string
index_file_contents("incorrectly serialized data");
285 const base::FilePath old_index_file
=
286 cache_path
.AppendASCII("the-real-index");
287 ASSERT_EQ(implicit_cast
<int>(index_file_contents
.size()),
288 base::WriteFile(old_index_file
,
289 index_file_contents
.data(),
290 index_file_contents
.size()));
292 // Upgrade the cache.
293 ASSERT_TRUE(disk_cache::UpgradeSimpleCacheOnDisk(cache_path
));
295 // Create the backend and initiate index flush by destroying the backend.
296 base::Thread
cache_thread("CacheThread");
297 ASSERT_TRUE(cache_thread
.StartWithOptions(
298 base::Thread::Options(base::MessageLoop::TYPE_IO
, 0)));
299 disk_cache::SimpleBackendImpl
* simple_cache
=
300 new disk_cache::SimpleBackendImpl(cache_path
,
303 cache_thread
.message_loop_proxy().get(),
305 net::TestCompletionCallback cb
;
306 int rv
= simple_cache
->Init(cb
.callback());
307 EXPECT_EQ(net::OK
, cb
.GetResult(rv
));
308 rv
= simple_cache
->index()->ExecuteWhenReady(cb
.callback());
309 EXPECT_EQ(net::OK
, cb
.GetResult(rv
));
312 // The backend flushes the index on destruction and does so on the cache
313 // thread, wait for the flushing to finish by posting a callback to the cache
314 // thread after that.
315 MessageLoopHelper helper
;
316 CallbackTest
cb_shutdown(&helper
, false);
317 cache_thread
.message_loop_proxy()->PostTask(
319 base::Bind(&CallbackTest::Run
, base::Unretained(&cb_shutdown
), net::OK
));
320 helper
.WaitUntilCacheIoFinished(1);
322 // Verify that the index file exists.
323 const base::FilePath
& index_file_path
=
324 cache_path
.AppendASCII("index-dir").AppendASCII("the-real-index");
325 EXPECT_TRUE(base::PathExists(index_file_path
));
327 // Verify that the version of the index file is correct.
328 std::string contents
;
329 EXPECT_TRUE(base::ReadFileToString(index_file_path
, &contents
));
330 base::Time when_index_last_saw_cache
;
331 SimpleIndexLoadResult deserialize_result
;
332 WrappedSimpleIndexFile::Deserialize(contents
.data(),
334 &when_index_last_saw_cache
,
335 &deserialize_result
);
336 EXPECT_TRUE(deserialize_result
.did_load
);
339 #endif // defined(OS_POSIX)
341 } // namespace disk_cache