Home | History | Annotate | Download | only in simple
      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 
     28 using base::Time;
     29 using disk_cache::SimpleIndexFile;
     30 using disk_cache::SimpleIndex;
     31 
     32 namespace 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 
     39 TEST(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 
     50 TEST(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.
     69 class 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 
     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  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 
    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];
    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 
    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();
    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.
    193 TEST_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 
    236 TEST_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.
    266 TEST_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