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 |