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