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/scoped_temp_dir.h"
7#include "base/hash.h"
8#include "base/logging.h"
9#include "base/memory/scoped_ptr.h"
10#include "base/message_loop/message_loop_proxy.h"
11#include "base/pickle.h"
12#include "base/run_loop.h"
13#include "base/strings/stringprintf.h"
14#include "base/threading/thread.h"
15#include "base/time/time.h"
16#include "net/base/cache_type.h"
17#include "net/base/test_completion_callback.h"
18#include "net/disk_cache/disk_cache_test_util.h"
19#include "net/disk_cache/simple/simple_backend_impl.h"
20#include "net/disk_cache/simple/simple_backend_version.h"
21#include "net/disk_cache/simple/simple_entry_format.h"
22#include "net/disk_cache/simple/simple_index.h"
23#include "net/disk_cache/simple/simple_index_file.h"
24#include "net/disk_cache/simple/simple_util.h"
25#include "net/disk_cache/simple/simple_version_upgrade.h"
26#include "testing/gtest/include/gtest/gtest.h"
27
28using base::Time;
29using disk_cache::SimpleIndexFile;
30using disk_cache::SimpleIndex;
31
32namespace disk_cache {
33
34// The Simple Cache backend requires a few guarantees from the filesystem like
35// atomic renaming of recently open files. Those guarantees are not provided in
36// general on Windows.
37#if defined(OS_POSIX)
38
39TEST(IndexMetadataTest, Basics) {
40  SimpleIndexFile::IndexMetadata index_metadata;
41
42  EXPECT_EQ(disk_cache::kSimpleIndexMagicNumber, index_metadata.magic_number_);
43  EXPECT_EQ(disk_cache::kSimpleVersion, index_metadata.version_);
44  EXPECT_EQ(0U, index_metadata.GetNumberOfEntries());
45  EXPECT_EQ(0U, index_metadata.cache_size_);
46
47  EXPECT_TRUE(index_metadata.CheckIndexMetadata());
48}
49
50TEST(IndexMetadataTest, Serialize) {
51  SimpleIndexFile::IndexMetadata index_metadata(123, 456);
52  Pickle pickle;
53  index_metadata.Serialize(&pickle);
54  PickleIterator it(pickle);
55  SimpleIndexFile::IndexMetadata new_index_metadata;
56  new_index_metadata.Deserialize(&it);
57
58  EXPECT_EQ(new_index_metadata.magic_number_, index_metadata.magic_number_);
59  EXPECT_EQ(new_index_metadata.version_, index_metadata.version_);
60  EXPECT_EQ(new_index_metadata.GetNumberOfEntries(),
61            index_metadata.GetNumberOfEntries());
62  EXPECT_EQ(new_index_metadata.cache_size_, index_metadata.cache_size_);
63
64  EXPECT_TRUE(new_index_metadata.CheckIndexMetadata());
65}
66
67// This friend derived class is able to reexport its ancestors private methods
68// as public, for use in tests.
69class WrappedSimpleIndexFile : public SimpleIndexFile {
70 public:
71  using SimpleIndexFile::Deserialize;
72  using SimpleIndexFile::LegacyIsIndexFileStale;
73  using SimpleIndexFile::Serialize;
74  using SimpleIndexFile::SerializeFinalData;
75
76  explicit WrappedSimpleIndexFile(const base::FilePath& index_file_directory)
77      : SimpleIndexFile(base::MessageLoopProxy::current().get(),
78                        base::MessageLoopProxy::current().get(),
79                        net::DISK_CACHE,
80                        index_file_directory) {}
81  virtual ~WrappedSimpleIndexFile() {
82  }
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
93class 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 protected:
103  SimpleIndexFileTest() : callback_called_(false) {}
104
105  base::Closure GetCallback() {
106    return base::Bind(&SimpleIndexFileTest::LoadIndexEntriesCallback,
107                      base::Unretained(this));
108  }
109
110  bool callback_called() { return callback_called_; }
111
112 private:
113  void LoadIndexEntriesCallback() {
114    EXPECT_FALSE(callback_called_);
115    callback_called_ = true;
116  }
117
118  bool callback_called_;
119};
120
121TEST_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];
126
127  SimpleIndexFile::IndexMetadata index_metadata(static_cast<uint64>(kNumHashes),
128                                                456);
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);
133  }
134
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()),
143                                      pickle->size(),
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());
150
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]));
155  }
156}
157
158TEST_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();
163
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();
168  EXPECT_TRUE(
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            file_util::WriteFile(index_path,
173                                 kDummyData.data(),
174                                 kDummyData.size()));
175  ASSERT_TRUE(simple_util::GetMTime(cache_path, &cache_mtime));
176  EXPECT_FALSE(
177      WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path));
178
179  const base::Time past_time = base::Time::Now() -
180      base::TimeDelta::FromSeconds(10);
181  EXPECT_TRUE(base::TouchFile(index_path, past_time, past_time));
182  EXPECT_TRUE(base::TouchFile(cache_path, past_time, past_time));
183  ASSERT_TRUE(simple_util::GetMTime(cache_path, &cache_mtime));
184  EXPECT_FALSE(
185      WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path));
186  const base::Time even_older = past_time - base::TimeDelta::FromSeconds(10);
187  EXPECT_TRUE(base::TouchFile(index_path, even_older, even_older));
188  EXPECT_TRUE(
189      WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path));
190}
191
192// This test is flaky, see http://crbug.com/255775.
193TEST_F(SimpleIndexFileTest, DISABLED_WriteThenLoadIndex) {
194  base::ScopedTempDir cache_dir;
195  ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
196
197  SimpleIndex::EntrySet entries;
198  static const uint64 kHashes[] = { 11, 22, 33 };
199  static const size_t kNumHashes = arraysize(kHashes);
200  EntryMetadata metadata_entries[kNumHashes];
201  for (size_t i = 0; i < kNumHashes; ++i) {
202    uint64 hash = kHashes[i];
203    metadata_entries[i] = EntryMetadata(Time(), hash);
204    SimpleIndex::InsertInEntrySet(hash, metadata_entries[i], &entries);
205  }
206
207  const uint64 kCacheSize = 456U;
208  {
209    WrappedSimpleIndexFile simple_index_file(cache_dir.path());
210    simple_index_file.WriteToDisk(entries, kCacheSize,
211                                  base::TimeTicks(), false);
212    base::RunLoop().RunUntilIdle();
213    EXPECT_TRUE(base::PathExists(simple_index_file.GetIndexFilePath()));
214  }
215
216  WrappedSimpleIndexFile simple_index_file(cache_dir.path());
217  base::Time fake_cache_mtime;
218  ASSERT_TRUE(simple_util::GetMTime(simple_index_file.GetIndexFilePath(),
219                                    &fake_cache_mtime));
220  SimpleIndexLoadResult load_index_result;
221  simple_index_file.LoadIndexEntries(fake_cache_mtime,
222                                     GetCallback(),
223                                     &load_index_result);
224  base::RunLoop().RunUntilIdle();
225
226  EXPECT_TRUE(base::PathExists(simple_index_file.GetIndexFilePath()));
227  ASSERT_TRUE(callback_called());
228  EXPECT_TRUE(load_index_result.did_load);
229  EXPECT_FALSE(load_index_result.flush_required);
230
231  EXPECT_EQ(kNumHashes, load_index_result.entries.size());
232  for (size_t i = 0; i < kNumHashes; ++i)
233    EXPECT_EQ(1U, load_index_result.entries.count(kHashes[i]));
234}
235
236TEST_F(SimpleIndexFileTest, LoadCorruptIndex) {
237  base::ScopedTempDir cache_dir;
238  ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
239
240  WrappedSimpleIndexFile simple_index_file(cache_dir.path());
241  ASSERT_TRUE(simple_index_file.CreateIndexFileDirectory());
242  const base::FilePath& index_path = simple_index_file.GetIndexFilePath();
243  const std::string kDummyData = "nothing to be seen here";
244  EXPECT_EQ(
245      implicit_cast<int>(kDummyData.size()),
246      file_util::WriteFile(index_path, kDummyData.data(), kDummyData.size()));
247  base::Time fake_cache_mtime;
248  ASSERT_TRUE(simple_util::GetMTime(simple_index_file.GetIndexFilePath(),
249                                    &fake_cache_mtime));
250  EXPECT_FALSE(WrappedSimpleIndexFile::LegacyIsIndexFileStale(fake_cache_mtime,
251                                                              index_path));
252
253  SimpleIndexLoadResult load_index_result;
254  simple_index_file.LoadIndexEntries(fake_cache_mtime,
255                                     GetCallback(),
256                                     &load_index_result);
257  base::RunLoop().RunUntilIdle();
258
259  EXPECT_FALSE(base::PathExists(index_path));
260  ASSERT_TRUE(callback_called());
261  EXPECT_TRUE(load_index_result.did_load);
262  EXPECT_TRUE(load_index_result.flush_required);
263}
264
265// Tests that after an upgrade the backend has the index file put in place.
266TEST_F(SimpleIndexFileTest, SimpleCacheUpgrade) {
267  base::ScopedTempDir cache_dir;
268  ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
269  const base::FilePath cache_path = cache_dir.path();
270
271  // Write an old fake index file.
272  base::PlatformFileError error;
273  base::PlatformFile file = base::CreatePlatformFile(
274      cache_path.AppendASCII("index"),
275      base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE,
276      NULL,
277      &error);
278  disk_cache::FakeIndexData file_contents;
279  file_contents.initial_magic_number = disk_cache::kSimpleInitialMagicNumber;
280  file_contents.version = 5;
281  int bytes_written = base::WritePlatformFile(
282      file, 0, reinterpret_cast<char*>(&file_contents), sizeof(file_contents));
283  ASSERT_TRUE(base::ClosePlatformFile(file));
284  ASSERT_EQ((int)sizeof(file_contents), bytes_written);
285
286  // Write the index file. The format is incorrect, but for transitioning from
287  // v5 it does not matter.
288  const std::string index_file_contents("incorrectly serialized data");
289  const base::FilePath old_index_file =
290      cache_path.AppendASCII("the-real-index");
291  ASSERT_EQ(implicit_cast<int>(index_file_contents.size()),
292            file_util::WriteFile(old_index_file,
293                                 index_file_contents.data(),
294                                 index_file_contents.size()));
295
296  // Upgrade the cache.
297  ASSERT_TRUE(disk_cache::UpgradeSimpleCacheOnDisk(cache_path));
298
299  // Create the backend and initiate index flush by destroying the backend.
300  base::Thread cache_thread("CacheThread");
301  ASSERT_TRUE(cache_thread.StartWithOptions(
302      base::Thread::Options(base::MessageLoop::TYPE_IO, 0)));
303  disk_cache::SimpleBackendImpl* simple_cache =
304      new disk_cache::SimpleBackendImpl(cache_path,
305                                        0,
306                                        net::DISK_CACHE,
307                                        cache_thread.message_loop_proxy().get(),
308                                        NULL);
309  net::TestCompletionCallback cb;
310  int rv = simple_cache->Init(cb.callback());
311  EXPECT_EQ(net::OK, cb.GetResult(rv));
312  rv = simple_cache->index()->ExecuteWhenReady(cb.callback());
313  EXPECT_EQ(net::OK, cb.GetResult(rv));
314  delete simple_cache;
315
316  // The backend flushes the index on destruction and does so on the cache
317  // thread, wait for the flushing to finish by posting a callback to the cache
318  // thread after that.
319  MessageLoopHelper helper;
320  CallbackTest cb_shutdown(&helper, false);
321  cache_thread.message_loop_proxy()->PostTask(
322      FROM_HERE,
323      base::Bind(&CallbackTest::Run, base::Unretained(&cb_shutdown), net::OK));
324  helper.WaitUntilCacheIoFinished(1);
325
326  // Verify that the index file exists.
327  const base::FilePath& index_file_path =
328      cache_path.AppendASCII("index-dir").AppendASCII("the-real-index");
329  EXPECT_TRUE(base::PathExists(index_file_path));
330
331  // Verify that the version of the index file is correct.
332  std::string contents;
333  EXPECT_TRUE(base::ReadFileToString(index_file_path, &contents));
334  base::Time when_index_last_saw_cache;
335  SimpleIndexLoadResult deserialize_result;
336  WrappedSimpleIndexFile::Deserialize(contents.data(),
337                                      contents.size(),
338                                      &when_index_last_saw_cache,
339                                      &deserialize_result);
340  EXPECT_TRUE(deserialize_result.did_load);
341}
342
343#endif  // defined(OS_POSIX)
344
345}  // namespace disk_cache
346