Home | History | Annotate | Download | only in file_system
      1 // Copyright 2013 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_system/download_operation.h"
      6 
      7 #include "base/file_util.h"
      8 #include "base/task_runner_util.h"
      9 #include "chrome/browser/chromeos/drive/fake_free_disk_space_getter.h"
     10 #include "chrome/browser/chromeos/drive/file_cache.h"
     11 #include "chrome/browser/chromeos/drive/file_system/operation_test_base.h"
     12 #include "chrome/browser/chromeos/drive/file_system_util.h"
     13 #include "chrome/browser/chromeos/drive/job_scheduler.h"
     14 #include "chrome/browser/drive/fake_drive_service.h"
     15 #include "google_apis/drive/test_util.h"
     16 #include "testing/gtest/include/gtest/gtest.h"
     17 #include "third_party/cros_system_api/constants/cryptohome.h"
     18 
     19 namespace drive {
     20 namespace file_system {
     21 
     22 class DownloadOperationTest : public OperationTestBase {
     23  protected:
     24   virtual void SetUp() OVERRIDE {
     25     OperationTestBase::SetUp();
     26 
     27     operation_.reset(new DownloadOperation(
     28         blocking_task_runner(), observer(), scheduler(), metadata(), cache(),
     29         temp_dir()));
     30   }
     31 
     32   scoped_ptr<DownloadOperation> operation_;
     33 };
     34 
     35 TEST_F(DownloadOperationTest,
     36        EnsureFileDownloadedByPath_FromServer_EnoughSpace) {
     37   base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
     38   ResourceEntry src_entry;
     39   ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry));
     40   const int64 file_size = src_entry.file_info().size();
     41 
     42   // Pretend we have enough space.
     43   fake_free_disk_space_getter()->set_default_value(
     44       file_size + cryptohome::kMinFreeSpaceInBytes);
     45 
     46   FileError error = FILE_ERROR_FAILED;
     47   base::FilePath file_path;
     48   scoped_ptr<ResourceEntry> entry;
     49   operation_->EnsureFileDownloadedByPath(
     50       file_in_root,
     51       ClientContext(USER_INITIATED),
     52       GetFileContentInitializedCallback(),
     53       google_apis::GetContentCallback(),
     54       google_apis::test_util::CreateCopyResultCallback(
     55           &error, &file_path, &entry));
     56   test_util::RunBlockingPoolTask();
     57 
     58   EXPECT_EQ(FILE_ERROR_OK, error);
     59   ASSERT_TRUE(entry);
     60   EXPECT_FALSE(entry->file_specific_info().is_hosted_document());
     61 
     62   // The transfered file is cached and the change of "offline available"
     63   // attribute is notified.
     64   EXPECT_EQ(1U, observer()->get_changed_paths().size());
     65   EXPECT_EQ(1U, observer()->get_changed_paths().count(file_in_root.DirName()));
     66 }
     67 
     68 TEST_F(DownloadOperationTest,
     69        EnsureFileDownloadedByPath_FromServer_NoSpaceAtAll) {
     70   base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
     71 
     72   // Pretend we have no space at all.
     73   fake_free_disk_space_getter()->set_default_value(0);
     74 
     75   FileError error = FILE_ERROR_OK;
     76   base::FilePath file_path;
     77   scoped_ptr<ResourceEntry> entry;
     78   operation_->EnsureFileDownloadedByPath(
     79       file_in_root,
     80       ClientContext(USER_INITIATED),
     81       GetFileContentInitializedCallback(),
     82       google_apis::GetContentCallback(),
     83       google_apis::test_util::CreateCopyResultCallback(
     84           &error, &file_path, &entry));
     85   test_util::RunBlockingPoolTask();
     86 
     87   EXPECT_EQ(FILE_ERROR_NO_LOCAL_SPACE, error);
     88 }
     89 
     90 TEST_F(DownloadOperationTest,
     91        EnsureFileDownloadedByPath_FromServer_NoEnoughSpaceButCanFreeUp) {
     92   base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
     93   ResourceEntry src_entry;
     94   ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry));
     95   const int64 file_size = src_entry.file_info().size();
     96 
     97   // Make another file cached.
     98   // This file's cache file will be removed to free up the disk space.
     99   base::FilePath cached_file(
    100       FILE_PATH_LITERAL("drive/root/Duplicate Name.txt"));
    101   FileError error = FILE_ERROR_FAILED;
    102   base::FilePath file_path;
    103   scoped_ptr<ResourceEntry> entry;
    104   operation_->EnsureFileDownloadedByPath(
    105       cached_file,
    106       ClientContext(USER_INITIATED),
    107       GetFileContentInitializedCallback(),
    108       google_apis::GetContentCallback(),
    109       google_apis::test_util::CreateCopyResultCallback(
    110           &error, &file_path, &entry));
    111   test_util::RunBlockingPoolTask();
    112   EXPECT_EQ(FILE_ERROR_OK, error);
    113   ASSERT_TRUE(entry);
    114   EXPECT_TRUE(entry->file_specific_info().cache_state().is_present());
    115 
    116   // Pretend we have no space first (checked before downloading a file),
    117   // but then start reporting we have space. This is to emulate that
    118   // the disk space was freed up by removing temporary files.
    119   fake_free_disk_space_getter()->set_default_value(
    120       file_size + cryptohome::kMinFreeSpaceInBytes);
    121   fake_free_disk_space_getter()->PushFakeValue(0);
    122 
    123   operation_->EnsureFileDownloadedByPath(
    124       file_in_root,
    125       ClientContext(USER_INITIATED),
    126       GetFileContentInitializedCallback(),
    127       google_apis::GetContentCallback(),
    128       google_apis::test_util::CreateCopyResultCallback(
    129           &error, &file_path, &entry));
    130   test_util::RunBlockingPoolTask();
    131 
    132   EXPECT_EQ(FILE_ERROR_OK, error);
    133   ASSERT_TRUE(entry);
    134   EXPECT_FALSE(entry->file_specific_info().is_hosted_document());
    135 
    136   // The transfered file is cached and the change of "offline available"
    137   // attribute is notified.
    138   EXPECT_EQ(1U, observer()->get_changed_paths().size());
    139   EXPECT_EQ(1U, observer()->get_changed_paths().count(file_in_root.DirName()));
    140 
    141   // The cache for the other file should be removed in order to free up space.
    142   ResourceEntry cached_file_entry;
    143   EXPECT_EQ(FILE_ERROR_OK,
    144             GetLocalResourceEntry(cached_file, &cached_file_entry));
    145   EXPECT_FALSE(
    146       cached_file_entry.file_specific_info().cache_state().is_present());
    147 }
    148 
    149 TEST_F(DownloadOperationTest,
    150        EnsureFileDownloadedByPath_FromServer_EnoughSpaceButBecomeFull) {
    151   base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
    152   ResourceEntry src_entry;
    153   ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry));
    154   const int64 file_size = src_entry.file_info().size();
    155 
    156   // Pretend we have enough space first (checked before downloading a file),
    157   // but then start reporting we have not enough space. This is to emulate that
    158   // the disk space becomes full after the file is downloaded for some reason
    159   // (ex. the actual file was larger than the expected size).
    160   fake_free_disk_space_getter()->PushFakeValue(
    161       file_size + cryptohome::kMinFreeSpaceInBytes);
    162   fake_free_disk_space_getter()->set_default_value(
    163       cryptohome::kMinFreeSpaceInBytes - 1);
    164 
    165   FileError error = FILE_ERROR_OK;
    166   base::FilePath file_path;
    167   scoped_ptr<ResourceEntry> entry;
    168   operation_->EnsureFileDownloadedByPath(
    169       file_in_root,
    170       ClientContext(USER_INITIATED),
    171       GetFileContentInitializedCallback(),
    172       google_apis::GetContentCallback(),
    173       google_apis::test_util::CreateCopyResultCallback(
    174           &error, &file_path, &entry));
    175   test_util::RunBlockingPoolTask();
    176 
    177   EXPECT_EQ(FILE_ERROR_NO_LOCAL_SPACE, error);
    178 }
    179 
    180 TEST_F(DownloadOperationTest, EnsureFileDownloadedByPath_FromCache) {
    181   base::FilePath temp_file;
    182   ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir(), &temp_file));
    183 
    184   base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
    185   ResourceEntry src_entry;
    186   ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry));
    187 
    188   // Store something as cached version of this file.
    189   FileError error = FILE_ERROR_OK;
    190   base::PostTaskAndReplyWithResult(
    191       blocking_task_runner(),
    192       FROM_HERE,
    193       base::Bind(&internal::FileCache::Store,
    194                  base::Unretained(cache()),
    195                  GetLocalId(file_in_root),
    196                  src_entry.file_specific_info().md5(),
    197                  temp_file,
    198                  internal::FileCache::FILE_OPERATION_COPY),
    199       google_apis::test_util::CreateCopyResultCallback(&error));
    200   test_util::RunBlockingPoolTask();
    201   EXPECT_EQ(FILE_ERROR_OK, error);
    202 
    203   base::FilePath file_path;
    204   scoped_ptr<ResourceEntry> entry;
    205   operation_->EnsureFileDownloadedByPath(
    206       file_in_root,
    207       ClientContext(USER_INITIATED),
    208       GetFileContentInitializedCallback(),
    209       google_apis::GetContentCallback(),
    210       google_apis::test_util::CreateCopyResultCallback(
    211           &error, &file_path, &entry));
    212   test_util::RunBlockingPoolTask();
    213 
    214   EXPECT_EQ(FILE_ERROR_OK, error);
    215   ASSERT_TRUE(entry);
    216   EXPECT_FALSE(entry->file_specific_info().is_hosted_document());
    217 }
    218 
    219 TEST_F(DownloadOperationTest, EnsureFileDownloadedByPath_HostedDocument) {
    220   base::FilePath file_in_root(FILE_PATH_LITERAL(
    221       "drive/root/Document 1 excludeDir-test.gdoc"));
    222 
    223   FileError error = FILE_ERROR_FAILED;
    224   base::FilePath file_path;
    225   scoped_ptr<ResourceEntry> entry;
    226   operation_->EnsureFileDownloadedByPath(
    227       file_in_root,
    228       ClientContext(USER_INITIATED),
    229       GetFileContentInitializedCallback(),
    230       google_apis::GetContentCallback(),
    231       google_apis::test_util::CreateCopyResultCallback(
    232           &error, &file_path, &entry));
    233   test_util::RunBlockingPoolTask();
    234 
    235   EXPECT_EQ(FILE_ERROR_OK, error);
    236   ASSERT_TRUE(entry);
    237   EXPECT_TRUE(entry->file_specific_info().is_hosted_document());
    238   EXPECT_FALSE(file_path.empty());
    239 
    240   EXPECT_EQ(GURL(entry->file_specific_info().alternate_url()),
    241             util::ReadUrlFromGDocFile(file_path));
    242   EXPECT_EQ(entry->resource_id(), util::ReadResourceIdFromGDocFile(file_path));
    243   EXPECT_EQ(FILE_PATH_LITERAL(".gdoc"), file_path.Extension());
    244 }
    245 
    246 TEST_F(DownloadOperationTest, EnsureFileDownloadedByLocalId) {
    247   base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
    248   ResourceEntry src_entry;
    249   ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry));
    250 
    251   FileError error = FILE_ERROR_OK;
    252   base::FilePath file_path;
    253   scoped_ptr<ResourceEntry> entry;
    254   operation_->EnsureFileDownloadedByLocalId(
    255       GetLocalId(file_in_root),
    256       ClientContext(USER_INITIATED),
    257       GetFileContentInitializedCallback(),
    258       google_apis::GetContentCallback(),
    259       google_apis::test_util::CreateCopyResultCallback(
    260           &error, &file_path, &entry));
    261   test_util::RunBlockingPoolTask();
    262 
    263   EXPECT_EQ(FILE_ERROR_OK, error);
    264   ASSERT_TRUE(entry);
    265   EXPECT_FALSE(entry->file_specific_info().is_hosted_document());
    266 
    267   // The transfered file is cached and the change of "offline available"
    268   // attribute is notified.
    269   EXPECT_EQ(1U, observer()->get_changed_paths().size());
    270   EXPECT_EQ(1U, observer()->get_changed_paths().count(file_in_root.DirName()));
    271 }
    272 
    273 TEST_F(DownloadOperationTest,
    274        EnsureFileDownloadedByPath_WithGetContentCallback) {
    275   base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
    276 
    277   {
    278     FileError initialized_error = FILE_ERROR_FAILED;
    279     scoped_ptr<ResourceEntry> entry, entry_dontcare;
    280     base::FilePath local_path, local_path_dontcare;
    281     google_apis::test_util::TestGetContentCallback get_content_callback;
    282     FileError completion_error = FILE_ERROR_FAILED;
    283     base::Closure cancel_download = operation_->EnsureFileDownloadedByPath(
    284         file_in_root,
    285         ClientContext(USER_INITIATED),
    286         google_apis::test_util::CreateCopyResultCallback(
    287             &initialized_error, &local_path, &entry),
    288         get_content_callback.callback(),
    289         google_apis::test_util::CreateCopyResultCallback(
    290             &completion_error, &local_path_dontcare, &entry_dontcare));
    291     test_util::RunBlockingPoolTask();
    292 
    293     // For the first time, file is downloaded from the remote server.
    294     // In this case, |local_path| is empty.
    295     EXPECT_EQ(FILE_ERROR_OK, initialized_error);
    296     ASSERT_TRUE(entry);
    297     ASSERT_TRUE(local_path.empty());
    298     EXPECT_FALSE(cancel_download.is_null());
    299     // Content is available through the second callback argument.
    300     EXPECT_EQ(static_cast<size_t>(entry->file_info().size()),
    301               get_content_callback.GetConcatenatedData().size());
    302     EXPECT_EQ(FILE_ERROR_OK, completion_error);
    303 
    304     // The transfered file is cached and the change of "offline available"
    305     // attribute is notified.
    306     EXPECT_EQ(1U, observer()->get_changed_paths().size());
    307     EXPECT_EQ(1U,
    308               observer()->get_changed_paths().count(file_in_root.DirName()));
    309   }
    310 
    311   {
    312     FileError initialized_error = FILE_ERROR_FAILED;
    313     scoped_ptr<ResourceEntry> entry, entry_dontcare;
    314     base::FilePath local_path, local_path_dontcare;
    315     google_apis::test_util::TestGetContentCallback get_content_callback;
    316     FileError completion_error = FILE_ERROR_FAILED;
    317     base::Closure cancel_download = operation_->EnsureFileDownloadedByPath(
    318         file_in_root,
    319         ClientContext(USER_INITIATED),
    320         google_apis::test_util::CreateCopyResultCallback(
    321             &initialized_error, &local_path, &entry),
    322         get_content_callback.callback(),
    323         google_apis::test_util::CreateCopyResultCallback(
    324             &completion_error, &local_path_dontcare, &entry_dontcare));
    325     test_util::RunBlockingPoolTask();
    326 
    327     // Try second download. In this case, the file should be cached, so
    328     // |local_path| should not be empty.
    329     EXPECT_EQ(FILE_ERROR_OK, initialized_error);
    330     ASSERT_TRUE(entry);
    331     ASSERT_TRUE(!local_path.empty());
    332     EXPECT_FALSE(cancel_download.is_null());
    333     // The content is available from the cache file.
    334     EXPECT_TRUE(get_content_callback.data().empty());
    335     int64 local_file_size = 0;
    336     base::GetFileSize(local_path, &local_file_size);
    337     EXPECT_EQ(entry->file_info().size(), local_file_size);
    338     EXPECT_EQ(FILE_ERROR_OK, completion_error);
    339   }
    340 }
    341 
    342 TEST_F(DownloadOperationTest, EnsureFileDownloadedByLocalId_FromCache) {
    343   base::FilePath temp_file;
    344   ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir(), &temp_file));
    345 
    346   base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
    347   ResourceEntry src_entry;
    348   ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry));
    349 
    350   // Store something as cached version of this file.
    351   FileError error = FILE_ERROR_FAILED;
    352   base::PostTaskAndReplyWithResult(
    353       blocking_task_runner(),
    354       FROM_HERE,
    355       base::Bind(&internal::FileCache::Store,
    356                  base::Unretained(cache()),
    357                  GetLocalId(file_in_root),
    358                  src_entry.file_specific_info().md5(),
    359                  temp_file,
    360                  internal::FileCache::FILE_OPERATION_COPY),
    361       google_apis::test_util::CreateCopyResultCallback(&error));
    362   test_util::RunBlockingPoolTask();
    363   EXPECT_EQ(FILE_ERROR_OK, error);
    364 
    365   // The file is obtained from the cache.
    366   // Hence the downloading should work even if the drive service is offline.
    367   fake_service()->set_offline(true);
    368 
    369   base::FilePath file_path;
    370   scoped_ptr<ResourceEntry> entry;
    371   operation_->EnsureFileDownloadedByLocalId(
    372       GetLocalId(file_in_root),
    373       ClientContext(USER_INITIATED),
    374       GetFileContentInitializedCallback(),
    375       google_apis::GetContentCallback(),
    376       google_apis::test_util::CreateCopyResultCallback(
    377           &error, &file_path, &entry));
    378   test_util::RunBlockingPoolTask();
    379 
    380   EXPECT_EQ(FILE_ERROR_OK, error);
    381   ASSERT_TRUE(entry);
    382   EXPECT_FALSE(entry->file_specific_info().is_hosted_document());
    383 }
    384 
    385 TEST_F(DownloadOperationTest, EnsureFileDownloadedByPath_DirtyCache) {
    386   base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
    387   ResourceEntry src_entry;
    388   ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry));
    389 
    390   // Prepare a dirty file to store to cache that has a different size than
    391   // stored in resource metadata.
    392   base::FilePath dirty_file = temp_dir().AppendASCII("dirty.txt");
    393   size_t dirty_size = src_entry.file_info().size() + 10;
    394   google_apis::test_util::WriteStringToFile(dirty_file,
    395                                             std::string(dirty_size, 'x'));
    396 
    397   // Store the file as a cache, marking it to be dirty.
    398   FileError error = FILE_ERROR_FAILED;
    399   base::PostTaskAndReplyWithResult(
    400       blocking_task_runner(),
    401       FROM_HERE,
    402       base::Bind(&internal::FileCache::Store,
    403                  base::Unretained(cache()),
    404                  GetLocalId(file_in_root),
    405                  std::string(),
    406                  dirty_file,
    407                  internal::FileCache::FILE_OPERATION_COPY),
    408       google_apis::test_util::CreateCopyResultCallback(&error));
    409   test_util::RunBlockingPoolTask();
    410   EXPECT_EQ(FILE_ERROR_OK, error);
    411 
    412   // Record values passed to GetFileContentInitializedCallback().
    413   FileError init_error;
    414   base::FilePath init_path;
    415   scoped_ptr<ResourceEntry> init_entry;
    416   base::FilePath file_path;
    417   scoped_ptr<ResourceEntry> entry;
    418   base::Closure cancel_callback = operation_->EnsureFileDownloadedByPath(
    419       file_in_root,
    420       ClientContext(USER_INITIATED),
    421       google_apis::test_util::CreateCopyResultCallback(
    422           &init_error, &init_path, &init_entry),
    423       google_apis::GetContentCallback(),
    424       google_apis::test_util::CreateCopyResultCallback(
    425           &error, &file_path, &entry));
    426   test_util::RunBlockingPoolTask();
    427 
    428   EXPECT_EQ(FILE_ERROR_OK, error);
    429   // Check that the result of local modification is propagated.
    430   EXPECT_EQ(static_cast<int64>(dirty_size), init_entry->file_info().size());
    431   EXPECT_EQ(static_cast<int64>(dirty_size), entry->file_info().size());
    432 }
    433 
    434 TEST_F(DownloadOperationTest, EnsureFileDownloadedByPath_LocallyCreatedFile) {
    435   // Add a new file with an empty resource ID.
    436   base::FilePath file_path(FILE_PATH_LITERAL("drive/root/New File.txt"));
    437   ResourceEntry parent;
    438   ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_path.DirName(), &parent));
    439 
    440   ResourceEntry new_file;
    441   new_file.set_title("New File.txt");
    442   new_file.set_parent_local_id(parent.local_id());
    443 
    444   FileError error = FILE_ERROR_FAILED;
    445   std::string local_id;
    446   base::PostTaskAndReplyWithResult(
    447       blocking_task_runner(),
    448       FROM_HERE,
    449       base::Bind(&internal::ResourceMetadata::AddEntry,
    450                  base::Unretained(metadata()),
    451                  new_file,
    452                  &local_id),
    453       google_apis::test_util::CreateCopyResultCallback(&error));
    454   test_util::RunBlockingPoolTask();
    455   EXPECT_EQ(FILE_ERROR_OK, error);
    456 
    457   // Empty cache file should be returned.
    458   base::FilePath cache_file_path;
    459   scoped_ptr<ResourceEntry> entry;
    460   operation_->EnsureFileDownloadedByPath(
    461       file_path,
    462       ClientContext(USER_INITIATED),
    463       GetFileContentInitializedCallback(),
    464       google_apis::GetContentCallback(),
    465       google_apis::test_util::CreateCopyResultCallback(
    466           &error, &cache_file_path, &entry));
    467   test_util::RunBlockingPoolTask();
    468   EXPECT_EQ(FILE_ERROR_OK, error);
    469 
    470   int64 cache_file_size = 0;
    471   EXPECT_TRUE(base::GetFileSize(cache_file_path, &cache_file_size));
    472   EXPECT_EQ(static_cast<int64>(0), cache_file_size);
    473   ASSERT_TRUE(entry);
    474   EXPECT_EQ(cache_file_size, entry->file_info().size());
    475 }
    476 
    477 TEST_F(DownloadOperationTest, CancelBeforeDownloadStarts) {
    478   base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
    479   ResourceEntry src_entry;
    480   ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry));
    481 
    482   // Start operation.
    483   FileError error = FILE_ERROR_OK;
    484   base::FilePath file_path;
    485   scoped_ptr<ResourceEntry> entry;
    486   base::Closure cancel_closure = operation_->EnsureFileDownloadedByLocalId(
    487       GetLocalId(file_in_root),
    488       ClientContext(USER_INITIATED),
    489       GetFileContentInitializedCallback(),
    490       google_apis::GetContentCallback(),
    491       google_apis::test_util::CreateCopyResultCallback(
    492           &error, &file_path, &entry));
    493 
    494   // Cancel immediately.
    495   ASSERT_FALSE(cancel_closure.is_null());
    496   cancel_closure.Run();
    497   test_util::RunBlockingPoolTask();
    498 
    499   EXPECT_EQ(FILE_ERROR_ABORT, error);
    500 }
    501 
    502 }  // namespace file_system
    503 }  // namespace drive
    504