summaryrefslogtreecommitdiffstats
path: root/components/drive/service
diff options
context:
space:
mode:
Diffstat (limited to 'components/drive/service')
-rw-r--r--components/drive/service/drive_api_service.cc872
-rw-r--r--components/drive/service/drive_api_service.h269
-rw-r--r--components/drive/service/drive_api_service_unittest.cc79
-rw-r--r--components/drive/service/drive_service_interface.cc27
-rw-r--r--components/drive/service/drive_service_interface.h467
-rw-r--r--components/drive/service/dummy_drive_service.cc224
-rw-r--r--components/drive/service/dummy_drive_service.h166
-rw-r--r--components/drive/service/fake_drive_service.cc1787
-rw-r--r--components/drive/service/fake_drive_service.h406
-rw-r--r--components/drive/service/fake_drive_service_unittest.cc2177
-rw-r--r--components/drive/service/test_util.cc193
-rw-r--r--components/drive/service/test_util.h19
12 files changed, 6686 insertions, 0 deletions
diff --git a/components/drive/service/drive_api_service.cc b/components/drive/service/drive_api_service.cc
new file mode 100644
index 0000000..644079a
--- /dev/null
+++ b/components/drive/service/drive_api_service.cc
@@ -0,0 +1,872 @@
+// Copyright (c) 2012 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/service/drive_api_service.h"
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/strings/stringprintf.h"
+#include "components/drive/drive_api_util.h"
+#include "google_apis/drive/auth_service.h"
+#include "google_apis/drive/base_requests.h"
+#include "google_apis/drive/drive_api_parser.h"
+#include "google_apis/drive/drive_api_requests.h"
+#include "google_apis/drive/files_list_request_runner.h"
+#include "google_apis/drive/request_sender.h"
+#include "google_apis/google_api_keys.h"
+#include "net/url_request/url_request_context_getter.h"
+
+using google_apis::AboutResourceCallback;
+using google_apis::AppList;
+using google_apis::AppListCallback;
+using google_apis::AuthStatusCallback;
+using google_apis::AuthorizeAppCallback;
+using google_apis::CancelCallback;
+using google_apis::ChangeList;
+using google_apis::ChangeListCallback;
+using google_apis::DownloadActionCallback;
+using google_apis::EntryActionCallback;
+using google_apis::FileList;
+using google_apis::FileListCallback;
+using google_apis::FileResource;
+using google_apis::FileResourceCallback;
+using google_apis::DRIVE_OTHER_ERROR;
+using google_apis::DRIVE_PARSE_ERROR;
+using google_apis::DriveApiErrorCode;
+using google_apis::GetContentCallback;
+using google_apis::GetShareUrlCallback;
+using google_apis::HTTP_NOT_IMPLEMENTED;
+using google_apis::HTTP_SUCCESS;
+using google_apis::InitiateUploadCallback;
+using google_apis::ProgressCallback;
+using google_apis::RequestSender;
+using google_apis::FilesListRequestRunner;
+using google_apis::UploadRangeResponse;
+using google_apis::drive::AboutGetRequest;
+using google_apis::drive::AppsListRequest;
+using google_apis::drive::ChangesListRequest;
+using google_apis::drive::ChangesListNextPageRequest;
+using google_apis::drive::ChildrenDeleteRequest;
+using google_apis::drive::ChildrenInsertRequest;
+using google_apis::drive::DownloadFileRequest;
+using google_apis::drive::FilesCopyRequest;
+using google_apis::drive::FilesGetRequest;
+using google_apis::drive::FilesInsertRequest;
+using google_apis::drive::FilesPatchRequest;
+using google_apis::drive::FilesListRequest;
+using google_apis::drive::FilesListNextPageRequest;
+using google_apis::drive::FilesDeleteRequest;
+using google_apis::drive::FilesTrashRequest;
+using google_apis::drive::GetUploadStatusRequest;
+using google_apis::drive::InitiateUploadExistingFileRequest;
+using google_apis::drive::InitiateUploadNewFileRequest;
+using google_apis::drive::ResumeUploadRequest;
+using google_apis::drive::UploadRangeCallback;
+
+namespace drive {
+
+namespace {
+
+// OAuth2 scopes for Drive API.
+const char kDriveScope[] = "https://www.googleapis.com/auth/drive";
+const char kDriveAppsReadonlyScope[] =
+ "https://www.googleapis.com/auth/drive.apps.readonly";
+const char kDriveAppsScope[] = "https://www.googleapis.com/auth/drive.apps";
+const char kDocsListScope[] = "https://docs.google.com/feeds/";
+
+// Mime type to create a directory.
+const char kFolderMimeType[] = "application/vnd.google-apps.folder";
+
+// Max number of file entries to be fetched in a single http request.
+//
+// The larger the number is,
+// - The total running time to fetch the whole file list will become shorter.
+// - The running time for a single request tends to become longer.
+// Since the file list fetching is a completely background task, for our side,
+// only the total time matters. However, the server seems to have a time limit
+// per single request, which disables us to set the largest value (1000).
+// TODO(kinaba): make it larger when the server gets faster.
+const int kMaxNumFilesResourcePerRequest = 300;
+const int kMaxNumFilesResourcePerRequestForSearch = 100;
+
+// For performance, we declare all fields we use.
+const char kAboutResourceFields[] =
+ "kind,quotaBytesTotal,quotaBytesUsedAggregate,largestChangeId,rootFolderId";
+const char kFileResourceFields[] =
+ "kind,id,title,createdDate,sharedWithMeDate,mimeType,"
+ "md5Checksum,fileSize,labels/trashed,imageMediaMetadata/width,"
+ "imageMediaMetadata/height,imageMediaMetadata/rotation,etag,"
+ "parents(id,parentLink),alternateLink,"
+ "modifiedDate,lastViewedByMeDate,shared";
+const char kFileResourceOpenWithLinksFields[] =
+ "kind,id,openWithLinks/*";
+const char kFileResourceShareLinkFields[] =
+ "kind,id,shareLink";
+const char kFileListFields[] =
+ "kind,items(kind,id,title,createdDate,sharedWithMeDate,"
+ "mimeType,md5Checksum,fileSize,labels/trashed,imageMediaMetadata/width,"
+ "imageMediaMetadata/height,imageMediaMetadata/rotation,etag,"
+ "parents(id,parentLink),alternateLink,"
+ "modifiedDate,lastViewedByMeDate,shared),nextLink";
+const char kChangeListFields[] =
+ "kind,items(file(kind,id,title,createdDate,sharedWithMeDate,"
+ "mimeType,md5Checksum,fileSize,labels/trashed,imageMediaMetadata/width,"
+ "imageMediaMetadata/height,imageMediaMetadata/rotation,etag,"
+ "parents(id,parentLink),alternateLink,modifiedDate,"
+ "lastViewedByMeDate,shared),deleted,id,fileId,modificationDate),nextLink,"
+ "largestChangeId";
+
+void ExtractOpenUrlAndRun(const std::string& app_id,
+ const AuthorizeAppCallback& callback,
+ DriveApiErrorCode error,
+ scoped_ptr<FileResource> value) {
+ DCHECK(!callback.is_null());
+
+ if (!value) {
+ callback.Run(error, GURL());
+ return;
+ }
+
+ const std::vector<FileResource::OpenWithLink>& open_with_links =
+ value->open_with_links();
+ for (size_t i = 0; i < open_with_links.size(); ++i) {
+ if (open_with_links[i].app_id == app_id) {
+ callback.Run(HTTP_SUCCESS, open_with_links[i].open_url);
+ return;
+ }
+ }
+
+ // Not found.
+ callback.Run(DRIVE_OTHER_ERROR, GURL());
+}
+
+void ExtractShareUrlAndRun(const GetShareUrlCallback& callback,
+ DriveApiErrorCode error,
+ scoped_ptr<FileResource> value) {
+ callback.Run(error, value ? value->share_link() : GURL());
+}
+
+// Ignores the |entry|, and runs the |callback|.
+void EntryActionCallbackAdapter(
+ const EntryActionCallback& callback,
+ DriveApiErrorCode error, scoped_ptr<FileResource> entry) {
+ callback.Run(error);
+}
+
+// The resource ID for the root directory for Drive API is defined in the spec:
+// https://developers.google.com/drive/folder
+const char kDriveApiRootDirectoryResourceId[] = "root";
+
+} // namespace
+
+BatchRequestConfigurator::BatchRequestConfigurator(
+ const base::WeakPtr<google_apis::drive::BatchUploadRequest>& batch_request,
+ base::SequencedTaskRunner* task_runner,
+ const google_apis::DriveApiUrlGenerator& url_generator,
+ const google_apis::CancelCallback& cancel_callback)
+ : batch_request_(batch_request),
+ task_runner_(task_runner),
+ url_generator_(url_generator),
+ cancel_callback_(cancel_callback) {
+}
+
+BatchRequestConfigurator::~BatchRequestConfigurator() {
+ // The batch requst has not been committed.
+ if (batch_request_)
+ cancel_callback_.Run();
+}
+
+google_apis::CancelCallback BatchRequestConfigurator::MultipartUploadNewFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& parent_resource_id,
+ const std::string& title,
+ const base::FilePath& local_file_path,
+ const UploadNewFileOptions& options,
+ const google_apis::FileResourceCallback& callback,
+ const google_apis::ProgressCallback& progress_callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ scoped_ptr<google_apis::BatchableDelegate> delegate(
+ new google_apis::drive::MultipartUploadNewFileDelegate(
+ task_runner_.get(), title, parent_resource_id, content_type,
+ content_length, options.modified_date, options.last_viewed_by_me_date,
+ local_file_path, options.properties, url_generator_, callback,
+ progress_callback));
+ // Batch request can be null when pre-authorization for the requst is failed
+ // in request sender.
+ if (batch_request_)
+ batch_request_->AddRequest(delegate.release());
+ else
+ delegate->NotifyError(DRIVE_OTHER_ERROR);
+ return cancel_callback_;
+}
+
+google_apis::CancelCallback
+BatchRequestConfigurator::MultipartUploadExistingFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& resource_id,
+ const base::FilePath& local_file_path,
+ const UploadExistingFileOptions& options,
+ const google_apis::FileResourceCallback& callback,
+ const google_apis::ProgressCallback& progress_callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ scoped_ptr<google_apis::BatchableDelegate> delegate(
+ new google_apis::drive::MultipartUploadExistingFileDelegate(
+ task_runner_.get(), options.title, resource_id,
+ options.parent_resource_id, content_type, content_length,
+ options.modified_date, options.last_viewed_by_me_date,
+ local_file_path, options.etag, options.properties, url_generator_,
+ callback, progress_callback));
+ // Batch request can be null when pre-authorization for the requst is failed
+ // in request sender.
+ if (batch_request_)
+ batch_request_->AddRequest(delegate.release());
+ else
+ delegate->NotifyError(DRIVE_OTHER_ERROR);
+ return cancel_callback_;
+}
+
+void BatchRequestConfigurator::Commit() {
+ DCHECK(CalledOnValidThread());
+ if (!batch_request_)
+ return;
+ batch_request_->Commit();
+ batch_request_.reset();
+}
+
+DriveAPIService::DriveAPIService(
+ OAuth2TokenService* oauth2_token_service,
+ net::URLRequestContextGetter* url_request_context_getter,
+ base::SequencedTaskRunner* blocking_task_runner,
+ const GURL& base_url,
+ const GURL& base_download_url,
+ const std::string& custom_user_agent)
+ : oauth2_token_service_(oauth2_token_service),
+ url_request_context_getter_(url_request_context_getter),
+ blocking_task_runner_(blocking_task_runner),
+ url_generator_(base_url, base_download_url),
+ custom_user_agent_(custom_user_agent) {
+}
+
+DriveAPIService::~DriveAPIService() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (sender_.get())
+ sender_->auth_service()->RemoveObserver(this);
+}
+
+void DriveAPIService::Initialize(const std::string& account_id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ std::vector<std::string> scopes;
+ scopes.push_back(kDriveScope);
+ scopes.push_back(kDriveAppsReadonlyScope);
+ scopes.push_back(kDriveAppsScope);
+
+ // Note: The following scope is used to support GetShareUrl on Drive API v2.
+ // Unfortunately, there is no support on Drive API v2, so we need to fall back
+ // to GData WAPI for the GetShareUrl.
+ scopes.push_back(kDocsListScope);
+
+ sender_.reset(new RequestSender(
+ new google_apis::AuthService(oauth2_token_service_,
+ account_id,
+ url_request_context_getter_.get(),
+ scopes),
+ url_request_context_getter_.get(),
+ blocking_task_runner_.get(),
+ custom_user_agent_));
+ sender_->auth_service()->AddObserver(this);
+
+ files_list_request_runner_.reset(
+ new FilesListRequestRunner(sender_.get(), url_generator_));
+}
+
+void DriveAPIService::AddObserver(DriveServiceObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void DriveAPIService::RemoveObserver(DriveServiceObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+bool DriveAPIService::CanSendRequest() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ return HasRefreshToken();
+}
+
+std::string DriveAPIService::GetRootResourceId() const {
+ return kDriveApiRootDirectoryResourceId;
+}
+
+CancelCallback DriveAPIService::GetAllFileList(
+ const FileListCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ FilesListRequest* request = new FilesListRequest(
+ sender_.get(), url_generator_, callback);
+ request->set_max_results(kMaxNumFilesResourcePerRequest);
+ request->set_q("trashed = false"); // Exclude trashed files.
+ request->set_fields(kFileListFields);
+ return sender_->StartRequestWithAuthRetry(request);
+}
+
+CancelCallback DriveAPIService::GetFileListInDirectory(
+ const std::string& directory_resource_id,
+ const FileListCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!directory_resource_id.empty());
+ DCHECK(!callback.is_null());
+
+ // Because children.list method on Drive API v2 returns only the list of
+ // children's references, but we need all file resource list.
+ // So, here we use files.list method instead, with setting parents query.
+ // After the migration from GData WAPI to Drive API v2, we should clean the
+ // code up by moving the responsibility to include "parents" in the query
+ // to client side.
+ // We aren't interested in files in trash in this context, neither.
+ return files_list_request_runner_->CreateAndStartWithSizeBackoff(
+ kMaxNumFilesResourcePerRequest,
+ base::StringPrintf(
+ "'%s' in parents and trashed = false",
+ util::EscapeQueryStringValue(directory_resource_id).c_str()),
+ kFileListFields, callback);
+}
+
+CancelCallback DriveAPIService::Search(
+ const std::string& search_query,
+ const FileListCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!search_query.empty());
+ DCHECK(!callback.is_null());
+
+ return files_list_request_runner_->CreateAndStartWithSizeBackoff(
+ kMaxNumFilesResourcePerRequestForSearch,
+ util::TranslateQuery(search_query), kFileListFields, callback);
+}
+
+CancelCallback DriveAPIService::SearchByTitle(
+ const std::string& title,
+ const std::string& directory_resource_id,
+ const FileListCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!title.empty());
+ DCHECK(!callback.is_null());
+
+ std::string query;
+ base::StringAppendF(&query, "title = '%s'",
+ util::EscapeQueryStringValue(title).c_str());
+ if (!directory_resource_id.empty()) {
+ base::StringAppendF(
+ &query, " and '%s' in parents",
+ util::EscapeQueryStringValue(directory_resource_id).c_str());
+ }
+ query += " and trashed = false";
+
+ FilesListRequest* request = new FilesListRequest(
+ sender_.get(), url_generator_, callback);
+ request->set_max_results(kMaxNumFilesResourcePerRequest);
+ request->set_q(query);
+ request->set_fields(kFileListFields);
+ return sender_->StartRequestWithAuthRetry(request);
+}
+
+CancelCallback DriveAPIService::GetChangeList(
+ int64 start_changestamp,
+ const ChangeListCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ ChangesListRequest* request = new ChangesListRequest(
+ sender_.get(), url_generator_, callback);
+ request->set_max_results(kMaxNumFilesResourcePerRequest);
+ request->set_start_change_id(start_changestamp);
+ request->set_fields(kChangeListFields);
+ return sender_->StartRequestWithAuthRetry(request);
+}
+
+CancelCallback DriveAPIService::GetRemainingChangeList(
+ const GURL& next_link,
+ const ChangeListCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!next_link.is_empty());
+ DCHECK(!callback.is_null());
+
+ ChangesListNextPageRequest* request = new ChangesListNextPageRequest(
+ sender_.get(), callback);
+ request->set_next_link(next_link);
+ request->set_fields(kChangeListFields);
+ return sender_->StartRequestWithAuthRetry(request);
+}
+
+CancelCallback DriveAPIService::GetRemainingFileList(
+ const GURL& next_link,
+ const FileListCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!next_link.is_empty());
+ DCHECK(!callback.is_null());
+
+ FilesListNextPageRequest* request = new FilesListNextPageRequest(
+ sender_.get(), callback);
+ request->set_next_link(next_link);
+ request->set_fields(kFileListFields);
+ return sender_->StartRequestWithAuthRetry(request);
+}
+
+CancelCallback DriveAPIService::GetFileResource(
+ const std::string& resource_id,
+ const FileResourceCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ FilesGetRequest* request = new FilesGetRequest(
+ sender_.get(), url_generator_, google_apis::IsGoogleChromeAPIKeyUsed(),
+ callback);
+ request->set_file_id(resource_id);
+ request->set_fields(kFileResourceFields);
+ return sender_->StartRequestWithAuthRetry(request);
+}
+
+CancelCallback DriveAPIService::GetShareUrl(
+ const std::string& resource_id,
+ const GURL& embed_origin,
+ const GetShareUrlCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ if (!google_apis::IsGoogleChromeAPIKeyUsed()) {
+ LOG(ERROR) << "Only the official build of Chrome OS can open share dialogs "
+ << "from the file manager.";
+ }
+
+ FilesGetRequest* request = new FilesGetRequest(
+ sender_.get(), url_generator_, google_apis::IsGoogleChromeAPIKeyUsed(),
+ base::Bind(&ExtractShareUrlAndRun, callback));
+ request->set_file_id(resource_id);
+ request->set_fields(kFileResourceShareLinkFields);
+ request->set_embed_origin(embed_origin);
+ return sender_->StartRequestWithAuthRetry(request);
+}
+
+CancelCallback DriveAPIService::GetAboutResource(
+ const AboutResourceCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ AboutGetRequest* request =
+ new AboutGetRequest(sender_.get(), url_generator_, callback);
+ request->set_fields(kAboutResourceFields);
+ return sender_->StartRequestWithAuthRetry(request);
+}
+
+CancelCallback DriveAPIService::GetAppList(const AppListCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ return sender_->StartRequestWithAuthRetry(
+ new AppsListRequest(sender_.get(), url_generator_,
+ google_apis::IsGoogleChromeAPIKeyUsed(), callback));
+}
+
+CancelCallback DriveAPIService::DownloadFile(
+ const base::FilePath& local_cache_path,
+ const std::string& resource_id,
+ const DownloadActionCallback& download_action_callback,
+ const GetContentCallback& get_content_callback,
+ const ProgressCallback& progress_callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!download_action_callback.is_null());
+ // get_content_callback may be null.
+
+ return sender_->StartRequestWithAuthRetry(new DownloadFileRequest(
+ sender_.get(), url_generator_, resource_id, local_cache_path,
+ download_action_callback, get_content_callback, progress_callback));
+}
+
+CancelCallback DriveAPIService::DeleteResource(
+ const std::string& resource_id,
+ const std::string& etag,
+ const EntryActionCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ FilesDeleteRequest* request = new FilesDeleteRequest(
+ sender_.get(), url_generator_, callback);
+ request->set_file_id(resource_id);
+ request->set_etag(etag);
+ return sender_->StartRequestWithAuthRetry(request);
+}
+
+CancelCallback DriveAPIService::TrashResource(
+ const std::string& resource_id,
+ const EntryActionCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ FilesTrashRequest* request = new FilesTrashRequest(
+ sender_.get(), url_generator_,
+ base::Bind(&EntryActionCallbackAdapter, callback));
+ request->set_file_id(resource_id);
+ request->set_fields(kFileResourceFields);
+ return sender_->StartRequestWithAuthRetry(request);
+}
+
+CancelCallback DriveAPIService::AddNewDirectory(
+ const std::string& parent_resource_id,
+ const std::string& directory_title,
+ const AddNewDirectoryOptions& options,
+ const FileResourceCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ FilesInsertRequest* request = new FilesInsertRequest(
+ sender_.get(), url_generator_, callback);
+ request->set_last_viewed_by_me_date(options.last_viewed_by_me_date);
+ request->set_mime_type(kFolderMimeType);
+ request->set_modified_date(options.modified_date);
+ request->add_parent(parent_resource_id);
+ request->set_title(directory_title);
+ request->set_properties(options.properties);
+ request->set_fields(kFileResourceFields);
+ return sender_->StartRequestWithAuthRetry(request);
+}
+
+CancelCallback DriveAPIService::CopyResource(
+ const std::string& resource_id,
+ const std::string& parent_resource_id,
+ const std::string& new_title,
+ const base::Time& last_modified,
+ const FileResourceCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ FilesCopyRequest* request = new FilesCopyRequest(
+ sender_.get(), url_generator_, callback);
+ request->set_file_id(resource_id);
+ request->add_parent(parent_resource_id);
+ request->set_title(new_title);
+ request->set_modified_date(last_modified);
+ request->set_fields(kFileResourceFields);
+ return sender_->StartRequestWithAuthRetry(request);
+}
+
+CancelCallback DriveAPIService::UpdateResource(
+ const std::string& resource_id,
+ const std::string& parent_resource_id,
+ const std::string& new_title,
+ const base::Time& last_modified,
+ const base::Time& last_viewed_by_me,
+ const google_apis::drive::Properties& properties,
+ const FileResourceCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ FilesPatchRequest* request = new FilesPatchRequest(
+ sender_.get(), url_generator_, callback);
+ request->set_file_id(resource_id);
+ request->set_title(new_title);
+ if (!parent_resource_id.empty())
+ request->add_parent(parent_resource_id);
+ if (!last_modified.is_null()) {
+ // Need to set setModifiedDate to true to overwrite modifiedDate.
+ request->set_set_modified_date(true);
+ request->set_modified_date(last_modified);
+ }
+ if (!last_viewed_by_me.is_null()) {
+ // Need to set updateViewedDate to false, otherwise the lastViewedByMeDate
+ // will be set to the request time (not the specified time via request).
+ request->set_update_viewed_date(false);
+ request->set_last_viewed_by_me_date(last_viewed_by_me);
+ }
+ request->set_fields(kFileResourceFields);
+ request->set_properties(properties);
+ return sender_->StartRequestWithAuthRetry(request);
+}
+
+CancelCallback DriveAPIService::AddResourceToDirectory(
+ const std::string& parent_resource_id,
+ const std::string& resource_id,
+ const EntryActionCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ ChildrenInsertRequest* request =
+ new ChildrenInsertRequest(sender_.get(), url_generator_, callback);
+ request->set_folder_id(parent_resource_id);
+ request->set_id(resource_id);
+ return sender_->StartRequestWithAuthRetry(request);
+}
+
+CancelCallback DriveAPIService::RemoveResourceFromDirectory(
+ const std::string& parent_resource_id,
+ const std::string& resource_id,
+ const EntryActionCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ ChildrenDeleteRequest* request =
+ new ChildrenDeleteRequest(sender_.get(), url_generator_, callback);
+ request->set_child_id(resource_id);
+ request->set_folder_id(parent_resource_id);
+ return sender_->StartRequestWithAuthRetry(request);
+}
+
+CancelCallback DriveAPIService::InitiateUploadNewFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& parent_resource_id,
+ const std::string& title,
+ const UploadNewFileOptions& options,
+ const InitiateUploadCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ InitiateUploadNewFileRequest* request =
+ new InitiateUploadNewFileRequest(sender_.get(),
+ url_generator_,
+ content_type,
+ content_length,
+ parent_resource_id,
+ title,
+ callback);
+ request->set_modified_date(options.modified_date);
+ request->set_last_viewed_by_me_date(options.last_viewed_by_me_date);
+ request->set_properties(options.properties);
+ return sender_->StartRequestWithAuthRetry(request);
+}
+
+CancelCallback DriveAPIService::InitiateUploadExistingFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& resource_id,
+ const UploadExistingFileOptions& options,
+ const InitiateUploadCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ InitiateUploadExistingFileRequest* request =
+ new InitiateUploadExistingFileRequest(sender_.get(),
+ url_generator_,
+ content_type,
+ content_length,
+ resource_id,
+ options.etag,
+ callback);
+ request->set_parent_resource_id(options.parent_resource_id);
+ request->set_title(options.title);
+ request->set_modified_date(options.modified_date);
+ request->set_last_viewed_by_me_date(options.last_viewed_by_me_date);
+ request->set_properties(options.properties);
+ return sender_->StartRequestWithAuthRetry(request);
+}
+
+CancelCallback DriveAPIService::ResumeUpload(
+ const GURL& upload_url,
+ int64 start_position,
+ int64 end_position,
+ int64 content_length,
+ const std::string& content_type,
+ const base::FilePath& local_file_path,
+ const UploadRangeCallback& callback,
+ const ProgressCallback& progress_callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ return sender_->StartRequestWithAuthRetry(new ResumeUploadRequest(
+ sender_.get(), upload_url, start_position, end_position, content_length,
+ content_type, local_file_path, callback, progress_callback));
+}
+
+CancelCallback DriveAPIService::GetUploadStatus(
+ const GURL& upload_url,
+ int64 content_length,
+ const UploadRangeCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ return sender_->StartRequestWithAuthRetry(new GetUploadStatusRequest(
+ sender_.get(), upload_url, content_length, callback));
+}
+
+CancelCallback DriveAPIService::MultipartUploadNewFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& parent_resource_id,
+ const std::string& title,
+ const base::FilePath& local_file_path,
+ const drive::UploadNewFileOptions& options,
+ const FileResourceCallback& callback,
+ const google_apis::ProgressCallback& progress_callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ return sender_->StartRequestWithAuthRetry(
+ new google_apis::drive::SingleBatchableDelegateRequest(
+ sender_.get(),
+ new google_apis::drive::MultipartUploadNewFileDelegate(
+ sender_->blocking_task_runner(), title, parent_resource_id,
+ content_type, content_length, options.modified_date,
+ options.last_viewed_by_me_date, local_file_path,
+ options.properties, url_generator_, callback,
+ progress_callback)));
+}
+
+CancelCallback DriveAPIService::MultipartUploadExistingFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& resource_id,
+ const base::FilePath& local_file_path,
+ const drive::UploadExistingFileOptions& options,
+ const FileResourceCallback& callback,
+ const google_apis::ProgressCallback& progress_callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ return sender_->StartRequestWithAuthRetry(
+ new google_apis::drive::SingleBatchableDelegateRequest(
+ sender_.get(),
+ new google_apis::drive::MultipartUploadExistingFileDelegate(
+ sender_->blocking_task_runner(), options.title, resource_id,
+ options.parent_resource_id, content_type, content_length,
+ options.modified_date, options.last_viewed_by_me_date,
+ local_file_path, options.etag, options.properties, url_generator_,
+ callback, progress_callback)));
+}
+
+CancelCallback DriveAPIService::AuthorizeApp(
+ const std::string& resource_id,
+ const std::string& app_id,
+ const AuthorizeAppCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ // Files.Authorize is only available for whitelisted clients like official
+ // Google Chrome. In other cases, we fall back to Files.Get that returns the
+ // same value as Files.Authorize without doing authorization. In that case,
+ // the app can open if it was authorized by other means (from whitelisted
+ // clients or drive.google.com web UI.)
+ if (google_apis::IsGoogleChromeAPIKeyUsed()) {
+ google_apis::drive::FilesAuthorizeRequest* request =
+ new google_apis::drive::FilesAuthorizeRequest(
+ sender_.get(), url_generator_,
+ base::Bind(&ExtractOpenUrlAndRun, app_id, callback));
+ request->set_app_id(app_id);
+ request->set_file_id(resource_id);
+ request->set_fields(kFileResourceOpenWithLinksFields);
+ return sender_->StartRequestWithAuthRetry(request);
+ } else {
+ FilesGetRequest* request = new FilesGetRequest(
+ sender_.get(), url_generator_, google_apis::IsGoogleChromeAPIKeyUsed(),
+ base::Bind(&ExtractOpenUrlAndRun, app_id, callback));
+ request->set_file_id(resource_id);
+ request->set_fields(kFileResourceOpenWithLinksFields);
+ return sender_->StartRequestWithAuthRetry(request);
+ }
+}
+
+CancelCallback DriveAPIService::UninstallApp(
+ const std::string& app_id,
+ const google_apis::EntryActionCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ google_apis::drive::AppsDeleteRequest* request =
+ new google_apis::drive::AppsDeleteRequest(sender_.get(), url_generator_,
+ callback);
+ request->set_app_id(app_id);
+ return sender_->StartRequestWithAuthRetry(request);
+}
+
+google_apis::CancelCallback DriveAPIService::AddPermission(
+ const std::string& resource_id,
+ const std::string& email,
+ google_apis::drive::PermissionRole role,
+ const google_apis::EntryActionCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ google_apis::drive::PermissionsInsertRequest* request =
+ new google_apis::drive::PermissionsInsertRequest(sender_.get(),
+ url_generator_,
+ callback);
+ request->set_id(resource_id);
+ request->set_role(role);
+ request->set_type(google_apis::drive::PERMISSION_TYPE_USER);
+ request->set_value(email);
+ return sender_->StartRequestWithAuthRetry(request);
+}
+
+bool DriveAPIService::HasAccessToken() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return sender_->auth_service()->HasAccessToken();
+}
+
+void DriveAPIService::RequestAccessToken(const AuthStatusCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ const std::string access_token = sender_->auth_service()->access_token();
+ if (!access_token.empty()) {
+ callback.Run(google_apis::HTTP_NOT_MODIFIED, access_token);
+ return;
+ }
+
+ // Retrieve the new auth token.
+ sender_->auth_service()->StartAuthentication(callback);
+}
+
+bool DriveAPIService::HasRefreshToken() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return sender_->auth_service()->HasRefreshToken();
+}
+
+void DriveAPIService::ClearAccessToken() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ sender_->auth_service()->ClearAccessToken();
+}
+
+void DriveAPIService::ClearRefreshToken() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ sender_->auth_service()->ClearRefreshToken();
+}
+
+void DriveAPIService::OnOAuth2RefreshTokenChanged() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (CanSendRequest()) {
+ FOR_EACH_OBSERVER(
+ DriveServiceObserver, observers_, OnReadyToSendRequests());
+ } else if (!HasRefreshToken()) {
+ FOR_EACH_OBSERVER(
+ DriveServiceObserver, observers_, OnRefreshTokenInvalid());
+ }
+}
+
+scoped_ptr<BatchRequestConfiguratorInterface>
+DriveAPIService::StartBatchRequest() {
+ scoped_ptr<google_apis::drive::BatchUploadRequest> request(
+ new google_apis::drive::BatchUploadRequest(sender_.get(),
+ url_generator_));
+ const base::WeakPtr<google_apis::drive::BatchUploadRequest> weak_ref =
+ request->GetWeakPtrAsBatchUploadRequest();
+ // Have sender_ manage the lifetime of the request.
+ // TODO(hirono): Currently we need to pass the ownership of the request to
+ // RequestSender before the request is committed because the request has a
+ // reference to RequestSender and we should ensure to delete the request when
+ // the sender is deleted. Resolve the circulating dependency and fix it.
+ const google_apis::CancelCallback callback =
+ sender_->StartRequestWithAuthRetry(request.release());
+ return make_scoped_ptr<BatchRequestConfiguratorInterface>(
+ new BatchRequestConfigurator(weak_ref, sender_->blocking_task_runner(),
+ url_generator_, callback));
+}
+
+} // namespace drive
diff --git a/components/drive/service/drive_api_service.h b/components/drive/service/drive_api_service.h
new file mode 100644
index 0000000..22bb7d4
--- /dev/null
+++ b/components/drive/service/drive_api_service.h
@@ -0,0 +1,269 @@
+// Copyright (c) 2012 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.
+
+#ifndef COMPONENTS_DRIVE_SERVICE_DRIVE_API_SERVICE_H_
+#define COMPONENTS_DRIVE_SERVICE_DRIVE_API_SERVICE_H_
+
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/threading/thread_checker.h"
+#include "components/drive/service/drive_service_interface.h"
+#include "google_apis/drive/auth_service_interface.h"
+#include "google_apis/drive/auth_service_observer.h"
+#include "google_apis/drive/drive_api_url_generator.h"
+
+class GURL;
+class OAuth2TokenService;
+
+namespace base {
+class FilePath;
+class SequencedTaskRunner;
+}
+
+namespace google_apis {
+class FilesListRequestRunner;
+class RequestSender;
+namespace drive {
+class BatchUploadRequest;
+} // namespace drive
+} // namespace google_apis
+
+namespace net {
+class URLRequestContextGetter;
+} // namespace net
+
+namespace drive {
+
+// Builder for batch request returned by |DriveAPIService|.
+class BatchRequestConfigurator : public BatchRequestConfiguratorInterface,
+ public base::NonThreadSafe {
+ public:
+ BatchRequestConfigurator(
+ const base::WeakPtr<google_apis::drive::BatchUploadRequest>&
+ batch_request,
+ base::SequencedTaskRunner* task_runner,
+ const google_apis::DriveApiUrlGenerator& url_generator,
+ const google_apis::CancelCallback& cancel_callback);
+ ~BatchRequestConfigurator() override;
+
+ // BatchRequestConfiguratorInterface overrides.
+ google_apis::CancelCallback MultipartUploadNewFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& parent_resource_id,
+ const std::string& title,
+ const base::FilePath& local_file_path,
+ const UploadNewFileOptions& options,
+ const google_apis::FileResourceCallback& callback,
+ const google_apis::ProgressCallback& progress_callback) override;
+ google_apis::CancelCallback MultipartUploadExistingFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& resource_id,
+ const base::FilePath& local_file_path,
+ const UploadExistingFileOptions& options,
+ const google_apis::FileResourceCallback& callback,
+ const google_apis::ProgressCallback& progress_callback) override;
+ void Commit() override;
+
+ private:
+ // Reference to batch request. It turns to null after committing.
+ base::WeakPtr<google_apis::drive::BatchUploadRequest> batch_request_;
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
+ google_apis::DriveApiUrlGenerator url_generator_;
+ google_apis::CancelCallback cancel_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(BatchRequestConfigurator);
+};
+
+// This class provides Drive request calls using Drive V2 API.
+// Details of API call are abstracted in each request class and this class
+// works as a thin wrapper for the API.
+class DriveAPIService : public DriveServiceInterface,
+ public google_apis::AuthServiceObserver {
+ public:
+ // |oauth2_token_service| is used for obtaining OAuth2 access tokens.
+ // |url_request_context_getter| is used to initialize URLFetcher.
+ // |blocking_task_runner| is used to run blocking tasks (like parsing JSON).
+ // |base_url| is used to generate URLs for communication with the drive API.
+ // |base_download_url| is used to generate URLs for downloading file from the
+ // drive API.
+ // |custom_user_agent| will be used for the User-Agent header in HTTP
+ // requests issues through the service if the value is not empty.
+ DriveAPIService(
+ OAuth2TokenService* oauth2_token_service,
+ net::URLRequestContextGetter* url_request_context_getter,
+ base::SequencedTaskRunner* blocking_task_runner,
+ const GURL& base_url,
+ const GURL& base_download_url,
+ const std::string& custom_user_agent);
+ ~DriveAPIService() override;
+
+ // DriveServiceInterface Overrides
+ void Initialize(const std::string& account_id) override;
+ void AddObserver(DriveServiceObserver* observer) override;
+ void RemoveObserver(DriveServiceObserver* observer) override;
+ bool CanSendRequest() const override;
+ bool HasAccessToken() const override;
+ void RequestAccessToken(
+ const google_apis::AuthStatusCallback& callback) override;
+ bool HasRefreshToken() const override;
+ void ClearAccessToken() override;
+ void ClearRefreshToken() override;
+ std::string GetRootResourceId() const override;
+ google_apis::CancelCallback GetAllFileList(
+ const google_apis::FileListCallback& callback) override;
+ google_apis::CancelCallback GetFileListInDirectory(
+ const std::string& directory_resource_id,
+ const google_apis::FileListCallback& callback) override;
+ google_apis::CancelCallback Search(
+ const std::string& search_query,
+ const google_apis::FileListCallback& callback) override;
+ google_apis::CancelCallback SearchByTitle(
+ const std::string& title,
+ const std::string& directory_resource_id,
+ const google_apis::FileListCallback& callback) override;
+ google_apis::CancelCallback GetChangeList(
+ int64 start_changestamp,
+ const google_apis::ChangeListCallback& callback) override;
+ google_apis::CancelCallback GetRemainingChangeList(
+ const GURL& next_link,
+ const google_apis::ChangeListCallback& callback) override;
+ google_apis::CancelCallback GetRemainingFileList(
+ const GURL& next_link,
+ const google_apis::FileListCallback& callback) override;
+ google_apis::CancelCallback GetFileResource(
+ const std::string& resource_id,
+ const google_apis::FileResourceCallback& callback) override;
+ google_apis::CancelCallback GetShareUrl(
+ const std::string& resource_id,
+ const GURL& embed_origin,
+ const google_apis::GetShareUrlCallback& callback) override;
+ google_apis::CancelCallback GetAboutResource(
+ const google_apis::AboutResourceCallback& callback) override;
+ google_apis::CancelCallback GetAppList(
+ const google_apis::AppListCallback& callback) override;
+ google_apis::CancelCallback DeleteResource(
+ const std::string& resource_id,
+ const std::string& etag,
+ const google_apis::EntryActionCallback& callback) override;
+ google_apis::CancelCallback TrashResource(
+ const std::string& resource_id,
+ const google_apis::EntryActionCallback& callback) override;
+ google_apis::CancelCallback DownloadFile(
+ const base::FilePath& local_cache_path,
+ const std::string& resource_id,
+ const google_apis::DownloadActionCallback& download_action_callback,
+ const google_apis::GetContentCallback& get_content_callback,
+ const google_apis::ProgressCallback& progress_callback) override;
+ google_apis::CancelCallback CopyResource(
+ const std::string& resource_id,
+ const std::string& parent_resource_id,
+ const std::string& new_title,
+ const base::Time& last_modified,
+ const google_apis::FileResourceCallback& callback) override;
+ google_apis::CancelCallback UpdateResource(
+ const std::string& resource_id,
+ const std::string& parent_resource_id,
+ const std::string& new_title,
+ const base::Time& last_modified,
+ const base::Time& last_viewed_by_me,
+ const google_apis::drive::Properties& properties,
+ const google_apis::FileResourceCallback& callback) override;
+ google_apis::CancelCallback AddResourceToDirectory(
+ const std::string& parent_resource_id,
+ const std::string& resource_id,
+ const google_apis::EntryActionCallback& callback) override;
+ google_apis::CancelCallback RemoveResourceFromDirectory(
+ const std::string& parent_resource_id,
+ const std::string& resource_id,
+ const google_apis::EntryActionCallback& callback) override;
+ google_apis::CancelCallback AddNewDirectory(
+ const std::string& parent_resource_id,
+ const std::string& directory_title,
+ const AddNewDirectoryOptions& options,
+ const google_apis::FileResourceCallback& callback) override;
+ google_apis::CancelCallback InitiateUploadNewFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& parent_resource_id,
+ const std::string& title,
+ const UploadNewFileOptions& options,
+ const google_apis::InitiateUploadCallback& callback) override;
+ google_apis::CancelCallback InitiateUploadExistingFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& resource_id,
+ const UploadExistingFileOptions& options,
+ const google_apis::InitiateUploadCallback& callback) override;
+ google_apis::CancelCallback ResumeUpload(
+ const GURL& upload_url,
+ int64 start_position,
+ int64 end_position,
+ int64 content_length,
+ const std::string& content_type,
+ const base::FilePath& local_file_path,
+ const google_apis::drive::UploadRangeCallback& callback,
+ const google_apis::ProgressCallback& progress_callback) override;
+ google_apis::CancelCallback GetUploadStatus(
+ const GURL& upload_url,
+ int64 content_length,
+ const google_apis::drive::UploadRangeCallback& callback) override;
+ google_apis::CancelCallback MultipartUploadNewFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& parent_resource_id,
+ const std::string& title,
+ const base::FilePath& local_file_path,
+ const drive::UploadNewFileOptions& options,
+ const google_apis::FileResourceCallback& callback,
+ const google_apis::ProgressCallback& progress_callback) override;
+ google_apis::CancelCallback MultipartUploadExistingFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& resource_id,
+ const base::FilePath& local_file_path,
+ const drive::UploadExistingFileOptions& options,
+ const google_apis::FileResourceCallback& callback,
+ const google_apis::ProgressCallback& progress_callback) override;
+ google_apis::CancelCallback AuthorizeApp(
+ const std::string& resource_id,
+ const std::string& app_id,
+ const google_apis::AuthorizeAppCallback& callback) override;
+ google_apis::CancelCallback UninstallApp(
+ const std::string& app_id,
+ const google_apis::EntryActionCallback& callback) override;
+ google_apis::CancelCallback AddPermission(
+ const std::string& resource_id,
+ const std::string& email,
+ google_apis::drive::PermissionRole role,
+ const google_apis::EntryActionCallback& callback) override;
+ scoped_ptr<BatchRequestConfiguratorInterface> StartBatchRequest() override;
+
+ private:
+ // AuthServiceObserver override.
+ void OnOAuth2RefreshTokenChanged() override;
+
+ // The class is expected to run on UI thread.
+ base::ThreadChecker thread_checker_;
+
+ OAuth2TokenService* oauth2_token_service_;
+ scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
+ scoped_ptr<google_apis::RequestSender> sender_;
+ scoped_ptr<google_apis::FilesListRequestRunner> files_list_request_runner_;
+ base::ObserverList<DriveServiceObserver> observers_;
+ google_apis::DriveApiUrlGenerator url_generator_;
+ const std::string custom_user_agent_;
+
+ DISALLOW_COPY_AND_ASSIGN(DriveAPIService);
+};
+
+} // namespace drive
+
+#endif // COMPONENTS_DRIVE_SERVICE_DRIVE_API_SERVICE_H_
diff --git a/components/drive/service/drive_api_service_unittest.cc b/components/drive/service/drive_api_service_unittest.cc
new file mode 100644
index 0000000..6655e68
--- /dev/null
+++ b/components/drive/service/drive_api_service_unittest.cc
@@ -0,0 +1,79 @@
+// Copyright 2015 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 "base/message_loop/message_loop.h"
+#include "base/test/test_simple_task_runner.h"
+#include "components/drive/service/drive_api_service.h"
+#include "google_apis/drive/dummy_auth_service.h"
+#include "google_apis/drive/request_sender.h"
+#include "google_apis/drive/test_util.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace drive {
+namespace {
+const char kTestUserAgent[] = "test-user-agent";
+}
+
+class TestAuthService : public google_apis::DummyAuthService {
+ public:
+ void StartAuthentication(
+ const google_apis::AuthStatusCallback& callback) override {
+ callback_ = callback;
+ }
+
+ bool HasAccessToken() const override { return false; }
+
+ void SendHttpError() {
+ ASSERT_FALSE(callback_.is_null());
+ callback_.Run(google_apis::HTTP_UNAUTHORIZED, "");
+ }
+
+ private:
+ google_apis::AuthStatusCallback callback_;
+};
+
+TEST(DriveAPIServiceTest, BatchRequestConfiguratorWithAuthFailure) {
+ const GURL test_base_url("http://localhost/");
+ google_apis::DriveApiUrlGenerator url_generator(test_base_url, test_base_url);
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner =
+ new base::TestSimpleTaskRunner();
+ scoped_refptr<net::TestURLRequestContextGetter> request_context_getter =
+ new net::TestURLRequestContextGetter(task_runner.get());
+ google_apis::RequestSender sender(new TestAuthService,
+ request_context_getter.get(),
+ task_runner.get(), kTestUserAgent);
+ google_apis::drive::BatchUploadRequest* const request =
+ new google_apis::drive::BatchUploadRequest(&sender, url_generator);
+ sender.StartRequestWithAuthRetry(request);
+ BatchRequestConfigurator configurator(
+ request->GetWeakPtrAsBatchUploadRequest(), task_runner.get(),
+ url_generator, google_apis::CancelCallback());
+ static_cast<TestAuthService*>(sender.auth_service())->SendHttpError();
+
+ {
+ google_apis::DriveApiErrorCode error = google_apis::HTTP_SUCCESS;
+ scoped_ptr<google_apis::FileResource> file_resource;
+ configurator.MultipartUploadNewFile(
+ "text/plain", 10, "", "title",
+ base::FilePath(FILE_PATH_LITERAL("/file")), UploadNewFileOptions(),
+ google_apis::test_util::CreateCopyResultCallback(&error,
+ &file_resource),
+ google_apis::ProgressCallback());
+ EXPECT_EQ(google_apis::DRIVE_OTHER_ERROR, error);
+ }
+ {
+ google_apis::DriveApiErrorCode error = google_apis::HTTP_SUCCESS;
+ scoped_ptr<google_apis::FileResource> file_resource;
+ configurator.MultipartUploadExistingFile(
+ "text/plain", 10, "resource_id",
+ base::FilePath(FILE_PATH_LITERAL("/file")), UploadExistingFileOptions(),
+ google_apis::test_util::CreateCopyResultCallback(&error,
+ &file_resource),
+ google_apis::ProgressCallback());
+ EXPECT_EQ(google_apis::DRIVE_OTHER_ERROR, error);
+ }
+}
+
+} // namespace drive
diff --git a/components/drive/service/drive_service_interface.cc b/components/drive/service/drive_service_interface.cc
new file mode 100644
index 0000000..76c1f20
--- /dev/null
+++ b/components/drive/service/drive_service_interface.cc
@@ -0,0 +1,27 @@
+// Copyright 2014 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/service/drive_service_interface.h"
+
+namespace drive {
+
+AddNewDirectoryOptions::AddNewDirectoryOptions() {
+}
+
+AddNewDirectoryOptions::~AddNewDirectoryOptions() {
+}
+
+UploadNewFileOptions::UploadNewFileOptions() {
+}
+
+UploadNewFileOptions::~UploadNewFileOptions() {
+}
+
+UploadExistingFileOptions::UploadExistingFileOptions() {
+}
+
+UploadExistingFileOptions::~UploadExistingFileOptions() {
+}
+
+} // namespace drive
diff --git a/components/drive/service/drive_service_interface.h b/components/drive/service/drive_service_interface.h
new file mode 100644
index 0000000..1c0dadd
--- /dev/null
+++ b/components/drive/service/drive_service_interface.h
@@ -0,0 +1,467 @@
+// Copyright (c) 2012 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.
+
+#ifndef COMPONENTS_DRIVE_SERVICE_DRIVE_SERVICE_INTERFACE_H_
+#define COMPONENTS_DRIVE_SERVICE_DRIVE_SERVICE_INTERFACE_H_
+
+#include <string>
+
+#include "base/time/time.h"
+#include "google_apis/drive/auth_service_interface.h"
+#include "google_apis/drive/base_requests.h"
+#include "google_apis/drive/drive_api_requests.h"
+#include "google_apis/drive/drive_common_callbacks.h"
+
+namespace base {
+class Time;
+}
+
+namespace drive {
+
+// Observer interface for DriveServiceInterface.
+class DriveServiceObserver {
+ public:
+ // Triggered when the service gets ready to send requests.
+ virtual void OnReadyToSendRequests() {}
+
+ // Called when the refresh token was found to be invalid.
+ virtual void OnRefreshTokenInvalid() {}
+
+ protected:
+ virtual ~DriveServiceObserver() {}
+};
+
+// Optional parameters for AddNewDirectory().
+struct AddNewDirectoryOptions {
+ AddNewDirectoryOptions();
+ ~AddNewDirectoryOptions();
+
+ // modified_date of the directory.
+ // Pass the null Time if you are not interested in setting this property.
+ base::Time modified_date;
+
+ // last_viewed_by_me_date of the directory.
+ // Pass the null Time if you are not interested in setting this property.
+ base::Time last_viewed_by_me_date;
+
+ // List of properties for a new directory.
+ google_apis::drive::Properties properties;
+};
+
+// Optional parameters for InitiateUploadNewFile() and
+// MultipartUploadNewFile().
+struct UploadNewFileOptions {
+ UploadNewFileOptions();
+ ~UploadNewFileOptions();
+
+ // modified_date of the file.
+ // Pass the null Time if you are not interested in setting this property.
+ base::Time modified_date;
+
+ // last_viewed_by_me_date of the file.
+ // Pass the null Time if you are not interested in setting this property.
+ base::Time last_viewed_by_me_date;
+
+ // List of properties for a new file.
+ google_apis::drive::Properties properties;
+};
+
+// Optional parameters for InitiateUploadExistingFile() and
+// MultipartUploadExistingFile().
+struct UploadExistingFileOptions {
+ UploadExistingFileOptions();
+ ~UploadExistingFileOptions();
+
+ // Expected ETag of the file. UPLOAD_ERROR_CONFLICT error is generated when
+ // matching fails.
+ // Pass the empty string to disable this behavior.
+ std::string etag;
+
+ // New parent of the file.
+ // Pass the empty string to keep the property unchanged.
+ std::string parent_resource_id;
+
+ // New title of the file.
+ // Pass the empty string to keep the property unchanged.
+ std::string title;
+
+ // New modified_date of the file.
+ // Pass the null Time if you are not interested in setting this property.
+ base::Time modified_date;
+
+ // New last_viewed_by_me_date of the file.
+ // Pass the null Time if you are not interested in setting this property.
+ base::Time last_viewed_by_me_date;
+
+ // List of new properties for an existing file (it will be merged with
+ // existing properties).
+ google_apis::drive::Properties properties;
+};
+
+// Interface where we define operations that can be sent in batch requests.
+class DriveServiceBatchOperationsInterface {
+ public:
+ virtual ~DriveServiceBatchOperationsInterface() {}
+
+ // Uploads a file by a single request with multipart body. It's more efficient
+ // for small files than using |InitiateUploadNewFile| and |ResumeUpload|.
+ // |content_type| and |content_length| should be the ones of the file to be
+ // uploaded. |callback| must not be null. |progress_callback| may be null.
+ virtual google_apis::CancelCallback MultipartUploadNewFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& parent_resource_id,
+ const std::string& title,
+ const base::FilePath& local_file_path,
+ const UploadNewFileOptions& options,
+ const google_apis::FileResourceCallback& callback,
+ const google_apis::ProgressCallback& progress_callback) = 0;
+
+ // Uploads a file by a single request with multipart body. It's more efficient
+ // for small files than using |InitiateUploadExistingFile| and |ResumeUpload|.
+ // |content_type| and |content_length| should be the ones of the file to be
+ // uploaded. |callback| must not be null. |progress_callback| may be null.
+ virtual google_apis::CancelCallback MultipartUploadExistingFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& resource_id,
+ const base::FilePath& local_file_path,
+ const UploadExistingFileOptions& options,
+ const google_apis::FileResourceCallback& callback,
+ const google_apis::ProgressCallback& progress_callback) = 0;
+};
+
+// Builder returned by DriveServiceInterface to build batch request.
+class BatchRequestConfiguratorInterface
+ : public DriveServiceBatchOperationsInterface {
+ public:
+ ~BatchRequestConfiguratorInterface() override {}
+
+ // Commits and sends the batch request.
+ virtual void Commit() = 0;
+};
+
+// This defines an interface for sharing by DriveService and MockDriveService
+// so that we can do testing of clients of DriveService.
+//
+// All functions must be called on UI thread. DriveService is built on top of
+// URLFetcher that runs on UI thread.
+class DriveServiceInterface : public DriveServiceBatchOperationsInterface {
+ public:
+ ~DriveServiceInterface() override {}
+
+ // Common service:
+
+ // Initializes the documents service with |account_id|.
+ virtual void Initialize(const std::string& account_id) = 0;
+
+ // Adds an observer.
+ virtual void AddObserver(DriveServiceObserver* observer) = 0;
+
+ // Removes an observer.
+ virtual void RemoveObserver(DriveServiceObserver* observer) = 0;
+
+ // True if ready to send requests.
+ virtual bool CanSendRequest() const = 0;
+
+ // Authentication service:
+
+ // True if OAuth2 access token is retrieved and believed to be fresh.
+ virtual bool HasAccessToken() const = 0;
+
+ // Gets the cached OAuth2 access token or if empty, then fetches a new one.
+ virtual void RequestAccessToken(
+ const google_apis::AuthStatusCallback& callback) = 0;
+
+ // True if OAuth2 refresh token is present.
+ virtual bool HasRefreshToken() const = 0;
+
+ // Clears OAuth2 access token.
+ virtual void ClearAccessToken() = 0;
+
+ // Clears OAuth2 refresh token.
+ virtual void ClearRefreshToken() = 0;
+
+ // Document access:
+
+ // Returns the resource id for the root directory.
+ virtual std::string GetRootResourceId() const = 0;
+
+ // Fetches a file list of the account. |callback| will be called upon
+ // completion.
+ // If the list is too long, it may be paged. In such a case, a URL to fetch
+ // remaining results will be included in the returned result. See also
+ // GetRemainingFileList.
+ //
+ // |callback| must not be null.
+ virtual google_apis::CancelCallback GetAllFileList(
+ const google_apis::FileListCallback& callback) = 0;
+
+ // Fetches a file list in the directory with |directory_resource_id|.
+ // |callback| will be called upon completion.
+ // If the list is too long, it may be paged. In such a case, a URL to fetch
+ // remaining results will be included in the returned result. See also
+ // GetRemainingFileList.
+ //
+ // |directory_resource_id| must not be empty.
+ // |callback| must not be null.
+ virtual google_apis::CancelCallback GetFileListInDirectory(
+ const std::string& directory_resource_id,
+ const google_apis::FileListCallback& callback) = 0;
+
+ // Searches the resources for the |search_query| from all the user's
+ // resources. |callback| will be called upon completion.
+ // If the list is too long, it may be paged. In such a case, a URL to fetch
+ // remaining results will be included in the returned result. See also
+ // GetRemainingFileList.
+ //
+ // |search_query| must not be empty.
+ // |callback| must not be null.
+ virtual google_apis::CancelCallback Search(
+ const std::string& search_query,
+ const google_apis::FileListCallback& callback) = 0;
+
+ // Searches the resources with the |title|.
+ // |directory_resource_id| is an optional parameter. If it is empty,
+ // the search target is all the existing resources. Otherwise, it is
+ // the resources directly under the directory with |directory_resource_id|.
+ // If the list is too long, it may be paged. In such a case, a URL to fetch
+ // remaining results will be included in the returned result. See also
+ // GetRemainingFileList.
+ //
+ // |title| must not be empty, and |callback| must not be null.
+ virtual google_apis::CancelCallback SearchByTitle(
+ const std::string& title,
+ const std::string& directory_resource_id,
+ const google_apis::FileListCallback& callback) = 0;
+
+ // Fetches change list since |start_changestamp|. |callback| will be
+ // called upon completion.
+ // If the list is too long, it may be paged. In such a case, a URL to fetch
+ // remaining results will be included in the returned result. See also
+ // GetRemainingChangeList.
+ //
+ // |callback| must not be null.
+ virtual google_apis::CancelCallback GetChangeList(
+ int64 start_changestamp,
+ const google_apis::ChangeListCallback& callback) = 0;
+
+ // The result of GetChangeList() may be paged.
+ // In such a case, a next link to fetch remaining result is returned.
+ // The page token can be used for this method. |callback| will be called upon
+ // completion.
+ //
+ // |next_link| must not be empty. |callback| must not be null.
+ virtual google_apis::CancelCallback GetRemainingChangeList(
+ const GURL& next_link,
+ const google_apis::ChangeListCallback& callback) = 0;
+
+ // The result of GetAllFileList(), GetFileListInDirectory(), Search()
+ // and SearchByTitle() may be paged. In such a case, a next link to fetch
+ // remaining result is returned. The page token can be used for this method.
+ // |callback| will be called upon completion.
+ //
+ // |next_link| must not be empty. |callback| must not be null.
+ virtual google_apis::CancelCallback GetRemainingFileList(
+ const GURL& next_link,
+ const google_apis::FileListCallback& callback) = 0;
+
+ // Fetches single entry metadata from server. The entry's file id equals
+ // |resource_id|.
+ // Upon completion, invokes |callback| with results on the calling thread.
+ // |callback| must not be null.
+ virtual google_apis::CancelCallback GetFileResource(
+ const std::string& resource_id,
+ const google_apis::FileResourceCallback& callback) = 0;
+
+ // Fetches an url for the sharing dialog for a single entry with id
+ // |resource_id|, to be embedded in a webview or an iframe with origin
+ // |embed_origin|. The url is returned via |callback| with results on the
+ // calling thread. |callback| must not be null.
+ virtual google_apis::CancelCallback GetShareUrl(
+ const std::string& resource_id,
+ const GURL& embed_origin,
+ const google_apis::GetShareUrlCallback& callback) = 0;
+
+ // Gets the about resource information from the server.
+ // Upon completion, invokes |callback| with results on the calling thread.
+ // |callback| must not be null.
+ virtual google_apis::CancelCallback GetAboutResource(
+ const google_apis::AboutResourceCallback& callback) = 0;
+
+ // Gets the application information from the server.
+ // Upon completion, invokes |callback| with results on the calling thread.
+ // |callback| must not be null.
+ virtual google_apis::CancelCallback GetAppList(
+ const google_apis::AppListCallback& callback) = 0;
+
+ // Permanently deletes a resource identified by its |resource_id|.
+ // If |etag| is not empty and did not match, the deletion fails with
+ // HTTP_PRECONDITION error.
+ // Upon completion, invokes |callback| with results on the calling thread.
+ // |callback| must not be null.
+ virtual google_apis::CancelCallback DeleteResource(
+ const std::string& resource_id,
+ const std::string& etag,
+ const google_apis::EntryActionCallback& callback) = 0;
+
+ // Trashes a resource identified by its |resource_id|.
+ // Upon completion, invokes |callback| with results on the calling thread.
+ // |callback| must not be null.
+ virtual google_apis::CancelCallback TrashResource(
+ const std::string& resource_id,
+ const google_apis::EntryActionCallback& callback) = 0;
+
+ // Makes a copy of a resource with |resource_id|.
+ // The new resource will be put under a directory with |parent_resource_id|,
+ // and it'll be named |new_title|.
+ // If |last_modified| is not null, the modified date of the resource on the
+ // server will be set to the date.
+ // Upon completion, invokes |callback| with results on the calling thread.
+ // |callback| must not be null.
+ virtual google_apis::CancelCallback CopyResource(
+ const std::string& resource_id,
+ const std::string& parent_resource_id,
+ const std::string& new_title,
+ const base::Time& last_modified,
+ const google_apis::FileResourceCallback& callback) = 0;
+
+ // Updates a resource with |resource_id| to the directory of
+ // |parent_resource_id| with renaming to |new_title|.
+ // If |last_modified| or |last_accessed| is not null, the modified/accessed
+ // date of the resource on the server will be set to the date.
+ // If |properties| are specified, then they will be set on |resource_id|.
+ // Upon completion, invokes |callback| with results on the calling thread.
+ // |callback| must not be null.
+ virtual google_apis::CancelCallback UpdateResource(
+ const std::string& resource_id,
+ const std::string& parent_resource_id,
+ const std::string& new_title,
+ const base::Time& last_modified,
+ const base::Time& last_viewed_by_me,
+ const google_apis::drive::Properties& properties,
+ const google_apis::FileResourceCallback& callback) = 0;
+
+ // Adds a resource (document, file, or collection) identified by its
+ // |resource_id| to a collection represented by the |parent_resource_id|.
+ // Upon completion, invokes |callback| with results on the calling thread.
+ // |callback| must not be null.
+ virtual google_apis::CancelCallback AddResourceToDirectory(
+ const std::string& parent_resource_id,
+ const std::string& resource_id,
+ const google_apis::EntryActionCallback& callback) = 0;
+
+ // Removes a resource (document, file, collection) identified by its
+ // |resource_id| from a collection represented by the |parent_resource_id|.
+ // Upon completion, invokes |callback| with results on the calling thread.
+ // |callback| must not be null.
+ virtual google_apis::CancelCallback RemoveResourceFromDirectory(
+ const std::string& parent_resource_id,
+ const std::string& resource_id,
+ const google_apis::EntryActionCallback& callback) = 0;
+
+ // Adds new collection with |directory_title| under parent directory
+ // identified with |parent_resource_id|. |parent_resource_id| can be the
+ // value returned by GetRootResourceId to represent the root directory.
+ // Upon completion, invokes |callback| and passes newly created entry on
+ // the calling thread.
+ // This function cannot be named as "CreateDirectory" as it conflicts with
+ // a macro on Windows.
+ // |callback| must not be null.
+ virtual google_apis::CancelCallback AddNewDirectory(
+ const std::string& parent_resource_id,
+ const std::string& directory_title,
+ const AddNewDirectoryOptions& options,
+ const google_apis::FileResourceCallback& callback) = 0;
+
+ // Downloads a file with |resourced_id|. The downloaded file will
+ // be stored at |local_cache_path| location. Upon completion, invokes
+ // |download_action_callback| with results on the calling thread.
+ // If |get_content_callback| is not empty,
+ // URLFetcherDelegate::OnURLFetchDownloadData will be called, which will in
+ // turn invoke |get_content_callback| on the calling thread.
+ // If |progress_callback| is not empty, it is invoked periodically when
+ // the download made some progress.
+ //
+ // |download_action_callback| must not be null.
+ // |get_content_callback| and |progress_callback| may be null.
+ virtual google_apis::CancelCallback DownloadFile(
+ const base::FilePath& local_cache_path,
+ const std::string& resource_id,
+ const google_apis::DownloadActionCallback& download_action_callback,
+ const google_apis::GetContentCallback& get_content_callback,
+ const google_apis::ProgressCallback& progress_callback) = 0;
+
+ // Initiates uploading of a new document/file.
+ // |content_type| and |content_length| should be the ones of the file to be
+ // uploaded.
+ // |callback| must not be null.
+ virtual google_apis::CancelCallback InitiateUploadNewFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& parent_resource_id,
+ const std::string& title,
+ const UploadNewFileOptions& options,
+ const google_apis::InitiateUploadCallback& callback) = 0;
+
+ // Initiates uploading of an existing document/file.
+ // |content_type| and |content_length| should be the ones of the file to be
+ // uploaded.
+ // |callback| must not be null.
+ virtual google_apis::CancelCallback InitiateUploadExistingFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& resource_id,
+ const UploadExistingFileOptions& options,
+ const google_apis::InitiateUploadCallback& callback) = 0;
+
+ // Resumes uploading of a document/file on the calling thread.
+ // |callback| must not be null. |progress_callback| may be null.
+ virtual google_apis::CancelCallback ResumeUpload(
+ const GURL& upload_url,
+ int64 start_position,
+ int64 end_position,
+ int64 content_length,
+ const std::string& content_type,
+ const base::FilePath& local_file_path,
+ const google_apis::drive::UploadRangeCallback& callback,
+ const google_apis::ProgressCallback& progress_callback) = 0;
+
+ // Gets the current status of the uploading to |upload_url| from the server.
+ // |drive_file_path| and |content_length| should be set to the same value
+ // which is used for ResumeUpload.
+ // |callback| must not be null.
+ virtual google_apis::CancelCallback GetUploadStatus(
+ const GURL& upload_url,
+ int64 content_length,
+ const google_apis::drive::UploadRangeCallback& callback) = 0;
+
+ // Authorizes a Drive app with the id |app_id| to open the given file.
+ // Upon completion, invokes |callback| with the link to open the file with
+ // the provided app. |callback| must not be null.
+ virtual google_apis::CancelCallback AuthorizeApp(
+ const std::string& resource_id,
+ const std::string& app_id,
+ const google_apis::AuthorizeAppCallback& callback) = 0;
+
+ // Uninstalls a Drive app with the id |app_id|. |callback| must not be null.
+ virtual google_apis::CancelCallback UninstallApp(
+ const std::string& app_id,
+ const google_apis::EntryActionCallback& callback) = 0;
+
+ // Authorizes the account |email| to access |resource_id| as a |role|.
+ // |callback| must not be null.
+ virtual google_apis::CancelCallback AddPermission(
+ const std::string& resource_id,
+ const std::string& email,
+ google_apis::drive::PermissionRole role,
+ const google_apis::EntryActionCallback& callback) = 0;
+
+ // Starts batch request and returns |BatchRequestConfigurator|.
+ virtual scoped_ptr<BatchRequestConfiguratorInterface> StartBatchRequest() = 0;
+};
+
+} // namespace drive
+
+#endif // COMPONENTS_DRIVE_SERVICE_DRIVE_SERVICE_INTERFACE_H_
diff --git a/components/drive/service/dummy_drive_service.cc b/components/drive/service/dummy_drive_service.cc
new file mode 100644
index 0000000..8700fce
--- /dev/null
+++ b/components/drive/service/dummy_drive_service.cc
@@ -0,0 +1,224 @@
+// Copyright (c) 2012 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/service/dummy_drive_service.h"
+
+#include "base/bind.h"
+
+using google_apis::AboutResourceCallback;
+using google_apis::AppListCallback;
+using google_apis::AuthStatusCallback;
+using google_apis::AuthorizeAppCallback;
+using google_apis::CancelCallback;
+using google_apis::ChangeListCallback;
+using google_apis::DownloadActionCallback;
+using google_apis::EntryActionCallback;
+using google_apis::FileListCallback;
+using google_apis::FileResourceCallback;
+using google_apis::GetContentCallback;
+using google_apis::GetShareUrlCallback;
+using google_apis::InitiateUploadCallback;
+using google_apis::ProgressCallback;
+using google_apis::drive::UploadRangeCallback;
+
+namespace drive {
+
+DummyDriveService::DummyDriveService() {}
+
+DummyDriveService::~DummyDriveService() {}
+
+void DummyDriveService::Initialize(const std::string& account_id) {}
+
+void DummyDriveService::AddObserver(DriveServiceObserver* observer) {}
+
+void DummyDriveService::RemoveObserver(DriveServiceObserver* observer) {}
+
+bool DummyDriveService::CanSendRequest() const { return true; }
+
+bool DummyDriveService::HasAccessToken() const { return true; }
+
+void DummyDriveService::RequestAccessToken(const AuthStatusCallback& callback) {
+ callback.Run(google_apis::HTTP_NOT_MODIFIED, "fake_access_token");
+}
+
+bool DummyDriveService::HasRefreshToken() const { return true; }
+
+void DummyDriveService::ClearAccessToken() { }
+
+void DummyDriveService::ClearRefreshToken() { }
+
+std::string DummyDriveService::GetRootResourceId() const {
+ return "dummy_root";
+}
+
+CancelCallback DummyDriveService::GetAllFileList(
+ const FileListCallback& callback) { return CancelCallback(); }
+
+CancelCallback DummyDriveService::GetFileListInDirectory(
+ const std::string& directory_resource_id,
+ const FileListCallback& callback) { return CancelCallback(); }
+
+CancelCallback DummyDriveService::Search(
+ const std::string& search_query,
+ const FileListCallback& callback) { return CancelCallback(); }
+
+CancelCallback DummyDriveService::SearchByTitle(
+ const std::string& title,
+ const std::string& directory_resource_id,
+ const FileListCallback& callback) { return CancelCallback(); }
+
+CancelCallback DummyDriveService::GetChangeList(
+ int64 start_changestamp,
+ const ChangeListCallback& callback) { return CancelCallback(); }
+
+CancelCallback DummyDriveService::GetRemainingChangeList(
+ const GURL& next_link,
+ const ChangeListCallback& callback) { return CancelCallback(); }
+
+CancelCallback DummyDriveService::GetRemainingFileList(
+ const GURL& next_link,
+ const FileListCallback& callback) { return CancelCallback(); }
+
+CancelCallback DummyDriveService::GetFileResource(
+ const std::string& resource_id,
+ const FileResourceCallback& callback) { return CancelCallback(); }
+
+CancelCallback DummyDriveService::GetShareUrl(
+ const std::string& resource_id,
+ const GURL& embed_origin,
+ const GetShareUrlCallback& callback) { return CancelCallback(); }
+
+CancelCallback DummyDriveService::GetAboutResource(
+ const AboutResourceCallback& callback) { return CancelCallback(); }
+
+CancelCallback DummyDriveService::GetAppList(
+ const AppListCallback& callback) { return CancelCallback(); }
+
+CancelCallback DummyDriveService::DeleteResource(
+ const std::string& resource_id,
+ const std::string& etag,
+ const EntryActionCallback& callback) { return CancelCallback(); }
+
+CancelCallback DummyDriveService::TrashResource(
+ const std::string& resource_id,
+ const EntryActionCallback& callback) { return CancelCallback(); }
+
+CancelCallback DummyDriveService::DownloadFile(
+ const base::FilePath& local_cache_path,
+ const std::string& resource_id,
+ const DownloadActionCallback& download_action_callback,
+ const GetContentCallback& get_content_callback,
+ const ProgressCallback& progress_callback) { return CancelCallback(); }
+
+CancelCallback DummyDriveService::CopyResource(
+ const std::string& resource_id,
+ const std::string& parent_resource_id,
+ const std::string& new_title,
+ const base::Time& last_modified,
+ const FileResourceCallback& callback) { return CancelCallback(); }
+
+CancelCallback DummyDriveService::UpdateResource(
+ const std::string& resource_id,
+ const std::string& parent_resource_id,
+ const std::string& new_title,
+ const base::Time& last_modified,
+ const base::Time& last_viewed_by_me,
+ const google_apis::drive::Properties& properties,
+ const google_apis::FileResourceCallback& callback) {
+ return CancelCallback();
+}
+
+CancelCallback DummyDriveService::AddResourceToDirectory(
+ const std::string& parent_resource_id,
+ const std::string& resource_id,
+ const EntryActionCallback& callback) { return CancelCallback(); }
+
+CancelCallback DummyDriveService::RemoveResourceFromDirectory(
+ const std::string& parent_resource_id,
+ const std::string& resource_id,
+ const EntryActionCallback& callback) { return CancelCallback(); }
+
+CancelCallback DummyDriveService::AddNewDirectory(
+ const std::string& parent_resource_id,
+ const std::string& directory_title,
+ const AddNewDirectoryOptions& options,
+ const FileResourceCallback& callback) { return CancelCallback(); }
+
+CancelCallback DummyDriveService::InitiateUploadNewFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& parent_resource_id,
+ const std::string& title,
+ const UploadNewFileOptions& options,
+ const InitiateUploadCallback& callback) {
+ return CancelCallback();
+}
+
+CancelCallback DummyDriveService::InitiateUploadExistingFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& resource_id,
+ const UploadExistingFileOptions& options,
+ const InitiateUploadCallback& callback) {
+ return CancelCallback();
+}
+
+CancelCallback DummyDriveService::ResumeUpload(
+ const GURL& upload_url,
+ int64 start_position,
+ int64 end_position,
+ int64 content_length,
+ const std::string& content_type,
+ const base::FilePath& local_file_path,
+ const UploadRangeCallback& callback,
+ const ProgressCallback& progress_callback) { return CancelCallback(); }
+
+CancelCallback DummyDriveService::GetUploadStatus(
+ const GURL& upload_url,
+ int64 content_length,
+ const UploadRangeCallback& callback) { return CancelCallback(); }
+
+CancelCallback DummyDriveService::MultipartUploadNewFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& parent_resource_id,
+ const std::string& title,
+ const base::FilePath& local_file_path,
+ const UploadNewFileOptions& options,
+ const FileResourceCallback& callback,
+ const ProgressCallback& progress_callback) {
+ return CancelCallback();
+}
+
+CancelCallback DummyDriveService::MultipartUploadExistingFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& resource_id,
+ const base::FilePath& local_file_path,
+ const UploadExistingFileOptions& options,
+ const FileResourceCallback& callback,
+ const ProgressCallback& progress_callback) {
+ return CancelCallback();
+}
+
+CancelCallback DummyDriveService::AuthorizeApp(
+ const std::string& resource_id,
+ const std::string& app_id,
+ const AuthorizeAppCallback& callback) { return CancelCallback(); }
+
+CancelCallback DummyDriveService::UninstallApp(
+ const std::string& app_id,
+ const EntryActionCallback& callback) { return CancelCallback(); }
+
+CancelCallback DummyDriveService::AddPermission(
+ const std::string& resource_id,
+ const std::string& email,
+ google_apis::drive::PermissionRole role,
+ const EntryActionCallback& callback) { return CancelCallback(); }
+scoped_ptr<BatchRequestConfiguratorInterface>
+DummyDriveService::StartBatchRequest() {
+ return scoped_ptr<BatchRequestConfiguratorInterface>();
+}
+
+} // namespace drive
diff --git a/components/drive/service/dummy_drive_service.h b/components/drive/service/dummy_drive_service.h
new file mode 100644
index 0000000..3317c45
--- /dev/null
+++ b/components/drive/service/dummy_drive_service.h
@@ -0,0 +1,166 @@
+// Copyright (c) 2012 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.
+
+#ifndef COMPONENTS_DRIVE_SERVICE_DUMMY_DRIVE_SERVICE_H_
+#define COMPONENTS_DRIVE_SERVICE_DUMMY_DRIVE_SERVICE_H_
+
+#include <string>
+
+#include "components/drive/service/drive_service_interface.h"
+#include "google_apis/drive/auth_service_interface.h"
+
+namespace drive {
+
+// Dummy implementation of DriveServiceInterface.
+// All functions do nothing, or return place holder values like 'true'.
+class DummyDriveService : public DriveServiceInterface {
+ public:
+ DummyDriveService();
+ ~DummyDriveService() override;
+
+ // DriveServiceInterface Overrides
+ void Initialize(const std::string& account_id) override;
+ void AddObserver(DriveServiceObserver* observer) override;
+ void RemoveObserver(DriveServiceObserver* observer) override;
+ bool CanSendRequest() const override;
+ bool HasAccessToken() const override;
+ void RequestAccessToken(
+ const google_apis::AuthStatusCallback& callback) override;
+ bool HasRefreshToken() const override;
+ void ClearAccessToken() override;
+ void ClearRefreshToken() override;
+ std::string GetRootResourceId() const override;
+ google_apis::CancelCallback GetAllFileList(
+ const google_apis::FileListCallback& callback) override;
+ google_apis::CancelCallback GetFileListInDirectory(
+ const std::string& directory_resource_id,
+ const google_apis::FileListCallback& callback) override;
+ google_apis::CancelCallback Search(
+ const std::string& search_query,
+ const google_apis::FileListCallback& callback) override;
+ google_apis::CancelCallback SearchByTitle(
+ const std::string& title,
+ const std::string& directory_resource_id,
+ const google_apis::FileListCallback& callback) override;
+ google_apis::CancelCallback GetChangeList(
+ int64 start_changestamp,
+ const google_apis::ChangeListCallback& callback) override;
+ google_apis::CancelCallback GetRemainingChangeList(
+ const GURL& next_link,
+ const google_apis::ChangeListCallback& callback) override;
+ google_apis::CancelCallback GetRemainingFileList(
+ const GURL& next_link,
+ const google_apis::FileListCallback& callback) override;
+ google_apis::CancelCallback GetFileResource(
+ const std::string& resource_id,
+ const google_apis::FileResourceCallback& callback) override;
+ google_apis::CancelCallback GetShareUrl(
+ const std::string& resource_id,
+ const GURL& embed_origin,
+ const google_apis::GetShareUrlCallback& callback) override;
+ google_apis::CancelCallback GetAboutResource(
+ const google_apis::AboutResourceCallback& callback) override;
+ google_apis::CancelCallback GetAppList(
+ const google_apis::AppListCallback& callback) override;
+ google_apis::CancelCallback DeleteResource(
+ const std::string& resource_id,
+ const std::string& etag,
+ const google_apis::EntryActionCallback& callback) override;
+ google_apis::CancelCallback TrashResource(
+ const std::string& resource_id,
+ const google_apis::EntryActionCallback& callback) override;
+ google_apis::CancelCallback DownloadFile(
+ const base::FilePath& local_cache_path,
+ const std::string& resource_id,
+ const google_apis::DownloadActionCallback& download_action_callback,
+ const google_apis::GetContentCallback& get_content_callback,
+ const google_apis::ProgressCallback& progress_callback) override;
+ google_apis::CancelCallback CopyResource(
+ const std::string& resource_id,
+ const std::string& parent_resource_id,
+ const std::string& new_title,
+ const base::Time& last_modified,
+ const google_apis::FileResourceCallback& callback) override;
+ google_apis::CancelCallback UpdateResource(
+ const std::string& resource_id,
+ const std::string& parent_resource_id,
+ const std::string& new_title,
+ const base::Time& last_modified,
+ const base::Time& last_viewed_by_me,
+ const google_apis::drive::Properties& properties,
+ const google_apis::FileResourceCallback& callback) override;
+ google_apis::CancelCallback AddResourceToDirectory(
+ const std::string& parent_resource_id,
+ const std::string& resource_id,
+ const google_apis::EntryActionCallback& callback) override;
+ google_apis::CancelCallback RemoveResourceFromDirectory(
+ const std::string& parent_resource_id,
+ const std::string& resource_id,
+ const google_apis::EntryActionCallback& callback) override;
+ google_apis::CancelCallback AddNewDirectory(
+ const std::string& parent_resource_id,
+ const std::string& directory_title,
+ const AddNewDirectoryOptions& options,
+ const google_apis::FileResourceCallback& callback) override;
+ google_apis::CancelCallback InitiateUploadNewFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& parent_resource_id,
+ const std::string& title,
+ const UploadNewFileOptions& options,
+ const google_apis::InitiateUploadCallback& callback) override;
+ google_apis::CancelCallback InitiateUploadExistingFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& resource_id,
+ const UploadExistingFileOptions& options,
+ const google_apis::InitiateUploadCallback& callback) override;
+ google_apis::CancelCallback ResumeUpload(
+ const GURL& upload_url,
+ int64 start_position,
+ int64 end_position,
+ int64 content_length,
+ const std::string& content_type,
+ const base::FilePath& local_file_path,
+ const google_apis::drive::UploadRangeCallback& callback,
+ const google_apis::ProgressCallback& progress_callback) override;
+ google_apis::CancelCallback GetUploadStatus(
+ const GURL& upload_url,
+ int64 content_length,
+ const google_apis::drive::UploadRangeCallback& callback) override;
+ google_apis::CancelCallback MultipartUploadNewFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& parent_resource_id,
+ const std::string& title,
+ const base::FilePath& local_file_path,
+ const UploadNewFileOptions& options,
+ const google_apis::FileResourceCallback& callback,
+ const google_apis::ProgressCallback& progress_callback) override;
+ google_apis::CancelCallback MultipartUploadExistingFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& resource_id,
+ const base::FilePath& local_file_path,
+ const UploadExistingFileOptions& options,
+ const google_apis::FileResourceCallback& callback,
+ const google_apis::ProgressCallback& progress_callback) override;
+ google_apis::CancelCallback AuthorizeApp(
+ const std::string& resource_id,
+ const std::string& app_id,
+ const google_apis::AuthorizeAppCallback& callback) override;
+ google_apis::CancelCallback UninstallApp(
+ const std::string& app_id,
+ const google_apis::EntryActionCallback& callback) override;
+ google_apis::CancelCallback AddPermission(
+ const std::string& resource_id,
+ const std::string& email,
+ google_apis::drive::PermissionRole role,
+ const google_apis::EntryActionCallback& callback) override;
+ scoped_ptr<BatchRequestConfiguratorInterface> StartBatchRequest() override;
+};
+
+} // namespace drive
+
+#endif // COMPONENTS_DRIVE_SERVICE_DUMMY_DRIVE_SERVICE_H_
diff --git a/components/drive/service/fake_drive_service.cc b/components/drive/service/fake_drive_service.cc
new file mode 100644
index 0000000..81b55d1
--- /dev/null
+++ b/components/drive/service/fake_drive_service.cc
@@ -0,0 +1,1787 @@
+// Copyright (c) 2012 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/service/fake_drive_service.h"
+
+#include <string>
+
+#include "base/files/file_util.h"
+#include "base/json/json_string_value_serializer.h"
+#include "base/logging.h"
+#include "base/md5.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_tokenizer.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/values.h"
+#include "components/drive/drive_api_util.h"
+#include "google_apis/drive/drive_api_parser.h"
+#include "google_apis/drive/test_util.h"
+#include "net/base/escape.h"
+#include "net/base/url_util.h"
+
+using google_apis::AboutResource;
+using google_apis::AboutResourceCallback;
+using google_apis::AppList;
+using google_apis::AppListCallback;
+using google_apis::AuthStatusCallback;
+using google_apis::AuthorizeAppCallback;
+using google_apis::CancelCallback;
+using google_apis::ChangeList;
+using google_apis::ChangeListCallback;
+using google_apis::ChangeResource;
+using google_apis::DownloadActionCallback;
+using google_apis::EntryActionCallback;
+using google_apis::FileList;
+using google_apis::FileListCallback;
+using google_apis::FileResource;
+using google_apis::FileResourceCallback;
+using google_apis::DRIVE_FILE_ERROR;
+using google_apis::DRIVE_NO_CONNECTION;
+using google_apis::DRIVE_OTHER_ERROR;
+using google_apis::DriveApiErrorCode;
+using google_apis::GetContentCallback;
+using google_apis::GetShareUrlCallback;
+using google_apis::HTTP_BAD_REQUEST;
+using google_apis::HTTP_CREATED;
+using google_apis::HTTP_FORBIDDEN;
+using google_apis::HTTP_NOT_FOUND;
+using google_apis::HTTP_NO_CONTENT;
+using google_apis::HTTP_PRECONDITION;
+using google_apis::HTTP_RESUME_INCOMPLETE;
+using google_apis::HTTP_SUCCESS;
+using google_apis::InitiateUploadCallback;
+using google_apis::ParentReference;
+using google_apis::ProgressCallback;
+using google_apis::UploadRangeResponse;
+using google_apis::drive::UploadRangeCallback;
+namespace test_util = google_apis::test_util;
+
+namespace drive {
+namespace {
+
+// Returns true if the entry matches with the search query.
+// Supports queries consist of following format.
+// - Phrases quoted by double/single quotes
+// - AND search for multiple words/phrases segmented by space
+// - Limited attribute search. Only "title:" is supported.
+bool EntryMatchWithQuery(const ChangeResource& entry,
+ const std::string& query) {
+ base::StringTokenizer tokenizer(query, " ");
+ tokenizer.set_quote_chars("\"'");
+ while (tokenizer.GetNext()) {
+ std::string key, value;
+ const std::string& token = tokenizer.token();
+ if (token.find(':') == std::string::npos) {
+ base::TrimString(token, "\"'", &value);
+ } else {
+ base::StringTokenizer key_value(token, ":");
+ key_value.set_quote_chars("\"'");
+ if (!key_value.GetNext())
+ return false;
+ key = key_value.token();
+ if (!key_value.GetNext())
+ return false;
+ base::TrimString(key_value.token(), "\"'", &value);
+ }
+
+ // TODO(peria): Deal with other attributes than title.
+ if (!key.empty() && key != "title")
+ return false;
+ // Search query in the title.
+ if (!entry.file() ||
+ entry.file()->title().find(value) == std::string::npos)
+ return false;
+ }
+ return true;
+}
+
+void ScheduleUploadRangeCallback(const UploadRangeCallback& callback,
+ int64 start_position,
+ int64 end_position,
+ DriveApiErrorCode error,
+ scoped_ptr<FileResource> entry) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ UploadRangeResponse(error,
+ start_position,
+ end_position),
+ base::Passed(&entry)));
+}
+
+void FileListCallbackAdapter(const FileListCallback& callback,
+ DriveApiErrorCode error,
+ scoped_ptr<ChangeList> change_list) {
+ scoped_ptr<FileList> file_list;
+ if (!change_list) {
+ callback.Run(error, file_list.Pass());
+ return;
+ }
+
+ file_list.reset(new FileList);
+ file_list->set_next_link(change_list->next_link());
+ for (size_t i = 0; i < change_list->items().size(); ++i) {
+ const ChangeResource& entry = *change_list->items()[i];
+ if (entry.file())
+ file_list->mutable_items()->push_back(new FileResource(*entry.file()));
+ }
+ callback.Run(error, file_list.Pass());
+}
+
+bool UserHasWriteAccess(google_apis::drive::PermissionRole user_permission) {
+ switch (user_permission) {
+ case google_apis::drive::PERMISSION_ROLE_OWNER:
+ case google_apis::drive::PERMISSION_ROLE_WRITER:
+ return true;
+ case google_apis::drive::PERMISSION_ROLE_READER:
+ case google_apis::drive::PERMISSION_ROLE_COMMENTER:
+ break;
+ }
+ return false;
+}
+
+void CallFileResouceCallback(const FileResourceCallback& callback,
+ const UploadRangeResponse& response,
+ scoped_ptr<FileResource> entry) {
+ callback.Run(response.code, entry.Pass());
+}
+
+struct CallResumeUpload {
+ CallResumeUpload() {}
+ ~CallResumeUpload() {}
+
+ void Run(DriveApiErrorCode code, const GURL& upload_url) {
+ if (service) {
+ service->ResumeUpload(
+ upload_url,
+ /* start position */ 0,
+ /* end position */ content_length,
+ content_length,
+ content_type,
+ local_file_path,
+ base::Bind(&CallFileResouceCallback, callback),
+ progress_callback);
+ }
+ }
+
+ base::WeakPtr<FakeDriveService> service;
+ int64 content_length;
+ std::string content_type;
+ base::FilePath local_file_path;
+ FileResourceCallback callback;
+ ProgressCallback progress_callback;
+};
+
+} // namespace
+
+struct FakeDriveService::EntryInfo {
+ EntryInfo() : user_permission(google_apis::drive::PERMISSION_ROLE_OWNER) {}
+
+ google_apis::ChangeResource change_resource;
+ GURL share_url;
+ std::string content_data;
+
+ // Behaves in the same way as "userPermission" described in
+ // https://developers.google.com/drive/v2/reference/files
+ google_apis::drive::PermissionRole user_permission;
+};
+
+struct FakeDriveService::UploadSession {
+ std::string content_type;
+ int64 content_length;
+ std::string parent_resource_id;
+ std::string resource_id;
+ std::string etag;
+ std::string title;
+
+ int64 uploaded_size;
+
+ UploadSession()
+ : content_length(0),
+ uploaded_size(0) {}
+
+ UploadSession(
+ std::string content_type,
+ int64 content_length,
+ std::string parent_resource_id,
+ std::string resource_id,
+ std::string etag,
+ std::string title)
+ : content_type(content_type),
+ content_length(content_length),
+ parent_resource_id(parent_resource_id),
+ resource_id(resource_id),
+ etag(etag),
+ title(title),
+ uploaded_size(0) {
+ }
+};
+
+FakeDriveService::FakeDriveService()
+ : about_resource_(new AboutResource),
+ published_date_seq_(0),
+ next_upload_sequence_number_(0),
+ default_max_results_(0),
+ resource_id_count_(0),
+ file_list_load_count_(0),
+ change_list_load_count_(0),
+ directory_load_count_(0),
+ about_resource_load_count_(0),
+ app_list_load_count_(0),
+ blocked_file_list_load_count_(0),
+ offline_(false),
+ never_return_all_file_list_(false),
+ share_url_base_("https://share_url/"),
+ weak_ptr_factory_(this) {
+ about_resource_->set_largest_change_id(654321);
+ about_resource_->set_quota_bytes_total(9876543210);
+ about_resource_->set_quota_bytes_used_aggregate(6789012345);
+ about_resource_->set_root_folder_id(GetRootResourceId());
+}
+
+FakeDriveService::~FakeDriveService() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ STLDeleteValues(&entries_);
+}
+
+bool FakeDriveService::LoadAppListForDriveApi(
+ const std::string& relative_path) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Load JSON data, which must be a dictionary.
+ scoped_ptr<base::Value> value = test_util::LoadJSONFile(relative_path);
+ CHECK_EQ(base::Value::TYPE_DICTIONARY, value->GetType());
+ app_info_value_.reset(
+ static_cast<base::DictionaryValue*>(value.release()));
+ return app_info_value_;
+}
+
+void FakeDriveService::AddApp(const std::string& app_id,
+ const std::string& app_name,
+ const std::string& product_id,
+ const std::string& create_url,
+ bool is_removable) {
+ if (app_json_template_.empty()) {
+ base::FilePath path =
+ test_util::GetTestFilePath("drive/applist_app_template.json");
+ CHECK(base::ReadFileToString(path, &app_json_template_));
+ }
+
+ std::string app_json = app_json_template_;
+ base::ReplaceSubstringsAfterOffset(&app_json, 0, "$AppId", app_id);
+ base::ReplaceSubstringsAfterOffset(&app_json, 0, "$AppName", app_name);
+ base::ReplaceSubstringsAfterOffset(&app_json, 0, "$ProductId", product_id);
+ base::ReplaceSubstringsAfterOffset(&app_json, 0, "$CreateUrl", create_url);
+ base::ReplaceSubstringsAfterOffset(
+ &app_json, 0, "$Removable", is_removable ? "true" : "false");
+
+ JSONStringValueDeserializer json(app_json);
+ std::string error_message;
+ scoped_ptr<base::Value> value(json.Deserialize(NULL, &error_message));
+ CHECK_EQ(base::Value::TYPE_DICTIONARY, value->GetType());
+
+ base::ListValue* item_list;
+ CHECK(app_info_value_->GetListWithoutPathExpansion("items", &item_list));
+ item_list->Append(value.release());
+}
+
+void FakeDriveService::RemoveAppByProductId(const std::string& product_id) {
+ base::ListValue* item_list;
+ CHECK(app_info_value_->GetListWithoutPathExpansion("items", &item_list));
+ for (size_t i = 0; i < item_list->GetSize(); ++i) {
+ base::DictionaryValue* item;
+ CHECK(item_list->GetDictionary(i, &item));
+ const char kKeyProductId[] = "productId";
+ std::string item_product_id;
+ if (item->GetStringWithoutPathExpansion(kKeyProductId, &item_product_id) &&
+ product_id == item_product_id) {
+ item_list->Remove(i, NULL);
+ return;
+ }
+ }
+}
+
+bool FakeDriveService::HasApp(const std::string& app_id) const {
+ base::ListValue* item_list;
+ CHECK(app_info_value_->GetListWithoutPathExpansion("items", &item_list));
+ for (size_t i = 0; i < item_list->GetSize(); ++i) {
+ base::DictionaryValue* item;
+ CHECK(item_list->GetDictionary(i, &item));
+ const char kKeyId[] = "id";
+ std::string item_id;
+ if (item->GetStringWithoutPathExpansion(kKeyId, &item_id) &&
+ item_id == app_id) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void FakeDriveService::SetQuotaValue(int64 used, int64 total) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ about_resource_->set_quota_bytes_used_aggregate(used);
+ about_resource_->set_quota_bytes_total(total);
+}
+
+GURL FakeDriveService::GetFakeLinkUrl(const std::string& resource_id) {
+ return GURL("https://fake_server/" + net::EscapePath(resource_id));
+}
+
+void FakeDriveService::Initialize(const std::string& account_id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void FakeDriveService::AddObserver(DriveServiceObserver* observer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void FakeDriveService::RemoveObserver(DriveServiceObserver* observer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+bool FakeDriveService::CanSendRequest() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return true;
+}
+
+bool FakeDriveService::HasAccessToken() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return true;
+}
+
+void FakeDriveService::RequestAccessToken(const AuthStatusCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+ callback.Run(google_apis::HTTP_NOT_MODIFIED, "fake_access_token");
+}
+
+bool FakeDriveService::HasRefreshToken() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return true;
+}
+
+void FakeDriveService::ClearAccessToken() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+void FakeDriveService::ClearRefreshToken() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+std::string FakeDriveService::GetRootResourceId() const {
+ return "fake_root";
+}
+
+CancelCallback FakeDriveService::GetAllFileList(
+ const FileListCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ if (never_return_all_file_list_) {
+ ++blocked_file_list_load_count_;
+ return CancelCallback();
+ }
+
+ GetChangeListInternal(0, // start changestamp
+ std::string(), // empty search query
+ std::string(), // no directory resource id,
+ 0, // start offset
+ default_max_results_,
+ &file_list_load_count_,
+ base::Bind(&FileListCallbackAdapter, callback));
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::GetFileListInDirectory(
+ const std::string& directory_resource_id,
+ const FileListCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!directory_resource_id.empty());
+ DCHECK(!callback.is_null());
+
+ GetChangeListInternal(0, // start changestamp
+ std::string(), // empty search query
+ directory_resource_id,
+ 0, // start offset
+ default_max_results_,
+ &directory_load_count_,
+ base::Bind(&FileListCallbackAdapter, callback));
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::Search(
+ const std::string& search_query,
+ const FileListCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!search_query.empty());
+ DCHECK(!callback.is_null());
+
+ GetChangeListInternal(0, // start changestamp
+ search_query,
+ std::string(), // no directory resource id,
+ 0, // start offset
+ default_max_results_,
+ NULL,
+ base::Bind(&FileListCallbackAdapter, callback));
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::SearchByTitle(
+ const std::string& title,
+ const std::string& directory_resource_id,
+ const FileListCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!title.empty());
+ DCHECK(!callback.is_null());
+
+ // Note: the search implementation here doesn't support quotation unescape,
+ // so don't escape here.
+ GetChangeListInternal(0, // start changestamp
+ base::StringPrintf("title:'%s'", title.c_str()),
+ directory_resource_id,
+ 0, // start offset
+ default_max_results_,
+ NULL,
+ base::Bind(&FileListCallbackAdapter, callback));
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::GetChangeList(
+ int64 start_changestamp,
+ const ChangeListCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ GetChangeListInternal(start_changestamp,
+ std::string(), // empty search query
+ std::string(), // no directory resource id,
+ 0, // start offset
+ default_max_results_,
+ &change_list_load_count_,
+ callback);
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::GetRemainingChangeList(
+ const GURL& next_link,
+ const ChangeListCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!next_link.is_empty());
+ DCHECK(!callback.is_null());
+
+ // "changestamp", "q", "parent" and "start-offset" are parameters to
+ // implement "paging" of the result on FakeDriveService.
+ // The URL should be the one filled in GetChangeListInternal of the
+ // previous method invocation, so it should start with "http://localhost/?".
+ // See also GetChangeListInternal.
+ DCHECK_EQ(next_link.host(), "localhost");
+ DCHECK_EQ(next_link.path(), "/");
+
+ int64 start_changestamp = 0;
+ std::string search_query;
+ std::string directory_resource_id;
+ int start_offset = 0;
+ int max_results = default_max_results_;
+ base::StringPairs parameters;
+ if (base::SplitStringIntoKeyValuePairs(
+ next_link.query(), '=', '&', &parameters)) {
+ for (size_t i = 0; i < parameters.size(); ++i) {
+ if (parameters[i].first == "changestamp") {
+ base::StringToInt64(parameters[i].second, &start_changestamp);
+ } else if (parameters[i].first == "q") {
+ search_query =
+ net::UnescapeURLComponent(parameters[i].second,
+ net::UnescapeRule::URL_SPECIAL_CHARS);
+ } else if (parameters[i].first == "parent") {
+ directory_resource_id =
+ net::UnescapeURLComponent(parameters[i].second,
+ net::UnescapeRule::URL_SPECIAL_CHARS);
+ } else if (parameters[i].first == "start-offset") {
+ base::StringToInt(parameters[i].second, &start_offset);
+ } else if (parameters[i].first == "max-results") {
+ base::StringToInt(parameters[i].second, &max_results);
+ }
+ }
+ }
+
+ GetChangeListInternal(start_changestamp, search_query, directory_resource_id,
+ start_offset, max_results, NULL, callback);
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::GetRemainingFileList(
+ const GURL& next_link,
+ const FileListCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!next_link.is_empty());
+ DCHECK(!callback.is_null());
+
+ return GetRemainingChangeList(
+ next_link, base::Bind(&FileListCallbackAdapter, callback));
+}
+
+CancelCallback FakeDriveService::GetFileResource(
+ const std::string& resource_id,
+ const FileResourceCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ if (offline_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ DRIVE_NO_CONNECTION,
+ base::Passed(scoped_ptr<FileResource>())));
+ return CancelCallback();
+ }
+
+ EntryInfo* entry = FindEntryByResourceId(resource_id);
+ if (entry && entry->change_resource.file()) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, HTTP_SUCCESS, base::Passed(make_scoped_ptr(
+ new FileResource(*entry->change_resource.file())))));
+ return CancelCallback();
+ }
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, HTTP_NOT_FOUND,
+ base::Passed(scoped_ptr<FileResource>())));
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::GetShareUrl(
+ const std::string& resource_id,
+ const GURL& /* embed_origin */,
+ const GetShareUrlCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ if (offline_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ DRIVE_NO_CONNECTION,
+ GURL()));
+ return CancelCallback();
+ }
+
+ EntryInfo* entry = FindEntryByResourceId(resource_id);
+ if (entry) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, HTTP_SUCCESS, entry->share_url));
+ return CancelCallback();
+ }
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, HTTP_NOT_FOUND, GURL()));
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::GetAboutResource(
+ const AboutResourceCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ if (offline_) {
+ scoped_ptr<AboutResource> null;
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ DRIVE_NO_CONNECTION, base::Passed(&null)));
+ return CancelCallback();
+ }
+
+ ++about_resource_load_count_;
+ scoped_ptr<AboutResource> about_resource(new AboutResource(*about_resource_));
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ HTTP_SUCCESS, base::Passed(&about_resource)));
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::GetAppList(const AppListCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+ DCHECK(app_info_value_);
+
+ if (offline_) {
+ scoped_ptr<AppList> null;
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ DRIVE_NO_CONNECTION,
+ base::Passed(&null)));
+ return CancelCallback();
+ }
+
+ ++app_list_load_count_;
+ scoped_ptr<AppList> app_list(AppList::CreateFrom(*app_info_value_));
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, HTTP_SUCCESS, base::Passed(&app_list)));
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::DeleteResource(
+ const std::string& resource_id,
+ const std::string& etag,
+ const EntryActionCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ if (offline_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, DRIVE_NO_CONNECTION));
+ return CancelCallback();
+ }
+
+ EntryInfo* entry = FindEntryByResourceId(resource_id);
+ if (!entry) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
+ return CancelCallback();
+ }
+
+ ChangeResource* change = &entry->change_resource;
+ const FileResource* file = change->file();
+ if (change->is_deleted()) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
+ return CancelCallback();
+ }
+
+ if (!etag.empty() && etag != file->etag()) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, HTTP_PRECONDITION));
+ return CancelCallback();
+ }
+
+ if (entry->user_permission != google_apis::drive::PERMISSION_ROLE_OWNER) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, HTTP_FORBIDDEN));
+ return CancelCallback();
+ }
+
+ change->set_deleted(true);
+ AddNewChangestamp(change);
+ change->set_file(scoped_ptr<FileResource>());
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, HTTP_NO_CONTENT));
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&FakeDriveService::NotifyObservers,
+ weak_ptr_factory_.GetWeakPtr()));
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::TrashResource(
+ const std::string& resource_id,
+ const EntryActionCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ if (offline_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, DRIVE_NO_CONNECTION));
+ return CancelCallback();
+ }
+
+ EntryInfo* entry = FindEntryByResourceId(resource_id);
+ if (!entry) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
+ return CancelCallback();
+ }
+
+ ChangeResource* change = &entry->change_resource;
+ FileResource* file = change->mutable_file();
+ if (change->is_deleted() || file->labels().is_trashed()) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
+ return CancelCallback();
+ }
+
+ if (entry->user_permission != google_apis::drive::PERMISSION_ROLE_OWNER) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, HTTP_FORBIDDEN));
+ return CancelCallback();
+ }
+
+ file->mutable_labels()->set_trashed(true);
+ AddNewChangestamp(change);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, HTTP_SUCCESS));
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&FakeDriveService::NotifyObservers,
+ weak_ptr_factory_.GetWeakPtr()));
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::DownloadFile(
+ const base::FilePath& local_cache_path,
+ const std::string& resource_id,
+ const DownloadActionCallback& download_action_callback,
+ const GetContentCallback& get_content_callback,
+ const ProgressCallback& progress_callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!download_action_callback.is_null());
+
+ if (offline_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(download_action_callback,
+ DRIVE_NO_CONNECTION,
+ base::FilePath()));
+ return CancelCallback();
+ }
+
+ EntryInfo* entry = FindEntryByResourceId(resource_id);
+ if (!entry || entry->change_resource.file()->IsHostedDocument()) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(download_action_callback, HTTP_NOT_FOUND, base::FilePath()));
+ return CancelCallback();
+ }
+
+ const FileResource* file = entry->change_resource.file();
+ const std::string& content_data = entry->content_data;
+ int64 file_size = file->file_size();
+ DCHECK_EQ(static_cast<size_t>(file_size), content_data.size());
+
+ if (!get_content_callback.is_null()) {
+ const int64 kBlockSize = 5;
+ for (int64 i = 0; i < file_size; i += kBlockSize) {
+ const int64 size = std::min(kBlockSize, file_size - i);
+ scoped_ptr<std::string> content_for_callback(
+ new std::string(content_data.substr(i, size)));
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(get_content_callback, HTTP_SUCCESS,
+ base::Passed(&content_for_callback)));
+ }
+ }
+
+ if (!test_util::WriteStringToFile(local_cache_path, content_data)) {
+ // Failed to write the content.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(download_action_callback,
+ DRIVE_FILE_ERROR, base::FilePath()));
+ return CancelCallback();
+ }
+
+ if (!progress_callback.is_null()) {
+ // See also the comment in ResumeUpload(). For testing that clients
+ // can handle the case progress_callback is called multiple times,
+ // here we invoke the callback twice.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(progress_callback, file_size / 2, file_size));
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(progress_callback, file_size, file_size));
+ }
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(download_action_callback,
+ HTTP_SUCCESS,
+ local_cache_path));
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::CopyResource(
+ const std::string& resource_id,
+ const std::string& in_parent_resource_id,
+ const std::string& new_title,
+ const base::Time& last_modified,
+ const FileResourceCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ if (offline_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ DRIVE_NO_CONNECTION,
+ base::Passed(scoped_ptr<FileResource>())));
+ return CancelCallback();
+ }
+
+ const std::string& parent_resource_id = in_parent_resource_id.empty() ?
+ GetRootResourceId() : in_parent_resource_id;
+
+ EntryInfo* entry = FindEntryByResourceId(resource_id);
+ if (!entry) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, HTTP_NOT_FOUND,
+ base::Passed(scoped_ptr<FileResource>())));
+ return CancelCallback();
+ }
+
+ // Make a copy and set the new resource ID and the new title.
+ scoped_ptr<EntryInfo> copied_entry(new EntryInfo);
+ copied_entry->content_data = entry->content_data;
+ copied_entry->share_url = entry->share_url;
+ copied_entry->change_resource.set_file(
+ make_scoped_ptr(new FileResource(*entry->change_resource.file())));
+
+ ChangeResource* new_change = &copied_entry->change_resource;
+ FileResource* new_file = new_change->mutable_file();
+ const std::string new_resource_id = GetNewResourceId();
+ new_change->set_file_id(new_resource_id);
+ new_file->set_file_id(new_resource_id);
+ new_file->set_title(new_title);
+
+ ParentReference parent;
+ parent.set_file_id(parent_resource_id);
+ parent.set_parent_link(GetFakeLinkUrl(parent_resource_id));
+ std::vector<ParentReference> parents;
+ parents.push_back(parent);
+ *new_file->mutable_parents() = parents;
+
+ if (!last_modified.is_null())
+ new_file->set_modified_date(last_modified);
+
+ AddNewChangestamp(new_change);
+ UpdateETag(new_file);
+
+ // Add the new entry to the map.
+ entries_[new_resource_id] = copied_entry.release();
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ HTTP_SUCCESS,
+ base::Passed(make_scoped_ptr(new FileResource(*new_file)))));
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&FakeDriveService::NotifyObservers,
+ weak_ptr_factory_.GetWeakPtr()));
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::UpdateResource(
+ const std::string& resource_id,
+ const std::string& parent_resource_id,
+ const std::string& new_title,
+ const base::Time& last_modified,
+ const base::Time& last_viewed_by_me,
+ const google_apis::drive::Properties& properties,
+ const google_apis::FileResourceCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ if (offline_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, DRIVE_NO_CONNECTION,
+ base::Passed(scoped_ptr<FileResource>())));
+ return CancelCallback();
+ }
+
+ EntryInfo* entry = FindEntryByResourceId(resource_id);
+ if (!entry) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, HTTP_NOT_FOUND,
+ base::Passed(scoped_ptr<FileResource>())));
+ return CancelCallback();
+ }
+
+ if (!UserHasWriteAccess(entry->user_permission)) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, HTTP_FORBIDDEN,
+ base::Passed(scoped_ptr<FileResource>())));
+ return CancelCallback();
+ }
+
+ ChangeResource* change = &entry->change_resource;
+ FileResource* file = change->mutable_file();
+
+ if (!new_title.empty())
+ file->set_title(new_title);
+
+ // Set parent if necessary.
+ if (!parent_resource_id.empty()) {
+ ParentReference parent;
+ parent.set_file_id(parent_resource_id);
+ parent.set_parent_link(GetFakeLinkUrl(parent_resource_id));
+
+ std::vector<ParentReference> parents;
+ parents.push_back(parent);
+ *file->mutable_parents() = parents;
+ }
+
+ if (!last_modified.is_null())
+ file->set_modified_date(last_modified);
+
+ if (!last_viewed_by_me.is_null())
+ file->set_last_viewed_by_me_date(last_viewed_by_me);
+
+ AddNewChangestamp(change);
+ UpdateETag(file);
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, HTTP_SUCCESS,
+ base::Passed(make_scoped_ptr(new FileResource(*file)))));
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&FakeDriveService::NotifyObservers,
+ weak_ptr_factory_.GetWeakPtr()));
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::AddResourceToDirectory(
+ const std::string& parent_resource_id,
+ const std::string& resource_id,
+ const EntryActionCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ if (offline_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, DRIVE_NO_CONNECTION));
+ return CancelCallback();
+ }
+
+ EntryInfo* entry = FindEntryByResourceId(resource_id);
+ if (!entry) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
+ return CancelCallback();
+ }
+
+ ChangeResource* change = &entry->change_resource;
+ // On the real Drive server, resources do not necessary shape a tree
+ // structure. That is, each resource can have multiple parent.
+ // We mimic the behavior here; AddResourceToDirectoy just adds
+ // one more parent, not overwriting old ones.
+ ParentReference parent;
+ parent.set_file_id(parent_resource_id);
+ parent.set_parent_link(GetFakeLinkUrl(parent_resource_id));
+ change->mutable_file()->mutable_parents()->push_back(parent);
+
+ AddNewChangestamp(change);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, HTTP_SUCCESS));
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&FakeDriveService::NotifyObservers,
+ weak_ptr_factory_.GetWeakPtr()));
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::RemoveResourceFromDirectory(
+ const std::string& parent_resource_id,
+ const std::string& resource_id,
+ const EntryActionCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ if (offline_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, DRIVE_NO_CONNECTION));
+ return CancelCallback();
+ }
+
+ EntryInfo* entry = FindEntryByResourceId(resource_id);
+ if (!entry) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
+ return CancelCallback();
+ }
+
+ ChangeResource* change = &entry->change_resource;
+ FileResource* file = change->mutable_file();
+ std::vector<ParentReference>* parents = file->mutable_parents();
+ for (size_t i = 0; i < parents->size(); ++i) {
+ if ((*parents)[i].file_id() == parent_resource_id) {
+ parents->erase(parents->begin() + i);
+ AddNewChangestamp(change);
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, HTTP_NO_CONTENT));
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&FakeDriveService::NotifyObservers,
+ weak_ptr_factory_.GetWeakPtr()));
+ return CancelCallback();
+ }
+ }
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::AddNewDirectory(
+ const std::string& parent_resource_id,
+ const std::string& directory_title,
+ const AddNewDirectoryOptions& options,
+ const FileResourceCallback& callback) {
+ return AddNewDirectoryWithResourceId(
+ "",
+ parent_resource_id.empty() ? GetRootResourceId() : parent_resource_id,
+ directory_title,
+ options,
+ callback);
+}
+
+CancelCallback FakeDriveService::InitiateUploadNewFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& parent_resource_id,
+ const std::string& title,
+ const UploadNewFileOptions& options,
+ const InitiateUploadCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ if (offline_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, DRIVE_NO_CONNECTION, GURL()));
+ return CancelCallback();
+ }
+
+ if (parent_resource_id != GetRootResourceId() &&
+ !entries_.count(parent_resource_id)) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, HTTP_NOT_FOUND, GURL()));
+ return CancelCallback();
+ }
+
+ GURL session_url = GetNewUploadSessionUrl();
+ upload_sessions_[session_url] =
+ UploadSession(content_type, content_length,
+ parent_resource_id,
+ "", // resource_id
+ "", // etag
+ title);
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, HTTP_SUCCESS, session_url));
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::InitiateUploadExistingFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& resource_id,
+ const UploadExistingFileOptions& options,
+ const InitiateUploadCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ if (offline_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, DRIVE_NO_CONNECTION, GURL()));
+ return CancelCallback();
+ }
+
+ EntryInfo* entry = FindEntryByResourceId(resource_id);
+ if (!entry) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, HTTP_NOT_FOUND, GURL()));
+ return CancelCallback();
+ }
+
+ if (!UserHasWriteAccess(entry->user_permission)) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, HTTP_FORBIDDEN, GURL()));
+ return CancelCallback();
+ }
+
+ FileResource* file = entry->change_resource.mutable_file();
+ if (!options.etag.empty() && options.etag != file->etag()) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, HTTP_PRECONDITION, GURL()));
+ return CancelCallback();
+ }
+ // TODO(hashimoto): Update |file|'s metadata with |options|.
+
+ GURL session_url = GetNewUploadSessionUrl();
+ upload_sessions_[session_url] =
+ UploadSession(content_type, content_length,
+ "", // parent_resource_id
+ resource_id,
+ file->etag(),
+ "" /* title */);
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, HTTP_SUCCESS, session_url));
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::GetUploadStatus(
+ const GURL& upload_url,
+ int64 content_length,
+ const UploadRangeCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::ResumeUpload(
+ const GURL& upload_url,
+ int64 start_position,
+ int64 end_position,
+ int64 content_length,
+ const std::string& content_type,
+ const base::FilePath& local_file_path,
+ const UploadRangeCallback& callback,
+ const ProgressCallback& progress_callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ FileResourceCallback completion_callback
+ = base::Bind(&ScheduleUploadRangeCallback,
+ callback, start_position, end_position);
+
+ if (offline_) {
+ completion_callback.Run(DRIVE_NO_CONNECTION, scoped_ptr<FileResource>());
+ return CancelCallback();
+ }
+
+ if (!upload_sessions_.count(upload_url)) {
+ completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<FileResource>());
+ return CancelCallback();
+ }
+
+ UploadSession* session = &upload_sessions_[upload_url];
+
+ // Chunks are required to be sent in such a ways that they fill from the start
+ // of the not-yet-uploaded part with no gaps nor overlaps.
+ if (session->uploaded_size != start_position) {
+ completion_callback.Run(HTTP_BAD_REQUEST, scoped_ptr<FileResource>());
+ return CancelCallback();
+ }
+
+ if (!progress_callback.is_null()) {
+ // In the real GDataWapi/Drive DriveService, progress is reported in
+ // nondeterministic timing. In this fake implementation, we choose to call
+ // it twice per one ResumeUpload. This is for making sure that client code
+ // works fine even if the callback is invoked more than once; it is the
+ // crucial difference of the progress callback from others.
+ // Note that progress is notified in the relative offset in each chunk.
+ const int64 chunk_size = end_position - start_position;
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(progress_callback, chunk_size / 2, chunk_size));
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(progress_callback, chunk_size, chunk_size));
+ }
+
+ if (content_length != end_position) {
+ session->uploaded_size = end_position;
+ completion_callback.Run(HTTP_RESUME_INCOMPLETE, scoped_ptr<FileResource>());
+ return CancelCallback();
+ }
+
+ std::string content_data;
+ if (!base::ReadFileToString(local_file_path, &content_data)) {
+ session->uploaded_size = end_position;
+ completion_callback.Run(DRIVE_FILE_ERROR, scoped_ptr<FileResource>());
+ return CancelCallback();
+ }
+ session->uploaded_size = end_position;
+
+ // |resource_id| is empty if the upload is for new file.
+ if (session->resource_id.empty()) {
+ DCHECK(!session->parent_resource_id.empty());
+ DCHECK(!session->title.empty());
+ const EntryInfo* new_entry = AddNewEntry(
+ "", // auto generate resource id.
+ session->content_type,
+ content_data,
+ session->parent_resource_id,
+ session->title,
+ false); // shared_with_me
+ if (!new_entry) {
+ completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<FileResource>());
+ return CancelCallback();
+ }
+
+ completion_callback.Run(HTTP_CREATED, make_scoped_ptr(
+ new FileResource(*new_entry->change_resource.file())));
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&FakeDriveService::NotifyObservers,
+ weak_ptr_factory_.GetWeakPtr()));
+ return CancelCallback();
+ }
+
+ EntryInfo* entry = FindEntryByResourceId(session->resource_id);
+ if (!entry) {
+ completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<FileResource>());
+ return CancelCallback();
+ }
+
+ ChangeResource* change = &entry->change_resource;
+ FileResource* file = change->mutable_file();
+ if (file->etag().empty() || session->etag != file->etag()) {
+ completion_callback.Run(HTTP_PRECONDITION, scoped_ptr<FileResource>());
+ return CancelCallback();
+ }
+
+ file->set_md5_checksum(base::MD5String(content_data));
+ entry->content_data = content_data;
+ file->set_file_size(end_position);
+ AddNewChangestamp(change);
+ UpdateETag(file);
+
+ completion_callback.Run(HTTP_SUCCESS, make_scoped_ptr(
+ new FileResource(*file)));
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&FakeDriveService::NotifyObservers,
+ weak_ptr_factory_.GetWeakPtr()));
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::MultipartUploadNewFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& parent_resource_id,
+ const std::string& title,
+ const base::FilePath& local_file_path,
+ const UploadNewFileOptions& options,
+ const FileResourceCallback& callback,
+ const ProgressCallback& progress_callback) {
+ CallResumeUpload* const call_resume_upload = new CallResumeUpload();
+ call_resume_upload->service = weak_ptr_factory_.GetWeakPtr();
+ call_resume_upload->content_type = content_type;
+ call_resume_upload->content_length = content_length;
+ call_resume_upload->local_file_path = local_file_path;
+ call_resume_upload->callback = callback;
+ call_resume_upload->progress_callback = progress_callback;
+ InitiateUploadNewFile(
+ content_type,
+ content_length,
+ parent_resource_id,
+ title,
+ options,
+ base::Bind(&CallResumeUpload::Run, base::Owned(call_resume_upload)));
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::MultipartUploadExistingFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& resource_id,
+ const base::FilePath& local_file_path,
+ const UploadExistingFileOptions& options,
+ const FileResourceCallback& callback,
+ const ProgressCallback& progress_callback) {
+ CallResumeUpload* const call_resume_upload = new CallResumeUpload();
+ call_resume_upload->service = weak_ptr_factory_.GetWeakPtr();
+ call_resume_upload->content_type = content_type;
+ call_resume_upload->content_length = content_length;
+ call_resume_upload->local_file_path = local_file_path;
+ call_resume_upload->callback = callback;
+ call_resume_upload->progress_callback = progress_callback;
+ InitiateUploadExistingFile(
+ content_type,
+ content_length,
+ resource_id,
+ options,
+ base::Bind(&CallResumeUpload::Run, base::Owned(call_resume_upload)));
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::AuthorizeApp(
+ const std::string& resource_id,
+ const std::string& app_id,
+ const AuthorizeAppCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ if (entries_.count(resource_id) == 0) {
+ callback.Run(google_apis::HTTP_NOT_FOUND, GURL());
+ return CancelCallback();
+ }
+
+ callback.Run(HTTP_SUCCESS,
+ GURL(base::StringPrintf(open_url_format_.c_str(),
+ resource_id.c_str(),
+ app_id.c_str())));
+ return CancelCallback();
+}
+
+CancelCallback FakeDriveService::UninstallApp(
+ const std::string& app_id,
+ const google_apis::EntryActionCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ if (offline_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, google_apis::DRIVE_NO_CONNECTION));
+ return CancelCallback();
+ }
+
+ // Find app_id from app_info_value_ and delete.
+ base::ListValue* items = NULL;
+ if (!app_info_value_->GetList("items", &items)) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, google_apis::HTTP_NOT_FOUND));
+ return CancelCallback();
+ }
+
+ for (size_t i = 0; i < items->GetSize(); ++i) {
+ base::DictionaryValue* item = NULL;
+ std::string id;
+ if (items->GetDictionary(i, &item) && item->GetString("id", &id) &&
+ id == app_id) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ items->Remove(i, NULL) ? google_apis::HTTP_NO_CONTENT
+ : google_apis::HTTP_NOT_FOUND));
+ return CancelCallback();
+ }
+ }
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, google_apis::HTTP_NOT_FOUND));
+ return CancelCallback();
+}
+
+void FakeDriveService::AddNewFile(const std::string& content_type,
+ const std::string& content_data,
+ const std::string& parent_resource_id,
+ const std::string& title,
+ bool shared_with_me,
+ const FileResourceCallback& callback) {
+ AddNewFileWithResourceId("", content_type, content_data, parent_resource_id,
+ title, shared_with_me, callback);
+}
+
+void FakeDriveService::AddNewFileWithResourceId(
+ const std::string& resource_id,
+ const std::string& content_type,
+ const std::string& content_data,
+ const std::string& parent_resource_id,
+ const std::string& title,
+ bool shared_with_me,
+ const FileResourceCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ if (offline_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ DRIVE_NO_CONNECTION,
+ base::Passed(scoped_ptr<FileResource>())));
+ return;
+ }
+
+ const EntryInfo* new_entry = AddNewEntry(resource_id,
+ content_type,
+ content_data,
+ parent_resource_id,
+ title,
+ shared_with_me);
+ if (!new_entry) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, HTTP_NOT_FOUND,
+ base::Passed(scoped_ptr<FileResource>())));
+ return;
+ }
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, HTTP_CREATED,
+ base::Passed(make_scoped_ptr(
+ new FileResource(*new_entry->change_resource.file())))));
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&FakeDriveService::NotifyObservers,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+CancelCallback FakeDriveService::AddNewDirectoryWithResourceId(
+ const std::string& resource_id,
+ const std::string& parent_resource_id,
+ const std::string& directory_title,
+ const AddNewDirectoryOptions& options,
+ const FileResourceCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ if (offline_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ DRIVE_NO_CONNECTION,
+ base::Passed(scoped_ptr<FileResource>())));
+ return CancelCallback();
+ }
+
+ const EntryInfo* new_entry = AddNewEntry(resource_id,
+ util::kDriveFolderMimeType,
+ "", // content_data
+ parent_resource_id,
+ directory_title,
+ false); // shared_with_me
+ if (!new_entry) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, HTTP_NOT_FOUND,
+ base::Passed(scoped_ptr<FileResource>())));
+ return CancelCallback();
+ }
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, HTTP_CREATED,
+ base::Passed(make_scoped_ptr(
+ new FileResource(*new_entry->change_resource.file())))));
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&FakeDriveService::NotifyObservers,
+ weak_ptr_factory_.GetWeakPtr()));
+ return CancelCallback();
+}
+
+void FakeDriveService::SetLastModifiedTime(
+ const std::string& resource_id,
+ const base::Time& last_modified_time,
+ const FileResourceCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ if (offline_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ DRIVE_NO_CONNECTION,
+ base::Passed(scoped_ptr<FileResource>())));
+ return;
+ }
+
+ EntryInfo* entry = FindEntryByResourceId(resource_id);
+ if (!entry) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, HTTP_NOT_FOUND,
+ base::Passed(scoped_ptr<FileResource>())));
+ return;
+ }
+
+ ChangeResource* change = &entry->change_resource;
+ FileResource* file = change->mutable_file();
+ file->set_modified_date(last_modified_time);
+
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, HTTP_SUCCESS,
+ base::Passed(make_scoped_ptr(new FileResource(*file)))));
+}
+
+google_apis::DriveApiErrorCode FakeDriveService::SetUserPermission(
+ const std::string& resource_id,
+ google_apis::drive::PermissionRole user_permission) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ EntryInfo* entry = FindEntryByResourceId(resource_id);
+ if (!entry)
+ return HTTP_NOT_FOUND;
+
+ entry->user_permission = user_permission;
+ return HTTP_SUCCESS;
+}
+
+void FakeDriveService::AddChangeObserver(ChangeObserver* change_observer) {
+ change_observers_.AddObserver(change_observer);
+}
+
+void FakeDriveService::RemoveChangeObserver(ChangeObserver* change_observer) {
+ change_observers_.RemoveObserver(change_observer);
+}
+
+FakeDriveService::EntryInfo* FakeDriveService::FindEntryByResourceId(
+ const std::string& resource_id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ EntryInfoMap::iterator it = entries_.find(resource_id);
+ // Deleted entries don't have FileResource.
+ return it != entries_.end() && it->second->change_resource.file() ?
+ it->second : NULL;
+}
+
+std::string FakeDriveService::GetNewResourceId() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ ++resource_id_count_;
+ return base::StringPrintf("resource_id_%d", resource_id_count_);
+}
+
+void FakeDriveService::UpdateETag(google_apis::FileResource* file) {
+ file->set_etag(
+ "etag_" + base::Int64ToString(about_resource_->largest_change_id()));
+}
+
+void FakeDriveService::AddNewChangestamp(google_apis::ChangeResource* change) {
+ about_resource_->set_largest_change_id(
+ about_resource_->largest_change_id() + 1);
+ change->set_change_id(about_resource_->largest_change_id());
+}
+
+const FakeDriveService::EntryInfo* FakeDriveService::AddNewEntry(
+ const std::string& given_resource_id,
+ const std::string& content_type,
+ const std::string& content_data,
+ const std::string& parent_resource_id,
+ const std::string& title,
+ bool shared_with_me) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (!parent_resource_id.empty() &&
+ parent_resource_id != GetRootResourceId() &&
+ !entries_.count(parent_resource_id)) {
+ return NULL;
+ }
+
+ const std::string resource_id =
+ given_resource_id.empty() ? GetNewResourceId() : given_resource_id;
+ if (entries_.count(resource_id))
+ return NULL;
+ GURL upload_url = GURL("https://xxx/upload/" + resource_id);
+
+ scoped_ptr<EntryInfo> new_entry(new EntryInfo);
+ ChangeResource* new_change = &new_entry->change_resource;
+ FileResource* new_file = new FileResource;
+ new_change->set_file(make_scoped_ptr(new_file));
+
+ // Set the resource ID and the title
+ new_change->set_file_id(resource_id);
+ new_file->set_file_id(resource_id);
+ new_file->set_title(title);
+ // Set the contents, size and MD5 for a file.
+ if (content_type != util::kDriveFolderMimeType &&
+ !util::IsKnownHostedDocumentMimeType(content_type)) {
+ new_entry->content_data = content_data;
+ new_file->set_file_size(content_data.size());
+ new_file->set_md5_checksum(base::MD5String(content_data));
+ }
+
+ if (shared_with_me) {
+ // Set current time to mark the file as shared_with_me.
+ new_file->set_shared_with_me_date(base::Time::Now());
+ }
+
+ std::string escaped_resource_id = net::EscapePath(resource_id);
+
+ // Set mime type.
+ new_file->set_mime_type(content_type);
+
+ // Set alternate link if needed.
+ if (content_type == util::kGoogleDocumentMimeType)
+ new_file->set_alternate_link(GURL("https://document_alternate_link"));
+
+ // Set parents.
+ if (!parent_resource_id.empty()) {
+ ParentReference parent;
+ parent.set_file_id(parent_resource_id);
+ parent.set_parent_link(GetFakeLinkUrl(parent.file_id()));
+ std::vector<ParentReference> parents;
+ parents.push_back(parent);
+ *new_file->mutable_parents() = parents;
+ }
+
+ new_entry->share_url = net::AppendOrReplaceQueryParameter(
+ share_url_base_, "name", title);
+
+ AddNewChangestamp(new_change);
+ UpdateETag(new_file);
+
+ base::Time published_date =
+ base::Time() + base::TimeDelta::FromMilliseconds(++published_date_seq_);
+ new_file->set_created_date(published_date);
+
+ EntryInfo* raw_new_entry = new_entry.release();
+ entries_[resource_id] = raw_new_entry;
+ return raw_new_entry;
+}
+
+void FakeDriveService::GetChangeListInternal(
+ int64 start_changestamp,
+ const std::string& search_query,
+ const std::string& directory_resource_id,
+ int start_offset,
+ int max_results,
+ int* load_counter,
+ const ChangeListCallback& callback) {
+ if (offline_) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ DRIVE_NO_CONNECTION,
+ base::Passed(scoped_ptr<ChangeList>())));
+ return;
+ }
+
+ // Filter out entries per parameters like |directory_resource_id| and
+ // |search_query|.
+ ScopedVector<ChangeResource> entries;
+ int num_entries_matched = 0;
+ for (EntryInfoMap::iterator it = entries_.begin(); it != entries_.end();
+ ++it) {
+ const ChangeResource& entry = it->second->change_resource;
+ bool should_exclude = false;
+
+ // If |directory_resource_id| is set, exclude the entry if it's not in
+ // the target directory.
+ if (!directory_resource_id.empty()) {
+ // Get the parent resource ID of the entry.
+ std::string parent_resource_id;
+ if (entry.file() && !entry.file()->parents().empty())
+ parent_resource_id = entry.file()->parents()[0].file_id();
+
+ if (directory_resource_id != parent_resource_id)
+ should_exclude = true;
+ }
+
+ // If |search_query| is set, exclude the entry if it does not contain the
+ // search query in the title.
+ if (!should_exclude && !search_query.empty() &&
+ !EntryMatchWithQuery(entry, search_query)) {
+ should_exclude = true;
+ }
+
+ // If |start_changestamp| is set, exclude the entry if the
+ // changestamp is older than |largest_changestamp|.
+ // See https://developers.google.com/google-apps/documents-list/
+ // #retrieving_all_changes_since_a_given_changestamp
+ if (start_changestamp > 0 && entry.change_id() < start_changestamp)
+ should_exclude = true;
+
+ // If the caller requests other list than change list by specifying
+ // zero-|start_changestamp|, exclude deleted entry from the result.
+ const bool deleted = entry.is_deleted() ||
+ (entry.file() && entry.file()->labels().is_trashed());
+ if (!start_changestamp && deleted)
+ should_exclude = true;
+
+ // The entry matched the criteria for inclusion.
+ if (!should_exclude)
+ ++num_entries_matched;
+
+ // If |start_offset| is set, exclude the entry if the entry is before the
+ // start index. <= instead of < as |num_entries_matched| was
+ // already incremented.
+ if (start_offset > 0 && num_entries_matched <= start_offset)
+ should_exclude = true;
+
+ if (!should_exclude) {
+ scoped_ptr<ChangeResource> entry_copied(new ChangeResource);
+ entry_copied->set_change_id(entry.change_id());
+ entry_copied->set_file_id(entry.file_id());
+ entry_copied->set_deleted(entry.is_deleted());
+ if (entry.file()) {
+ entry_copied->set_file(
+ make_scoped_ptr(new FileResource(*entry.file())));
+ }
+ entry_copied->set_modification_date(entry.modification_date());
+ entries.push_back(entry_copied.release());
+ }
+ }
+
+ scoped_ptr<ChangeList> change_list(new ChangeList);
+ if (start_changestamp > 0 && start_offset == 0) {
+ change_list->set_largest_change_id(about_resource_->largest_change_id());
+ }
+
+ // If |max_results| is set, trim the entries if the number exceeded the max
+ // results.
+ if (max_results > 0 && entries.size() > static_cast<size_t>(max_results)) {
+ entries.erase(entries.begin() + max_results, entries.end());
+ // Adds the next URL.
+ // Here, we embed information which is needed for continuing the
+ // GetChangeList request in the next invocation into url query
+ // parameters.
+ GURL next_url(base::StringPrintf(
+ "http://localhost/?start-offset=%d&max-results=%d",
+ start_offset + max_results,
+ max_results));
+ if (start_changestamp > 0) {
+ next_url = net::AppendOrReplaceQueryParameter(
+ next_url, "changestamp",
+ base::Int64ToString(start_changestamp).c_str());
+ }
+ if (!search_query.empty()) {
+ next_url = net::AppendOrReplaceQueryParameter(
+ next_url, "q", search_query);
+ }
+ if (!directory_resource_id.empty()) {
+ next_url = net::AppendOrReplaceQueryParameter(
+ next_url, "parent", directory_resource_id);
+ }
+
+ change_list->set_next_link(next_url);
+ }
+ *change_list->mutable_items() = entries.Pass();
+
+ if (load_counter)
+ *load_counter += 1;
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, HTTP_SUCCESS, base::Passed(&change_list)));
+}
+
+GURL FakeDriveService::GetNewUploadSessionUrl() {
+ return GURL("https://upload_session_url/" +
+ base::Int64ToString(next_upload_sequence_number_++));
+}
+
+google_apis::CancelCallback FakeDriveService::AddPermission(
+ const std::string& resource_id,
+ const std::string& email,
+ google_apis::drive::PermissionRole role,
+ const google_apis::EntryActionCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!callback.is_null());
+
+ NOTREACHED();
+ return CancelCallback();
+}
+
+scoped_ptr<BatchRequestConfiguratorInterface>
+FakeDriveService::StartBatchRequest() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ NOTREACHED();
+ return scoped_ptr<BatchRequestConfiguratorInterface>();
+}
+
+void FakeDriveService::NotifyObservers() {
+ FOR_EACH_OBSERVER(ChangeObserver, change_observers_, OnNewChangeAvailable());
+}
+
+} // namespace drive
diff --git a/components/drive/service/fake_drive_service.h b/components/drive/service/fake_drive_service.h
new file mode 100644
index 0000000..09ae14a
--- /dev/null
+++ b/components/drive/service/fake_drive_service.h
@@ -0,0 +1,406 @@
+// Copyright (c) 2012 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.
+
+#ifndef COMPONENTS_DRIVE_SERVICE_FAKE_DRIVE_SERVICE_H_
+#define COMPONENTS_DRIVE_SERVICE_FAKE_DRIVE_SERVICE_H_
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/threading/thread_checker.h"
+#include "components/drive/service/drive_service_interface.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace google_apis {
+class AboutResource;
+class ChangeResource;
+class FileResource;
+}
+
+namespace drive {
+
+// This class implements a fake DriveService which acts like a real Drive
+// service. The fake service works as follows:
+//
+// 1) Load JSON files and construct the in-memory resource list.
+// 2) Return valid responses based on the the in-memory resource list.
+// 3) Update the in-memory resource list by requests like DeleteResource().
+class FakeDriveService : public DriveServiceInterface {
+ public:
+ class ChangeObserver {
+ public:
+ virtual ~ChangeObserver() {}
+ virtual void OnNewChangeAvailable() = 0;
+ };
+
+ FakeDriveService();
+ ~FakeDriveService() override;
+
+ // Loads the app list for Drive API. Returns true on success.
+ bool LoadAppListForDriveApi(const std::string& relative_path);
+
+ // Adds an app to app list.
+ void AddApp(const std::string& app_id,
+ const std::string& app_name,
+ const std::string& product_id,
+ const std::string& create_url,
+ bool is_removable);
+
+ // Removes an app by product id.
+ void RemoveAppByProductId(const std::string& product_id);
+
+ // Returns true if the service knows the given drive app id.
+ bool HasApp(const std::string& app_id) const;
+
+ // Changes the offline state. All functions fail with DRIVE_NO_CONNECTION
+ // when offline. By default the offline state is false.
+ void set_offline(bool offline) { offline_ = offline; }
+
+ // GetAllFileList never returns result when this is set to true.
+ // Used to emulate the real server's slowness.
+ void set_never_return_all_file_list(bool value) {
+ never_return_all_file_list_ = value;
+ }
+
+ // Changes the default max results returned from GetAllFileList().
+ // By default, it's set to 0, which is unlimited.
+ void set_default_max_results(int default_max_results) {
+ default_max_results_ = default_max_results;
+ }
+
+ // Sets the url to the test server to be used as a base for generated share
+ // urls to the share dialog.
+ void set_share_url_base(const GURL& share_url_base) {
+ share_url_base_ = share_url_base;
+ }
+
+ // Changes the quota fields returned from GetAboutResource().
+ void SetQuotaValue(int64 used, int64 total);
+
+ // Returns the AboutResource.
+ const google_apis::AboutResource& about_resource() const {
+ return *about_resource_;
+ }
+
+ // Returns the number of times the file list is successfully loaded by
+ // GetAllFileList().
+ int file_list_load_count() const { return file_list_load_count_; }
+
+ // Returns the number of times the resource list is successfully loaded by
+ // GetChangeList().
+ int change_list_load_count() const { return change_list_load_count_; }
+
+ // Returns the number of times the resource list is successfully loaded by
+ // GetFileListInDirectory().
+ int directory_load_count() const { return directory_load_count_; }
+
+ // Returns the number of times the about resource is successfully loaded
+ // by GetAboutResource().
+ int about_resource_load_count() const {
+ return about_resource_load_count_;
+ }
+
+ // Returns the number of times the app list is successfully loaded by
+ // GetAppList().
+ int app_list_load_count() const { return app_list_load_count_; }
+
+ // Returns the number of times GetAllFileList are blocked due to
+ // set_never_return_all_file_list().
+ int blocked_file_list_load_count() const {
+ return blocked_file_list_load_count_;
+ }
+
+ // Returns the file path whose request is cancelled just before this method
+ // invocation.
+ const base::FilePath& last_cancelled_file() const {
+ return last_cancelled_file_;
+ }
+
+ // Returns the (fake) URL for the link.
+ static GURL GetFakeLinkUrl(const std::string& resource_id);
+
+ // Sets the printf format for constructing the response of AuthorizeApp().
+ // The format string must include two %s that are to be filled with
+ // resource_id and app_id.
+ void set_open_url_format(const std::string& url_format) {
+ open_url_format_ = url_format;
+ }
+
+ // DriveServiceInterface Overrides
+ void Initialize(const std::string& account_id) override;
+ void AddObserver(DriveServiceObserver* observer) override;
+ void RemoveObserver(DriveServiceObserver* observer) override;
+ bool CanSendRequest() const override;
+ std::string GetRootResourceId() const override;
+ bool HasAccessToken() const override;
+ void RequestAccessToken(
+ const google_apis::AuthStatusCallback& callback) override;
+ bool HasRefreshToken() const override;
+ void ClearAccessToken() override;
+ void ClearRefreshToken() override;
+ google_apis::CancelCallback GetAllFileList(
+ const google_apis::FileListCallback& callback) override;
+ google_apis::CancelCallback GetFileListInDirectory(
+ const std::string& directory_resource_id,
+ const google_apis::FileListCallback& callback) override;
+ // See the comment for EntryMatchWidthQuery() in .cc file for details about
+ // the supported search query types.
+ google_apis::CancelCallback Search(
+ const std::string& search_query,
+ const google_apis::FileListCallback& callback) override;
+ google_apis::CancelCallback SearchByTitle(
+ const std::string& title,
+ const std::string& directory_resource_id,
+ const google_apis::FileListCallback& callback) override;
+ google_apis::CancelCallback GetChangeList(
+ int64 start_changestamp,
+ const google_apis::ChangeListCallback& callback) override;
+ google_apis::CancelCallback GetRemainingChangeList(
+ const GURL& next_link,
+ const google_apis::ChangeListCallback& callback) override;
+ google_apis::CancelCallback GetRemainingFileList(
+ const GURL& next_link,
+ const google_apis::FileListCallback& callback) override;
+ google_apis::CancelCallback GetFileResource(
+ const std::string& resource_id,
+ const google_apis::FileResourceCallback& callback) override;
+ google_apis::CancelCallback GetShareUrl(
+ const std::string& resource_id,
+ const GURL& embed_origin,
+ const google_apis::GetShareUrlCallback& callback) override;
+ google_apis::CancelCallback GetAboutResource(
+ const google_apis::AboutResourceCallback& callback) override;
+ google_apis::CancelCallback GetAppList(
+ const google_apis::AppListCallback& callback) override;
+ google_apis::CancelCallback DeleteResource(
+ const std::string& resource_id,
+ const std::string& etag,
+ const google_apis::EntryActionCallback& callback) override;
+ google_apis::CancelCallback TrashResource(
+ const std::string& resource_id,
+ const google_apis::EntryActionCallback& callback) override;
+ google_apis::CancelCallback DownloadFile(
+ const base::FilePath& local_cache_path,
+ const std::string& resource_id,
+ const google_apis::DownloadActionCallback& download_action_callback,
+ const google_apis::GetContentCallback& get_content_callback,
+ const google_apis::ProgressCallback& progress_callback) override;
+ google_apis::CancelCallback CopyResource(
+ const std::string& resource_id,
+ const std::string& parent_resource_id,
+ const std::string& new_title,
+ const base::Time& last_modified,
+ const google_apis::FileResourceCallback& callback) override;
+ google_apis::CancelCallback UpdateResource(
+ const std::string& resource_id,
+ const std::string& parent_resource_id,
+ const std::string& new_title,
+ const base::Time& last_modified,
+ const base::Time& last_viewed_by_me,
+ const google_apis::drive::Properties& properties,
+ const google_apis::FileResourceCallback& callback) override;
+ google_apis::CancelCallback AddResourceToDirectory(
+ const std::string& parent_resource_id,
+ const std::string& resource_id,
+ const google_apis::EntryActionCallback& callback) override;
+ google_apis::CancelCallback RemoveResourceFromDirectory(
+ const std::string& parent_resource_id,
+ const std::string& resource_id,
+ const google_apis::EntryActionCallback& callback) override;
+ google_apis::CancelCallback AddNewDirectory(
+ const std::string& parent_resource_id,
+ const std::string& directory_title,
+ const AddNewDirectoryOptions& options,
+ const google_apis::FileResourceCallback& callback) override;
+ google_apis::CancelCallback InitiateUploadNewFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& parent_resource_id,
+ const std::string& title,
+ const UploadNewFileOptions& options,
+ const google_apis::InitiateUploadCallback& callback) override;
+ google_apis::CancelCallback InitiateUploadExistingFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& resource_id,
+ const UploadExistingFileOptions& options,
+ const google_apis::InitiateUploadCallback& callback) override;
+ google_apis::CancelCallback ResumeUpload(
+ const GURL& upload_url,
+ int64 start_position,
+ int64 end_position,
+ int64 content_length,
+ const std::string& content_type,
+ const base::FilePath& local_file_path,
+ const google_apis::drive::UploadRangeCallback& callback,
+ const google_apis::ProgressCallback& progress_callback) override;
+ google_apis::CancelCallback GetUploadStatus(
+ const GURL& upload_url,
+ int64 content_length,
+ const google_apis::drive::UploadRangeCallback& callback) override;
+ google_apis::CancelCallback MultipartUploadNewFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& parent_resource_id,
+ const std::string& title,
+ const base::FilePath& local_file_path,
+ const UploadNewFileOptions& options,
+ const google_apis::FileResourceCallback& callback,
+ const google_apis::ProgressCallback& progress_callback) override;
+ google_apis::CancelCallback MultipartUploadExistingFile(
+ const std::string& content_type,
+ int64 content_length,
+ const std::string& resource_id,
+ const base::FilePath& local_file_path,
+ const UploadExistingFileOptions& options,
+ const google_apis::FileResourceCallback& callback,
+ const google_apis::ProgressCallback& progress_callback) override;
+ google_apis::CancelCallback AuthorizeApp(
+ const std::string& resource_id,
+ const std::string& app_id,
+ const google_apis::AuthorizeAppCallback& callback) override;
+ google_apis::CancelCallback UninstallApp(
+ const std::string& app_id,
+ const google_apis::EntryActionCallback& callback) override;
+ google_apis::CancelCallback AddPermission(
+ const std::string& resource_id,
+ const std::string& email,
+ google_apis::drive::PermissionRole role,
+ const google_apis::EntryActionCallback& callback) override;
+ scoped_ptr<BatchRequestConfiguratorInterface> StartBatchRequest() override;
+
+ // Adds a new file with the given parameters. On success, returns
+ // HTTP_CREATED with the parsed entry.
+ // |callback| must not be null.
+ void AddNewFile(const std::string& content_type,
+ const std::string& content_data,
+ const std::string& parent_resource_id,
+ const std::string& title,
+ bool shared_with_me,
+ const google_apis::FileResourceCallback& callback);
+
+ // Adds a new file with the given |resource_id|. If the id already exists,
+ // it's an error. This is used for testing cross profile file sharing that
+ // needs to have matching resource IDs in different fake service instances.
+ // |callback| must not be null.
+ void AddNewFileWithResourceId(
+ const std::string& resource_id,
+ const std::string& content_type,
+ const std::string& content_data,
+ const std::string& parent_resource_id,
+ const std::string& title,
+ bool shared_with_me,
+ const google_apis::FileResourceCallback& callback);
+
+ // Adds a new directory with the given |resource_id|.
+ // |callback| must not be null.
+ google_apis::CancelCallback AddNewDirectoryWithResourceId(
+ const std::string& resource_id,
+ const std::string& parent_resource_id,
+ const std::string& directory_title,
+ const AddNewDirectoryOptions& options,
+ const google_apis::FileResourceCallback& callback);
+
+ // Sets the last modified time for an entry specified by |resource_id|.
+ // On success, returns HTTP_SUCCESS with the parsed entry.
+ // |callback| must not be null.
+ void SetLastModifiedTime(
+ const std::string& resource_id,
+ const base::Time& last_modified_time,
+ const google_apis::FileResourceCallback& callback);
+
+ // Sets the user's permission for an entry specified by |resource_id|.
+ google_apis::DriveApiErrorCode SetUserPermission(
+ const std::string& resource_id,
+ google_apis::drive::PermissionRole user_permission);
+
+ void AddChangeObserver(ChangeObserver* observer);
+ void RemoveChangeObserver(ChangeObserver* observer);
+
+ private:
+ struct EntryInfo;
+ struct UploadSession;
+
+ // Returns a pointer to the entry that matches |resource_id|, or NULL if
+ // not found.
+ EntryInfo* FindEntryByResourceId(const std::string& resource_id);
+
+ // Returns a new resource ID, which looks like "resource_id_<num>" where
+ // <num> is a monotonically increasing number starting from 1.
+ std::string GetNewResourceId();
+
+ // Increments |largest_changestamp_| and adds the new changestamp.
+ void AddNewChangestamp(google_apis::ChangeResource* change);
+
+ // Update ETag of |file| based on |largest_changestamp_|.
+ void UpdateETag(google_apis::FileResource* file);
+
+ // Adds a new entry based on the given parameters.
+ // |resource_id| can be empty, in the case, the id is automatically generated.
+ // Returns a pointer to the newly added entry, or NULL if failed.
+ const EntryInfo* AddNewEntry(
+ const std::string& resource_id,
+ const std::string& content_type,
+ const std::string& content_data,
+ const std::string& parent_resource_id,
+ const std::string& title,
+ bool shared_with_me);
+
+ // Core implementation of GetChangeList.
+ // This method returns the slice of the all matched entries, and its range
+ // is between |start_offset| (inclusive) and |start_offset| + |max_results|
+ // (exclusive).
+ // Increments *load_counter by 1 before it returns successfully.
+ void GetChangeListInternal(
+ int64 start_changestamp,
+ const std::string& search_query,
+ const std::string& directory_resource_id,
+ int start_offset,
+ int max_results,
+ int* load_counter,
+ const google_apis::ChangeListCallback& callback);
+
+ // Returns new upload session URL.
+ GURL GetNewUploadSessionUrl();
+
+ void NotifyObservers();
+
+ // The class is expected to run on UI thread.
+ base::ThreadChecker thread_checker_;
+
+ typedef std::map<std::string, EntryInfo*> EntryInfoMap;
+ EntryInfoMap entries_;
+ scoped_ptr<google_apis::AboutResource> about_resource_;
+ scoped_ptr<base::DictionaryValue> app_info_value_;
+ std::map<GURL, UploadSession> upload_sessions_;
+ int64 published_date_seq_;
+ int64 next_upload_sequence_number_;
+ int default_max_results_;
+ int resource_id_count_;
+ int file_list_load_count_;
+ int change_list_load_count_;
+ int directory_load_count_;
+ int about_resource_load_count_;
+ int app_list_load_count_;
+ int blocked_file_list_load_count_;
+ bool offline_;
+ bool never_return_all_file_list_;
+ base::FilePath last_cancelled_file_;
+ GURL share_url_base_;
+ std::string app_json_template_;
+ std::string open_url_format_;
+
+ base::ObserverList<ChangeObserver> change_observers_;
+
+ base::WeakPtrFactory<FakeDriveService> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeDriveService);
+};
+
+} // namespace drive
+
+#endif // COMPONENTS_DRIVE_SERVICE_FAKE_DRIVE_SERVICE_H_
diff --git a/components/drive/service/fake_drive_service_unittest.cc b/components/drive/service/fake_drive_service_unittest.cc
new file mode 100644
index 0000000..009f8ca
--- /dev/null
+++ b/components/drive/service/fake_drive_service_unittest.cc
@@ -0,0 +1,2177 @@
+// Copyright (c) 2012 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/service/fake_drive_service.h"
+
+#include <string>
+#include <vector>
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/md5.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/drive/service/test_util.h"
+#include "google_apis/drive/drive_api_parser.h"
+#include "google_apis/drive/test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using google_apis::AboutResource;
+using google_apis::AppList;
+using google_apis::ChangeList;
+using google_apis::ChangeResource;
+using google_apis::FileList;
+using google_apis::FileResource;
+using google_apis::DRIVE_NO_CONNECTION;
+using google_apis::DRIVE_OTHER_ERROR;
+using google_apis::DriveApiErrorCode;
+using google_apis::GetContentCallback;
+using google_apis::HTTP_CREATED;
+using google_apis::HTTP_FORBIDDEN;
+using google_apis::HTTP_NOT_FOUND;
+using google_apis::HTTP_NO_CONTENT;
+using google_apis::HTTP_PRECONDITION;
+using google_apis::HTTP_RESUME_INCOMPLETE;
+using google_apis::HTTP_SUCCESS;
+using google_apis::ProgressCallback;
+using google_apis::UploadRangeResponse;
+
+namespace drive {
+
+namespace test_util {
+
+using google_apis::test_util::AppendProgressCallbackResult;
+using google_apis::test_util::CreateCopyResultCallback;
+using google_apis::test_util::ProgressInfo;
+using google_apis::test_util::TestGetContentCallback;
+using google_apis::test_util::WriteStringToFile;
+
+} // namespace test_util
+
+namespace {
+
+class FakeDriveServiceTest : public testing::Test {
+ protected:
+ // Returns the resource entry that matches |resource_id|.
+ scoped_ptr<FileResource> FindEntry(const std::string& resource_id) {
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.GetFileResource(
+ resource_id, test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+ return entry.Pass();
+ }
+
+ // Returns true if the resource identified by |resource_id| exists.
+ bool Exists(const std::string& resource_id) {
+ scoped_ptr<FileResource> entry = FindEntry(resource_id);
+ return entry && !entry->labels().is_trashed();
+ }
+
+ // Adds a new directory at |parent_resource_id| with the given name.
+ // Returns true on success.
+ bool AddNewDirectory(const std::string& parent_resource_id,
+ const std::string& directory_title) {
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.AddNewDirectory(
+ parent_resource_id, directory_title, AddNewDirectoryOptions(),
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+ return error == HTTP_CREATED;
+ }
+
+ // Returns true if the resource identified by |resource_id| has a parent
+ // identified by |parent_id|.
+ bool HasParent(const std::string& resource_id, const std::string& parent_id) {
+ scoped_ptr<FileResource> entry = FindEntry(resource_id);
+ if (entry) {
+ for (size_t i = 0; i < entry->parents().size(); ++i) {
+ if (entry->parents()[i].file_id() == parent_id)
+ return true;
+ }
+ }
+ return false;
+ }
+
+ int64 GetLargestChangeByAboutResource() {
+ DriveApiErrorCode error;
+ scoped_ptr<AboutResource> about_resource;
+ fake_service_.GetAboutResource(
+ test_util::CreateCopyResultCallback(&error, &about_resource));
+ base::RunLoop().RunUntilIdle();
+ return about_resource->largest_change_id();
+ }
+
+ base::MessageLoop message_loop_;
+ FakeDriveService fake_service_;
+};
+
+TEST_F(FakeDriveServiceTest, GetAllFileList) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileList> file_list;
+ fake_service_.GetAllFileList(
+ test_util::CreateCopyResultCallback(&error, &file_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(file_list);
+ // Do some sanity check.
+ EXPECT_EQ(15U, file_list->items().size());
+ EXPECT_EQ(1, fake_service_.file_list_load_count());
+}
+
+TEST_F(FakeDriveServiceTest, GetAllFileList_Offline) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ fake_service_.set_offline(true);
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileList> file_list;
+ fake_service_.GetAllFileList(
+ test_util::CreateCopyResultCallback(&error, &file_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(DRIVE_NO_CONNECTION, error);
+ EXPECT_FALSE(file_list);
+}
+
+TEST_F(FakeDriveServiceTest, GetFileListInDirectory_InRootDirectory) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileList> file_list;
+ fake_service_.GetFileListInDirectory(
+ fake_service_.GetRootResourceId(),
+ test_util::CreateCopyResultCallback(&error, &file_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(file_list);
+ // Do some sanity check. There are 8 entries in the root directory.
+ EXPECT_EQ(8U, file_list->items().size());
+ EXPECT_EQ(1, fake_service_.directory_load_count());
+}
+
+TEST_F(FakeDriveServiceTest, GetFileListInDirectory_InNonRootDirectory) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileList> file_list;
+ fake_service_.GetFileListInDirectory(
+ "1_folder_resource_id",
+ test_util::CreateCopyResultCallback(&error, &file_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(file_list);
+ // Do some sanity check. There is three entries in 1_folder_resource_id
+ // directory.
+ EXPECT_EQ(3U, file_list->items().size());
+ EXPECT_EQ(1, fake_service_.directory_load_count());
+}
+
+TEST_F(FakeDriveServiceTest, GetFileListInDirectory_Offline) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ fake_service_.set_offline(true);
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileList> file_list;
+ fake_service_.GetFileListInDirectory(
+ fake_service_.GetRootResourceId(),
+ test_util::CreateCopyResultCallback(&error, &file_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(DRIVE_NO_CONNECTION, error);
+ EXPECT_FALSE(file_list);
+}
+
+TEST_F(FakeDriveServiceTest, Search) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileList> file_list;
+ fake_service_.Search(
+ "File", // search_query
+ test_util::CreateCopyResultCallback(&error, &file_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(file_list);
+ // Do some sanity check. There are 4 entries that contain "File" in their
+ // titles.
+ EXPECT_EQ(4U, file_list->items().size());
+}
+
+TEST_F(FakeDriveServiceTest, Search_WithAttribute) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileList> file_list;
+ fake_service_.Search(
+ "title:1.txt", // search_query
+ test_util::CreateCopyResultCallback(&error, &file_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(file_list);
+ // Do some sanity check. There are 4 entries that contain "1.txt" in their
+ // titles.
+ EXPECT_EQ(4U, file_list->items().size());
+}
+
+TEST_F(FakeDriveServiceTest, Search_MultipleQueries) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileList> file_list;
+ fake_service_.Search(
+ "Directory 1", // search_query
+ test_util::CreateCopyResultCallback(&error, &file_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(file_list);
+ // There are 2 entries that contain both "Directory" and "1" in their titles.
+ EXPECT_EQ(2U, file_list->items().size());
+
+ fake_service_.Search(
+ "\"Directory 1\"", // search_query
+ test_util::CreateCopyResultCallback(&error, &file_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(file_list);
+ // There is 1 entry that contain "Directory 1" in its title.
+ EXPECT_EQ(1U, file_list->items().size());
+}
+
+TEST_F(FakeDriveServiceTest, Search_Offline) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ fake_service_.set_offline(true);
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileList> file_list;
+ fake_service_.Search(
+ "Directory 1", // search_query
+ test_util::CreateCopyResultCallback(&error, &file_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(DRIVE_NO_CONNECTION, error);
+ EXPECT_FALSE(file_list);
+}
+
+TEST_F(FakeDriveServiceTest, Search_Deleted) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ fake_service_.DeleteResource("2_file_resource_id",
+ std::string(), // etag
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(HTTP_NO_CONTENT, error);
+
+ error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileList> file_list;
+ fake_service_.Search(
+ "File", // search_query
+ test_util::CreateCopyResultCallback(&error, &file_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(file_list);
+ // Do some sanity check. There are 4 entries that contain "File" in their
+ // titles and one of them is deleted.
+ EXPECT_EQ(3U, file_list->items().size());
+}
+
+TEST_F(FakeDriveServiceTest, Search_Trashed) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ fake_service_.TrashResource("2_file_resource_id",
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(HTTP_SUCCESS, error);
+
+ error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileList> file_list;
+ fake_service_.Search(
+ "File", // search_query
+ test_util::CreateCopyResultCallback(&error, &file_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(file_list);
+ // Do some sanity check. There are 4 entries that contain "File" in their
+ // titles and one of them is deleted.
+ EXPECT_EQ(3U, file_list->items().size());
+}
+
+TEST_F(FakeDriveServiceTest, SearchByTitle) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileList> file_list;
+ fake_service_.SearchByTitle(
+ "1.txt", // title
+ fake_service_.GetRootResourceId(), // directory_resource_id
+ test_util::CreateCopyResultCallback(&error, &file_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(file_list);
+ // Do some sanity check. There are 2 entries that contain "1.txt" in their
+ // titles directly under the root directory.
+ EXPECT_EQ(2U, file_list->items().size());
+}
+
+TEST_F(FakeDriveServiceTest, SearchByTitle_EmptyDirectoryResourceId) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileList> file_list;
+ fake_service_.SearchByTitle(
+ "1.txt", // title
+ "", // directory resource id
+ test_util::CreateCopyResultCallback(&error, &file_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(file_list);
+ // Do some sanity check. There are 4 entries that contain "1.txt" in their
+ // titles.
+ EXPECT_EQ(4U, file_list->items().size());
+}
+
+TEST_F(FakeDriveServiceTest, SearchByTitle_Offline) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ fake_service_.set_offline(true);
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileList> file_list;
+ fake_service_.SearchByTitle(
+ "Directory 1", // title
+ fake_service_.GetRootResourceId(), // directory_resource_id
+ test_util::CreateCopyResultCallback(&error, &file_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(DRIVE_NO_CONNECTION, error);
+ EXPECT_FALSE(file_list);
+}
+
+TEST_F(FakeDriveServiceTest, GetChangeList_NoNewEntries) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<ChangeList> change_list;
+ fake_service_.GetChangeList(
+ fake_service_.about_resource().largest_change_id() + 1,
+ test_util::CreateCopyResultCallback(&error, &change_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(change_list);
+ EXPECT_EQ(fake_service_.about_resource().largest_change_id(),
+ change_list->largest_change_id());
+ // This should be empty as the latest changestamp was passed to
+ // GetChangeList(), hence there should be no new entries.
+ EXPECT_EQ(0U, change_list->items().size());
+ // It's considered loaded even if the result is empty.
+ EXPECT_EQ(1, fake_service_.change_list_load_count());
+}
+
+TEST_F(FakeDriveServiceTest, GetChangeList_WithNewEntry) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ const int64 old_largest_change_id =
+ fake_service_.about_resource().largest_change_id();
+
+ // Add a new directory in the root directory.
+ ASSERT_TRUE(AddNewDirectory(
+ fake_service_.GetRootResourceId(), "new directory"));
+
+ // Get the resource list newer than old_largest_change_id.
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<ChangeList> change_list;
+ fake_service_.GetChangeList(
+ old_largest_change_id + 1,
+ test_util::CreateCopyResultCallback(&error, &change_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(change_list);
+ EXPECT_EQ(fake_service_.about_resource().largest_change_id(),
+ change_list->largest_change_id());
+ // The result should only contain the newly created directory.
+ ASSERT_EQ(1U, change_list->items().size());
+ ASSERT_TRUE(change_list->items()[0]->file());
+ EXPECT_EQ("new directory", change_list->items()[0]->file()->title());
+ EXPECT_EQ(1, fake_service_.change_list_load_count());
+}
+
+TEST_F(FakeDriveServiceTest, GetChangeList_Offline) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ fake_service_.set_offline(true);
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<ChangeList> change_list;
+ fake_service_.GetChangeList(
+ 654321, // start_changestamp
+ test_util::CreateCopyResultCallback(&error, &change_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(DRIVE_NO_CONNECTION, error);
+ EXPECT_FALSE(change_list);
+}
+
+TEST_F(FakeDriveServiceTest, GetChangeList_DeletedEntry) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ ASSERT_TRUE(Exists("2_file_resource_id"));
+ const int64 old_largest_change_id =
+ fake_service_.about_resource().largest_change_id();
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ fake_service_.DeleteResource("2_file_resource_id",
+ std::string(), // etag
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(HTTP_NO_CONTENT, error);
+ ASSERT_FALSE(Exists("2_file_resource_id"));
+
+ // Get the resource list newer than old_largest_change_id.
+ error = DRIVE_OTHER_ERROR;
+ scoped_ptr<ChangeList> change_list;
+ fake_service_.GetChangeList(
+ old_largest_change_id + 1,
+ test_util::CreateCopyResultCallback(&error, &change_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(change_list);
+ EXPECT_EQ(fake_service_.about_resource().largest_change_id(),
+ change_list->largest_change_id());
+ // The result should only contain the deleted file.
+ ASSERT_EQ(1U, change_list->items().size());
+ const ChangeResource& item = *change_list->items()[0];
+ EXPECT_EQ("2_file_resource_id", item.file_id());
+ EXPECT_FALSE(item.file());
+ EXPECT_TRUE(item.is_deleted());
+ EXPECT_EQ(1, fake_service_.change_list_load_count());
+}
+
+TEST_F(FakeDriveServiceTest, GetChangeList_TrashedEntry) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ ASSERT_TRUE(Exists("2_file_resource_id"));
+ const int64 old_largest_change_id =
+ fake_service_.about_resource().largest_change_id();
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ fake_service_.TrashResource("2_file_resource_id",
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(HTTP_SUCCESS, error);
+ ASSERT_FALSE(Exists("2_file_resource_id"));
+
+ // Get the resource list newer than old_largest_change_id.
+ error = DRIVE_OTHER_ERROR;
+ scoped_ptr<ChangeList> change_list;
+ fake_service_.GetChangeList(
+ old_largest_change_id + 1,
+ test_util::CreateCopyResultCallback(&error, &change_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(change_list);
+ EXPECT_EQ(fake_service_.about_resource().largest_change_id(),
+ change_list->largest_change_id());
+ // The result should only contain the trashed file.
+ ASSERT_EQ(1U, change_list->items().size());
+ const ChangeResource& item = *change_list->items()[0];
+ EXPECT_EQ("2_file_resource_id", item.file_id());
+ ASSERT_TRUE(item.file());
+ EXPECT_TRUE(item.file()->labels().is_trashed());
+ EXPECT_EQ(1, fake_service_.change_list_load_count());
+}
+
+TEST_F(FakeDriveServiceTest, GetRemainingFileList_GetAllFileList) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ fake_service_.set_default_max_results(6);
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileList> file_list;
+ fake_service_.GetAllFileList(
+ test_util::CreateCopyResultCallback(&error, &file_list));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(file_list);
+
+ // Do some sanity check.
+ // The number of results is 14 entries. Thus, it should split into three
+ // chunks: 6, 6, and then 2.
+ EXPECT_EQ(6U, file_list->items().size());
+ EXPECT_EQ(1, fake_service_.file_list_load_count());
+
+ // Second page loading.
+ // Keep the next url before releasing the |file_list|.
+ GURL next_url(file_list->next_link());
+
+ error = DRIVE_OTHER_ERROR;
+ file_list.reset();
+ fake_service_.GetRemainingFileList(
+ next_url,
+ test_util::CreateCopyResultCallback(&error, &file_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(file_list);
+
+ EXPECT_EQ(6U, file_list->items().size());
+ EXPECT_EQ(1, fake_service_.file_list_load_count());
+
+ // Third page loading.
+ next_url = file_list->next_link();
+
+ error = DRIVE_OTHER_ERROR;
+ file_list.reset();
+ fake_service_.GetRemainingFileList(
+ next_url,
+ test_util::CreateCopyResultCallback(&error, &file_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(file_list);
+
+ EXPECT_EQ(3U, file_list->items().size());
+ EXPECT_EQ(1, fake_service_.file_list_load_count());
+}
+
+TEST_F(FakeDriveServiceTest, GetRemainingFileList_GetFileListInDirectory) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ fake_service_.set_default_max_results(3);
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileList> file_list;
+ fake_service_.GetFileListInDirectory(
+ fake_service_.GetRootResourceId(),
+ test_util::CreateCopyResultCallback(&error, &file_list));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(file_list);
+
+ // Do some sanity check.
+ // The number of results is 8 entries. Thus, it should split into three
+ // chunks: 3, 3, and then 2.
+ EXPECT_EQ(3U, file_list->items().size());
+ EXPECT_EQ(1, fake_service_.directory_load_count());
+
+ // Second page loading.
+ // Keep the next url before releasing the |file_list|.
+ GURL next_url = file_list->next_link();
+
+ error = DRIVE_OTHER_ERROR;
+ file_list.reset();
+ fake_service_.GetRemainingFileList(
+ next_url,
+ test_util::CreateCopyResultCallback(&error, &file_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(file_list);
+
+ EXPECT_EQ(3U, file_list->items().size());
+ EXPECT_EQ(1, fake_service_.directory_load_count());
+
+ // Third page loading.
+ next_url = file_list->next_link();
+
+ error = DRIVE_OTHER_ERROR;
+ file_list.reset();
+ fake_service_.GetRemainingFileList(
+ next_url,
+ test_util::CreateCopyResultCallback(&error, &file_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(file_list);
+
+ EXPECT_EQ(2U, file_list->items().size());
+ EXPECT_EQ(1, fake_service_.directory_load_count());
+}
+
+TEST_F(FakeDriveServiceTest, GetRemainingFileList_Search) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ fake_service_.set_default_max_results(2);
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileList> file_list;
+ fake_service_.Search(
+ "File", // search_query
+ test_util::CreateCopyResultCallback(&error, &file_list));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(file_list);
+
+ // Do some sanity check.
+ // The number of results is 4 entries. Thus, it should split into two
+ // chunks: 2, and then 2
+ EXPECT_EQ(2U, file_list->items().size());
+
+ // Second page loading.
+ // Keep the next url before releasing the |file_list|.
+ GURL next_url = file_list->next_link();
+
+ error = DRIVE_OTHER_ERROR;
+ file_list.reset();
+ fake_service_.GetRemainingFileList(
+ next_url,
+ test_util::CreateCopyResultCallback(&error, &file_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(file_list);
+
+ EXPECT_EQ(2U, file_list->items().size());
+}
+
+TEST_F(FakeDriveServiceTest, GetRemainingChangeList_GetChangeList) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ fake_service_.set_default_max_results(2);
+ const int64 old_largest_change_id =
+ fake_service_.about_resource().largest_change_id();
+
+ // Add 5 new directory in the root directory.
+ for (int i = 0; i < 5; ++i) {
+ ASSERT_TRUE(AddNewDirectory(
+ fake_service_.GetRootResourceId(),
+ base::StringPrintf("new directory %d", i)));
+ }
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<ChangeList> change_list;
+ fake_service_.GetChangeList(
+ old_largest_change_id + 1, // start_changestamp
+ test_util::CreateCopyResultCallback(&error, &change_list));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(change_list);
+
+ // Do some sanity check.
+ // The number of results is 5 entries. Thus, it should split into three
+ // chunks: 2, 2 and then 1.
+ EXPECT_EQ(2U, change_list->items().size());
+ EXPECT_EQ(1, fake_service_.change_list_load_count());
+
+ // Second page loading.
+ // Keep the next url before releasing the |change_list|.
+ GURL next_url = change_list->next_link();
+
+ error = DRIVE_OTHER_ERROR;
+ change_list.reset();
+ fake_service_.GetRemainingChangeList(
+ next_url,
+ test_util::CreateCopyResultCallback(&error, &change_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(change_list);
+
+ EXPECT_EQ(2U, change_list->items().size());
+ EXPECT_EQ(1, fake_service_.change_list_load_count());
+
+ // Third page loading.
+ next_url = change_list->next_link();
+
+ error = DRIVE_OTHER_ERROR;
+ change_list.reset();
+ fake_service_.GetRemainingChangeList(
+ next_url,
+ test_util::CreateCopyResultCallback(&error, &change_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(change_list);
+
+ EXPECT_EQ(1U, change_list->items().size());
+ EXPECT_EQ(1, fake_service_.change_list_load_count());
+}
+
+TEST_F(FakeDriveServiceTest, GetAboutResource) {
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<AboutResource> about_resource;
+ fake_service_.GetAboutResource(
+ test_util::CreateCopyResultCallback(&error, &about_resource));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+
+ ASSERT_TRUE(about_resource);
+ // Do some sanity check.
+ EXPECT_EQ(fake_service_.GetRootResourceId(),
+ about_resource->root_folder_id());
+ EXPECT_EQ(1, fake_service_.about_resource_load_count());
+}
+
+TEST_F(FakeDriveServiceTest, GetAboutResource_Offline) {
+ fake_service_.set_offline(true);
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<AboutResource> about_resource;
+ fake_service_.GetAboutResource(
+ test_util::CreateCopyResultCallback(&error, &about_resource));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(DRIVE_NO_CONNECTION, error);
+ EXPECT_FALSE(about_resource);
+}
+
+TEST_F(FakeDriveServiceTest, GetAppList) {
+ ASSERT_TRUE(fake_service_.LoadAppListForDriveApi(
+ "drive/applist.json"));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<AppList> app_list;
+ fake_service_.GetAppList(
+ test_util::CreateCopyResultCallback(&error, &app_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+
+ ASSERT_TRUE(app_list);
+ EXPECT_EQ(1, fake_service_.app_list_load_count());
+}
+
+TEST_F(FakeDriveServiceTest, GetAppList_Offline) {
+ ASSERT_TRUE(fake_service_.LoadAppListForDriveApi(
+ "drive/applist.json"));
+ fake_service_.set_offline(true);
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<AppList> app_list;
+ fake_service_.GetAppList(
+ test_util::CreateCopyResultCallback(&error, &app_list));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(DRIVE_NO_CONNECTION, error);
+ EXPECT_FALSE(app_list);
+}
+
+TEST_F(FakeDriveServiceTest, GetFileResource_ExistingFile) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ const std::string kResourceId = "2_file_resource_id";
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.GetFileResource(
+ kResourceId, test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(entry);
+ // Do some sanity check.
+ EXPECT_EQ(kResourceId, entry->file_id());
+}
+
+TEST_F(FakeDriveServiceTest, GetFileResource_NonexistingFile) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ const std::string kResourceId = "nonexisting_resource_id";
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.GetFileResource(
+ kResourceId, test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_NOT_FOUND, error);
+ ASSERT_FALSE(entry);
+}
+
+TEST_F(FakeDriveServiceTest, GetFileResource_Offline) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ fake_service_.set_offline(true);
+
+ const std::string kResourceId = "2_file_resource_id";
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.GetFileResource(
+ kResourceId, test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(DRIVE_NO_CONNECTION, error);
+ EXPECT_FALSE(entry);
+}
+
+TEST_F(FakeDriveServiceTest, GetShareUrl) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ const std::string kResourceId = "2_file_resource_id";
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ GURL share_url;
+ fake_service_.GetShareUrl(
+ kResourceId,
+ GURL(), // embed origin
+ test_util::CreateCopyResultCallback(&error, &share_url));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ EXPECT_FALSE(share_url.is_empty());
+}
+
+TEST_F(FakeDriveServiceTest, DeleteResource_ExistingFile) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ // Resource "2_file_resource_id" should now exist.
+ ASSERT_TRUE(Exists("2_file_resource_id"));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ fake_service_.DeleteResource("2_file_resource_id",
+ std::string(), // etag
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_NO_CONTENT, error);
+ // Resource "2_file_resource_id" should be gone now.
+ EXPECT_FALSE(Exists("2_file_resource_id"));
+
+ error = DRIVE_OTHER_ERROR;
+ fake_service_.DeleteResource("2_file_resource_id",
+ std::string(), // etag
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(HTTP_NOT_FOUND, error);
+ EXPECT_FALSE(Exists("2_file_resource_id"));
+}
+
+TEST_F(FakeDriveServiceTest, DeleteResource_NonexistingFile) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ fake_service_.DeleteResource("nonexisting_resource_id",
+ std::string(), // etag
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_NOT_FOUND, error);
+}
+
+TEST_F(FakeDriveServiceTest, DeleteResource_ETagMatch) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ // Resource "2_file_resource_id" should now exist.
+ scoped_ptr<FileResource> entry = FindEntry("2_file_resource_id");
+ ASSERT_TRUE(entry);
+ ASSERT_FALSE(entry->labels().is_trashed());
+ ASSERT_FALSE(entry->etag().empty());
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ fake_service_.DeleteResource("2_file_resource_id",
+ entry->etag() + "_mismatch",
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_PRECONDITION, error);
+ // Resource "2_file_resource_id" should still exist.
+ EXPECT_TRUE(Exists("2_file_resource_id"));
+
+ error = DRIVE_OTHER_ERROR;
+ fake_service_.DeleteResource("2_file_resource_id",
+ entry->etag(),
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(HTTP_NO_CONTENT, error);
+ // Resource "2_file_resource_id" should be gone now.
+ EXPECT_FALSE(Exists("2_file_resource_id"));
+}
+
+TEST_F(FakeDriveServiceTest, DeleteResource_Offline) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ fake_service_.set_offline(true);
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ fake_service_.DeleteResource("2_file_resource_id",
+ std::string(), // etag
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(DRIVE_NO_CONNECTION, error);
+}
+
+TEST_F(FakeDriveServiceTest, DeleteResource_Forbidden) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ EXPECT_EQ(HTTP_SUCCESS, fake_service_.SetUserPermission(
+ "2_file_resource_id", google_apis::drive::PERMISSION_ROLE_READER));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ fake_service_.DeleteResource("2_file_resource_id",
+ std::string(), // etag
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_FORBIDDEN, error);
+}
+
+TEST_F(FakeDriveServiceTest, TrashResource_ExistingFile) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ // Resource "2_file_resource_id" should now exist.
+ ASSERT_TRUE(Exists("2_file_resource_id"));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ fake_service_.TrashResource("2_file_resource_id",
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ // Resource "2_file_resource_id" should be gone now.
+ EXPECT_FALSE(Exists("2_file_resource_id"));
+
+ error = DRIVE_OTHER_ERROR;
+ fake_service_.TrashResource("2_file_resource_id",
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(HTTP_NOT_FOUND, error);
+ EXPECT_FALSE(Exists("2_file_resource_id"));
+}
+
+TEST_F(FakeDriveServiceTest, TrashResource_NonexistingFile) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ fake_service_.TrashResource("nonexisting_resource_id",
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_NOT_FOUND, error);
+}
+
+TEST_F(FakeDriveServiceTest, TrashResource_Offline) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ fake_service_.set_offline(true);
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ fake_service_.TrashResource("2_file_resource_id",
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(DRIVE_NO_CONNECTION, error);
+}
+
+TEST_F(FakeDriveServiceTest, TrashResource_Forbidden) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ EXPECT_EQ(HTTP_SUCCESS, fake_service_.SetUserPermission(
+ "2_file_resource_id", google_apis::drive::PERMISSION_ROLE_READER));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ fake_service_.TrashResource("2_file_resource_id",
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_FORBIDDEN, error);
+}
+
+TEST_F(FakeDriveServiceTest, DownloadFile_ExistingFile) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ std::vector<test_util::ProgressInfo> download_progress_values;
+
+ const base::FilePath kOutputFilePath =
+ temp_dir.path().AppendASCII("whatever.txt");
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ base::FilePath output_file_path;
+ test_util::TestGetContentCallback get_content_callback;
+ fake_service_.DownloadFile(
+ kOutputFilePath,
+ "2_file_resource_id",
+ test_util::CreateCopyResultCallback(&error, &output_file_path),
+ get_content_callback.callback(),
+ base::Bind(&test_util::AppendProgressCallbackResult,
+ &download_progress_values));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ EXPECT_EQ(output_file_path, kOutputFilePath);
+ std::string content;
+ ASSERT_TRUE(base::ReadFileToString(output_file_path, &content));
+ EXPECT_EQ("This is some test content.", content);
+ ASSERT_TRUE(!download_progress_values.empty());
+ EXPECT_TRUE(base::STLIsSorted(download_progress_values));
+ EXPECT_LE(0, download_progress_values.front().first);
+ EXPECT_GE(26, download_progress_values.back().first);
+ EXPECT_EQ(content, get_content_callback.GetConcatenatedData());
+}
+
+TEST_F(FakeDriveServiceTest, DownloadFile_NonexistingFile) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ const base::FilePath kOutputFilePath =
+ temp_dir.path().AppendASCII("whatever.txt");
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ base::FilePath output_file_path;
+ fake_service_.DownloadFile(
+ kOutputFilePath,
+ "non_existent_file_resource_id",
+ test_util::CreateCopyResultCallback(&error, &output_file_path),
+ GetContentCallback(),
+ ProgressCallback());
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_NOT_FOUND, error);
+}
+
+TEST_F(FakeDriveServiceTest, DownloadFile_Offline) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ fake_service_.set_offline(true);
+
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ const base::FilePath kOutputFilePath =
+ temp_dir.path().AppendASCII("whatever.txt");
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ base::FilePath output_file_path;
+ fake_service_.DownloadFile(
+ kOutputFilePath,
+ "2_file_resource_id",
+ test_util::CreateCopyResultCallback(&error, &output_file_path),
+ GetContentCallback(),
+ ProgressCallback());
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(DRIVE_NO_CONNECTION, error);
+}
+
+TEST_F(FakeDriveServiceTest, CopyResource) {
+ const base::Time::Exploded kModifiedDate = {2012, 7, 0, 19, 15, 59, 13, 123};
+
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ int64 old_largest_change_id = GetLargestChangeByAboutResource();
+
+ const std::string kResourceId = "2_file_resource_id";
+ const std::string kParentResourceId = "2_folder_resource_id";
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.CopyResource(
+ kResourceId,
+ kParentResourceId,
+ "new title",
+ base::Time::FromUTCExploded(kModifiedDate),
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(entry);
+ // The copied entry should have the new resource ID and the title.
+ EXPECT_NE(kResourceId, entry->file_id());
+ EXPECT_EQ("new title", entry->title());
+ EXPECT_EQ(base::Time::FromUTCExploded(kModifiedDate), entry->modified_date());
+ EXPECT_TRUE(HasParent(entry->file_id(), kParentResourceId));
+ // Should be incremented as a new hosted document was created.
+ EXPECT_EQ(old_largest_change_id + 1,
+ fake_service_.about_resource().largest_change_id());
+ EXPECT_EQ(old_largest_change_id + 1, GetLargestChangeByAboutResource());
+}
+
+TEST_F(FakeDriveServiceTest, CopyResource_NonExisting) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ const std::string kResourceId = "nonexisting_resource_id";
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.CopyResource(
+ kResourceId,
+ "1_folder_resource_id",
+ "new title",
+ base::Time(),
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_NOT_FOUND, error);
+}
+
+TEST_F(FakeDriveServiceTest, CopyResource_EmptyParentResourceId) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ int64 old_largest_change_id = GetLargestChangeByAboutResource();
+
+ const std::string kResourceId = "2_file_resource_id";
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.CopyResource(
+ kResourceId,
+ std::string(),
+ "new title",
+ base::Time(),
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(entry);
+ // The copied entry should have the new resource ID and the title.
+ EXPECT_NE(kResourceId, entry->file_id());
+ EXPECT_EQ("new title", entry->title());
+ EXPECT_TRUE(HasParent(kResourceId, fake_service_.GetRootResourceId()));
+ // Should be incremented as a new hosted document was created.
+ EXPECT_EQ(old_largest_change_id + 1,
+ fake_service_.about_resource().largest_change_id());
+ EXPECT_EQ(old_largest_change_id + 1, GetLargestChangeByAboutResource());
+}
+
+TEST_F(FakeDriveServiceTest, CopyResource_Offline) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ fake_service_.set_offline(true);
+
+ const std::string kResourceId = "2_file_resource_id";
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.CopyResource(
+ kResourceId,
+ "1_folder_resource_id",
+ "new title",
+ base::Time(),
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(DRIVE_NO_CONNECTION, error);
+ EXPECT_FALSE(entry);
+}
+
+TEST_F(FakeDriveServiceTest, UpdateResource) {
+ const base::Time::Exploded kModifiedDate = {2012, 7, 0, 19, 15, 59, 13, 123};
+ const base::Time::Exploded kViewedDate = {2013, 8, 1, 20, 16, 00, 14, 234};
+
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ int64 old_largest_change_id = GetLargestChangeByAboutResource();
+
+ const std::string kResourceId = "2_file_resource_id";
+ const std::string kParentResourceId = "2_folder_resource_id";
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.UpdateResource(
+ kResourceId, kParentResourceId, "new title",
+ base::Time::FromUTCExploded(kModifiedDate),
+ base::Time::FromUTCExploded(kViewedDate),
+ google_apis::drive::Properties(),
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(entry);
+ // The updated entry should have the new title.
+ EXPECT_EQ(kResourceId, entry->file_id());
+ EXPECT_EQ("new title", entry->title());
+ EXPECT_EQ(base::Time::FromUTCExploded(kModifiedDate),
+ entry->modified_date());
+ EXPECT_EQ(base::Time::FromUTCExploded(kViewedDate),
+ entry->last_viewed_by_me_date());
+ EXPECT_TRUE(HasParent(kResourceId, kParentResourceId));
+ // Should be incremented as a new hosted document was created.
+ EXPECT_EQ(old_largest_change_id + 1,
+ fake_service_.about_resource().largest_change_id());
+ EXPECT_EQ(old_largest_change_id + 1, GetLargestChangeByAboutResource());
+}
+
+TEST_F(FakeDriveServiceTest, UpdateResource_NonExisting) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ const std::string kResourceId = "nonexisting_resource_id";
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.UpdateResource(
+ kResourceId, "1_folder_resource_id", "new title", base::Time(),
+ base::Time(), google_apis::drive::Properties(),
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_NOT_FOUND, error);
+}
+
+TEST_F(FakeDriveServiceTest, UpdateResource_EmptyParentResourceId) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ int64 old_largest_change_id = GetLargestChangeByAboutResource();
+
+ const std::string kResourceId = "2_file_resource_id";
+
+ // Just make sure that the resource is under root.
+ ASSERT_TRUE(HasParent(kResourceId, "fake_root"));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.UpdateResource(
+ kResourceId, std::string(), "new title", base::Time(), base::Time(),
+ google_apis::drive::Properties(),
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(entry);
+ // The updated entry should have the new title.
+ EXPECT_EQ(kResourceId, entry->file_id());
+ EXPECT_EQ("new title", entry->title());
+ EXPECT_TRUE(HasParent(kResourceId, "fake_root"));
+ // Should be incremented as a new hosted document was created.
+ EXPECT_EQ(old_largest_change_id + 1,
+ fake_service_.about_resource().largest_change_id());
+ EXPECT_EQ(old_largest_change_id + 1, GetLargestChangeByAboutResource());
+}
+
+TEST_F(FakeDriveServiceTest, UpdateResource_Offline) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ fake_service_.set_offline(true);
+
+ const std::string kResourceId = "2_file_resource_id";
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.UpdateResource(
+ kResourceId, std::string(), "new title", base::Time(), base::Time(),
+ google_apis::drive::Properties(),
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(DRIVE_NO_CONNECTION, error);
+ EXPECT_FALSE(entry);
+}
+
+TEST_F(FakeDriveServiceTest, UpdateResource_Forbidden) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ const std::string kResourceId = "2_file_resource_id";
+ EXPECT_EQ(HTTP_SUCCESS, fake_service_.SetUserPermission(
+ kResourceId, google_apis::drive::PERMISSION_ROLE_READER));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.UpdateResource(
+ kResourceId, std::string(), "new title", base::Time(), base::Time(),
+ google_apis::drive::Properties(),
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_FORBIDDEN, error);
+ EXPECT_FALSE(entry);
+}
+
+TEST_F(FakeDriveServiceTest, AddResourceToDirectory_FileInRootDirectory) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ int64 old_largest_change_id = GetLargestChangeByAboutResource();
+
+ const std::string kResourceId = "2_file_resource_id";
+ const std::string kOldParentResourceId = fake_service_.GetRootResourceId();
+ const std::string kNewParentResourceId = "1_folder_resource_id";
+
+ // Here's the original parent link.
+ EXPECT_TRUE(HasParent(kResourceId, kOldParentResourceId));
+ EXPECT_FALSE(HasParent(kResourceId, kNewParentResourceId));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ fake_service_.AddResourceToDirectory(
+ kNewParentResourceId,
+ kResourceId,
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+
+ // The parent link should now be changed.
+ EXPECT_TRUE(HasParent(kResourceId, kOldParentResourceId));
+ EXPECT_TRUE(HasParent(kResourceId, kNewParentResourceId));
+ // Should be incremented as a file was moved.
+ EXPECT_EQ(old_largest_change_id + 1,
+ fake_service_.about_resource().largest_change_id());
+ EXPECT_EQ(old_largest_change_id + 1, GetLargestChangeByAboutResource());
+}
+
+TEST_F(FakeDriveServiceTest, AddResourceToDirectory_FileInNonRootDirectory) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ int64 old_largest_change_id = GetLargestChangeByAboutResource();
+
+ const std::string kResourceId = "subdirectory_file_1_id";
+ const std::string kOldParentResourceId = "1_folder_resource_id";
+ const std::string kNewParentResourceId = "2_folder_resource_id";
+
+ // Here's the original parent link.
+ EXPECT_TRUE(HasParent(kResourceId, kOldParentResourceId));
+ EXPECT_FALSE(HasParent(kResourceId, kNewParentResourceId));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ fake_service_.AddResourceToDirectory(
+ kNewParentResourceId,
+ kResourceId,
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+
+ // The parent link should now be changed.
+ EXPECT_TRUE(HasParent(kResourceId, kOldParentResourceId));
+ EXPECT_TRUE(HasParent(kResourceId, kNewParentResourceId));
+ // Should be incremented as a file was moved.
+ EXPECT_EQ(old_largest_change_id + 1,
+ fake_service_.about_resource().largest_change_id());
+ EXPECT_EQ(old_largest_change_id + 1, GetLargestChangeByAboutResource());
+}
+
+TEST_F(FakeDriveServiceTest, AddResourceToDirectory_NonexistingFile) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ const std::string kResourceId = "nonexisting_file";
+ const std::string kNewParentResourceId = "1_folder_resource_id";
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ fake_service_.AddResourceToDirectory(
+ kNewParentResourceId,
+ kResourceId,
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_NOT_FOUND, error);
+}
+
+TEST_F(FakeDriveServiceTest, AddResourceToDirectory_OrphanFile) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ int64 old_largest_change_id = GetLargestChangeByAboutResource();
+
+ const std::string kResourceId = "1_orphanfile_resource_id";
+ const std::string kNewParentResourceId = "1_folder_resource_id";
+
+ // The file does not belong to any directory, even to the root.
+ EXPECT_FALSE(HasParent(kResourceId, kNewParentResourceId));
+ EXPECT_FALSE(HasParent(kResourceId, fake_service_.GetRootResourceId()));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ fake_service_.AddResourceToDirectory(
+ kNewParentResourceId,
+ kResourceId,
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+
+ // The parent link should now be changed.
+ EXPECT_TRUE(HasParent(kResourceId, kNewParentResourceId));
+ EXPECT_FALSE(HasParent(kResourceId, fake_service_.GetRootResourceId()));
+ // Should be incremented as a file was moved.
+ EXPECT_EQ(old_largest_change_id + 1,
+ fake_service_.about_resource().largest_change_id());
+ EXPECT_EQ(old_largest_change_id + 1, GetLargestChangeByAboutResource());
+}
+
+TEST_F(FakeDriveServiceTest, AddResourceToDirectory_Offline) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ fake_service_.set_offline(true);
+
+ const std::string kResourceId = "2_file_resource_id";
+ const std::string kNewParentResourceId = "1_folder_resource_id";
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ fake_service_.AddResourceToDirectory(
+ kNewParentResourceId,
+ kResourceId,
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(DRIVE_NO_CONNECTION, error);
+}
+
+TEST_F(FakeDriveServiceTest, RemoveResourceFromDirectory_ExistingFile) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ int64 old_largest_change_id = GetLargestChangeByAboutResource();
+
+ const std::string kResourceId = "subdirectory_file_1_id";
+ const std::string kParentResourceId = "1_folder_resource_id";
+
+ scoped_ptr<FileResource> entry = FindEntry(kResourceId);
+ ASSERT_TRUE(entry);
+ // The entry should have a parent now.
+ ASSERT_FALSE(entry->parents().empty());
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ fake_service_.RemoveResourceFromDirectory(
+ kParentResourceId,
+ kResourceId,
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_NO_CONTENT, error);
+
+ entry = FindEntry(kResourceId);
+ ASSERT_TRUE(entry);
+ // The entry should have no parent now.
+ ASSERT_TRUE(entry->parents().empty());
+ // Should be incremented as a file was moved to the root directory.
+ EXPECT_EQ(old_largest_change_id + 1,
+ fake_service_.about_resource().largest_change_id());
+ EXPECT_EQ(old_largest_change_id + 1, GetLargestChangeByAboutResource());
+}
+
+TEST_F(FakeDriveServiceTest, RemoveResourceFromDirectory_NonexistingFile) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ const std::string kResourceId = "nonexisting_file";
+ const std::string kParentResourceId = "1_folder_resource_id";
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ fake_service_.RemoveResourceFromDirectory(
+ kParentResourceId,
+ kResourceId,
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_NOT_FOUND, error);
+}
+
+TEST_F(FakeDriveServiceTest, RemoveResourceFromDirectory_OrphanFile) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ const std::string kResourceId = "1_orphanfile_resource_id";
+ const std::string kParentResourceId = fake_service_.GetRootResourceId();
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ fake_service_.RemoveResourceFromDirectory(
+ kParentResourceId,
+ kResourceId,
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_NOT_FOUND, error);
+}
+
+TEST_F(FakeDriveServiceTest, RemoveResourceFromDirectory_Offline) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ fake_service_.set_offline(true);
+
+ const std::string kResourceId = "subdirectory_file_1_id";
+ const std::string kParentResourceId = "1_folder_resource_id";
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ fake_service_.RemoveResourceFromDirectory(
+ kParentResourceId,
+ kResourceId,
+ test_util::CreateCopyResultCallback(&error));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(DRIVE_NO_CONNECTION, error);
+}
+
+TEST_F(FakeDriveServiceTest, AddNewDirectory_EmptyParent) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ int64 old_largest_change_id = GetLargestChangeByAboutResource();
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.AddNewDirectory(
+ std::string(), "new directory", AddNewDirectoryOptions(),
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_CREATED, error);
+ ASSERT_TRUE(entry);
+ EXPECT_TRUE(entry->IsDirectory());
+ EXPECT_EQ("resource_id_1", entry->file_id());
+ EXPECT_EQ("new directory", entry->title());
+ EXPECT_TRUE(HasParent(entry->file_id(), fake_service_.GetRootResourceId()));
+ // Should be incremented as a new directory was created.
+ EXPECT_EQ(old_largest_change_id + 1,
+ fake_service_.about_resource().largest_change_id());
+ EXPECT_EQ(old_largest_change_id + 1, GetLargestChangeByAboutResource());
+}
+
+TEST_F(FakeDriveServiceTest, AddNewDirectory_ToRootDirectory) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ int64 old_largest_change_id = GetLargestChangeByAboutResource();
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.AddNewDirectory(
+ fake_service_.GetRootResourceId(), "new directory",
+ AddNewDirectoryOptions(),
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_CREATED, error);
+ ASSERT_TRUE(entry);
+ EXPECT_TRUE(entry->IsDirectory());
+ EXPECT_EQ("resource_id_1", entry->file_id());
+ EXPECT_EQ("new directory", entry->title());
+ EXPECT_TRUE(HasParent(entry->file_id(), fake_service_.GetRootResourceId()));
+ // Should be incremented as a new directory was created.
+ EXPECT_EQ(old_largest_change_id + 1,
+ fake_service_.about_resource().largest_change_id());
+ EXPECT_EQ(old_largest_change_id + 1, GetLargestChangeByAboutResource());
+}
+
+TEST_F(FakeDriveServiceTest, AddNewDirectory_ToRootDirectoryOnEmptyFileSystem) {
+ int64 old_largest_change_id = GetLargestChangeByAboutResource();
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.AddNewDirectory(
+ fake_service_.GetRootResourceId(), "new directory",
+ AddNewDirectoryOptions(),
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_CREATED, error);
+ ASSERT_TRUE(entry);
+ EXPECT_TRUE(entry->IsDirectory());
+ EXPECT_EQ("resource_id_1", entry->file_id());
+ EXPECT_EQ("new directory", entry->title());
+ EXPECT_TRUE(HasParent(entry->file_id(), fake_service_.GetRootResourceId()));
+ // Should be incremented as a new directory was created.
+ EXPECT_EQ(old_largest_change_id + 1,
+ fake_service_.about_resource().largest_change_id());
+ EXPECT_EQ(old_largest_change_id + 1, GetLargestChangeByAboutResource());
+}
+
+TEST_F(FakeDriveServiceTest, AddNewDirectory_ToNonRootDirectory) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ int64 old_largest_change_id = GetLargestChangeByAboutResource();
+
+ const std::string kParentResourceId = "1_folder_resource_id";
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.AddNewDirectory(
+ kParentResourceId, "new directory", AddNewDirectoryOptions(),
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_CREATED, error);
+ ASSERT_TRUE(entry);
+ EXPECT_TRUE(entry->IsDirectory());
+ EXPECT_EQ("resource_id_1", entry->file_id());
+ EXPECT_EQ("new directory", entry->title());
+ EXPECT_TRUE(HasParent(entry->file_id(), kParentResourceId));
+ // Should be incremented as a new directory was created.
+ EXPECT_EQ(old_largest_change_id + 1,
+ fake_service_.about_resource().largest_change_id());
+ EXPECT_EQ(old_largest_change_id + 1, GetLargestChangeByAboutResource());
+}
+
+TEST_F(FakeDriveServiceTest, AddNewDirectory_ToNonexistingDirectory) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ const std::string kParentResourceId = "nonexisting_resource_id";
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.AddNewDirectory(
+ kParentResourceId, "new directory", AddNewDirectoryOptions(),
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_NOT_FOUND, error);
+ EXPECT_FALSE(entry);
+}
+
+TEST_F(FakeDriveServiceTest, AddNewDirectory_Offline) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ fake_service_.set_offline(true);
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.AddNewDirectory(
+ fake_service_.GetRootResourceId(), "new directory",
+ AddNewDirectoryOptions(),
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(DRIVE_NO_CONNECTION, error);
+ EXPECT_FALSE(entry);
+}
+
+TEST_F(FakeDriveServiceTest, InitiateUploadNewFile_Offline) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ fake_service_.set_offline(true);
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ GURL upload_location;
+ fake_service_.InitiateUploadNewFile(
+ "test/foo", 13, "1_folder_resource_id", "new file.foo",
+ UploadNewFileOptions(),
+ test_util::CreateCopyResultCallback(&error, &upload_location));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(DRIVE_NO_CONNECTION, error);
+ EXPECT_TRUE(upload_location.is_empty());
+}
+
+TEST_F(FakeDriveServiceTest, InitiateUploadNewFile_NotFound) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ GURL upload_location;
+ fake_service_.InitiateUploadNewFile(
+ "test/foo", 13, "non_existent", "new file.foo", UploadNewFileOptions(),
+ test_util::CreateCopyResultCallback(&error, &upload_location));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_NOT_FOUND, error);
+ EXPECT_TRUE(upload_location.is_empty());
+}
+
+TEST_F(FakeDriveServiceTest, InitiateUploadNewFile) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ GURL upload_location;
+ fake_service_.InitiateUploadNewFile(
+ "test/foo", 13, "1_folder_resource_id", "new file.foo",
+ UploadNewFileOptions(),
+ test_util::CreateCopyResultCallback(&error, &upload_location));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ EXPECT_FALSE(upload_location.is_empty());
+ EXPECT_NE(GURL("https://1_folder_resumable_create_media_link?mode=newfile"),
+ upload_location);
+}
+
+TEST_F(FakeDriveServiceTest, InitiateUploadExistingFile_Offline) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ fake_service_.set_offline(true);
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ GURL upload_location;
+ fake_service_.InitiateUploadExistingFile(
+ "test/foo", 13, "2_file_resource_id", UploadExistingFileOptions(),
+ test_util::CreateCopyResultCallback(&error, &upload_location));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(DRIVE_NO_CONNECTION, error);
+ EXPECT_TRUE(upload_location.is_empty());
+}
+
+TEST_F(FakeDriveServiceTest, InitiateUploadExistingFile_Forbidden) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ EXPECT_EQ(HTTP_SUCCESS, fake_service_.SetUserPermission(
+ "2_file_resource_id", google_apis::drive::PERMISSION_ROLE_READER));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ GURL upload_location;
+ fake_service_.InitiateUploadExistingFile(
+ "test/foo", 13, "2_file_resource_id", UploadExistingFileOptions(),
+ test_util::CreateCopyResultCallback(&error, &upload_location));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_FORBIDDEN, error);
+ EXPECT_TRUE(upload_location.is_empty());
+}
+
+TEST_F(FakeDriveServiceTest, InitiateUploadExistingFile_NotFound) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ GURL upload_location;
+ fake_service_.InitiateUploadExistingFile(
+ "test/foo", 13, "non_existent", UploadExistingFileOptions(),
+ test_util::CreateCopyResultCallback(&error, &upload_location));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_NOT_FOUND, error);
+ EXPECT_TRUE(upload_location.is_empty());
+}
+
+TEST_F(FakeDriveServiceTest, InitiateUploadExistingFile_WrongETag) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ UploadExistingFileOptions options;
+ options.etag = "invalid_etag";
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ GURL upload_location;
+ fake_service_.InitiateUploadExistingFile(
+ "text/plain",
+ 13,
+ "2_file_resource_id",
+ options,
+ test_util::CreateCopyResultCallback(&error, &upload_location));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_PRECONDITION, error);
+ EXPECT_TRUE(upload_location.is_empty());
+}
+
+TEST_F(FakeDriveServiceTest, InitiateUpload_ExistingFile) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ scoped_ptr<FileResource> entry = FindEntry("2_file_resource_id");
+ ASSERT_TRUE(entry);
+
+ UploadExistingFileOptions options;
+ options.etag = entry->etag();
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ GURL upload_location;
+ fake_service_.InitiateUploadExistingFile(
+ "text/plain",
+ 13,
+ "2_file_resource_id",
+ options,
+ test_util::CreateCopyResultCallback(&error, &upload_location));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ EXPECT_TRUE(upload_location.is_valid());
+}
+
+TEST_F(FakeDriveServiceTest, ResumeUpload_Offline) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ GURL upload_location;
+ fake_service_.InitiateUploadNewFile(
+ "test/foo", 15, "1_folder_resource_id", "new file.foo",
+ UploadNewFileOptions(),
+ test_util::CreateCopyResultCallback(&error, &upload_location));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ EXPECT_FALSE(upload_location.is_empty());
+ EXPECT_NE(GURL("https://1_folder_resumable_create_media_link"),
+ upload_location);
+
+ fake_service_.set_offline(true);
+
+ UploadRangeResponse response;
+ scoped_ptr<FileResource> entry;
+ fake_service_.ResumeUpload(
+ upload_location,
+ 0, 13, 15, "test/foo",
+ base::FilePath(),
+ test_util::CreateCopyResultCallback(&response, &entry),
+ ProgressCallback());
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(DRIVE_NO_CONNECTION, response.code);
+ EXPECT_FALSE(entry.get());
+}
+
+TEST_F(FakeDriveServiceTest, ResumeUpload_NotFound) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ GURL upload_location;
+ fake_service_.InitiateUploadNewFile(
+ "test/foo", 15, "1_folder_resource_id", "new file.foo",
+ UploadNewFileOptions(),
+ test_util::CreateCopyResultCallback(&error, &upload_location));
+ base::RunLoop().RunUntilIdle();
+
+ ASSERT_EQ(HTTP_SUCCESS, error);
+
+ UploadRangeResponse response;
+ scoped_ptr<FileResource> entry;
+ fake_service_.ResumeUpload(
+ GURL("https://foo.com/"),
+ 0, 13, 15, "test/foo",
+ base::FilePath(),
+ test_util::CreateCopyResultCallback(&response, &entry),
+ ProgressCallback());
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_NOT_FOUND, response.code);
+ EXPECT_FALSE(entry.get());
+}
+
+TEST_F(FakeDriveServiceTest, ResumeUpload_ExistingFile) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ base::FilePath local_file_path =
+ temp_dir.path().Append(FILE_PATH_LITERAL("File 1.txt"));
+ std::string contents("hogefugapiyo");
+ ASSERT_TRUE(test_util::WriteStringToFile(local_file_path, contents));
+
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ scoped_ptr<FileResource> entry = FindEntry("2_file_resource_id");
+ ASSERT_TRUE(entry);
+
+ UploadExistingFileOptions options;
+ options.etag = entry->etag();
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ GURL upload_location;
+ fake_service_.InitiateUploadExistingFile(
+ "text/plain",
+ contents.size(),
+ "2_file_resource_id",
+ options,
+ test_util::CreateCopyResultCallback(&error, &upload_location));
+ base::RunLoop().RunUntilIdle();
+
+ ASSERT_EQ(HTTP_SUCCESS, error);
+
+ UploadRangeResponse response;
+ entry.reset();
+ std::vector<test_util::ProgressInfo> upload_progress_values;
+ fake_service_.ResumeUpload(
+ upload_location,
+ 0, contents.size() / 2, contents.size(), "text/plain",
+ local_file_path,
+ test_util::CreateCopyResultCallback(&response, &entry),
+ base::Bind(&test_util::AppendProgressCallbackResult,
+ &upload_progress_values));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_RESUME_INCOMPLETE, response.code);
+ EXPECT_FALSE(entry.get());
+ ASSERT_TRUE(!upload_progress_values.empty());
+ EXPECT_TRUE(base::STLIsSorted(upload_progress_values));
+ EXPECT_LE(0, upload_progress_values.front().first);
+ EXPECT_GE(static_cast<int64>(contents.size() / 2),
+ upload_progress_values.back().first);
+
+ upload_progress_values.clear();
+ fake_service_.ResumeUpload(
+ upload_location,
+ contents.size() / 2, contents.size(), contents.size(), "text/plain",
+ local_file_path,
+ test_util::CreateCopyResultCallback(&response, &entry),
+ base::Bind(&test_util::AppendProgressCallbackResult,
+ &upload_progress_values));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, response.code);
+ EXPECT_TRUE(entry.get());
+ EXPECT_EQ(static_cast<int64>(contents.size()), entry->file_size());
+ EXPECT_TRUE(Exists(entry->file_id()));
+ ASSERT_TRUE(!upload_progress_values.empty());
+ EXPECT_TRUE(base::STLIsSorted(upload_progress_values));
+ EXPECT_LE(0, upload_progress_values.front().first);
+ EXPECT_GE(static_cast<int64>(contents.size() - contents.size() / 2),
+ upload_progress_values.back().first);
+ EXPECT_EQ(base::MD5String(contents), entry->md5_checksum());
+}
+
+TEST_F(FakeDriveServiceTest, ResumeUpload_NewFile) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ base::FilePath local_file_path =
+ temp_dir.path().Append(FILE_PATH_LITERAL("new file.foo"));
+ std::string contents("hogefugapiyo");
+ ASSERT_TRUE(test_util::WriteStringToFile(local_file_path, contents));
+
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ GURL upload_location;
+ fake_service_.InitiateUploadNewFile(
+ "test/foo", contents.size(), "1_folder_resource_id", "new file.foo",
+ UploadNewFileOptions(),
+ test_util::CreateCopyResultCallback(&error, &upload_location));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ EXPECT_FALSE(upload_location.is_empty());
+ EXPECT_NE(GURL("https://1_folder_resumable_create_media_link"),
+ upload_location);
+
+ UploadRangeResponse response;
+ scoped_ptr<FileResource> entry;
+ std::vector<test_util::ProgressInfo> upload_progress_values;
+ fake_service_.ResumeUpload(
+ upload_location,
+ 0, contents.size() / 2, contents.size(), "test/foo",
+ local_file_path,
+ test_util::CreateCopyResultCallback(&response, &entry),
+ base::Bind(&test_util::AppendProgressCallbackResult,
+ &upload_progress_values));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_RESUME_INCOMPLETE, response.code);
+ EXPECT_FALSE(entry.get());
+ ASSERT_TRUE(!upload_progress_values.empty());
+ EXPECT_TRUE(base::STLIsSorted(upload_progress_values));
+ EXPECT_LE(0, upload_progress_values.front().first);
+ EXPECT_GE(static_cast<int64>(contents.size() / 2),
+ upload_progress_values.back().first);
+
+ upload_progress_values.clear();
+ fake_service_.ResumeUpload(
+ upload_location,
+ contents.size() / 2, contents.size(), contents.size(), "test/foo",
+ local_file_path,
+ test_util::CreateCopyResultCallback(&response, &entry),
+ base::Bind(&test_util::AppendProgressCallbackResult,
+ &upload_progress_values));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_CREATED, response.code);
+ EXPECT_TRUE(entry.get());
+ EXPECT_EQ(static_cast<int64>(contents.size()), entry->file_size());
+ EXPECT_TRUE(Exists(entry->file_id()));
+ ASSERT_TRUE(!upload_progress_values.empty());
+ EXPECT_TRUE(base::STLIsSorted(upload_progress_values));
+ EXPECT_LE(0, upload_progress_values.front().first);
+ EXPECT_GE(static_cast<int64>(contents.size() - contents.size() / 2),
+ upload_progress_values.back().first);
+ EXPECT_EQ(base::MD5String(contents), entry->md5_checksum());
+}
+
+TEST_F(FakeDriveServiceTest, AddNewFile_ToRootDirectory) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ int64 old_largest_change_id = GetLargestChangeByAboutResource();
+
+ const std::string kContentType = "text/plain";
+ const std::string kContentData = "This is some test content.";
+ const std::string kTitle = "new file";
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.AddNewFile(
+ kContentType,
+ kContentData,
+ fake_service_.GetRootResourceId(),
+ kTitle,
+ false, // shared_with_me
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_CREATED, error);
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(kContentType, entry->mime_type());
+ EXPECT_EQ(static_cast<int64>(kContentData.size()), entry->file_size());
+ EXPECT_EQ("resource_id_1", entry->file_id());
+ EXPECT_EQ(kTitle, entry->title());
+ EXPECT_TRUE(HasParent(entry->file_id(), fake_service_.GetRootResourceId()));
+ // Should be incremented as a new directory was created.
+ EXPECT_EQ(old_largest_change_id + 1,
+ fake_service_.about_resource().largest_change_id());
+ EXPECT_EQ(old_largest_change_id + 1, GetLargestChangeByAboutResource());
+ EXPECT_EQ(base::MD5String(kContentData), entry->md5_checksum());
+}
+
+TEST_F(FakeDriveServiceTest, AddNewFile_ToRootDirectoryOnEmptyFileSystem) {
+ int64 old_largest_change_id = GetLargestChangeByAboutResource();
+
+ const std::string kContentType = "text/plain";
+ const std::string kContentData = "This is some test content.";
+ const std::string kTitle = "new file";
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.AddNewFile(
+ kContentType,
+ kContentData,
+ fake_service_.GetRootResourceId(),
+ kTitle,
+ false, // shared_with_me
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_CREATED, error);
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(kContentType, entry->mime_type());
+ EXPECT_EQ(static_cast<int64>(kContentData.size()), entry->file_size());
+ EXPECT_EQ("resource_id_1", entry->file_id());
+ EXPECT_EQ(kTitle, entry->title());
+ EXPECT_TRUE(HasParent(entry->file_id(), fake_service_.GetRootResourceId()));
+ // Should be incremented as a new directory was created.
+ EXPECT_EQ(old_largest_change_id + 1,
+ fake_service_.about_resource().largest_change_id());
+ EXPECT_EQ(old_largest_change_id + 1, GetLargestChangeByAboutResource());
+ EXPECT_EQ(base::MD5String(kContentData), entry->md5_checksum());
+}
+
+TEST_F(FakeDriveServiceTest, AddNewFile_ToNonRootDirectory) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ int64 old_largest_change_id = GetLargestChangeByAboutResource();
+
+ const std::string kContentType = "text/plain";
+ const std::string kContentData = "This is some test content.";
+ const std::string kTitle = "new file";
+ const std::string kParentResourceId = "1_folder_resource_id";
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.AddNewFile(
+ kContentType,
+ kContentData,
+ kParentResourceId,
+ kTitle,
+ false, // shared_with_me
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_CREATED, error);
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(kContentType, entry->mime_type());
+ EXPECT_EQ(static_cast<int64>(kContentData.size()), entry->file_size());
+ EXPECT_EQ("resource_id_1", entry->file_id());
+ EXPECT_EQ(kTitle, entry->title());
+ EXPECT_TRUE(HasParent(entry->file_id(), kParentResourceId));
+ // Should be incremented as a new directory was created.
+ EXPECT_EQ(old_largest_change_id + 1,
+ fake_service_.about_resource().largest_change_id());
+ EXPECT_EQ(old_largest_change_id + 1, GetLargestChangeByAboutResource());
+ EXPECT_EQ(base::MD5String(kContentData), entry->md5_checksum());
+}
+
+TEST_F(FakeDriveServiceTest, AddNewFile_ToNonexistingDirectory) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ const std::string kContentType = "text/plain";
+ const std::string kContentData = "This is some test content.";
+ const std::string kTitle = "new file";
+ const std::string kParentResourceId = "nonexisting_resource_id";
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.AddNewFile(
+ kContentType,
+ kContentData,
+ kParentResourceId,
+ kTitle,
+ false, // shared_with_me
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_NOT_FOUND, error);
+ EXPECT_FALSE(entry);
+}
+
+TEST_F(FakeDriveServiceTest, AddNewFile_Offline) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ fake_service_.set_offline(true);
+
+ const std::string kContentType = "text/plain";
+ const std::string kContentData = "This is some test content.";
+ const std::string kTitle = "new file";
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.AddNewFile(
+ kContentType,
+ kContentData,
+ fake_service_.GetRootResourceId(),
+ kTitle,
+ false, // shared_with_me
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(DRIVE_NO_CONNECTION, error);
+ EXPECT_FALSE(entry);
+}
+
+TEST_F(FakeDriveServiceTest, AddNewFile_SharedWithMeLabel) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ const std::string kContentType = "text/plain";
+ const std::string kContentData = "This is some test content.";
+ const std::string kTitle = "new file";
+
+ int64 old_largest_change_id = GetLargestChangeByAboutResource();
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.AddNewFile(
+ kContentType,
+ kContentData,
+ fake_service_.GetRootResourceId(),
+ kTitle,
+ true, // shared_with_me
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_CREATED, error);
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(kContentType, entry->mime_type());
+ EXPECT_EQ(static_cast<int64>(kContentData.size()), entry->file_size());
+ EXPECT_EQ("resource_id_1", entry->file_id());
+ EXPECT_EQ(kTitle, entry->title());
+ EXPECT_TRUE(HasParent(entry->file_id(), fake_service_.GetRootResourceId()));
+ EXPECT_FALSE(entry->shared_with_me_date().is_null());
+ // Should be incremented as a new directory was created.
+ EXPECT_EQ(old_largest_change_id + 1,
+ fake_service_.about_resource().largest_change_id());
+ EXPECT_EQ(old_largest_change_id + 1, GetLargestChangeByAboutResource());
+ EXPECT_EQ(base::MD5String(kContentData), entry->md5_checksum());
+}
+
+TEST_F(FakeDriveServiceTest, SetLastModifiedTime_ExistingFile) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ const std::string kResourceId = "2_file_resource_id";
+ base::Time time;
+ ASSERT_TRUE(base::Time::FromString("1 April 2013 12:34:56", &time));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.SetLastModifiedTime(
+ kResourceId,
+ time,
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_SUCCESS, error);
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(time, entry->modified_date());
+}
+
+TEST_F(FakeDriveServiceTest, SetLastModifiedTime_NonexistingFile) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+
+ const std::string kResourceId = "nonexisting_resource_id";
+ base::Time time;
+ ASSERT_TRUE(base::Time::FromString("1 April 2013 12:34:56", &time));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.SetLastModifiedTime(
+ kResourceId,
+ time,
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(HTTP_NOT_FOUND, error);
+ EXPECT_FALSE(entry);
+}
+
+TEST_F(FakeDriveServiceTest, SetLastModifiedTime_Offline) {
+ ASSERT_TRUE(test_util::SetUpTestEntries(&fake_service_));
+ fake_service_.set_offline(true);
+
+ const std::string kResourceId = "2_file_resource_id";
+ base::Time time;
+ ASSERT_TRUE(base::Time::FromString("1 April 2013 12:34:56", &time));
+
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+ fake_service_.SetLastModifiedTime(
+ kResourceId,
+ time,
+ test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(DRIVE_NO_CONNECTION, error);
+ EXPECT_FALSE(entry);
+}
+
+} // namespace
+
+} // namespace drive
diff --git a/components/drive/service/test_util.cc b/components/drive/service/test_util.cc
new file mode 100644
index 0000000..42c54f1
--- /dev/null
+++ b/components/drive/service/test_util.cc
@@ -0,0 +1,193 @@
+// Copyright 2014 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/service/test_util.h"
+
+#include "base/run_loop.h"
+#include "components/drive/drive_api_util.h"
+#include "components/drive/service/fake_drive_service.h"
+#include "google_apis/drive/drive_api_parser.h"
+#include "google_apis/drive/test_util.h"
+
+using google_apis::FileResource;
+using google_apis::DRIVE_OTHER_ERROR;
+using google_apis::DriveApiErrorCode;
+using google_apis::HTTP_CREATED;
+
+namespace drive {
+namespace test_util {
+
+bool SetUpTestEntries(FakeDriveService* drive_service) {
+ DriveApiErrorCode error = DRIVE_OTHER_ERROR;
+ scoped_ptr<FileResource> entry;
+
+ drive_service->AddNewFileWithResourceId(
+ "2_file_resource_id",
+ "audio/mpeg",
+ "This is some test content.",
+ drive_service->GetRootResourceId(),
+ "File 1.txt",
+ false, // shared_with_me
+ google_apis::test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+ if (error != HTTP_CREATED)
+ return false;
+
+ drive_service->AddNewFileWithResourceId(
+ "slash_file_resource_id",
+ "audio/mpeg",
+ "This is some test content.",
+ drive_service->GetRootResourceId(),
+ "Slash / in file 1.txt",
+ false, // shared_with_me
+ google_apis::test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+ if (error != HTTP_CREATED)
+ return false;
+
+ drive_service->AddNewFileWithResourceId(
+ "3_file_resource_id",
+ "audio/mpeg",
+ "This is some test content.",
+ drive_service->GetRootResourceId(),
+ "Duplicate Name.txt",
+ false, // shared_with_me
+ google_apis::test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+ if (error != HTTP_CREATED)
+ return false;
+
+ drive_service->AddNewFileWithResourceId(
+ "4_file_resource_id",
+ "audio/mpeg",
+ "This is some test content.",
+ drive_service->GetRootResourceId(),
+ "Duplicate Name.txt",
+ false, // shared_with_me
+ google_apis::test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+ if (error != HTTP_CREATED)
+ return false;
+
+ drive_service->AddNewFileWithResourceId(
+ "5_document_resource_id",
+ util::kGoogleDocumentMimeType,
+ std::string(),
+ drive_service->GetRootResourceId(),
+ "Document 1 excludeDir-test",
+ false, // shared_with_me
+ google_apis::test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+ if (error != HTTP_CREATED)
+ return false;
+
+ drive_service->AddNewFileWithResourceId(
+ "1_folder_resource_id",
+ util::kDriveFolderMimeType,
+ std::string(),
+ drive_service->GetRootResourceId(),
+ "Directory 1",
+ false, // shared_with_me
+ google_apis::test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+ if (error != HTTP_CREATED)
+ return false;
+
+ drive_service->AddNewFileWithResourceId(
+ "subdirectory_file_1_id",
+ "audio/mpeg",
+ "This is some test content.",
+ "1_folder_resource_id",
+ "SubDirectory File 1.txt",
+ false, // shared_with_me
+ google_apis::test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+ if (error != HTTP_CREATED)
+ return false;
+
+ drive_service->AddNewFileWithResourceId(
+ "subdirectory_unowned_file_1_id",
+ "audio/mpeg",
+ "This is some test content.",
+ "1_folder_resource_id",
+ "Shared to The Account Owner.txt",
+ true, // shared_with_me
+ google_apis::test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+ if (error != HTTP_CREATED)
+ return false;
+
+ drive_service->AddNewDirectoryWithResourceId(
+ "sub_dir_folder_resource_id", "1_folder_resource_id",
+ "Sub Directory Folder", AddNewDirectoryOptions(),
+ google_apis::test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+ if (error != HTTP_CREATED)
+ return false;
+
+ drive_service->AddNewDirectoryWithResourceId(
+ "sub_sub_directory_folder_id", "sub_dir_folder_resource_id",
+ "Sub Sub Directory Folder", AddNewDirectoryOptions(),
+ google_apis::test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+ if (error != HTTP_CREATED)
+ return false;
+
+ drive_service->AddNewDirectoryWithResourceId(
+ "slash_dir_folder_resource_id", drive_service->GetRootResourceId(),
+ "Slash / in directory", AddNewDirectoryOptions(),
+ google_apis::test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+ if (error != HTTP_CREATED)
+ return false;
+
+ drive_service->AddNewFileWithResourceId(
+ "slash_subdir_file",
+ "audio/mpeg",
+ "This is some test content.",
+ "slash_dir_folder_resource_id",
+ "Slash SubDir File.txt",
+ false, // shared_with_me
+ google_apis::test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+ if (error != HTTP_CREATED)
+ return false;
+
+ drive_service->AddNewDirectoryWithResourceId(
+ "sub_dir_folder_2_self_link", drive_service->GetRootResourceId(),
+ "Directory 2 excludeDir-test", AddNewDirectoryOptions(),
+ google_apis::test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+ if (error != HTTP_CREATED)
+ return false;
+
+ drive_service->AddNewFileWithResourceId(
+ "1_orphanfile_resource_id",
+ "text/plain",
+ "This is some test content.",
+ std::string(),
+ "Orphan File 1.txt",
+ true, // shared_with_me
+ google_apis::test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+ if (error != HTTP_CREATED)
+ return false;
+
+ drive_service->AddNewFileWithResourceId(
+ "orphan_doc_1",
+ util::kGoogleDocumentMimeType,
+ std::string(),
+ std::string(),
+ "Orphan Document",
+ true, // shared_with_me
+ google_apis::test_util::CreateCopyResultCallback(&error, &entry));
+ base::RunLoop().RunUntilIdle();
+ if (error != HTTP_CREATED)
+ return false;
+
+ return true;
+}
+
+} // namespace test_util
+} // namespace drive
diff --git a/components/drive/service/test_util.h b/components/drive/service/test_util.h
new file mode 100644
index 0000000..9cd25ae
--- /dev/null
+++ b/components/drive/service/test_util.h
@@ -0,0 +1,19 @@
+// Copyright 2014 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.
+
+#ifndef COMPONENTS_DRIVE_SERVICE_TEST_UTIL_H_
+#define COMPONENTS_DRIVE_SERVICE_TEST_UTIL_H_
+
+namespace drive {
+
+class FakeDriveService;
+
+namespace test_util {
+
+bool SetUpTestEntries(FakeDriveService* drive_service);
+
+} // namespace test_util
+} // namespace drive
+
+#endif // COMPONENTS_DRIVE_SERVICE_TEST_UTIL_H_