diff options
Diffstat (limited to 'components/drive/sync/entry_update_performer_unittest.cc')
-rw-r--r-- | components/drive/sync/entry_update_performer_unittest.cc | 658 |
1 files changed, 658 insertions, 0 deletions
diff --git a/components/drive/sync/entry_update_performer_unittest.cc b/components/drive/sync/entry_update_performer_unittest.cc new file mode 100644 index 0000000..145e1ff --- /dev/null +++ b/components/drive/sync/entry_update_performer_unittest.cc @@ -0,0 +1,658 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/drive/sync/entry_update_performer.h" + +#include "base/callback_helpers.h" +#include "base/files/file_util.h" +#include "base/md5.h" +#include "base/task_runner_util.h" +#include "components/drive/drive_api_util.h" +#include "components/drive/file_cache.h" +#include "components/drive/file_system/download_operation.h" +#include "components/drive/file_system/operation_test_base.h" +#include "components/drive/job_scheduler.h" +#include "components/drive/resource_metadata.h" +#include "components/drive/service/fake_drive_service.h" +#include "content/public/test/test_utils.h" +#include "google_apis/drive/drive_api_parser.h" +#include "google_apis/drive/test_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace drive { +namespace internal { + +class EntryUpdatePerformerTest : public file_system::OperationTestBase { + protected: + void SetUp() override { + OperationTestBase::SetUp(); + performer_.reset(new EntryUpdatePerformer(blocking_task_runner(), + delegate(), + scheduler(), + metadata(), + cache(), + loader_controller())); + } + + // Stores |content| to the cache and mark it as dirty. + FileError StoreAndMarkDirty(const std::string& local_id, + const std::string& content) { + base::FilePath path; + if (!base::CreateTemporaryFileInDir(temp_dir(), &path) || + !google_apis::test_util::WriteStringToFile(path, content)) + return FILE_ERROR_FAILED; + + // Store the file to cache. + FileError error = FILE_ERROR_FAILED; + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&FileCache::Store, + base::Unretained(cache()), + local_id, std::string(), path, + FileCache::FILE_OPERATION_COPY), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + return error; + } + + scoped_ptr<EntryUpdatePerformer> performer_; +}; + +TEST_F(EntryUpdatePerformerTest, UpdateEntry) { + base::FilePath src_path( + FILE_PATH_LITERAL("drive/root/Directory 1/SubDirectory File 1.txt")); + base::FilePath dest_path( + FILE_PATH_LITERAL("drive/root/Directory 1/Sub Directory Folder")); + + ResourceEntry src_entry, dest_entry; + EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(src_path, &src_entry)); + EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(dest_path, &dest_entry)); + + // Update local entry. + base::Time new_last_modified = base::Time::FromInternalValue( + src_entry.file_info().last_modified()) + base::TimeDelta::FromSeconds(1); + base::Time new_last_accessed = base::Time::FromInternalValue( + src_entry.file_info().last_accessed()) + base::TimeDelta::FromSeconds(2); + + src_entry.set_parent_local_id(dest_entry.local_id()); + src_entry.set_title("Moved" + src_entry.title()); + src_entry.mutable_file_info()->set_last_modified( + new_last_modified.ToInternalValue()); + src_entry.mutable_file_info()->set_last_accessed( + new_last_accessed.ToInternalValue()); + src_entry.set_metadata_edit_state(ResourceEntry::DIRTY); + + FileError error = FILE_ERROR_FAILED; + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&ResourceMetadata::RefreshEntry, + base::Unretained(metadata()), + src_entry), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // Perform server side update. + error = FILE_ERROR_FAILED; + performer_->UpdateEntry( + src_entry.local_id(), + ClientContext(USER_INITIATED), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // Verify the file is updated on the server. + google_apis::DriveApiErrorCode gdata_error = google_apis::DRIVE_OTHER_ERROR; + scoped_ptr<google_apis::FileResource> gdata_entry; + fake_service()->GetFileResource( + src_entry.resource_id(), + google_apis::test_util::CreateCopyResultCallback(&gdata_error, + &gdata_entry)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(google_apis::HTTP_SUCCESS, gdata_error); + ASSERT_TRUE(gdata_entry); + + EXPECT_EQ(src_entry.title(), gdata_entry->title()); + EXPECT_EQ(new_last_modified, gdata_entry->modified_date()); + EXPECT_EQ(new_last_accessed, gdata_entry->last_viewed_by_me_date()); + + ASSERT_FALSE(gdata_entry->parents().empty()); + EXPECT_EQ(dest_entry.resource_id(), gdata_entry->parents()[0].file_id()); +} + +TEST_F(EntryUpdatePerformerTest, UpdateEntry_SetProperties) { + base::FilePath entry_path( + FILE_PATH_LITERAL("drive/root/Directory 1/SubDirectory File 1.txt")); + + ResourceEntry entry; + EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(entry_path, &entry)); + + Property* const first_property = entry.mutable_new_properties()->Add(); + first_property->set_key("hello"); + first_property->set_value("world"); + first_property->set_visibility(Property_Visibility_PUBLIC); + + Property* const second_property = entry.mutable_new_properties()->Add(); + second_property->set_key("this"); + second_property->set_value("will-change"); + second_property->set_visibility(Property_Visibility_PUBLIC); + entry.set_metadata_edit_state(ResourceEntry::DIRTY); + + FileError error = FILE_ERROR_FAILED; + base::PostTaskAndReplyWithResult( + blocking_task_runner(), FROM_HERE, + base::Bind(&ResourceMetadata::RefreshEntry, base::Unretained(metadata()), + entry), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // Perform server side update. + error = FILE_ERROR_FAILED; + performer_->UpdateEntry( + entry.local_id(), ClientContext(USER_INITIATED), + google_apis::test_util::CreateCopyResultCallback(&error)); + + // Add a new property during an update. + Property* const property = entry.mutable_new_properties()->Add(); + property->set_key("tokyo"); + property->set_value("kyoto"); + property->set_visibility(Property_Visibility_PUBLIC); + + // Modify an existing property during an update. + second_property->set_value("changed"); + + // Change the resource entry during an update. + base::PostTaskAndReplyWithResult( + blocking_task_runner(), FROM_HERE, + base::Bind(&ResourceMetadata::RefreshEntry, base::Unretained(metadata()), + entry), + google_apis::test_util::CreateCopyResultCallback(&error)); + + // Wait until the update is fully completed. + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // List of synced properties should be removed from the proto. + EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(entry_path, &entry)); + ASSERT_EQ(2, entry.new_properties().size()); + EXPECT_EQ("changed", entry.new_properties().Get(0).value()); + EXPECT_EQ("tokyo", entry.new_properties().Get(1).key()); +} + +// Tests updating metadata of a file with a non-dirty cache file. +TEST_F(EntryUpdatePerformerTest, UpdateEntry_WithNonDirtyCache) { + base::FilePath src_path( + FILE_PATH_LITERAL("drive/root/Directory 1/SubDirectory File 1.txt")); + + // Download the file content to prepare a non-dirty cache file. + file_system::DownloadOperation download_operation( + blocking_task_runner(), delegate(), scheduler(), metadata(), cache(), + temp_dir()); + FileError error = FILE_ERROR_FAILED; + base::FilePath cache_file_path; + scoped_ptr<ResourceEntry> src_entry; + download_operation.EnsureFileDownloadedByPath( + src_path, + ClientContext(USER_INITIATED), + GetFileContentInitializedCallback(), + google_apis::GetContentCallback(), + google_apis::test_util::CreateCopyResultCallback( + &error, &cache_file_path, &src_entry)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + ASSERT_TRUE(src_entry); + + // Update the entry locally. + src_entry->set_title("Updated" + src_entry->title()); + src_entry->set_metadata_edit_state(ResourceEntry::DIRTY); + + error = FILE_ERROR_FAILED; + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&ResourceMetadata::RefreshEntry, + base::Unretained(metadata()), + *src_entry), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // Perform server side update. This shouldn't fail. (crbug.com/358590) + error = FILE_ERROR_FAILED; + performer_->UpdateEntry( + src_entry->local_id(), + ClientContext(USER_INITIATED), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // Verify the file is updated on the server. + google_apis::DriveApiErrorCode gdata_error = google_apis::DRIVE_OTHER_ERROR; + scoped_ptr<google_apis::FileResource> gdata_entry; + fake_service()->GetFileResource( + src_entry->resource_id(), + google_apis::test_util::CreateCopyResultCallback(&gdata_error, + &gdata_entry)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(google_apis::HTTP_SUCCESS, gdata_error); + ASSERT_TRUE(gdata_entry); + EXPECT_EQ(src_entry->title(), gdata_entry->title()); +} + +TEST_F(EntryUpdatePerformerTest, UpdateEntry_NotFound) { + const std::string id = "this ID should result in NOT_FOUND"; + FileError error = FILE_ERROR_FAILED; + performer_->UpdateEntry( + id, ClientContext(USER_INITIATED), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_NOT_FOUND, error); +} + +TEST_F(EntryUpdatePerformerTest, UpdateEntry_ContentUpdate) { + const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/File 1.txt")); + const std::string kResourceId("2_file_resource_id"); + + const std::string local_id = GetLocalId(kFilePath); + EXPECT_FALSE(local_id.empty()); + + const std::string kTestFileContent = "I'm being uploaded! Yay!"; + EXPECT_EQ(FILE_ERROR_OK, StoreAndMarkDirty(local_id, kTestFileContent)); + + int64 original_changestamp = + fake_service()->about_resource().largest_change_id(); + + // The callback will be called upon completion of UpdateEntry(). + FileError error = FILE_ERROR_FAILED; + performer_->UpdateEntry( + local_id, + ClientContext(USER_INITIATED), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // Check that the server has received an update. + EXPECT_LT(original_changestamp, + fake_service()->about_resource().largest_change_id()); + + // Check that the file size is updated to that of the updated content. + google_apis::DriveApiErrorCode gdata_error = google_apis::DRIVE_OTHER_ERROR; + scoped_ptr<google_apis::FileResource> server_entry; + fake_service()->GetFileResource( + kResourceId, + google_apis::test_util::CreateCopyResultCallback(&gdata_error, + &server_entry)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(google_apis::HTTP_SUCCESS, gdata_error); + EXPECT_EQ(static_cast<int64>(kTestFileContent.size()), + server_entry->file_size()); + + // Make sure that the cache is no longer dirty. + ResourceEntry entry; + EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry)); + EXPECT_FALSE(entry.file_specific_info().cache_state().is_dirty()); +} + +TEST_F(EntryUpdatePerformerTest, UpdateEntry_ContentUpdateMd5Check) { + const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/File 1.txt")); + const std::string kResourceId("2_file_resource_id"); + + const std::string local_id = GetLocalId(kFilePath); + EXPECT_FALSE(local_id.empty()); + + const std::string kTestFileContent = "I'm being uploaded! Yay!"; + EXPECT_EQ(FILE_ERROR_OK, StoreAndMarkDirty(local_id, kTestFileContent)); + + int64 original_changestamp = + fake_service()->about_resource().largest_change_id(); + + // The callback will be called upon completion of UpdateEntry(). + FileError error = FILE_ERROR_FAILED; + performer_->UpdateEntry( + local_id, + ClientContext(USER_INITIATED), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // Check that the server has received an update. + EXPECT_LT(original_changestamp, + fake_service()->about_resource().largest_change_id()); + + // Check that the file size is updated to that of the updated content. + google_apis::DriveApiErrorCode gdata_error = google_apis::DRIVE_OTHER_ERROR; + scoped_ptr<google_apis::FileResource> server_entry; + fake_service()->GetFileResource( + kResourceId, + google_apis::test_util::CreateCopyResultCallback(&gdata_error, + &server_entry)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(google_apis::HTTP_SUCCESS, gdata_error); + EXPECT_EQ(static_cast<int64>(kTestFileContent.size()), + server_entry->file_size()); + + // Make sure that the cache is no longer dirty. + ResourceEntry entry; + EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry)); + EXPECT_FALSE(entry.file_specific_info().cache_state().is_dirty()); + + // Again mark the cache file dirty. + scoped_ptr<base::ScopedClosureRunner> file_closer; + error = FILE_ERROR_FAILED; + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&FileCache::OpenForWrite, + base::Unretained(cache()), + local_id, + &file_closer), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + file_closer.reset(); + + // And call UpdateEntry again. + // In this case, although the file is marked as dirty, but the content + // hasn't been changed. Thus, the actual uploading should be skipped. + original_changestamp = fake_service()->about_resource().largest_change_id(); + error = FILE_ERROR_FAILED; + performer_->UpdateEntry( + local_id, + ClientContext(USER_INITIATED), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + EXPECT_EQ(original_changestamp, + fake_service()->about_resource().largest_change_id()); + + // Make sure that the cache is no longer dirty. + EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry)); + EXPECT_FALSE(entry.file_specific_info().cache_state().is_dirty()); +} + +TEST_F(EntryUpdatePerformerTest, UpdateEntry_OpenedForWrite) { + const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/File 1.txt")); + const std::string kResourceId("2_file_resource_id"); + + const std::string local_id = GetLocalId(kFilePath); + EXPECT_FALSE(local_id.empty()); + + const std::string kTestFileContent = "I'm being uploaded! Yay!"; + EXPECT_EQ(FILE_ERROR_OK, StoreAndMarkDirty(local_id, kTestFileContent)); + + // Emulate a situation where someone is writing to the file. + scoped_ptr<base::ScopedClosureRunner> file_closer; + FileError error = FILE_ERROR_FAILED; + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&FileCache::OpenForWrite, + base::Unretained(cache()), + local_id, + &file_closer), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // Update. This should not clear the dirty bit. + error = FILE_ERROR_FAILED; + performer_->UpdateEntry( + local_id, + ClientContext(USER_INITIATED), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // Make sure that the cache is still dirty. + ResourceEntry entry; + EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry)); + EXPECT_TRUE(entry.file_specific_info().cache_state().is_dirty()); + + // Close the file. + file_closer.reset(); + + // Update. This should clear the dirty bit. + error = FILE_ERROR_FAILED; + performer_->UpdateEntry( + local_id, + ClientContext(USER_INITIATED), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // Make sure that the cache is no longer dirty. + EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry)); + EXPECT_FALSE(entry.file_specific_info().cache_state().is_dirty()); +} + +TEST_F(EntryUpdatePerformerTest, UpdateEntry_UploadNewFile) { + // Create a new file locally. + const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/New File.txt")); + + ResourceEntry parent; + EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath.DirName(), &parent)); + + ResourceEntry entry; + entry.set_parent_local_id(parent.local_id()); + entry.set_title(kFilePath.BaseName().AsUTF8Unsafe()); + entry.mutable_file_specific_info()->set_content_mime_type("text/plain"); + entry.set_metadata_edit_state(ResourceEntry::DIRTY); + + FileError error = FILE_ERROR_FAILED; + std::string local_id; + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&internal::ResourceMetadata::AddEntry, + base::Unretained(metadata()), + entry, + &local_id), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // Update. This should result in creating a new file on the server. + error = FILE_ERROR_FAILED; + performer_->UpdateEntry( + local_id, + ClientContext(USER_INITIATED), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // The entry got a resource ID. + EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry)); + EXPECT_FALSE(entry.resource_id().empty()); + EXPECT_EQ(ResourceEntry::CLEAN, entry.metadata_edit_state()); + + // Make sure that the cache is no longer dirty. + EXPECT_FALSE(entry.file_specific_info().cache_state().is_dirty()); + + // Make sure that we really created a file. + google_apis::DriveApiErrorCode status = google_apis::DRIVE_OTHER_ERROR; + scoped_ptr<google_apis::FileResource> server_entry; + fake_service()->GetFileResource( + entry.resource_id(), + google_apis::test_util::CreateCopyResultCallback(&status, &server_entry)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(google_apis::HTTP_SUCCESS, status); + ASSERT_TRUE(server_entry); + EXPECT_FALSE(server_entry->IsDirectory()); +} + +TEST_F(EntryUpdatePerformerTest, UpdateEntry_NewFileOpendForWrite) { + // Create a new file locally. + const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/New File.txt")); + + ResourceEntry parent; + EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath.DirName(), &parent)); + + ResourceEntry entry; + entry.set_parent_local_id(parent.local_id()); + entry.set_title(kFilePath.BaseName().AsUTF8Unsafe()); + entry.mutable_file_specific_info()->set_content_mime_type("text/plain"); + entry.set_metadata_edit_state(ResourceEntry::DIRTY); + + FileError error = FILE_ERROR_FAILED; + std::string local_id; + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&internal::ResourceMetadata::AddEntry, + base::Unretained(metadata()), + entry, + &local_id), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + const std::string kTestFileContent = "This is a new file."; + EXPECT_EQ(FILE_ERROR_OK, StoreAndMarkDirty(local_id, kTestFileContent)); + + // Emulate a situation where someone is writing to the file. + scoped_ptr<base::ScopedClosureRunner> file_closer; + error = FILE_ERROR_FAILED; + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&FileCache::OpenForWrite, + base::Unretained(cache()), + local_id, + &file_closer), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // Update, but no update is performed because the file is opened. + error = FILE_ERROR_FAILED; + performer_->UpdateEntry( + local_id, + ClientContext(USER_INITIATED), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // The entry hasn't got a resource ID yet. + EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry)); + EXPECT_TRUE(entry.resource_id().empty()); + + // Close the file. + file_closer.reset(); + + // Update. This should result in creating a new file on the server. + error = FILE_ERROR_FAILED; + performer_->UpdateEntry( + local_id, + ClientContext(USER_INITIATED), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // The entry got a resource ID. + EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kFilePath, &entry)); + EXPECT_FALSE(entry.resource_id().empty()); + EXPECT_EQ(ResourceEntry::CLEAN, entry.metadata_edit_state()); +} + +TEST_F(EntryUpdatePerformerTest, UpdateEntry_CreateDirectory) { + // Create a new directory locally. + const base::FilePath kPath(FILE_PATH_LITERAL("drive/root/New Directory")); + + ResourceEntry parent; + EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kPath.DirName(), &parent)); + + ResourceEntry entry; + entry.set_parent_local_id(parent.local_id()); + entry.set_title(kPath.BaseName().AsUTF8Unsafe()); + entry.mutable_file_info()->set_is_directory(true); + entry.set_metadata_edit_state(ResourceEntry::DIRTY); + + FileError error = FILE_ERROR_FAILED; + std::string local_id; + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&internal::ResourceMetadata::AddEntry, + base::Unretained(metadata()), + entry, + &local_id), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // Update. This should result in creating a new directory on the server. + error = FILE_ERROR_FAILED; + performer_->UpdateEntry( + local_id, + ClientContext(USER_INITIATED), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // The entry got a resource ID. + EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(kPath, &entry)); + EXPECT_FALSE(entry.resource_id().empty()); + EXPECT_EQ(ResourceEntry::CLEAN, entry.metadata_edit_state()); + + // Make sure that we really created a directory. + google_apis::DriveApiErrorCode status = google_apis::DRIVE_OTHER_ERROR; + scoped_ptr<google_apis::FileResource> server_entry; + fake_service()->GetFileResource( + entry.resource_id(), + google_apis::test_util::CreateCopyResultCallback(&status, &server_entry)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(google_apis::HTTP_SUCCESS, status); + ASSERT_TRUE(server_entry); + EXPECT_TRUE(server_entry->IsDirectory()); +} + +TEST_F(EntryUpdatePerformerTest, UpdateEntry_InsufficientPermission) { + base::FilePath src_path( + FILE_PATH_LITERAL("drive/root/Directory 1/SubDirectory File 1.txt")); + + ResourceEntry src_entry; + EXPECT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(src_path, &src_entry)); + + // Update local entry. + ResourceEntry updated_entry(src_entry); + updated_entry.set_title("Moved" + src_entry.title()); + updated_entry.set_metadata_edit_state(ResourceEntry::DIRTY); + + FileError error = FILE_ERROR_FAILED; + base::PostTaskAndReplyWithResult( + blocking_task_runner(), + FROM_HERE, + base::Bind(&ResourceMetadata::RefreshEntry, + base::Unretained(metadata()), + updated_entry), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // Set user permission to forbid server side update. + EXPECT_EQ(google_apis::HTTP_SUCCESS, fake_service()->SetUserPermission( + src_entry.resource_id(), google_apis::drive::PERMISSION_ROLE_READER)); + + // Try to perform update. + error = FILE_ERROR_FAILED; + performer_->UpdateEntry( + src_entry.local_id(), + ClientContext(USER_INITIATED), + google_apis::test_util::CreateCopyResultCallback(&error)); + content::RunAllBlockingPoolTasksUntilIdle(); + EXPECT_EQ(FILE_ERROR_OK, error); + + // This should result in reverting the local change. + ResourceEntry result_entry; + EXPECT_EQ(FILE_ERROR_OK, + GetLocalResourceEntryById(src_entry.local_id(), &result_entry)); + EXPECT_EQ(src_entry.title(), result_entry.title()); +} + +} // namespace internal +} // namespace drive |