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