diff options
author | nhiroki@chromium.org <nhiroki@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-19 11:56:04 +0000 |
---|---|---|
committer | nhiroki@chromium.org <nhiroki@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-19 11:56:04 +0000 |
commit | 18a348016380ac8731ed0bce9bd2b89dd1dd3a79 (patch) | |
tree | 8f2fd0d6cc3ad148a0812de0d4c4126bc13b1f88 | |
parent | 96c328c079d5262352bed24641f4ee6dcbf25c8c (diff) | |
download | chromium_src-18a348016380ac8731ed0bce9bd2b89dd1dd3a79.zip chromium_src-18a348016380ac8731ed0bce9bd2b89dd1dd3a79.tar.gz chromium_src-18a348016380ac8731ed0bce9bd2b89dd1dd3a79.tar.bz2 |
Implement upload/download/delete functions in DriveFileSyncClient
BUG=157815
TEST=unit_tests --gtest_filter=\*DriveFileSyncClient\*
Review URL: https://codereview.chromium.org/11362201
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@168509 0039d316-1c4b-4281-b951-d872f2087c98
6 files changed, 690 insertions, 20 deletions
diff --git a/chrome/browser/google_apis/gdata_errorcode.h b/chrome/browser/google_apis/gdata_errorcode.h index c18fb0c..0e95785 100644 --- a/chrome/browser/google_apis/gdata_errorcode.h +++ b/chrome/browser/google_apis/gdata_errorcode.h @@ -29,6 +29,7 @@ enum GDataErrorCode { GDATA_OTHER_ERROR = -103, GDATA_NO_CONNECTION = -104, GDATA_NOT_READY = -105, + GDATA_NO_SPACE = -106, }; } // namespace google_apis diff --git a/chrome/browser/sync_file_system/drive_file_sync_client.cc b/chrome/browser/sync_file_system/drive_file_sync_client.cc index 8a81a7c5..5ef82b6 100644 --- a/chrome/browser/sync_file_system/drive_file_sync_client.cc +++ b/chrome/browser/sync_file_system/drive_file_sync_client.cc @@ -12,6 +12,7 @@ #include "chrome/browser/google_apis/drive_uploader.h" #include "chrome/browser/google_apis/gdata_wapi_service.h" #include "net/base/escape.h" +#include "net/base/mime_util.h" namespace sync_file_system { @@ -21,6 +22,11 @@ const char kRootResourceId[] = ""; const char kSyncRootDirectoryName[] = "Chrome Syncable FileSystem"; const char kResourceLinkPrefix[] = "https://docs.google.com/feeds/default/private/full/"; +const char kMimeTypeOctetStream[] = "application/octet-stream"; + +// This path is not actually used but is required by DriveUploaderInterface. +const FilePath::CharType kDummyDrivePath[] = + FILE_PATH_LITERAL("/dummy/drive/path"); bool HasParentLinkTo(const ScopedVector<google_apis::Link>& links, const std::string& parent_resource_id) { @@ -125,10 +131,8 @@ void DriveFileSyncClient::DidGetDirectory( return; } - google_apis::DocumentEntry* entry = - GetDocumentByTitleAndParent(feed->entries(), - parent_resource_id, - ASCIIToUTF16(directory_name)); + google_apis::DocumentEntry* entry = GetDocumentByTitleAndParent( + feed->entries(), parent_resource_id, ASCIIToUTF16(directory_name)); if (!entry) { if (parent_resource_id.empty()) { // Use empty content URL for root directory. @@ -166,9 +170,15 @@ void DriveFileSyncClient::DidGetParentDirectoryForCreateDirectory( callback.Run(error, std::string()); return; } + DCHECK(data); scoped_ptr<google_apis::DocumentEntry> entry( google_apis::DocumentEntry::ExtractAndParse(*data)); + if (!entry) { + callback.Run(google_apis::GDATA_PARSE_ERROR, std::string()); + return; + } + drive_service_->AddNewDirectory( entry->content_url(), directory_name, @@ -193,6 +203,7 @@ void DriveFileSyncClient::DidCreateDirectory( // to create the directory, we might make duplicated directories. scoped_ptr<google_apis::DocumentEntry> entry( google_apis::DocumentEntry::ExtractAndParse(*data)); + DCHECK(entry); callback.Run(error, entry->resource_id()); } @@ -226,7 +237,6 @@ void DriveFileSyncClient::SearchFilesInDirectory( const std::string& search_query, const DocumentFeedCallback& callback) { DCHECK(CalledOnValidThread()); - drive_service_->GetDocuments( GURL(), // feed_url 0, // start_changestamp @@ -240,7 +250,6 @@ void DriveFileSyncClient::SearchFilesInDirectory( void DriveFileSyncClient::ListFiles(const std::string& directory_resource_id, const DocumentFeedCallback& callback) { DCHECK(CalledOnValidThread()); - SearchFilesInDirectory(directory_resource_id, std::string() /* search_query */, callback); @@ -278,8 +287,14 @@ void DriveFileSyncClient::DownloadFile( const std::string& local_file_md5, const FilePath& local_file_path, const DownloadFileCallback& callback) { - // TODO(nhiroki): implement. - NOTIMPLEMENTED(); + DCHECK(CalledOnValidThread()); + drive_service_->GetDocumentEntry( + resource_id, + base::Bind(&DriveFileSyncClient::DidGetDocumentEntryData, + AsWeakPtr(), + base::Bind(&DriveFileSyncClient::DownloadFileInternal, + AsWeakPtr(), local_file_md5, local_file_path, + callback))); } void DriveFileSyncClient::UploadNewFile( @@ -288,8 +303,14 @@ void DriveFileSyncClient::UploadNewFile( const std::string& title, int64 file_size, const UploadFileCallback& callback) { - // TODO(nhiroki): implement. - NOTIMPLEMENTED(); + DCHECK(CalledOnValidThread()); + drive_service_->GetDocumentEntry( + directory_resource_id, + base::Bind(&DriveFileSyncClient::DidGetDocumentEntryData, + AsWeakPtr(), + base::Bind(&DriveFileSyncClient::UploadNewFileInternal, + AsWeakPtr(), local_file_path, title, file_size, + callback))); } void DriveFileSyncClient::UploadExistingFile( @@ -298,16 +319,27 @@ void DriveFileSyncClient::UploadExistingFile( const FilePath& local_file_path, int64 file_size, const UploadFileCallback& callback) { - // TODO(nhiroki): implement. - NOTIMPLEMENTED(); + DCHECK(CalledOnValidThread()); + drive_service_->GetDocumentEntry( + resource_id, + base::Bind(&DriveFileSyncClient::DidGetDocumentEntryData, + AsWeakPtr(), + base::Bind(&DriveFileSyncClient::UploadExistingFileInternal, + AsWeakPtr(), remote_file_md5, local_file_path, + file_size, callback))); } void DriveFileSyncClient::DeleteFile( const std::string& resource_id, const std::string& remote_file_md5, const GDataErrorCallback& callback) { - // TODO(nhiroki): implement. - NOTIMPLEMENTED(); + DCHECK(CalledOnValidThread()); + drive_service_->GetDocumentEntry( + resource_id, + base::Bind(&DriveFileSyncClient::DidGetDocumentEntryData, + AsWeakPtr(), + base::Bind(&DriveFileSyncClient::DeleteFileInternal, + AsWeakPtr(), remote_file_md5, callback))); } void DriveFileSyncClient::DidGetDocumentFeedData( @@ -316,10 +348,36 @@ void DriveFileSyncClient::DidGetDocumentFeedData( scoped_ptr<base::Value> data) { DCHECK(CalledOnValidThread()); - callback.Run(error, - error == google_apis::HTTP_SUCCESS ? - google_apis::DocumentFeed::ExtractAndParse(*data) : - scoped_ptr<google_apis::DocumentFeed>()); + if (error != google_apis::HTTP_SUCCESS) { + callback.Run(error, scoped_ptr<google_apis::DocumentFeed>()); + return; + } + + DCHECK(data); + scoped_ptr<google_apis::DocumentFeed> feed( + google_apis::DocumentFeed::ExtractAndParse(*data)); + if (!feed) + error = google_apis::GDATA_PARSE_ERROR; + callback.Run(error, feed.Pass()); +} + +void DriveFileSyncClient::DidGetDocumentEntryData( + const DocumentEntryCallback& callback, + google_apis::GDataErrorCode error, + scoped_ptr<base::Value> data) { + DCHECK(CalledOnValidThread()); + + if (error != google_apis::HTTP_SUCCESS) { + callback.Run(error, scoped_ptr<google_apis::DocumentEntry>()); + return; + } + + DCHECK(data); + scoped_ptr<google_apis::DocumentEntry> entry( + google_apis::DocumentEntry::ExtractAndParse(*data)); + if (!entry) + error = google_apis::GDATA_PARSE_ERROR; + callback.Run(error, entry.Pass()); } // static @@ -349,4 +407,189 @@ std::string DriveFileSyncClient::FormatTitleQuery(const std::string& title) { return out.str(); } +void DriveFileSyncClient::DownloadFileInternal( + const std::string& local_file_md5, + const FilePath& local_file_path, + const DownloadFileCallback& callback, + google_apis::GDataErrorCode error, + scoped_ptr<google_apis::DocumentEntry> entry) { + DCHECK(CalledOnValidThread()); + + if (error != google_apis::HTTP_SUCCESS) { + callback.Run(error, std::string()); + return; + } + DCHECK(entry); + + // If local file and remote file are same, cancel the download. + if (local_file_md5 == entry->file_md5()) { + callback.Run(google_apis::HTTP_NOT_MODIFIED, local_file_md5); + return; + } + + // TODO(nhiroki): support ETag. Currently we assume there is no change between + // GetDocumentEntry and DownloadFile call. + drive_service_->DownloadFile( + FilePath(kDummyDrivePath), + local_file_path, + entry->content_url(), + base::Bind(&DriveFileSyncClient::DidDownloadFile, + AsWeakPtr(), entry->file_md5(), callback), + google_apis::GetContentCallback()); +} + +void DriveFileSyncClient::DidDownloadFile( + const std::string& downloaded_file_md5, + const DownloadFileCallback& callback, + google_apis::GDataErrorCode error, + const GURL& content_url, + const FilePath& downloaded_file_path) { + DCHECK(CalledOnValidThread()); + callback.Run(error, downloaded_file_md5); +} + +void DriveFileSyncClient::UploadNewFileInternal( + const FilePath& local_file_path, + const std::string& title, + int64 file_size, + const UploadFileCallback& callback, + google_apis::GDataErrorCode error, + scoped_ptr<google_apis::DocumentEntry> parent_directory_entry) { + DCHECK(CalledOnValidThread()); + + if (error != google_apis::HTTP_SUCCESS) { + callback.Run(error, std::string(), std::string()); + return; + } + DCHECK(parent_directory_entry); + + std::string mime_type; + if (!net::GetWellKnownMimeTypeFromExtension( + local_file_path.Extension(), &mime_type)) + mime_type = kMimeTypeOctetStream; + + drive_uploader_->UploadNewFile( + parent_directory_entry->GetLinkByType( + google_apis::Link::LINK_RESUMABLE_CREATE_MEDIA)->href(), + FilePath(kDummyDrivePath), + local_file_path, + title, + mime_type, + file_size, // content_length. + file_size, + base::Bind(&DriveFileSyncClient::DidUploadFile, + AsWeakPtr(), callback), + google_apis::UploaderReadyCallback()); +} + +void DriveFileSyncClient::UploadExistingFileInternal( + const std::string& remote_file_md5, + const FilePath& local_file_path, + int64 file_size, + const UploadFileCallback& callback, + google_apis::GDataErrorCode error, + scoped_ptr<google_apis::DocumentEntry> entry) { + DCHECK(CalledOnValidThread()); + + if (error != google_apis::HTTP_SUCCESS) { + callback.Run(error, std::string(), std::string()); + return; + } + DCHECK(entry); + + // If remote file's hash value is different from the expected one, conflict + // might have occurred. + if (remote_file_md5 != entry->file_md5()) { + callback.Run(google_apis::HTTP_CONFLICT, std::string(), std::string()); + return; + } + + std::string mime_type; + if (!net::GetWellKnownMimeTypeFromExtension( + local_file_path.Extension(), &mime_type)) + mime_type = kMimeTypeOctetStream; + + // TODO(nhiroki): support ETag. Currently we assume there is no change between + // GetDocumentEntry and UploadExistingFile call. + drive_uploader_->UploadExistingFile( + entry->GetLinkByType( + google_apis::Link::LINK_RESUMABLE_EDIT_MEDIA)->href(), + FilePath(kDummyDrivePath), + local_file_path, + mime_type, + file_size, + base::Bind(&DriveFileSyncClient::DidUploadFile, + AsWeakPtr(), callback), + google_apis::UploaderReadyCallback()); +} + +void DriveFileSyncClient::DidUploadFile( + const UploadFileCallback& callback, + google_apis::DriveUploadError error, + const FilePath& drive_path, + const FilePath& file_path, + scoped_ptr<google_apis::DocumentEntry> entry) { + DCHECK(CalledOnValidThread()); + + // Convert DriveUploadError to GDataErrorCode. + switch (error) { + case google_apis::DRIVE_UPLOAD_OK: + DCHECK(entry); + callback.Run(google_apis::HTTP_SUCCESS, + entry->resource_id(), entry->file_md5()); + return; + case google_apis::DRIVE_UPLOAD_ERROR_NOT_FOUND: + callback.Run(google_apis::HTTP_NOT_FOUND, + std::string(), std::string()); + return; + case google_apis::DRIVE_UPLOAD_ERROR_NO_SPACE: + callback.Run(google_apis::GDATA_NO_SPACE, + std::string(), std::string()); + return; + case google_apis::DRIVE_UPLOAD_ERROR_ABORT: + callback.Run(google_apis::GDATA_OTHER_ERROR, + std::string(), std::string()); + return; + } + NOTREACHED(); + callback.Run(google_apis::GDATA_OTHER_ERROR, std::string(), std::string()); +} + +void DriveFileSyncClient::DeleteFileInternal( + const std::string& remote_file_md5, + const GDataErrorCallback& callback, + google_apis::GDataErrorCode error, + scoped_ptr<google_apis::DocumentEntry> entry) { + DCHECK(CalledOnValidThread()); + + if (error != google_apis::HTTP_SUCCESS) { + callback.Run(error); + return; + } + DCHECK(entry); + + // If remote file's hash value is different from the expected one, conflict + // might have occurred. + if (remote_file_md5 != entry->file_md5()) { + callback.Run(google_apis::HTTP_CONFLICT); + return; + } + + // Move the file to trash (don't delete it completely). + // TODO(nhiroki): support ETag. Currently we assume there is no change between + // GetDocumentEntry and DeleteFile call. + drive_service_->DeleteDocument( + GURL(entry->GetLinkByType(google_apis::Link::LINK_SELF)->href()), + base::Bind(&DriveFileSyncClient::DidDeleteFile, + AsWeakPtr(), callback)); +} + +void DriveFileSyncClient::DidDeleteFile( + const GDataErrorCallback& callback, + google_apis::GDataErrorCode error, + const GURL& document_url) { + DCHECK(CalledOnValidThread()); + callback.Run(error); +} + } // namespace sync_file_system diff --git a/chrome/browser/sync_file_system/drive_file_sync_client.h b/chrome/browser/sync_file_system/drive_file_sync_client.h index 360225f..7900bbe5 100644 --- a/chrome/browser/sync_file_system/drive_file_sync_client.h +++ b/chrome/browser/sync_file_system/drive_file_sync_client.h @@ -12,6 +12,7 @@ #include "base/memory/weak_ptr.h" #include "base/threading/non_thread_safe.h" #include "chrome/browser/google_apis/drive_service_interface.h" +#include "chrome/browser/google_apis/drive_upload_error.h" #include "chrome/browser/google_apis/gdata_errorcode.h" #include "chrome/browser/google_apis/gdata_wapi_parser.h" @@ -38,6 +39,7 @@ class DriveFileSyncClient : public base::NonThreadSafe, const std::string& file_md5)> DownloadFileCallback; typedef base::Callback<void(google_apis::GDataErrorCode error, + const std::string& resource_id, const std::string& file_md5)> UploadFileCallback; typedef base::Callback<void(google_apis::GDataErrorCode error, @@ -48,6 +50,9 @@ class DriveFileSyncClient : public base::NonThreadSafe, typedef base::Callback<void(google_apis::GDataErrorCode error, scoped_ptr<google_apis::DocumentFeed> feed)> DocumentFeedCallback; + typedef base::Callback<void(google_apis::GDataErrorCode error, + scoped_ptr<google_apis::DocumentEntry> entry)> + DocumentEntryCallback; explicit DriveFileSyncClient(Profile* profile); virtual ~DriveFileSyncClient(); @@ -175,6 +180,53 @@ class DriveFileSyncClient : public base::NonThreadSafe, google_apis::GDataErrorCode error, scoped_ptr<base::Value> data); + void DidGetDocumentEntryData(const DocumentEntryCallback& callback, + google_apis::GDataErrorCode error, + scoped_ptr<base::Value> data); + + void DownloadFileInternal(const std::string& local_file_md5, + const FilePath& local_file_path, + const DownloadFileCallback& callback, + google_apis::GDataErrorCode error, + scoped_ptr<google_apis::DocumentEntry> entry); + + void DidDownloadFile(const std::string& downloaded_file_md5, + const DownloadFileCallback& callback, + google_apis::GDataErrorCode error, + const GURL& content_url, + const FilePath& downloaded_file_path); + + void UploadNewFileInternal( + const FilePath& local_file_path, + const std::string& title, + int64 file_size, + const UploadFileCallback& callback, + google_apis::GDataErrorCode error, + scoped_ptr<google_apis::DocumentEntry> parent_directory_entry); + + void UploadExistingFileInternal( + const std::string& remote_file_md5, + const FilePath& local_file_path, + int64 file_size, + const UploadFileCallback& callback, + google_apis::GDataErrorCode error, + scoped_ptr<google_apis::DocumentEntry> entry); + + void DidUploadFile(const UploadFileCallback& callback, + google_apis::DriveUploadError error, + const FilePath& drive_path, + const FilePath& file_path, + scoped_ptr<google_apis::DocumentEntry> entry); + + void DeleteFileInternal(const std::string& remote_file_md5, + const GDataErrorCallback& callback, + google_apis::GDataErrorCode error, + scoped_ptr<google_apis::DocumentEntry> entry); + + void DidDeleteFile(const GDataErrorCallback& callback, + google_apis::GDataErrorCode error, + const GURL& document_url); + static std::string FormatTitleQuery(const std::string& title); scoped_ptr<google_apis::DriveServiceInterface> drive_service_; diff --git a/chrome/browser/sync_file_system/drive_file_sync_client_unittest.cc b/chrome/browser/sync_file_system/drive_file_sync_client_unittest.cc index f9aec7f..126ade2 100644 --- a/chrome/browser/sync_file_system/drive_file_sync_client_unittest.cc +++ b/chrome/browser/sync_file_system/drive_file_sync_client_unittest.cc @@ -16,13 +16,17 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#define FPL(x) FILE_PATH_LITERAL(x) + using ::testing::StrictMock; using ::testing::_; +using google_apis::DocumentEntry; using google_apis::DocumentFeed; using google_apis::DriveServiceInterface; using google_apis::DriveUploaderInterface; using google_apis::GDataErrorCode; +using google_apis::Link; using google_apis::MockDriveService; using google_apis::MockDriveUploader; @@ -115,6 +119,39 @@ ACTION_P2(InvokeGetDataCallback5, error, result) { base::Bind(arg5, error, base::Passed(&value))); } +// Invokes |arg3| as a DownloadActionCallback. +ACTION_P3(InvokeDownloadActionCallback3, + error, content_url, downloaded_file_path) { + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, + base::Bind(arg3, error, content_url, downloaded_file_path)); +} + +// Invokes |arg5| as a UploadCompletionCallback. +ACTION_P4(InvokeUploadCompletionCallback5, + error, drive_path, file_path, document_entry) { + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, + base::Bind(arg5, error, drive_path, file_path, document_entry)); + return 1; // Return dummy upload ID. +} + +// Invokes |arg7| as a UploadCompletionCallback. +ACTION_P4(InvokeUploadCompletionCallback7, + error, drive_path, file_path, document_entry) { + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, + base::Bind(arg7, error, drive_path, file_path, document_entry)); + return 1; // Return dummy upload ID. +} + +// Invokes |arg1| as a EntryActionCallback. +ACTION_P2(InvokeEntryActionCallback2, error, document_url) { + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, + base::Bind(arg1, error, document_url)); +} + void DidGetResourceID(bool* done_out, GDataErrorCode* error_out, std::string* resource_id_out, @@ -150,6 +187,36 @@ void DidGetDocumentFeed(bool* done_out, *document_feed_out = document_feed.Pass(); } +void DidDownloadFile(bool* done_out, + std::string* expected_file_md5_out, + GDataErrorCode* error_out, + GDataErrorCode error, + const std::string& expected_file_md5) { + EXPECT_FALSE(*done_out); + *done_out = true; + *error_out = error; + *expected_file_md5_out = expected_file_md5; +} + +void DidUploadFile(bool* done_out, + GDataErrorCode* error_out, + std::string* resource_id_out, + GDataErrorCode error, + const std::string& resource_id, + const std::string& file_md5) { + EXPECT_FALSE(*done_out); + *done_out = true; + *error_out = error; +} + +void DidDeleteFile(bool* done_out, + GDataErrorCode* error_out, + GDataErrorCode error) { + EXPECT_FALSE(*done_out); + *done_out = true; + *error_out = error; +} + TEST_F(DriveFileSyncClientTest, GetSyncRoot) { scoped_ptr<base::Value> found_result(google_apis::test_util::LoadJSONFile( "sync_file_system/sync_root_found.json").Pass()); @@ -157,7 +224,7 @@ TEST_F(DriveFileSyncClientTest, GetSyncRoot) { // Expected to call GetDocuments from GetDriveDirectoryForSyncRoot. EXPECT_CALL(*mock_drive_service(), GetDocuments(GURL(), // feed_url - 0, // start_changestamp, + 0, // start_changestamp FormatTitleQuery(kSyncRootDirectoryName), false, // shared_with_me std::string(), // directory_resource_id, @@ -445,6 +512,307 @@ TEST_F(DriveFileSyncClientTest, ListChanges) { EXPECT_FALSE(document_feed->entries().empty()); } +TEST_F(DriveFileSyncClientTest, DownloadFile) { + const std::string kResourceId = "file:resource_id"; + const std::string kLocalFileMD5 = "123456"; + const FilePath kLocalFilePath(FPL("/tmp/dir/file")); + + scoped_ptr<base::Value> file_entry_data( + google_apis::test_util::LoadJSONFile("gdata/file_entry.json").Pass()); + scoped_ptr<DocumentEntry> entry( + DocumentEntry::ExtractAndParse(*file_entry_data)); + + testing::InSequence sequence; + + // Expected to call GetDocumentEntry from DriveFileSyncClient::UploadNewFile. + EXPECT_CALL(*mock_drive_service(), + GetDocumentEntry(kResourceId, _)) + .WillOnce(InvokeGetDataCallback1(google_apis::HTTP_SUCCESS, + base::Passed(&file_entry_data))) + .RetiresOnSaturation(); + + // Expected to call DriveUploaderInterface::DownloadFile from + // DidGetDocumentEntryForDownloadFile. + EXPECT_CALL(*mock_drive_service(), + DownloadFile(_, // drive_path + kLocalFilePath, + entry->content_url(), + _, _)) + .WillOnce(InvokeDownloadActionCallback3(google_apis::HTTP_SUCCESS, + entry->content_url(), + kLocalFilePath)) + .RetiresOnSaturation(); + + bool done = false; + std::string file_md5; + GDataErrorCode error = google_apis::GDATA_OTHER_ERROR; + sync_client()->DownloadFile(kResourceId, + kLocalFileMD5, + kLocalFilePath, + base::Bind(&DidDownloadFile, + &done, &file_md5, &error)); + message_loop()->RunUntilIdle(); + + EXPECT_TRUE(done); + EXPECT_EQ(entry->file_md5(), file_md5); + EXPECT_EQ(google_apis::HTTP_SUCCESS, error); +} + +TEST_F(DriveFileSyncClientTest, DownloadFileInNotModified) { + const std::string kResourceId = "file:resource_id"; + const FilePath kLocalFilePath(FPL("/tmp/dir/file")); + + scoped_ptr<base::Value> file_entry_data( + google_apis::test_util::LoadJSONFile("gdata/file_entry.json").Pass()); + scoped_ptr<DocumentEntry> entry( + DocumentEntry::ExtractAndParse(*file_entry_data)); + + // Since local file's hash value is equal to remote file's one, it is expected + // to cancel download the file and to return NOT_MODIFIED status code. + const std::string kLocalFileMD5 = entry->file_md5(); + + testing::InSequence sequence; + + // Expected to call GetDocumentEntry from DriveFileSyncClient::UploadNewFile. + EXPECT_CALL(*mock_drive_service(), + GetDocumentEntry(kResourceId, _)) + .WillOnce(InvokeGetDataCallback1(google_apis::HTTP_SUCCESS, + base::Passed(&file_entry_data))) + .RetiresOnSaturation(); + + bool done = false; + std::string file_md5; + GDataErrorCode error = google_apis::GDATA_OTHER_ERROR; + sync_client()->DownloadFile(kResourceId, + kLocalFileMD5, + kLocalFilePath, + base::Bind(&DidDownloadFile, + &done, &file_md5, &error)); + message_loop()->RunUntilIdle(); + + EXPECT_TRUE(done); + EXPECT_EQ(entry->file_md5(), file_md5); + EXPECT_EQ(google_apis::HTTP_NOT_MODIFIED, error); +} + +TEST_F(DriveFileSyncClientTest, UploadNewFile) { + const std::string kDirectoryResourceId = "folder:directory_resource_id"; + const FilePath kLocalFilePath(FPL("/tmp/dir/file")); + const std::string kTitle("testfile"); + int64 kFileSize = 1024; + + scoped_ptr<base::Value> dir_entry_data(google_apis::test_util::LoadJSONFile( + "gdata/directory_entry.json").Pass()); + scoped_ptr<base::Value> file_entry_data(google_apis::test_util::LoadJSONFile( + "gdata/file_entry.json").Pass()); + scoped_ptr<DocumentEntry> dir_entry( + DocumentEntry::ExtractAndParse(*dir_entry_data)); + scoped_ptr<DocumentEntry> file_entry( + DocumentEntry::ExtractAndParse(*file_entry_data)); + const GURL link_url = + dir_entry->GetLinkByType(Link::LINK_RESUMABLE_CREATE_MEDIA)->href(); + + testing::InSequence sequence; + + // Expected to call GetDocumentEntry from DriveFileSyncClient::UploadNewFile. + EXPECT_CALL(*mock_drive_service(), + GetDocumentEntry(kDirectoryResourceId, _)) + .WillOnce(InvokeGetDataCallback1(google_apis::HTTP_SUCCESS, + base::Passed(&dir_entry_data))) + .RetiresOnSaturation(); + + // Expected to call DriveUploaderInterface::UploadNewFile from + // DidGetDocumentEntryForUploadNewFile. + EXPECT_CALL(*mock_drive_uploader(), + UploadNewFile(link_url, + _, // drive_path + kLocalFilePath, + kTitle, + _, // content_type + kFileSize, // content_length + kFileSize, + _, _)) + .WillOnce(InvokeUploadCompletionCallback7(google_apis::DRIVE_UPLOAD_OK, + FilePath(), + kLocalFilePath, + base::Passed(&file_entry))) + .RetiresOnSaturation(); + + bool done = false; + GDataErrorCode error = google_apis::GDATA_OTHER_ERROR; + std::string resource_id; + sync_client()->UploadNewFile(kDirectoryResourceId, + kLocalFilePath, + kTitle, + kFileSize, + base::Bind(&DidUploadFile, + &done, &error, &resource_id)); + message_loop()->RunUntilIdle(); + + EXPECT_TRUE(done); + EXPECT_EQ(google_apis::HTTP_SUCCESS, error); +} + +TEST_F(DriveFileSyncClientTest, UploadExistingFile) { + const std::string kResourceId = "file:resource_id"; + const FilePath kLocalFilePath(FPL("/tmp/dir/file")); + int64 kFileSize = 1024; + + scoped_ptr<base::Value> file_entry_data( + google_apis::test_util::LoadJSONFile("gdata/file_entry.json").Pass()); + scoped_ptr<DocumentEntry> entry( + DocumentEntry::ExtractAndParse(*file_entry_data)); + const std::string expected_remote_file_md5 = entry->file_md5(); + const GURL link_url = + entry->GetLinkByType(Link::LINK_RESUMABLE_EDIT_MEDIA)->href(); + + testing::InSequence sequence; + + // Expected to call GetDocumentEntry from + // DriveFileSyncClient::UploadExistingFile. + EXPECT_CALL(*mock_drive_service(), + GetDocumentEntry(kResourceId, _)) + .WillOnce(InvokeGetDataCallback1(google_apis::HTTP_SUCCESS, + base::Passed(&file_entry_data))) + .RetiresOnSaturation(); + + // Expected to call DriveUploaderInterface::UploadExistingFile from + // DidGetDocumentEntryForUploadExistingFile. + EXPECT_CALL(*mock_drive_uploader(), + UploadExistingFile(link_url, + _, // drive_path + kLocalFilePath, + _, // content_type + kFileSize, // content_length + _, _)) + .WillOnce(InvokeUploadCompletionCallback5(google_apis::DRIVE_UPLOAD_OK, + FilePath(), + kLocalFilePath, + base::Passed(&entry))) + .RetiresOnSaturation(); + + bool done = false; + GDataErrorCode error = google_apis::GDATA_OTHER_ERROR; + std::string resource_id; + sync_client()->UploadExistingFile(kResourceId, + expected_remote_file_md5, + kLocalFilePath, + kFileSize, + base::Bind(&DidUploadFile, + &done, &error, &resource_id)); + message_loop()->RunUntilIdle(); + + EXPECT_TRUE(done); + EXPECT_EQ(google_apis::HTTP_SUCCESS, error); +} + +TEST_F(DriveFileSyncClientTest, UploadExistingFileInConflict) { + const std::string kResourceId = "file:resource_id"; + const FilePath kLocalFilePath(FPL("/tmp/dir/file")); + int64 kFileSize = 1024; + + // Since remote file's hash value is different from the expected one, it is + // expected to cancel upload the file and to return CONFLICT status code. + const std::string kExpectedRemoteFileMD5 = "123456"; + + scoped_ptr<base::Value> file_entry_data( + google_apis::test_util::LoadJSONFile("gdata/file_entry.json").Pass()); + scoped_ptr<DocumentEntry> entry( + DocumentEntry::ExtractAndParse(*file_entry_data)); + + testing::InSequence sequence; + + // Expected to call GetDocumentEntry from + // DriveFileSyncClient::UploadExistingFile. + EXPECT_CALL(*mock_drive_service(), + GetDocumentEntry(kResourceId, _)) + .WillOnce(InvokeGetDataCallback1(google_apis::HTTP_SUCCESS, + base::Passed(&file_entry_data))) + .RetiresOnSaturation(); + + bool done = false; + GDataErrorCode error = google_apis::GDATA_OTHER_ERROR; + std::string resource_id; + sync_client()->UploadExistingFile(kResourceId, + kExpectedRemoteFileMD5, + kLocalFilePath, + kFileSize, + base::Bind(&DidUploadFile, + &done, &error, &resource_id)); + message_loop()->RunUntilIdle(); + + EXPECT_TRUE(done); + EXPECT_EQ(google_apis::HTTP_CONFLICT, error); +} + +TEST_F(DriveFileSyncClientTest, DeleteFile) { + const std::string kResourceId = "file:resource_id"; + + scoped_ptr<base::Value> file_entry_data( + google_apis::test_util::LoadJSONFile("gdata/file_entry.json").Pass()); + scoped_ptr<DocumentEntry> entry( + DocumentEntry::ExtractAndParse(*file_entry_data)); + const std::string kExpectedRemoteFileMD5 = entry->file_md5(); + + testing::InSequence sequence; + + // Expected to call GetDocumentEntry from DriveFileSyncClient::DeleteFile. + EXPECT_CALL(*mock_drive_service(), GetDocumentEntry(kResourceId, _)) + .WillOnce(InvokeGetDataCallback1(google_apis::HTTP_SUCCESS, + base::Passed(&file_entry_data))) + .RetiresOnSaturation(); + + // Expected to call DriveUploaderInterface::DeleteDocument from + // DidGetDocumentEntryForDeleteFile. + EXPECT_CALL(*mock_drive_service(), + DeleteDocument(entry->GetLinkByType(Link::LINK_SELF)->href(), _)) + .WillOnce(InvokeEntryActionCallback2(google_apis::HTTP_SUCCESS, GURL())) + .RetiresOnSaturation(); + + bool done = false; + GDataErrorCode error = google_apis::GDATA_OTHER_ERROR; + std::string resource_id; + sync_client()->DeleteFile(kResourceId, + kExpectedRemoteFileMD5, + base::Bind(&DidDeleteFile, &done, &error)); + message_loop()->RunUntilIdle(); + + EXPECT_TRUE(done); + EXPECT_EQ(google_apis::HTTP_SUCCESS, error); +} + +TEST_F(DriveFileSyncClientTest, DeleteFileInConflict) { + const std::string kResourceId = "file:resource_id"; + + // Since remote file's hash value is different from the expected one, it is + // expected to cancel delete the file and to return CONFLICT status code. + const std::string kExpectedRemoteFileMD5 = "123456"; + + scoped_ptr<base::Value> file_entry_data( + google_apis::test_util::LoadJSONFile("gdata/file_entry.json").Pass()); + scoped_ptr<DocumentEntry> entry( + DocumentEntry::ExtractAndParse(*file_entry_data)); + + testing::InSequence sequence; + + // Expected to call GetDocumentEntry from DriveFileSyncClient::DeleteFile. + EXPECT_CALL(*mock_drive_service(), GetDocumentEntry(kResourceId, _)) + .WillOnce(InvokeGetDataCallback1(google_apis::HTTP_SUCCESS, + base::Passed(&file_entry_data))) + .RetiresOnSaturation(); + + bool done = false; + GDataErrorCode error = google_apis::GDATA_OTHER_ERROR; + std::string resource_id; + sync_client()->DeleteFile(kResourceId, + kExpectedRemoteFileMD5, + base::Bind(&DidDeleteFile, &done, &error)); + message_loop()->RunUntilIdle(); + + EXPECT_TRUE(done); + EXPECT_EQ(google_apis::HTTP_CONFLICT, error); +} + #endif // !defined(OS_ANDROID) } // namespace sync_file_system diff --git a/chrome/browser/sync_file_system/drive_file_sync_util.cc b/chrome/browser/sync_file_system/drive_file_sync_util.cc index 4799221..4317f125 100644 --- a/chrome/browser/sync_file_system/drive_file_sync_util.cc +++ b/chrome/browser/sync_file_system/drive_file_sync_util.cc @@ -16,9 +16,11 @@ fileapi::SyncStatusCode GDataErrorCodeToSyncStatusCode( case google_apis::HTTP_SUCCESS: case google_apis::HTTP_CREATED: case google_apis::HTTP_FOUND: - case google_apis::HTTP_NOT_MODIFIED: return fileapi::SYNC_STATUS_OK; + case google_apis::HTTP_NOT_MODIFIED: + return fileapi::SYNC_STATUS_NOT_MODIFIED; + case google_apis::HTTP_CONFLICT: return fileapi::SYNC_STATUS_HAS_CONFLICT; @@ -48,6 +50,9 @@ fileapi::SyncStatusCode GDataErrorCodeToSyncStatusCode( case google_apis::GDATA_PARSE_ERROR: case google_apis::GDATA_OTHER_ERROR: return fileapi::SYNC_STATUS_FAILED; + + case google_apis::GDATA_NO_SPACE: + return fileapi::SYNC_FILE_ERROR_NO_SPACE; } NOTREACHED(); return fileapi::SYNC_STATUS_FAILED; diff --git a/webkit/fileapi/syncable/sync_status_code.h b/webkit/fileapi/syncable/sync_status_code.h index 17092af..e99a217 100644 --- a/webkit/fileapi/syncable/sync_status_code.h +++ b/webkit/fileapi/syncable/sync_status_code.h @@ -55,6 +55,7 @@ enum SyncStatusCode { SYNC_STATUS_NETWORK_ERROR = -106, SYNC_STATUS_AUTHENTICATION_FAILED = -107, SYNC_STATUS_NOT_INITIALIZED = -108, + SYNC_STATUS_NOT_MODIFIED = -109, }; WEBKIT_STORAGE_EXPORT SyncStatusCode LevelDBStatusToSyncStatusCode( |