Home | History | Annotate | Download | only in drive
      1 // Copyright (c) 2012 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 "chrome/browser/chromeos/drive/file_cache.h"
      6 
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "base/file_util.h"
     11 #include "base/files/file_enumerator.h"
     12 #include "base/files/scoped_temp_dir.h"
     13 #include "base/md5.h"
     14 #include "base/path_service.h"
     15 #include "base/run_loop.h"
     16 #include "base/threading/sequenced_worker_pool.h"
     17 #include "chrome/browser/chromeos/drive/drive.pb.h"
     18 #include "chrome/browser/chromeos/drive/fake_free_disk_space_getter.h"
     19 #include "chrome/browser/chromeos/drive/file_system_util.h"
     20 #include "chrome/browser/chromeos/drive/resource_metadata_storage.h"
     21 #include "chrome/browser/chromeos/drive/test_util.h"
     22 #include "content/public/browser/browser_thread.h"
     23 #include "content/public/test/test_browser_thread_bundle.h"
     24 #include "google_apis/drive/test_util.h"
     25 #include "testing/gtest/include/gtest/gtest.h"
     26 
     27 namespace drive {
     28 namespace internal {
     29 namespace {
     30 
     31 const char kCacheFileDirectory[] = "files";
     32 
     33 // Bitmask of cache states in FileCacheEntry.
     34 enum TestFileCacheState {
     35   TEST_CACHE_STATE_NONE       = 0,
     36   TEST_CACHE_STATE_PINNED     = 1 << 0,
     37   TEST_CACHE_STATE_PRESENT    = 1 << 1,
     38   TEST_CACHE_STATE_DIRTY      = 1 << 2,
     39 };
     40 
     41 }  // namespace
     42 
     43 // Tests FileCache methods from UI thread. It internally uses a real blocking
     44 // pool and tests the interaction among threads.
     45 // TODO(hashimoto): remove this class. crbug.com/231221.
     46 class FileCacheTestOnUIThread : public testing::Test {
     47  protected:
     48   FileCacheTestOnUIThread() : expected_error_(FILE_ERROR_OK),
     49                               expected_cache_state_(0) {
     50   }
     51 
     52   virtual void SetUp() OVERRIDE {
     53     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     54     const base::FilePath metadata_dir = temp_dir_.path().AppendASCII("meta");
     55     const base::FilePath cache_dir =
     56         temp_dir_.path().AppendASCII(kCacheFileDirectory);
     57 
     58     ASSERT_TRUE(base::CreateDirectory(metadata_dir));
     59     ASSERT_TRUE(base::CreateDirectory(cache_dir));
     60 
     61     ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(),
     62                                                &dummy_file_path_));
     63     fake_free_disk_space_getter_.reset(new FakeFreeDiskSpaceGetter);
     64 
     65     scoped_refptr<base::SequencedWorkerPool> pool =
     66         content::BrowserThread::GetBlockingPool();
     67     blocking_task_runner_ =
     68         pool->GetSequencedTaskRunner(pool->GetSequenceToken());
     69 
     70     metadata_storage_.reset(new ResourceMetadataStorage(
     71         metadata_dir,
     72         blocking_task_runner_.get()));
     73 
     74     bool success = false;
     75     base::PostTaskAndReplyWithResult(
     76         blocking_task_runner_.get(),
     77         FROM_HERE,
     78         base::Bind(&ResourceMetadataStorage::Initialize,
     79                    base::Unretained(metadata_storage_.get())),
     80         google_apis::test_util::CreateCopyResultCallback(&success));
     81     test_util::RunBlockingPoolTask();
     82     ASSERT_TRUE(success);
     83 
     84     cache_.reset(new FileCache(
     85         metadata_storage_.get(),
     86         cache_dir,
     87         blocking_task_runner_.get(),
     88         fake_free_disk_space_getter_.get()));
     89 
     90     success = false;
     91     base::PostTaskAndReplyWithResult(
     92         blocking_task_runner_.get(),
     93         FROM_HERE,
     94         base::Bind(&FileCache::Initialize, base::Unretained(cache_.get())),
     95         google_apis::test_util::CreateCopyResultCallback(&success));
     96     test_util::RunBlockingPoolTask();
     97     ASSERT_TRUE(success);
     98   }
     99 
    100   void TestStoreToCache(const std::string& id,
    101                         const std::string& md5,
    102                         const base::FilePath& source_path,
    103                         FileError expected_error,
    104                         int expected_cache_state) {
    105     expected_error_ = expected_error;
    106     expected_cache_state_ = expected_cache_state;
    107 
    108     FileError error = FILE_ERROR_OK;
    109     base::PostTaskAndReplyWithResult(
    110         blocking_task_runner_,
    111         FROM_HERE,
    112         base::Bind(&internal::FileCache::Store,
    113                    base::Unretained(cache_.get()),
    114                    id, md5, source_path,
    115                    FileCache::FILE_OPERATION_COPY),
    116         google_apis::test_util::CreateCopyResultCallback(&error));
    117     test_util::RunBlockingPoolTask();
    118 
    119     if (error == FILE_ERROR_OK) {
    120       FileCacheEntry cache_entry;
    121       EXPECT_TRUE(GetCacheEntryFromOriginThread(id, &cache_entry));
    122       EXPECT_EQ(md5, cache_entry.md5());
    123     }
    124 
    125     VerifyCacheFileState(error, id);
    126   }
    127 
    128   void TestRemoveFromCache(const std::string& id, FileError expected_error) {
    129     expected_error_ = expected_error;
    130 
    131     FileError error = FILE_ERROR_OK;
    132     base::PostTaskAndReplyWithResult(
    133         blocking_task_runner_,
    134         FROM_HERE,
    135         base::Bind(&internal::FileCache::Remove,
    136                    base::Unretained(cache_.get()),
    137                    id),
    138         google_apis::test_util::CreateCopyResultCallback(&error));
    139     test_util::RunBlockingPoolTask();
    140     VerifyRemoveFromCache(error, id);
    141   }
    142 
    143   void VerifyRemoveFromCache(FileError error, const std::string& id) {
    144     EXPECT_EQ(expected_error_, error);
    145 
    146     FileCacheEntry cache_entry;
    147     if (!GetCacheEntryFromOriginThread(id, &cache_entry)) {
    148       EXPECT_EQ(FILE_ERROR_OK, error);
    149 
    150       const base::FilePath path = cache_->GetCacheFilePath(id);
    151       EXPECT_FALSE(base::PathExists(path));
    152     }
    153   }
    154 
    155   void TestPin(const std::string& id,
    156                FileError expected_error,
    157                int expected_cache_state) {
    158     expected_error_ = expected_error;
    159     expected_cache_state_ = expected_cache_state;
    160 
    161     FileError error = FILE_ERROR_OK;
    162     base::PostTaskAndReplyWithResult(
    163         blocking_task_runner_,
    164         FROM_HERE,
    165         base::Bind(&internal::FileCache::Pin,
    166                    base::Unretained(cache_.get()),
    167                    id),
    168         google_apis::test_util::CreateCopyResultCallback(&error));
    169     test_util::RunBlockingPoolTask();
    170     VerifyCacheFileState(error, id);
    171   }
    172 
    173   void TestUnpin(const std::string& id,
    174                  FileError expected_error,
    175                  int expected_cache_state) {
    176     expected_error_ = expected_error;
    177     expected_cache_state_ = expected_cache_state;
    178 
    179     FileError error = FILE_ERROR_OK;
    180     base::PostTaskAndReplyWithResult(
    181         blocking_task_runner_,
    182         FROM_HERE,
    183         base::Bind(&internal::FileCache::Unpin,
    184                    base::Unretained(cache_.get()),
    185                    id),
    186         google_apis::test_util::CreateCopyResultCallback(&error));
    187     test_util::RunBlockingPoolTask();
    188     VerifyCacheFileState(error, id);
    189   }
    190 
    191   void TestMarkDirty(const std::string& id,
    192                      FileError expected_error,
    193                      int expected_cache_state) {
    194     expected_error_ = expected_error;
    195     expected_cache_state_ = expected_cache_state;
    196 
    197     FileError error = FILE_ERROR_OK;
    198     base::PostTaskAndReplyWithResult(
    199         blocking_task_runner_,
    200         FROM_HERE,
    201         base::Bind(&internal::FileCache::MarkDirty,
    202                    base::Unretained(cache_.get()),
    203                    id),
    204         google_apis::test_util::CreateCopyResultCallback(&error));
    205     test_util::RunBlockingPoolTask();
    206 
    207     VerifyCacheFileState(error, id);
    208 
    209     // Verify filename.
    210     if (error == FILE_ERROR_OK) {
    211       base::FilePath cache_file_path;
    212       base::PostTaskAndReplyWithResult(
    213           blocking_task_runner_,
    214           FROM_HERE,
    215           base::Bind(&FileCache::GetFile,
    216                      base::Unretained(cache_.get()),
    217                      id, &cache_file_path),
    218           google_apis::test_util::CreateCopyResultCallback(&error));
    219       test_util::RunBlockingPoolTask();
    220 
    221       EXPECT_EQ(FILE_ERROR_OK, error);
    222       EXPECT_EQ(util::EscapeCacheFileName(id),
    223                 cache_file_path.BaseName().AsUTF8Unsafe());
    224     }
    225   }
    226 
    227   void TestClearDirty(const std::string& id,
    228                       const std::string& md5,
    229                       FileError expected_error,
    230                       int expected_cache_state) {
    231     expected_error_ = expected_error;
    232     expected_cache_state_ = expected_cache_state;
    233 
    234     FileError error = FILE_ERROR_OK;
    235     base::PostTaskAndReplyWithResult(
    236         blocking_task_runner_.get(),
    237         FROM_HERE,
    238         base::Bind(&FileCache::ClearDirty,
    239                    base::Unretained(cache_.get()),
    240                    id,
    241                    md5),
    242         google_apis::test_util::CreateCopyResultCallback(&error));
    243     test_util::RunBlockingPoolTask();
    244 
    245     if (error == FILE_ERROR_OK) {
    246       FileCacheEntry cache_entry;
    247       EXPECT_TRUE(GetCacheEntryFromOriginThread(id, &cache_entry));
    248       EXPECT_EQ(md5, cache_entry.md5());
    249     }
    250 
    251     VerifyCacheFileState(error, id);
    252   }
    253 
    254   void TestMarkAsMounted(const std::string& id,
    255                          FileError expected_error,
    256                          int expected_cache_state) {
    257     expected_error_ = expected_error;
    258     expected_cache_state_ = expected_cache_state;
    259 
    260     FileCacheEntry entry;
    261     EXPECT_TRUE(GetCacheEntryFromOriginThread(id, &entry));
    262 
    263     FileError error = FILE_ERROR_OK;
    264     base::FilePath cache_file_path;
    265 
    266     base::PostTaskAndReplyWithResult(
    267         blocking_task_runner_.get(),
    268         FROM_HERE,
    269         base::Bind(&FileCache::MarkAsMounted,
    270                    base::Unretained(cache_.get()),
    271                    id,
    272                    &cache_file_path),
    273         google_apis::test_util::CreateCopyResultCallback(&error));
    274     test_util::RunBlockingPoolTask();
    275 
    276     EXPECT_TRUE(base::PathExists(cache_file_path));
    277     EXPECT_EQ(cache_file_path, cache_->GetCacheFilePath(id));
    278   }
    279 
    280   void TestMarkAsUnmounted(const std::string& id,
    281                            const base::FilePath& file_path,
    282                            FileError expected_error,
    283                            int expected_cache_state) {
    284     expected_error_ = expected_error;
    285     expected_cache_state_ = expected_cache_state;
    286 
    287     FileError error = FILE_ERROR_OK;
    288     base::PostTaskAndReplyWithResult(
    289         blocking_task_runner_.get(),
    290         FROM_HERE,
    291         base::Bind(&FileCache::MarkAsUnmounted,
    292                    base::Unretained(cache_.get()),
    293                    file_path),
    294         google_apis::test_util::CreateCopyResultCallback(&error));
    295     test_util::RunBlockingPoolTask();
    296 
    297     base::FilePath cache_file_path;
    298     base::PostTaskAndReplyWithResult(
    299         blocking_task_runner_,
    300         FROM_HERE,
    301         base::Bind(&FileCache::GetFile,
    302                    base::Unretained(cache_.get()),
    303                    id, &cache_file_path),
    304         google_apis::test_util::CreateCopyResultCallback(&error));
    305     test_util::RunBlockingPoolTask();
    306     EXPECT_EQ(FILE_ERROR_OK, error);
    307 
    308     EXPECT_TRUE(base::PathExists(cache_file_path));
    309     EXPECT_EQ(cache_file_path, cache_->GetCacheFilePath(id));
    310   }
    311 
    312   void VerifyCacheFileState(FileError error, const std::string& id) {
    313     EXPECT_EQ(expected_error_, error);
    314 
    315     // Verify cache map.
    316     FileCacheEntry cache_entry;
    317     const bool cache_entry_found =
    318         GetCacheEntryFromOriginThread(id, &cache_entry);
    319     if ((expected_cache_state_ & TEST_CACHE_STATE_PRESENT) ||
    320         (expected_cache_state_ & TEST_CACHE_STATE_PINNED)) {
    321       ASSERT_TRUE(cache_entry_found);
    322       EXPECT_EQ((expected_cache_state_ & TEST_CACHE_STATE_PINNED) != 0,
    323                 cache_entry.is_pinned());
    324       EXPECT_EQ((expected_cache_state_ & TEST_CACHE_STATE_PRESENT) != 0,
    325                 cache_entry.is_present());
    326       EXPECT_EQ((expected_cache_state_ & TEST_CACHE_STATE_DIRTY) != 0,
    327                 cache_entry.is_dirty());
    328     } else {
    329       EXPECT_FALSE(cache_entry_found);
    330     }
    331 
    332     // Verify actual cache file.
    333     base::FilePath dest_path = cache_->GetCacheFilePath(id);
    334     EXPECT_EQ((expected_cache_state_ & TEST_CACHE_STATE_PRESENT) != 0,
    335               base::PathExists(dest_path));
    336   }
    337 
    338   // Helper function to call GetCacheEntry from origin thread.
    339   bool GetCacheEntryFromOriginThread(const std::string& id,
    340                                      FileCacheEntry* cache_entry) {
    341     bool result = false;
    342     base::PostTaskAndReplyWithResult(
    343         blocking_task_runner_,
    344         FROM_HERE,
    345         base::Bind(&internal::FileCache::GetCacheEntry,
    346                    base::Unretained(cache_.get()),
    347                    id,
    348                    cache_entry),
    349         google_apis::test_util::CreateCopyResultCallback(&result));
    350     test_util::RunBlockingPoolTask();
    351     return result;
    352   }
    353 
    354   // Returns true if the cache entry exists for the given ID.
    355   bool CacheEntryExists(const std::string& id) {
    356     FileCacheEntry cache_entry;
    357     return GetCacheEntryFromOriginThread(id, &cache_entry);
    358   }
    359 
    360   // Returns the number of the cache files with name <id>, and Confirm
    361   // that they have the <md5>. This should return 1 or 0.
    362   size_t CountCacheFiles(const std::string& id, const std::string& md5) {
    363     base::FilePath path = cache_->GetCacheFilePath(id);
    364     base::FileEnumerator enumerator(path.DirName(),
    365                                     false,  // recursive
    366                                     base::FileEnumerator::FILES,
    367                                     path.BaseName().value());
    368     size_t num_files_found = 0;
    369     for (base::FilePath current = enumerator.Next(); !current.empty();
    370          current = enumerator.Next()) {
    371       ++num_files_found;
    372       EXPECT_EQ(util::EscapeCacheFileName(id),
    373                 current.BaseName().AsUTF8Unsafe());
    374     }
    375     return num_files_found;
    376   }
    377 
    378   content::TestBrowserThreadBundle thread_bundle_;
    379   scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
    380   base::ScopedTempDir temp_dir_;
    381   base::FilePath dummy_file_path_;
    382 
    383   scoped_ptr<ResourceMetadataStorage, test_util::DestroyHelperForTests>
    384       metadata_storage_;
    385   scoped_ptr<FileCache, test_util::DestroyHelperForTests> cache_;
    386   scoped_ptr<FakeFreeDiskSpaceGetter> fake_free_disk_space_getter_;
    387 
    388   FileError expected_error_;
    389   int expected_cache_state_;
    390   std::string expected_file_extension_;
    391 };
    392 
    393 TEST_F(FileCacheTestOnUIThread, StoreToCacheSimple) {
    394   std::string id("pdf:1a2b");
    395   std::string md5("abcdef0123456789");
    396 
    397   // Store an existing file.
    398   TestStoreToCache(id, md5, dummy_file_path_,
    399                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
    400 
    401   // Store a non-existent file to the same |id| and |md5|.
    402   TestStoreToCache(id, md5,
    403                    base::FilePath::FromUTF8Unsafe("non_existent_file"),
    404                    FILE_ERROR_FAILED,
    405                    TEST_CACHE_STATE_PRESENT);
    406 
    407   // Store a different existing file to the same |id| but different
    408   // |md5|.
    409   md5 = "new_md5";
    410   TestStoreToCache(id, md5, dummy_file_path_,
    411                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
    412 
    413   // Verify that there's only one file with name <id>, i.e. previously
    414   // cached file with the different md5 should be deleted.
    415   EXPECT_EQ(1U, CountCacheFiles(id, md5));
    416 }
    417 
    418 TEST_F(FileCacheTestOnUIThread, RemoveFromCacheSimple) {
    419   std::string id("pdf:1a2b");
    420   std::string md5("abcdef0123456789");
    421   // First store a file to cache.
    422   TestStoreToCache(id, md5, dummy_file_path_,
    423                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
    424 
    425   // Then try to remove existing file from cache.
    426   TestRemoveFromCache(id, FILE_ERROR_OK);
    427 
    428   // Repeat using non-alphanumeric characters for ID, including '.'
    429   // which is an extension separator.
    430   id = "pdf:`~!@#$%^&*()-_=+[{|]}\\;',<.>/?";
    431   TestStoreToCache(id, md5, dummy_file_path_,
    432                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
    433 
    434   TestRemoveFromCache(id, FILE_ERROR_OK);
    435 }
    436 
    437 TEST_F(FileCacheTestOnUIThread, PinAndUnpin) {
    438   std::string id("pdf:1a2b");
    439   std::string md5("abcdef0123456789");
    440 
    441   // First store a file to cache.
    442   TestStoreToCache(id, md5, dummy_file_path_,
    443                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
    444 
    445   // Pin the existing file in cache.
    446   TestPin(id, FILE_ERROR_OK,
    447           TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
    448 
    449   // Unpin the existing file in cache.
    450   TestUnpin(id, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
    451 
    452   // Pin back the same existing file in cache.
    453   TestPin(id, FILE_ERROR_OK,
    454           TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
    455 
    456   // Pin a non-existent file in cache.
    457   id = "document:1a2b";
    458 
    459   TestPin(id, FILE_ERROR_OK, TEST_CACHE_STATE_PINNED);
    460 
    461   // Unpin the previously pinned non-existent file in cache.
    462   TestUnpin(id, FILE_ERROR_OK, TEST_CACHE_STATE_NONE);
    463 
    464   // Unpin a file that doesn't exist in cache and is not pinned, i.e. cache
    465   // has zero knowledge of the file.
    466   id = "not-in-cache:1a2b";
    467 
    468   TestUnpin(id, FILE_ERROR_NOT_FOUND, TEST_CACHE_STATE_NONE);
    469 }
    470 
    471 TEST_F(FileCacheTestOnUIThread, StoreToCachePinned) {
    472   std::string id("pdf:1a2b");
    473   std::string md5("abcdef0123456789");
    474 
    475   // Pin a non-existent file.
    476   TestPin(id, FILE_ERROR_OK, TEST_CACHE_STATE_PINNED);
    477 
    478   // Store an existing file to a previously pinned file.
    479   TestStoreToCache(id, md5, dummy_file_path_, FILE_ERROR_OK,
    480                    TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
    481 
    482   // Store a non-existent file to a previously pinned and stored file.
    483   TestStoreToCache(id, md5,
    484                    base::FilePath::FromUTF8Unsafe("non_existent_file"),
    485                    FILE_ERROR_FAILED,
    486                    TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
    487 }
    488 
    489 TEST_F(FileCacheTestOnUIThread, RemoveFromCachePinned) {
    490   std::string id("pdf:1a2b");
    491   std::string md5("abcdef0123456789");
    492 
    493   // Store a file to cache, and pin it.
    494   TestStoreToCache(id, md5, dummy_file_path_,
    495                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
    496   TestPin(id, FILE_ERROR_OK,
    497           TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
    498 
    499   // Remove |id| from cache.
    500   TestRemoveFromCache(id, FILE_ERROR_OK);
    501 
    502   // Use non-alphanumeric characters for ID, including '.'
    503   // which is an extension separator.
    504   id = "pdf:`~!@#$%^&*()-_=+[{|]}\\;',<.>/?";
    505 
    506   TestStoreToCache(id, md5, dummy_file_path_,
    507                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
    508   TestPin(id, FILE_ERROR_OK,
    509           TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
    510 
    511   TestRemoveFromCache(id, FILE_ERROR_OK);
    512 }
    513 
    514 TEST_F(FileCacheTestOnUIThread, DirtyCacheSimple) {
    515   std::string id("pdf:1a2b");
    516   std::string md5("abcdef0123456789");
    517 
    518   // First store a file to cache.
    519   TestStoreToCache(id, md5, dummy_file_path_,
    520                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
    521 
    522   // Mark the file dirty.
    523   TestMarkDirty(id, FILE_ERROR_OK,
    524                 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY);
    525 
    526   // Clear dirty state of the file.
    527   TestClearDirty(id, md5, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
    528 }
    529 
    530 TEST_F(FileCacheTestOnUIThread, DirtyCachePinned) {
    531   std::string id("pdf:1a2b");
    532   std::string md5("abcdef0123456789");
    533 
    534   // First store a file to cache and pin it.
    535   TestStoreToCache(id, md5, dummy_file_path_,
    536                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
    537   TestPin(id, FILE_ERROR_OK,
    538           TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
    539 
    540   // Mark the file dirty.
    541   TestMarkDirty(id, FILE_ERROR_OK,
    542                 TEST_CACHE_STATE_PRESENT |
    543                 TEST_CACHE_STATE_DIRTY |
    544                 TEST_CACHE_STATE_PINNED);
    545 
    546   // Clear dirty state of the file.
    547   TestClearDirty(id, md5, FILE_ERROR_OK,
    548                  TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
    549 }
    550 
    551 TEST_F(FileCacheTestOnUIThread, PinAndUnpinDirtyCache) {
    552   std::string id("pdf:1a2b");
    553   std::string md5("abcdef0123456789");
    554 
    555   // First store a file to cache and mark it as dirty.
    556   TestStoreToCache(id, md5, dummy_file_path_,
    557                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
    558   TestMarkDirty(id, FILE_ERROR_OK,
    559                 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY);
    560 
    561   // Verifies dirty file exists.
    562   base::FilePath dirty_path;
    563   FileError error = FILE_ERROR_FAILED;
    564   base::PostTaskAndReplyWithResult(
    565       blocking_task_runner_,
    566       FROM_HERE,
    567       base::Bind(&FileCache::GetFile,
    568                  base::Unretained(cache_.get()),
    569                  id, &dirty_path),
    570       google_apis::test_util::CreateCopyResultCallback(&error));
    571   test_util::RunBlockingPoolTask();
    572   EXPECT_EQ(FILE_ERROR_OK, error);
    573   EXPECT_TRUE(base::PathExists(dirty_path));
    574 
    575   // Pin the dirty file.
    576   TestPin(id, FILE_ERROR_OK,
    577           TEST_CACHE_STATE_PRESENT |
    578           TEST_CACHE_STATE_DIRTY |
    579           TEST_CACHE_STATE_PINNED);
    580 
    581   // Verify dirty file still exist at the same pathname.
    582   EXPECT_TRUE(base::PathExists(dirty_path));
    583 
    584   // Unpin the dirty file.
    585   TestUnpin(id, FILE_ERROR_OK,
    586             TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY);
    587 
    588   // Verify dirty file still exist at the same pathname.
    589   EXPECT_TRUE(base::PathExists(dirty_path));
    590 }
    591 
    592 TEST_F(FileCacheTestOnUIThread, DirtyCacheRepetitive) {
    593   std::string id("pdf:1a2b");
    594   std::string md5("abcdef0123456789");
    595 
    596   // First store a file to cache.
    597   TestStoreToCache(id, md5, dummy_file_path_,
    598                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
    599 
    600   // Mark the file dirty.
    601   TestMarkDirty(id, FILE_ERROR_OK,
    602                 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY);
    603 
    604   // Again, mark the file dirty.  Nothing should change.
    605   TestMarkDirty(id, FILE_ERROR_OK,
    606                 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY);
    607 
    608   // Clear dirty state of the file.
    609   TestClearDirty(id, md5, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
    610 
    611   // Again, clear dirty state of the file, which is no longer dirty.
    612   TestClearDirty(id, md5, FILE_ERROR_INVALID_OPERATION,
    613                  TEST_CACHE_STATE_PRESENT);
    614 }
    615 
    616 TEST_F(FileCacheTestOnUIThread, DirtyCacheInvalid) {
    617   std::string id("pdf:1a2b");
    618   std::string md5("abcdef0123456789");
    619 
    620   // Mark a non-existent file dirty.
    621   TestMarkDirty(id, FILE_ERROR_NOT_FOUND, TEST_CACHE_STATE_NONE);
    622 
    623   // Clear dirty state of a non-existent file.
    624   TestClearDirty(id, md5, FILE_ERROR_NOT_FOUND, TEST_CACHE_STATE_NONE);
    625 
    626   // Store a file to cache.
    627   TestStoreToCache(id, md5, dummy_file_path_,
    628                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
    629 
    630   // Clear dirty state of a non-dirty existing file.
    631   TestClearDirty(id, md5, FILE_ERROR_INVALID_OPERATION,
    632                  TEST_CACHE_STATE_PRESENT);
    633 
    634   // Mark an existing file dirty, then store a new file to the same ID
    635   // but different md5, which should fail.
    636   TestMarkDirty(id, FILE_ERROR_OK,
    637                 TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY);
    638   md5 = "new_md5";
    639   TestStoreToCache(id, md5, dummy_file_path_, FILE_ERROR_IN_USE,
    640                    TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_DIRTY);
    641 }
    642 
    643 TEST_F(FileCacheTestOnUIThread, RemoveFromDirtyCache) {
    644   std::string id("pdf:1a2b");
    645   std::string md5("abcdef0123456789");
    646 
    647   // Store a file to cache, pin it, mark it dirty and commit it.
    648   TestStoreToCache(id, md5, dummy_file_path_,
    649                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
    650   TestPin(id, FILE_ERROR_OK,
    651           TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
    652   TestMarkDirty(id, FILE_ERROR_OK,
    653                 TEST_CACHE_STATE_PRESENT |
    654                 TEST_CACHE_STATE_PINNED |
    655                 TEST_CACHE_STATE_DIRTY);
    656 
    657   // Try to remove the file.  Dirty caches can be removed at the level of
    658   // FileCache::Remove. Upper layer cache clearance functions like
    659   // FreeDiskSpaceIfNeededFor() and RemoveStaleCacheFiles() takes care of
    660   // securing dirty files.
    661   TestRemoveFromCache(id, FILE_ERROR_OK);
    662 }
    663 
    664 TEST_F(FileCacheTestOnUIThread, MountUnmount) {
    665   std::string id("pdf:1a2b");
    666   std::string md5("abcdef0123456789");
    667 
    668   // First store a file to cache.
    669   TestStoreToCache(id, md5, dummy_file_path_,
    670                    FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
    671 
    672   // Mark the file mounted.
    673   TestMarkAsMounted(id, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
    674   EXPECT_TRUE(CacheEntryExists(id));
    675 
    676   // Try to remove the file.
    677   TestRemoveFromCache(id, FILE_ERROR_IN_USE);
    678 
    679   // Clear mounted state of the file.
    680   base::FilePath file_path;
    681   FileError error = FILE_ERROR_FAILED;
    682   base::PostTaskAndReplyWithResult(
    683       blocking_task_runner_,
    684       FROM_HERE,
    685       base::Bind(&FileCache::GetFile,
    686                  base::Unretained(cache_.get()),
    687                  id, &file_path),
    688       google_apis::test_util::CreateCopyResultCallback(&error));
    689   test_util::RunBlockingPoolTask();
    690   EXPECT_EQ(FILE_ERROR_OK, error);
    691 
    692   TestMarkAsUnmounted(id, file_path, FILE_ERROR_OK, TEST_CACHE_STATE_PRESENT);
    693   EXPECT_TRUE(CacheEntryExists(id));
    694 
    695   // Try to remove the file.
    696   TestRemoveFromCache(id, FILE_ERROR_OK);
    697 }
    698 
    699 TEST_F(FileCacheTestOnUIThread, StoreToCacheNoSpace) {
    700   fake_free_disk_space_getter_->set_default_value(0);
    701 
    702   std::string id("pdf:1a2b");
    703   std::string md5("abcdef0123456789");
    704 
    705   // Try to store an existing file.
    706   TestStoreToCache(id, md5, dummy_file_path_,
    707                    FILE_ERROR_NO_LOCAL_SPACE,
    708                    TEST_CACHE_STATE_NONE);
    709 
    710   // Verify that there's no files added.
    711   EXPECT_EQ(0U, CountCacheFiles(id, md5));
    712 }
    713 
    714 TEST_F(FileCacheTestOnUIThread, UpdatePinnedCache) {
    715   std::string id("pdf:1a2b");
    716   std::string md5("abcdef0123456789");
    717   std::string md5_modified("aaaaaa0000000000");
    718 
    719   // Store an existing file.
    720   TestStoreToCache(id, md5, dummy_file_path_, FILE_ERROR_OK,
    721                    TEST_CACHE_STATE_PRESENT);
    722 
    723   // Pin the file.
    724   TestPin(id, FILE_ERROR_OK,
    725           TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
    726 
    727   // Store the file with a modified content and md5. It should stay pinned.
    728   TestStoreToCache(id, md5_modified, dummy_file_path_, FILE_ERROR_OK,
    729                    TEST_CACHE_STATE_PRESENT | TEST_CACHE_STATE_PINNED);
    730 }
    731 
    732 // Tests FileCache methods working with the blocking task runner.
    733 class FileCacheTest : public testing::Test {
    734  protected:
    735   virtual void SetUp() OVERRIDE {
    736     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
    737     const base::FilePath metadata_dir = temp_dir_.path().AppendASCII("meta");
    738     cache_files_dir_ = temp_dir_.path().AppendASCII(kCacheFileDirectory);
    739 
    740     ASSERT_TRUE(base::CreateDirectory(metadata_dir));
    741     ASSERT_TRUE(base::CreateDirectory(cache_files_dir_));
    742 
    743     fake_free_disk_space_getter_.reset(new FakeFreeDiskSpaceGetter);
    744 
    745     metadata_storage_.reset(new ResourceMetadataStorage(
    746         metadata_dir,
    747         base::MessageLoopProxy::current().get()));
    748     ASSERT_TRUE(metadata_storage_->Initialize());
    749 
    750     cache_.reset(new FileCache(
    751         metadata_storage_.get(),
    752         cache_files_dir_,
    753         base::MessageLoopProxy::current().get(),
    754         fake_free_disk_space_getter_.get()));
    755     ASSERT_TRUE(cache_->Initialize());
    756   }
    757 
    758   static bool RenameCacheFilesToNewFormat(FileCache* cache) {
    759     return cache->RenameCacheFilesToNewFormat();
    760   }
    761 
    762   content::TestBrowserThreadBundle thread_bundle_;
    763   base::ScopedTempDir temp_dir_;
    764   base::FilePath cache_files_dir_;
    765 
    766   scoped_ptr<ResourceMetadataStorage, test_util::DestroyHelperForTests>
    767       metadata_storage_;
    768   scoped_ptr<FileCache, test_util::DestroyHelperForTests> cache_;
    769   scoped_ptr<FakeFreeDiskSpaceGetter> fake_free_disk_space_getter_;
    770 };
    771 
    772 TEST_F(FileCacheTest, RecoverFilesFromCacheDirectory) {
    773   base::FilePath dir_source_root;
    774   EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &dir_source_root));
    775   const base::FilePath src_path =
    776       dir_source_root.AppendASCII("chrome/test/data/chromeos/drive/image.png");
    777 
    778   // Store files. This file should not be moved.
    779   EXPECT_EQ(FILE_ERROR_OK, cache_->Store("id_foo", "md5", src_path,
    780                                          FileCache::FILE_OPERATION_COPY));
    781 
    782   // Set up files in the cache directory. These files should be moved.
    783   const base::FilePath file_directory =
    784       temp_dir_.path().AppendASCII(kCacheFileDirectory);
    785   ASSERT_TRUE(base::CopyFile(src_path, file_directory.AppendASCII("id_bar")));
    786   ASSERT_TRUE(base::CopyFile(src_path, file_directory.AppendASCII("id_baz")));
    787 
    788   // Insert a dirty entry with "id_baz" to |recovered_cache_info|.
    789   // This should not prevent the file from being recovered.
    790   ResourceMetadataStorage::RecoveredCacheInfoMap recovered_cache_info;
    791   recovered_cache_info["id_baz"].is_dirty = true;
    792   recovered_cache_info["id_baz"].title = "baz.png";
    793 
    794   // Recover files.
    795   const base::FilePath dest_directory = temp_dir_.path().AppendASCII("dest");
    796   EXPECT_TRUE(cache_->RecoverFilesFromCacheDirectory(dest_directory,
    797                                                      recovered_cache_info));
    798 
    799   // Only two files should be recovered.
    800   EXPECT_TRUE(base::PathExists(dest_directory));
    801   // base::FileEnumerator does not guarantee the order.
    802   if (base::PathExists(dest_directory.AppendASCII("baz00000001.png"))) {
    803     EXPECT_TRUE(base::ContentsEqual(
    804         src_path,
    805         dest_directory.AppendASCII("baz00000001.png")));
    806     EXPECT_TRUE(base::ContentsEqual(
    807         src_path,
    808         dest_directory.AppendASCII("image00000002.png")));
    809   } else {
    810     EXPECT_TRUE(base::ContentsEqual(
    811         src_path,
    812         dest_directory.AppendASCII("image00000001.png")));
    813     EXPECT_TRUE(base::ContentsEqual(
    814         src_path,
    815         dest_directory.AppendASCII("baz00000002.png")));
    816   }
    817   EXPECT_FALSE(base::PathExists(
    818       dest_directory.AppendASCII("image00000003.png")));
    819 }
    820 
    821 TEST_F(FileCacheTest, Iterator) {
    822   base::FilePath src_file;
    823   ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &src_file));
    824 
    825   // Prepare entries.
    826   std::map<std::string, std::string> md5s;
    827   md5s["id1"] = "md5-1";
    828   md5s["id2"] = "md5-2";
    829   md5s["id3"] = "md5-3";
    830   md5s["id4"] = "md5-4";
    831   for (std::map<std::string, std::string>::iterator it = md5s.begin();
    832        it != md5s.end(); ++it) {
    833     EXPECT_EQ(FILE_ERROR_OK, cache_->Store(
    834         it->first, it->second, src_file, FileCache::FILE_OPERATION_COPY));
    835   }
    836 
    837   // Iterate.
    838   std::map<std::string, std::string> result;
    839   scoped_ptr<FileCache::Iterator> it = cache_->GetIterator();
    840   for (; !it->IsAtEnd(); it->Advance())
    841     result[it->GetID()] = it->GetValue().md5();
    842   EXPECT_EQ(md5s, result);
    843   EXPECT_FALSE(it->HasError());
    844 }
    845 
    846 TEST_F(FileCacheTest, FreeDiskSpaceIfNeededFor) {
    847   base::FilePath src_file;
    848   ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &src_file));
    849 
    850   // Store a file as a 'temporary' file and remember the path.
    851   const std::string id_tmp = "id_tmp", md5_tmp = "md5_tmp";
    852   ASSERT_EQ(FILE_ERROR_OK,
    853             cache_->Store(id_tmp, md5_tmp, src_file,
    854                           FileCache::FILE_OPERATION_COPY));
    855   base::FilePath tmp_path;
    856   ASSERT_EQ(FILE_ERROR_OK, cache_->GetFile(id_tmp, &tmp_path));
    857 
    858   // Store a file as a pinned file and remember the path.
    859   const std::string id_pinned = "id_pinned", md5_pinned = "md5_pinned";
    860   ASSERT_EQ(FILE_ERROR_OK,
    861             cache_->Store(id_pinned, md5_pinned, src_file,
    862                           FileCache::FILE_OPERATION_COPY));
    863   ASSERT_EQ(FILE_ERROR_OK, cache_->Pin(id_pinned));
    864   base::FilePath pinned_path;
    865   ASSERT_EQ(FILE_ERROR_OK, cache_->GetFile(id_pinned, &pinned_path));
    866 
    867   // Call FreeDiskSpaceIfNeededFor().
    868   fake_free_disk_space_getter_->set_default_value(test_util::kLotsOfSpace);
    869   fake_free_disk_space_getter_->PushFakeValue(0);
    870   const int64 kNeededBytes = 1;
    871   EXPECT_TRUE(cache_->FreeDiskSpaceIfNeededFor(kNeededBytes));
    872 
    873   // Only 'temporary' file gets removed.
    874   FileCacheEntry entry;
    875   EXPECT_FALSE(cache_->GetCacheEntry(id_tmp, &entry));
    876   EXPECT_FALSE(base::PathExists(tmp_path));
    877 
    878   EXPECT_TRUE(cache_->GetCacheEntry(id_pinned, &entry));
    879   EXPECT_TRUE(base::PathExists(pinned_path));
    880 
    881   // Returns false when disk space cannot be freed.
    882   fake_free_disk_space_getter_->set_default_value(0);
    883   EXPECT_FALSE(cache_->FreeDiskSpaceIfNeededFor(kNeededBytes));
    884 }
    885 
    886 TEST_F(FileCacheTest, GetFile) {
    887   const base::FilePath src_file_path = temp_dir_.path().Append("test.dat");
    888   const std::string src_contents = "test";
    889   EXPECT_TRUE(google_apis::test_util::WriteStringToFile(src_file_path,
    890                                                         src_contents));
    891   std::string id("id1");
    892   std::string md5(base::MD5String(src_contents));
    893 
    894   const base::FilePath cache_file_directory =
    895       temp_dir_.path().AppendASCII(kCacheFileDirectory);
    896 
    897   // Try to get an existing file from cache.
    898   EXPECT_EQ(FILE_ERROR_OK, cache_->Store(id, md5, src_file_path,
    899                                          FileCache::FILE_OPERATION_COPY));
    900   base::FilePath cache_file_path;
    901   EXPECT_EQ(FILE_ERROR_OK, cache_->GetFile(id, &cache_file_path));
    902   EXPECT_EQ(
    903       cache_file_directory.AppendASCII(util::EscapeCacheFileName(id)).value(),
    904       cache_file_path.value());
    905 
    906   std::string contents;
    907   EXPECT_TRUE(base::ReadFileToString(cache_file_path, &contents));
    908   EXPECT_EQ(src_contents, contents);
    909 
    910   // Get file from cache with different id.
    911   id = "id2";
    912   EXPECT_EQ(FILE_ERROR_NOT_FOUND, cache_->GetFile(id, &cache_file_path));
    913 
    914   // Pin a non-existent file.
    915   EXPECT_EQ(FILE_ERROR_OK, cache_->Pin(id));
    916 
    917   // Get the non-existent pinned file from cache.
    918   EXPECT_EQ(FILE_ERROR_NOT_FOUND, cache_->GetFile(id, &cache_file_path));
    919 
    920   // Get a previously pinned and stored file from cache.
    921   EXPECT_EQ(FILE_ERROR_OK, cache_->Store(id, md5, src_file_path,
    922                                          FileCache::FILE_OPERATION_COPY));
    923 
    924   EXPECT_EQ(FILE_ERROR_OK, cache_->GetFile(id, &cache_file_path));
    925   EXPECT_EQ(
    926       cache_file_directory.AppendASCII(util::EscapeCacheFileName(id)).value(),
    927       cache_file_path.value());
    928 
    929   contents.clear();
    930   EXPECT_TRUE(base::ReadFileToString(cache_file_path, &contents));
    931   EXPECT_EQ(src_contents, contents);
    932 }
    933 
    934 TEST_F(FileCacheTest, RenameCacheFilesToNewFormat) {
    935   const base::FilePath file_directory =
    936       temp_dir_.path().AppendASCII(kCacheFileDirectory);
    937 
    938   // File with an old style "<prefix>:<ID>.<MD5>" name.
    939   ASSERT_TRUE(google_apis::test_util::WriteStringToFile(
    940       file_directory.AppendASCII("file:id_koo.md5"), "koo"));
    941 
    942   // File with multiple extensions should be removed.
    943   ASSERT_TRUE(google_apis::test_util::WriteStringToFile(
    944       file_directory.AppendASCII("id_kyu.md5.mounted"), "kyu (mounted)"));
    945   ASSERT_TRUE(google_apis::test_util::WriteStringToFile(
    946       file_directory.AppendASCII("id_kyu.md5"), "kyu"));
    947 
    948   // Rename and verify the result.
    949   EXPECT_TRUE(RenameCacheFilesToNewFormat(cache_.get()));
    950   std::string contents;
    951   EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_koo"),
    952                                      &contents));
    953   EXPECT_EQ("koo", contents);
    954   contents.clear();
    955   EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_kyu"),
    956                                      &contents));
    957   EXPECT_EQ("kyu", contents);
    958 
    959   // Rename again.
    960   EXPECT_TRUE(RenameCacheFilesToNewFormat(cache_.get()));
    961 
    962   // Files with new style names are not affected.
    963   contents.clear();
    964   EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_koo"),
    965                                      &contents));
    966   EXPECT_EQ("koo", contents);
    967   contents.clear();
    968   EXPECT_TRUE(base::ReadFileToString(file_directory.AppendASCII("id_kyu"),
    969                                      &contents));
    970   EXPECT_EQ("kyu", contents);
    971 }
    972 
    973 TEST_F(FileCacheTest, ClearAll) {
    974   const std::string id("pdf:1a2b");
    975   const std::string md5("abcdef0123456789");
    976 
    977   // Store an existing file.
    978   base::FilePath src_file;
    979   ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &src_file));
    980   ASSERT_EQ(FILE_ERROR_OK,
    981             cache_->Store(id, md5, src_file, FileCache::FILE_OPERATION_COPY));
    982 
    983   // Verify that the cache entry is created.
    984   FileCacheEntry cache_entry;
    985   ASSERT_TRUE(cache_->GetCacheEntry(id, &cache_entry));
    986 
    987   // Clear cache.
    988   EXPECT_TRUE(cache_->ClearAll());
    989 
    990   // Verify that the cache is removed.
    991   EXPECT_FALSE(cache_->GetCacheEntry(id, &cache_entry));
    992   EXPECT_TRUE(base::IsDirectoryEmpty(cache_files_dir_));
    993 }
    994 
    995 }  // namespace internal
    996 }  // namespace drive
    997