summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornhiroki@chromium.org <nhiroki@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-19 11:56:04 +0000
committernhiroki@chromium.org <nhiroki@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-19 11:56:04 +0000
commit18a348016380ac8731ed0bce9bd2b89dd1dd3a79 (patch)
tree8f2fd0d6cc3ad148a0812de0d4c4126bc13b1f88
parent96c328c079d5262352bed24641f4ee6dcbf25c8c (diff)
downloadchromium_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
-rw-r--r--chrome/browser/google_apis/gdata_errorcode.h1
-rw-r--r--chrome/browser/sync_file_system/drive_file_sync_client.cc279
-rw-r--r--chrome/browser/sync_file_system/drive_file_sync_client.h52
-rw-r--r--chrome/browser/sync_file_system/drive_file_sync_client_unittest.cc370
-rw-r--r--chrome/browser/sync_file_system/drive_file_sync_util.cc7
-rw-r--r--webkit/fileapi/syncable/sync_status_code.h1
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(