Home | History | Annotate | Download | only in sync
      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/sync/entry_update_performer.h"
      6 
      7 #include "base/callback_helpers.h"
      8 #include "base/file_util.h"
      9 #include "base/md5.h"
     10 #include "base/task_runner_util.h"
     11 #include "chrome/browser/chromeos/drive/file_cache.h"
     12 #include "chrome/browser/chromeos/drive/file_system/download_operation.h"
     13 #include "chrome/browser/chromeos/drive/file_system/operation_test_base.h"
     14 #include "chrome/browser/chromeos/drive/job_scheduler.h"
     15 #include "chrome/browser/chromeos/drive/resource_metadata.h"
     16 #include "chrome/browser/drive/drive_api_util.h"
     17 #include "chrome/browser/drive/fake_drive_service.h"
     18 #include "google_apis/drive/drive_api_parser.h"
     19 #include "google_apis/drive/test_util.h"
     20 #include "testing/gtest/include/gtest/gtest.h"
     21 
     22 namespace drive {
     23 namespace internal {
     24 
     25 class EntryUpdatePerformerTest : public file_system::OperationTestBase {
     26  protected:
     27   virtual void SetUp() OVERRIDE {
     28     OperationTestBase::SetUp();
     29     performer_.reset(new EntryUpdatePerformer(blocking_task_runner(),
     30                                               observer(),
     31                                               scheduler(),
     32                                               metadata(),
     33                                               cache(),
     34                                               loader_controller()));
     35   }
     36 
     37   // Stores |content| to the cache and mark it as dirty.
     38   FileError StoreAndMarkDirty(const std::string& local_id,
     39                               const std::string& content) {
     40     base::FilePath path;
     41     if (!base::CreateTemporaryFileInDir(temp_dir(), &path) ||
     42         !google_apis::test_util::WriteStringToFile(path, content))
     43       return FILE_ERROR_FAILED;
     44 
     45     // Store the file to cache.
     46     FileError error = FILE_ERROR_FAILED;
     47     base::PostTaskAndReplyWithResult(
     48         blocking_task_runner(),
     49         FROM_HERE,
     50         base::Bind(&FileCache::Store,
     51                    base::Unretained(cache()),
     52                    local_id, std::string(), path,
     53                    FileCache::FILE_OPERATION_COPY),
     54         google_apis::test_util::CreateCopyResultCallback(&error));
     55     test_util::RunBlockingPoolTask();
     56     return error;
     57   }
     58 
     59   scoped_ptr<EntryUpdatePerformer> performer_;
     60 };
     61 
     62 TEST_F(EntryUpdatePerformerTest, UpdateEntry) {
     63   base::FilePath src_path(
     64       FILE_PATH_LITERAL("drive/root/Directory 1/SubDirectory File 1.txt"));
     65   base::FilePath dest_path(
     66       FILE_PATH_LITERAL("drive/root/Directory 1/Sub Directory Folder"));
     67 
     68   ResourceEntry src_entry, dest_entry;
     69   EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(src_path, &src_entry));
     70   EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(dest_path, &dest_entry));
     71 
     72   // Update local entry.
     73   base::Time new_last_modified = base::Time::FromInternalValue(
     74       src_entry.file_info().last_modified()) + base::TimeDelta::FromSeconds(1);
     75   base::Time new_last_accessed = base::Time::FromInternalValue(
     76       src_entry.file_info().last_accessed()) + base::TimeDelta::FromSeconds(2);
     77 
     78   src_entry.set_parent_local_id(dest_entry.local_id());
     79   src_entry.set_title("Moved" + src_entry.title());
     80   src_entry.mutable_file_info()->set_last_modified(
     81       new_last_modified.ToInternalValue());
     82   src_entry.mutable_file_info()->set_last_accessed(
     83       new_last_accessed.ToInternalValue());
     84   src_entry.set_metadata_edit_state(ResourceEntry::DIRTY);
     85 
     86   FileError error = FILE_ERROR_FAILED;
     87   base::PostTaskAndReplyWithResult(
     88       blocking_task_runner(),
     89       FROM_HERE,
     90       base::Bind(&ResourceMetadata::RefreshEntry,
     91                  base::Unretained(metadata()),
     92                  src_entry),
     93       google_apis::test_util::CreateCopyResultCallback(&error));
     94   test_util::RunBlockingPoolTask();
     95   EXPECT_EQ(FILE_ERROR_OK, error);
     96 
     97   // Perform server side update.
     98   error = FILE_ERROR_FAILED;
     99   performer_->UpdateEntry(
    100       src_entry.local_id(),
    101       ClientContext(USER_INITIATED),
    102       google_apis::test_util::CreateCopyResultCallback(&error));
    103   test_util::RunBlockingPoolTask();
    104   EXPECT_EQ(FILE_ERROR_OK, error);
    105 
    106   // Verify the file is updated on the server.
    107   google_apis::GDataErrorCode gdata_error = google_apis::GDATA_OTHER_ERROR;
    108   scoped_ptr<google_apis::FileResource> gdata_entry;
    109   fake_service()->GetFileResource(
    110       src_entry.resource_id(),
    111       google_apis::test_util::CreateCopyResultCallback(&gdata_error,
    112                                                        &gdata_entry));
    113   test_util::RunBlockingPoolTask();
    114   EXPECT_EQ(google_apis::HTTP_SUCCESS, gdata_error);
    115   ASSERT_TRUE(gdata_entry);
    116 
    117   EXPECT_EQ(src_entry.title(), gdata_entry->title());
    118   EXPECT_EQ(new_last_modified, gdata_entry->modified_date());
    119   EXPECT_EQ(new_last_accessed, gdata_entry->last_viewed_by_me_date());
    120 
    121   ASSERT_FALSE(gdata_entry->parents().empty());
    122   EXPECT_EQ(dest_entry.resource_id(), gdata_entry->parents()[0].file_id());
    123 }
    124 
    125 // Tests updating metadata of a file with a non-dirty cache file.
    126 TEST_F(EntryUpdatePerformerTest, UpdateEntry_WithNonDirtyCache) {
    127   base::FilePath src_path(
    128       FILE_PATH_LITERAL("drive/root/Directory 1/SubDirectory File 1.txt"));
    129 
    130   // Download the file content to prepare a non-dirty cache file.
    131   file_system::DownloadOperation download_operation(
    132       blocking_task_runner(), observer(), scheduler(), metadata(), cache(),
    133       temp_dir());
    134   FileError error = FILE_ERROR_FAILED;
    135   base::FilePath cache_file_path;
    136   scoped_ptr<ResourceEntry> src_entry;
    137   download_operation.EnsureFileDownloadedByPath(
    138       src_path,
    139       ClientContext(USER_INITIATED),
    140       GetFileContentInitializedCallback(),
    141       google_apis::GetContentCallback(),
    142       google_apis::test_util::CreateCopyResultCallback(
    143           &error, &cache_file_path, &src_entry));
    144   test_util::RunBlockingPoolTask();
    145   EXPECT_EQ(FILE_ERROR_OK, error);
    146   ASSERT_TRUE(src_entry);
    147 
    148   // Update the entry locally.
    149   src_entry->set_title("Updated" + src_entry->title());
    150   src_entry->set_metadata_edit_state(ResourceEntry::DIRTY);
    151 
    152   error = FILE_ERROR_FAILED;
    153   base::PostTaskAndReplyWithResult(
    154       blocking_task_runner(),
    155       FROM_HERE,
    156       base::Bind(&ResourceMetadata::RefreshEntry,
    157                  base::Unretained(metadata()),
    158                  *src_entry),
    159       google_apis::test_util::CreateCopyResultCallback(&error));
    160   test_util::RunBlockingPoolTask();
    161   EXPECT_EQ(FILE_ERROR_OK, error);
    162 
    163   // Perform server side update. This shouldn't fail. (crbug.com/358590)
    164   error = FILE_ERROR_FAILED;
    165   performer_->UpdateEntry(
    166       src_entry->local_id(),
    167       ClientContext(USER_INITIATED),
    168       google_apis::test_util::CreateCopyResultCallback(&error));
    169   test_util::RunBlockingPoolTask();
    170   EXPECT_EQ(FILE_ERROR_OK, error);
    171 
    172   // Verify the file is updated on the server.
    173   google_apis::GDataErrorCode gdata_error = google_apis::GDATA_OTHER_ERROR;
    174   scoped_ptr<google_apis::FileResource> gdata_entry;
    175   fake_service()->GetFileResource(
    176       src_entry->resource_id(),
    177       google_apis::test_util::CreateCopyResultCallback(&gdata_error,
    178                                                        &gdata_entry));
    179   test_util::RunBlockingPoolTask();
    180   EXPECT_EQ(google_apis::HTTP_SUCCESS, gdata_error);
    181   ASSERT_TRUE(gdata_entry);
    182   EXPECT_EQ(src_entry->title(), gdata_entry->title());
    183 }
    184 
    185 TEST_F(EntryUpdatePerformerTest, UpdateEntry_NotFound) {
    186   const std::string id = "this ID should result in NOT_FOUND";
    187   FileError error = FILE_ERROR_FAILED;
    188   performer_->UpdateEntry(
    189       id, ClientContext(USER_INITIATED),
    190       google_apis::test_util::CreateCopyResultCallback(&error));
    191   test_util::RunBlockingPoolTask();
    192   EXPECT_EQ(FILE_ERROR_NOT_FOUND, error);
    193 }
    194 
    195 TEST_F(EntryUpdatePerformerTest, UpdateEntry_ContentUpdate) {
    196   const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/File 1.txt"));
    197   const std::string kResourceId("file:2_file_resource_id");
    198 
    199   const std::string local_id = GetLocalId(kFilePath);
    200   EXPECT_FALSE(local_id.empty());
    201 
    202   const std::string kTestFileContent = "I'm being uploaded! Yay!";
    203   EXPECT_EQ(FILE_ERROR_OK, StoreAndMarkDirty(local_id, kTestFileContent));
    204 
    205   int64 original_changestamp =
    206       fake_service()->about_resource().largest_change_id();
    207 
    208   // The callback will be called upon completion of UpdateEntry().
    209   FileError error = FILE_ERROR_FAILED;
    210   performer_->UpdateEntry(
    211       local_id,
    212       ClientContext(USER_INITIATED),
    213       google_apis::test_util::CreateCopyResultCallback(&error));
    214   test_util::RunBlockingPoolTask();
    215   EXPECT_EQ(FILE_ERROR_OK, error);
    216 
    217   // Check that the server has received an update.
    218   EXPECT_LT(original_changestamp,
    219             fake_service()->about_resource().largest_change_id());
    220 
    221   // Check that the file size is updated to that of the updated content.
    222   google_apis::GDataErrorCode gdata_error = google_apis::GDATA_OTHER_ERROR;
    223   scoped_ptr<google_apis::FileResource> server_entry;
    224   fake_service()->GetFileResource(
    225       kResourceId,
    226       google_apis::test_util::CreateCopyResultCallback(&gdata_error,
    227                                                        &server_entry));
    228   test_util::RunBlockingPoolTask();
    229   EXPECT_EQ(google_apis::HTTP_SUCCESS, gdata_error);
    230   EXPECT_EQ(static_cast<int64>(kTestFileContent.size()),
    231             server_entry->file_size());
    232 
    233   // Make sure that the cache is no longer dirty.
    234   ResourceEntry entry;
    235   EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry));
    236   EXPECT_FALSE(entry.file_specific_info().cache_state().is_dirty());
    237 }
    238 
    239 TEST_F(EntryUpdatePerformerTest, UpdateEntry_ContentUpdateMd5Check) {
    240   const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/File 1.txt"));
    241   const std::string kResourceId("file:2_file_resource_id");
    242 
    243   const std::string local_id = GetLocalId(kFilePath);
    244   EXPECT_FALSE(local_id.empty());
    245 
    246   const std::string kTestFileContent = "I'm being uploaded! Yay!";
    247   EXPECT_EQ(FILE_ERROR_OK, StoreAndMarkDirty(local_id, kTestFileContent));
    248 
    249   int64 original_changestamp =
    250       fake_service()->about_resource().largest_change_id();
    251 
    252   // The callback will be called upon completion of UpdateEntry().
    253   FileError error = FILE_ERROR_FAILED;
    254   performer_->UpdateEntry(
    255       local_id,
    256       ClientContext(USER_INITIATED),
    257       google_apis::test_util::CreateCopyResultCallback(&error));
    258   test_util::RunBlockingPoolTask();
    259   EXPECT_EQ(FILE_ERROR_OK, error);
    260 
    261   // Check that the server has received an update.
    262   EXPECT_LT(original_changestamp,
    263             fake_service()->about_resource().largest_change_id());
    264 
    265   // Check that the file size is updated to that of the updated content.
    266   google_apis::GDataErrorCode gdata_error = google_apis::GDATA_OTHER_ERROR;
    267   scoped_ptr<google_apis::FileResource> server_entry;
    268   fake_service()->GetFileResource(
    269       kResourceId,
    270       google_apis::test_util::CreateCopyResultCallback(&gdata_error,
    271                                                        &server_entry));
    272   test_util::RunBlockingPoolTask();
    273   EXPECT_EQ(google_apis::HTTP_SUCCESS, gdata_error);
    274   EXPECT_EQ(static_cast<int64>(kTestFileContent.size()),
    275             server_entry->file_size());
    276 
    277   // Make sure that the cache is no longer dirty.
    278   ResourceEntry entry;
    279   EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry));
    280   EXPECT_FALSE(entry.file_specific_info().cache_state().is_dirty());
    281 
    282   // Again mark the cache file dirty.
    283   scoped_ptr<base::ScopedClosureRunner> file_closer;
    284   error = FILE_ERROR_FAILED;
    285   base::PostTaskAndReplyWithResult(
    286       blocking_task_runner(),
    287       FROM_HERE,
    288       base::Bind(&FileCache::OpenForWrite,
    289                  base::Unretained(cache()),
    290                  local_id,
    291                  &file_closer),
    292       google_apis::test_util::CreateCopyResultCallback(&error));
    293   test_util::RunBlockingPoolTask();
    294   EXPECT_EQ(FILE_ERROR_OK, error);
    295   file_closer.reset();
    296 
    297   // And call UpdateEntry again.
    298   // In this case, although the file is marked as dirty, but the content
    299   // hasn't been changed. Thus, the actual uploading should be skipped.
    300   original_changestamp = fake_service()->about_resource().largest_change_id();
    301   error = FILE_ERROR_FAILED;
    302   performer_->UpdateEntry(
    303       local_id,
    304       ClientContext(USER_INITIATED),
    305       google_apis::test_util::CreateCopyResultCallback(&error));
    306   test_util::RunBlockingPoolTask();
    307   EXPECT_EQ(FILE_ERROR_OK, error);
    308 
    309   EXPECT_EQ(original_changestamp,
    310             fake_service()->about_resource().largest_change_id());
    311 
    312   // Make sure that the cache is no longer dirty.
    313   EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry));
    314   EXPECT_FALSE(entry.file_specific_info().cache_state().is_dirty());
    315 }
    316 
    317 TEST_F(EntryUpdatePerformerTest, UpdateEntry_OpenedForWrite) {
    318   const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/File 1.txt"));
    319   const std::string kResourceId("file:2_file_resource_id");
    320 
    321   const std::string local_id = GetLocalId(kFilePath);
    322   EXPECT_FALSE(local_id.empty());
    323 
    324   const std::string kTestFileContent = "I'm being uploaded! Yay!";
    325   EXPECT_EQ(FILE_ERROR_OK, StoreAndMarkDirty(local_id, kTestFileContent));
    326 
    327   // Emulate a situation where someone is writing to the file.
    328   scoped_ptr<base::ScopedClosureRunner> file_closer;
    329   FileError error = FILE_ERROR_FAILED;
    330   base::PostTaskAndReplyWithResult(
    331       blocking_task_runner(),
    332       FROM_HERE,
    333       base::Bind(&FileCache::OpenForWrite,
    334                  base::Unretained(cache()),
    335                  local_id,
    336                  &file_closer),
    337       google_apis::test_util::CreateCopyResultCallback(&error));
    338   test_util::RunBlockingPoolTask();
    339   EXPECT_EQ(FILE_ERROR_OK, error);
    340 
    341   // Update. This should not clear the dirty bit.
    342   error = FILE_ERROR_FAILED;
    343   performer_->UpdateEntry(
    344       local_id,
    345       ClientContext(USER_INITIATED),
    346       google_apis::test_util::CreateCopyResultCallback(&error));
    347   test_util::RunBlockingPoolTask();
    348   EXPECT_EQ(FILE_ERROR_OK, error);
    349 
    350   // Make sure that the cache is still dirty.
    351   ResourceEntry entry;
    352   EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry));
    353   EXPECT_TRUE(entry.file_specific_info().cache_state().is_dirty());
    354 
    355   // Close the file.
    356   file_closer.reset();
    357 
    358   // Update. This should clear the dirty bit.
    359   error = FILE_ERROR_FAILED;
    360   performer_->UpdateEntry(
    361       local_id,
    362       ClientContext(USER_INITIATED),
    363       google_apis::test_util::CreateCopyResultCallback(&error));
    364   test_util::RunBlockingPoolTask();
    365   EXPECT_EQ(FILE_ERROR_OK, error);
    366 
    367   // Make sure that the cache is no longer dirty.
    368   EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry));
    369   EXPECT_FALSE(entry.file_specific_info().cache_state().is_dirty());
    370 }
    371 
    372 TEST_F(EntryUpdatePerformerTest, UpdateEntry_UploadNewFile) {
    373   // Create a new file locally.
    374   const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/New File.txt"));
    375 
    376   ResourceEntry parent;
    377   EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath.DirName(), &parent));
    378 
    379   ResourceEntry entry;
    380   entry.set_parent_local_id(parent.local_id());
    381   entry.set_title(kFilePath.BaseName().AsUTF8Unsafe());
    382   entry.mutable_file_specific_info()->set_content_mime_type("text/plain");
    383   entry.set_metadata_edit_state(ResourceEntry::DIRTY);
    384 
    385   FileError error = FILE_ERROR_FAILED;
    386   std::string local_id;
    387   base::PostTaskAndReplyWithResult(
    388       blocking_task_runner(),
    389       FROM_HERE,
    390       base::Bind(&internal::ResourceMetadata::AddEntry,
    391                  base::Unretained(metadata()),
    392                  entry,
    393                  &local_id),
    394       google_apis::test_util::CreateCopyResultCallback(&error));
    395   test_util::RunBlockingPoolTask();
    396   EXPECT_EQ(FILE_ERROR_OK, error);
    397 
    398   // Update. This should result in creating a new file on the server.
    399   error = FILE_ERROR_FAILED;
    400   performer_->UpdateEntry(
    401       local_id,
    402       ClientContext(USER_INITIATED),
    403       google_apis::test_util::CreateCopyResultCallback(&error));
    404   test_util::RunBlockingPoolTask();
    405   EXPECT_EQ(FILE_ERROR_OK, error);
    406 
    407   // The entry got a resource ID.
    408   EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry));
    409   EXPECT_FALSE(entry.resource_id().empty());
    410   EXPECT_EQ(ResourceEntry::CLEAN, entry.metadata_edit_state());
    411 
    412   // Make sure that the cache is no longer dirty.
    413   EXPECT_FALSE(entry.file_specific_info().cache_state().is_dirty());
    414 
    415   // Make sure that we really created a file.
    416   google_apis::GDataErrorCode status = google_apis::GDATA_OTHER_ERROR;
    417   scoped_ptr<google_apis::FileResource> server_entry;
    418   fake_service()->GetFileResource(
    419       entry.resource_id(),
    420       google_apis::test_util::CreateCopyResultCallback(&status, &server_entry));
    421   test_util::RunBlockingPoolTask();
    422   EXPECT_EQ(google_apis::HTTP_SUCCESS, status);
    423   ASSERT_TRUE(server_entry);
    424   EXPECT_FALSE(server_entry->IsDirectory());
    425 }
    426 
    427 TEST_F(EntryUpdatePerformerTest, UpdateEntry_NewFileOpendForWrite) {
    428   // Create a new file locally.
    429   const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/New File.txt"));
    430 
    431   ResourceEntry parent;
    432   EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath.DirName(), &parent));
    433 
    434   ResourceEntry entry;
    435   entry.set_parent_local_id(parent.local_id());
    436   entry.set_title(kFilePath.BaseName().AsUTF8Unsafe());
    437   entry.mutable_file_specific_info()->set_content_mime_type("text/plain");
    438   entry.set_metadata_edit_state(ResourceEntry::DIRTY);
    439 
    440   FileError error = FILE_ERROR_FAILED;
    441   std::string local_id;
    442   base::PostTaskAndReplyWithResult(
    443       blocking_task_runner(),
    444       FROM_HERE,
    445       base::Bind(&internal::ResourceMetadata::AddEntry,
    446                  base::Unretained(metadata()),
    447                  entry,
    448                  &local_id),
    449       google_apis::test_util::CreateCopyResultCallback(&error));
    450   test_util::RunBlockingPoolTask();
    451   EXPECT_EQ(FILE_ERROR_OK, error);
    452 
    453   const std::string kTestFileContent = "This is a new file.";
    454   EXPECT_EQ(FILE_ERROR_OK, StoreAndMarkDirty(local_id, kTestFileContent));
    455 
    456   // Emulate a situation where someone is writing to the file.
    457   scoped_ptr<base::ScopedClosureRunner> file_closer;
    458   error = FILE_ERROR_FAILED;
    459   base::PostTaskAndReplyWithResult(
    460       blocking_task_runner(),
    461       FROM_HERE,
    462       base::Bind(&FileCache::OpenForWrite,
    463                  base::Unretained(cache()),
    464                  local_id,
    465                  &file_closer),
    466       google_apis::test_util::CreateCopyResultCallback(&error));
    467   test_util::RunBlockingPoolTask();
    468   EXPECT_EQ(FILE_ERROR_OK, error);
    469 
    470   // Update, but no update is performed because the file is opened.
    471   error = FILE_ERROR_FAILED;
    472   performer_->UpdateEntry(
    473       local_id,
    474       ClientContext(USER_INITIATED),
    475       google_apis::test_util::CreateCopyResultCallback(&error));
    476   test_util::RunBlockingPoolTask();
    477   EXPECT_EQ(FILE_ERROR_OK, error);
    478 
    479   // The entry hasn't got a resource ID yet.
    480   EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry));
    481   EXPECT_TRUE(entry.resource_id().empty());
    482 
    483   // Close the file.
    484   file_closer.reset();
    485 
    486   // Update. This should result in creating a new file on the server.
    487   error = FILE_ERROR_FAILED;
    488   performer_->UpdateEntry(
    489       local_id,
    490       ClientContext(USER_INITIATED),
    491       google_apis::test_util::CreateCopyResultCallback(&error));
    492   test_util::RunBlockingPoolTask();
    493   EXPECT_EQ(FILE_ERROR_OK, error);
    494 
    495   // The entry got a resource ID.
    496   EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry));
    497   EXPECT_FALSE(entry.resource_id().empty());
    498   EXPECT_EQ(ResourceEntry::CLEAN, entry.metadata_edit_state());
    499 }
    500 
    501 TEST_F(EntryUpdatePerformerTest, UpdateEntry_CreateDirectory) {
    502   // Create a new directory locally.
    503   const base::FilePath kPath(FILE_PATH_LITERAL("drive/root/New Directory"));
    504 
    505   ResourceEntry parent;
    506   EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kPath.DirName(), &parent));
    507 
    508   ResourceEntry entry;
    509   entry.set_parent_local_id(parent.local_id());
    510   entry.set_title(kPath.BaseName().AsUTF8Unsafe());
    511   entry.mutable_file_info()->set_is_directory(true);
    512   entry.set_metadata_edit_state(ResourceEntry::DIRTY);
    513 
    514   FileError error = FILE_ERROR_FAILED;
    515   std::string local_id;
    516   base::PostTaskAndReplyWithResult(
    517       blocking_task_runner(),
    518       FROM_HERE,
    519       base::Bind(&internal::ResourceMetadata::AddEntry,
    520                  base::Unretained(metadata()),
    521                  entry,
    522                  &local_id),
    523       google_apis::test_util::CreateCopyResultCallback(&error));
    524   test_util::RunBlockingPoolTask();
    525   EXPECT_EQ(FILE_ERROR_OK, error);
    526 
    527   // Update. This should result in creating a new directory on the server.
    528   error = FILE_ERROR_FAILED;
    529   performer_->UpdateEntry(
    530       local_id,
    531       ClientContext(USER_INITIATED),
    532       google_apis::test_util::CreateCopyResultCallback(&error));
    533   test_util::RunBlockingPoolTask();
    534   EXPECT_EQ(FILE_ERROR_OK, error);
    535 
    536   // The entry got a resource ID.
    537   EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kPath, &entry));
    538   EXPECT_FALSE(entry.resource_id().empty());
    539   EXPECT_EQ(ResourceEntry::CLEAN, entry.metadata_edit_state());
    540 
    541   // Make sure that we really created a directory.
    542   google_apis::GDataErrorCode status = google_apis::GDATA_OTHER_ERROR;
    543   scoped_ptr<google_apis::FileResource> server_entry;
    544   fake_service()->GetFileResource(
    545       entry.resource_id(),
    546       google_apis::test_util::CreateCopyResultCallback(&status, &server_entry));
    547   test_util::RunBlockingPoolTask();
    548   EXPECT_EQ(google_apis::HTTP_SUCCESS, status);
    549   ASSERT_TRUE(server_entry);
    550   EXPECT_TRUE(server_entry->IsDirectory());
    551 }
    552 
    553 }  // namespace internal
    554 }  // namespace drive
    555