diff options
author | lukasza <lukasza@chromium.org> | 2015-07-20 13:57:20 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-07-20 20:58:23 +0000 |
commit | 8acc4eb18b6919f36413a10718a607f2a34fb8f6 (patch) | |
tree | 1e53fa500119384a12b38075ebcc97a469750dd7 /components/drive/drive_uploader_unittest.cc | |
parent | 1f37aaf390288b2864efe98a07427dbf91771489 (diff) | |
download | chromium_src-8acc4eb18b6919f36413a10718a607f2a34fb8f6.zip chromium_src-8acc4eb18b6919f36413a10718a607f2a34fb8f6.tar.gz chromium_src-8acc4eb18b6919f36413a10718a607f2a34fb8f6.tar.bz2 |
Move (most of) chrome/browser/drive into components/drive.
Note that drive_notification_manager_factory is left behind in
chrome/browser/drive. This is because it needs to stay dependent on the
browser. This is okay, because drive_notification_manager_factory is
not needed by drive::FileSystem and other parts of drive libraries that
we want to componentize.
Also note that some things moved to components/drive continue to
have slightly undesirable dependencies:
- Tests are still built and executed as part of the browser test suites.
- drive_uploader.cc depends on content/public/browser/power_save_blocker.h
This means that to use components/drive outside of the browser,
users of components/drive have to provide a no-op implementation
of power_save_blocker.h
The most desirable approach in the long-term would be to componentize
power_save_blocker.h. An alternative would be to continue with
the state introduced by the current changelist (or to introduce
drive-specific abstraction similar to power_save_blocker.h, but
outside of the browser).
- drive_api_util.cc depends on storage/browser/fileapi/file_stream_reader.h
To get rid of this dependency, it is probably best to move
FileStreamMd5Digester class closer to the only point of usage
(c/b/chromeos/extensions/file_manager/private_api_file_system.cc).
Landing via CQ with NOPRESUBMIT=true, because "git cl presubmit" incorrectly
identifies target directory of a dependency (we are depending on
third_party/cacheinvalidation/src/google/cacheinvalidation/types.proto
and including the generated header via google/cacheinvalidation/types.pb.h).
I believe this is a case of crbug.com/448570. All other presubmit checks
are passing AFAIK:
$ git cl presubmit
Running presubmit commit checks ...
** Presubmit ERRORS **
Missing LGTM from OWNERS of dependencies added to DEPS:
'+google/cacheinvalidation/types.pb.h',
Presubmit checks took 2.9s to calculate.
Test steps:
1. Verify that things still build via GYP (and unit tests pass).
$ GYP_DEFINES="use_goma=1 gomadir=... chromeos=1" gclient sync
$ ninja -C out/Debug -j 150 chrome unit_tests \
interactive_ui_tests browser_tests drive
$ out/Debug/unit_tests
2. Verify that things still builds via GN.
$ gn gen out/Default --args='target_os="chromeos" use_goma=true'
$ ninja -C out/Default -j 150 chrome unit_tests \
interactive_ui_tests browser_tests components/drive
TEST=Please see "Test steps" above.
BUG=257943, 498951
NOPRESUBMIT=true
Review URL: https://codereview.chromium.org/1190203002
Cr-Commit-Position: refs/heads/master@{#339512}
Diffstat (limited to 'components/drive/drive_uploader_unittest.cc')
-rw-r--r-- | components/drive/drive_uploader_unittest.cc | 955 |
1 files changed, 955 insertions, 0 deletions
diff --git a/components/drive/drive_uploader_unittest.cc b/components/drive/drive_uploader_unittest.cc new file mode 100644 index 0000000..29fbf26ac --- /dev/null +++ b/components/drive/drive_uploader_unittest.cc @@ -0,0 +1,955 @@ +// 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/drive_uploader.h" + +#include <algorithm> +#include <string> +#include <vector> + +#include "base/bind.h" +#include "base/files/scoped_temp_dir.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/thread_task_runner_handle.h" +#include "base/values.h" +#include "components/drive/service/dummy_drive_service.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::CancelCallback; +using google_apis::FileResource; +using google_apis::DriveApiErrorCode; +using google_apis::DRIVE_NO_CONNECTION; +using google_apis::DRIVE_OTHER_ERROR; +using google_apis::HTTP_CONFLICT; +using google_apis::HTTP_CREATED; +using google_apis::HTTP_NOT_FOUND; +using google_apis::HTTP_PRECONDITION; +using google_apis::HTTP_RESUME_INCOMPLETE; +using google_apis::HTTP_SUCCESS; +using google_apis::InitiateUploadCallback; +using google_apis::ProgressCallback; +using google_apis::UploadRangeResponse; +using google_apis::drive::UploadRangeCallback; +namespace test_util = google_apis::test_util; + +namespace drive { + +namespace { + +const char kTestDummyMd5[] = "dummy_md5"; +const char kTestDocumentTitle[] = "Hello world"; +const char kTestInitiateUploadParentResourceId[] = "parent_resource_id"; +const char kTestInitiateUploadResourceId[] = "resource_id"; +const char kTestMimeType[] = "text/plain"; +const char kTestUploadNewFileURL[] = "http://test/upload_location/new_file"; +const char kTestUploadExistingFileURL[] = + "http://test/upload_location/existing_file"; +const int64 kUploadChunkSize = 1024 * 1024 * 1024; +const char kTestETag[] = "test_etag"; + +CancelCallback SendMultipartUploadResult( + DriveApiErrorCode response_code, + int64 content_length, + const google_apis::FileResourceCallback& callback, + const google_apis::ProgressCallback& progress_callback) { + // Callback progress + if (!progress_callback.is_null()) { + // For the testing purpose, it always notifies the progress at the end of + // whole file uploading. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(progress_callback, content_length, content_length)); + } + + // MultipartUploadXXXFile is an asynchronous function, so don't callback + // directly. + scoped_ptr<FileResource> entry; + entry.reset(new FileResource); + entry->set_md5_checksum(kTestDummyMd5); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(callback, response_code, base::Passed(&entry))); + return CancelCallback(); +} + +// Mock DriveService that verifies if the uploaded content matches the preset +// expectation. +class MockDriveServiceWithUploadExpectation : public DummyDriveService { + public: + // Sets up an expected upload content. InitiateUpload and ResumeUpload will + // verify that the specified data is correctly uploaded. + MockDriveServiceWithUploadExpectation( + const base::FilePath& expected_upload_file, + int64 expected_content_length) + : expected_upload_file_(expected_upload_file), + expected_content_length_(expected_content_length), + received_bytes_(0), + resume_upload_call_count_(0), + multipart_upload_call_count_(0) {} + + int64 received_bytes() const { return received_bytes_; } + void set_received_bytes(int64 received_bytes) { + received_bytes_ = received_bytes; + } + + int64 resume_upload_call_count() const { return resume_upload_call_count_; } + int64 multipart_upload_call_count() const { + return multipart_upload_call_count_; + } + + private: + // DriveServiceInterface overrides. + // Handles a request for obtaining an upload location URL. + CancelCallback 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) override { + EXPECT_EQ(kTestDocumentTitle, title); + EXPECT_EQ(kTestMimeType, content_type); + EXPECT_EQ(expected_content_length_, content_length); + EXPECT_EQ(kTestInitiateUploadParentResourceId, parent_resource_id); + + // Calls back the upload URL for subsequent ResumeUpload requests. + // InitiateUpload is an asynchronous function, so don't callback directly. + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::Bind(callback, HTTP_SUCCESS, GURL(kTestUploadNewFileURL))); + return CancelCallback(); + } + + CancelCallback InitiateUploadExistingFile( + const std::string& content_type, + int64 content_length, + const std::string& resource_id, + const UploadExistingFileOptions& options, + const InitiateUploadCallback& callback) override { + EXPECT_EQ(kTestMimeType, content_type); + EXPECT_EQ(expected_content_length_, content_length); + EXPECT_EQ(kTestInitiateUploadResourceId, resource_id); + + if (!options.etag.empty() && options.etag != kTestETag) { + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::Bind(callback, HTTP_PRECONDITION, GURL())); + return CancelCallback(); + } + + // Calls back the upload URL for subsequent ResumeUpload requests. + // InitiateUpload is an asynchronous function, so don't callback directly. + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::Bind(callback, HTTP_SUCCESS, GURL(kTestUploadExistingFileURL))); + return CancelCallback(); + } + + // Handles a request for uploading a chunk of bytes. + CancelCallback ResumeUpload( + const GURL& upload_location, + 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) override { + // The upload range should start from the current first unreceived byte. + EXPECT_EQ(received_bytes_, start_position); + EXPECT_EQ(expected_upload_file_, local_file_path); + + // The upload data must be split into 512KB chunks. + const int64 expected_chunk_end = + std::min(received_bytes_ + kUploadChunkSize, expected_content_length_); + EXPECT_EQ(expected_chunk_end, end_position); + + // The upload URL returned by InitiateUpload() must be used. + EXPECT_TRUE(GURL(kTestUploadNewFileURL) == upload_location || + GURL(kTestUploadExistingFileURL) == upload_location); + + // Other parameters should be the exact values passed to DriveUploader. + EXPECT_EQ(expected_content_length_, content_length); + EXPECT_EQ(kTestMimeType, content_type); + + // Update the internal status of the current upload session. + resume_upload_call_count_++; + received_bytes_ = end_position; + + // Callback progress + if (!progress_callback.is_null()) { + // For the testing purpose, it always notifies the progress at the end of + // each chunk uploading. + int64 chunk_size = end_position - start_position; + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::Bind(progress_callback, chunk_size, chunk_size)); + } + + SendUploadRangeResponse(upload_location, callback); + return CancelCallback(); + } + + // Handles a request to fetch the current upload status. + CancelCallback GetUploadStatus(const GURL& upload_location, + int64 content_length, + const UploadRangeCallback& callback) override { + EXPECT_EQ(expected_content_length_, content_length); + // The upload URL returned by InitiateUpload() must be used. + EXPECT_TRUE(GURL(kTestUploadNewFileURL) == upload_location || + GURL(kTestUploadExistingFileURL) == upload_location); + + SendUploadRangeResponse(upload_location, callback); + return CancelCallback(); + } + + // Runs |callback| with the current upload status. + void SendUploadRangeResponse(const GURL& upload_location, + const UploadRangeCallback& callback) { + // Callback with response. + UploadRangeResponse response; + scoped_ptr<FileResource> entry; + if (received_bytes_ == expected_content_length_) { + DriveApiErrorCode response_code = + upload_location == GURL(kTestUploadNewFileURL) ? + HTTP_CREATED : HTTP_SUCCESS; + response = UploadRangeResponse(response_code, -1, -1); + + entry.reset(new FileResource); + entry->set_md5_checksum(kTestDummyMd5); + } else { + response = UploadRangeResponse( + HTTP_RESUME_INCOMPLETE, 0, received_bytes_); + } + // ResumeUpload is an asynchronous function, so don't callback directly. + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::Bind(callback, response, base::Passed(&entry))); + } + + 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 { + EXPECT_EQ(kTestMimeType, content_type); + EXPECT_EQ(expected_content_length_, content_length); + EXPECT_EQ(kTestInitiateUploadParentResourceId, parent_resource_id); + EXPECT_EQ(kTestDocumentTitle, title); + EXPECT_EQ(expected_upload_file_, local_file_path); + + received_bytes_ = content_length; + multipart_upload_call_count_++; + return SendMultipartUploadResult(HTTP_CREATED, content_length, callback, + progress_callback); + } + + 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 { + EXPECT_EQ(kTestMimeType, content_type); + EXPECT_EQ(expected_content_length_, content_length); + EXPECT_EQ(kTestInitiateUploadResourceId, resource_id); + EXPECT_EQ(expected_upload_file_, local_file_path); + + if (!options.etag.empty() && options.etag != kTestETag) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(callback, HTTP_PRECONDITION, + base::Passed(make_scoped_ptr<FileResource>(NULL)))); + return CancelCallback(); + } + + received_bytes_ = content_length; + multipart_upload_call_count_++; + return SendMultipartUploadResult(HTTP_SUCCESS, content_length, callback, + progress_callback); + } + + const base::FilePath expected_upload_file_; + const int64 expected_content_length_; + int64 received_bytes_; + int64 resume_upload_call_count_; + int64 multipart_upload_call_count_; +}; + +// Mock DriveService that returns a failure at InitiateUpload(). +class MockDriveServiceNoConnectionAtInitiate : public DummyDriveService { + // Returns error. + CancelCallback 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) override { + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::Bind(callback, DRIVE_NO_CONNECTION, GURL())); + return CancelCallback(); + } + + CancelCallback InitiateUploadExistingFile( + const std::string& content_type, + int64 content_length, + const std::string& resource_id, + const UploadExistingFileOptions& options, + const InitiateUploadCallback& callback) override { + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::Bind(callback, DRIVE_NO_CONNECTION, GURL())); + return CancelCallback(); + } + + // Should not be used. + 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 UploadRangeCallback& callback, + const ProgressCallback& progress_callback) override { + NOTREACHED(); + return CancelCallback(); + } + + 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 { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(callback, DRIVE_NO_CONNECTION, + base::Passed(make_scoped_ptr<FileResource>(NULL)))); + return CancelCallback(); + } + + 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 { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(callback, DRIVE_NO_CONNECTION, + base::Passed(make_scoped_ptr<FileResource>(NULL)))); + return CancelCallback(); + } +}; + +// Mock DriveService that returns a failure at ResumeUpload(). +class MockDriveServiceNoConnectionAtResume : public DummyDriveService { + // Succeeds and returns an upload location URL. + CancelCallback 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) override { + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::Bind(callback, HTTP_SUCCESS, GURL(kTestUploadNewFileURL))); + return CancelCallback(); + } + + CancelCallback InitiateUploadExistingFile( + const std::string& content_type, + int64 content_length, + const std::string& resource_id, + const UploadExistingFileOptions& options, + const InitiateUploadCallback& callback) override { + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::Bind(callback, HTTP_SUCCESS, GURL(kTestUploadExistingFileURL))); + return CancelCallback(); + } + + // Returns error. + 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 UploadRangeCallback& callback, + const ProgressCallback& progress_callback) override { + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::Bind(callback, + UploadRangeResponse(DRIVE_NO_CONNECTION, -1, -1), + base::Passed(scoped_ptr<FileResource>()))); + return CancelCallback(); + } +}; + +// Mock DriveService that returns a failure at GetUploadStatus(). +class MockDriveServiceNoConnectionAtGetUploadStatus : public DummyDriveService { + // Returns error. + CancelCallback GetUploadStatus(const GURL& upload_url, + int64 content_length, + const UploadRangeCallback& callback) override { + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::Bind(callback, + UploadRangeResponse(DRIVE_NO_CONNECTION, -1, -1), + base::Passed(scoped_ptr<FileResource>()))); + return CancelCallback(); + } +}; + +class DriveUploaderTest : public testing::Test { + public: + void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); } + + protected: + base::MessageLoop message_loop_; + base::ScopedTempDir temp_dir_; +}; + +} // namespace + +TEST_F(DriveUploaderTest, UploadExisting0KB) { + base::FilePath local_path; + std::string data; + ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize( + temp_dir_.path(), 0, &local_path, &data)); + + DriveApiErrorCode error = DRIVE_OTHER_ERROR; + GURL upload_location; + scoped_ptr<FileResource> entry; + + MockDriveServiceWithUploadExpectation mock_service(local_path, data.size()); + DriveUploader uploader(&mock_service, + base::ThreadTaskRunnerHandle::Get().get()); + std::vector<test_util::ProgressInfo> upload_progress_values; + uploader.UploadExistingFile( + kTestInitiateUploadResourceId, local_path, kTestMimeType, + UploadExistingFileOptions(), + test_util::CreateCopyResultCallback(&error, &upload_location, &entry), + base::Bind(&test_util::AppendProgressCallbackResult, + &upload_progress_values)); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(0, mock_service.resume_upload_call_count()); + EXPECT_EQ(1, mock_service.multipart_upload_call_count()); + EXPECT_EQ(0, mock_service.received_bytes()); + EXPECT_EQ(HTTP_SUCCESS, error); + EXPECT_TRUE(upload_location.is_empty()); + ASSERT_TRUE(entry); + EXPECT_EQ(kTestDummyMd5, entry->md5_checksum()); + ASSERT_EQ(1U, upload_progress_values.size()); + EXPECT_EQ(test_util::ProgressInfo(0, 0), upload_progress_values[0]); +} + +TEST_F(DriveUploaderTest, UploadExisting512KB) { + base::FilePath local_path; + std::string data; + ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize( + temp_dir_.path(), 512 * 1024, &local_path, &data)); + + DriveApiErrorCode error = DRIVE_OTHER_ERROR; + GURL upload_location; + scoped_ptr<FileResource> entry; + + MockDriveServiceWithUploadExpectation mock_service(local_path, data.size()); + DriveUploader uploader(&mock_service, + base::ThreadTaskRunnerHandle::Get().get()); + std::vector<test_util::ProgressInfo> upload_progress_values; + uploader.UploadExistingFile( + kTestInitiateUploadResourceId, local_path, kTestMimeType, + UploadExistingFileOptions(), + test_util::CreateCopyResultCallback(&error, &upload_location, &entry), + base::Bind(&test_util::AppendProgressCallbackResult, + &upload_progress_values)); + base::RunLoop().RunUntilIdle(); + + // 512KB upload should be uploaded as multipart body. + EXPECT_EQ(0, mock_service.resume_upload_call_count()); + EXPECT_EQ(1, mock_service.multipart_upload_call_count()); + EXPECT_EQ(512 * 1024, mock_service.received_bytes()); + EXPECT_EQ(HTTP_SUCCESS, error); + EXPECT_TRUE(upload_location.is_empty()); + ASSERT_TRUE(entry); + EXPECT_EQ(kTestDummyMd5, entry->md5_checksum()); + ASSERT_EQ(1U, upload_progress_values.size()); + EXPECT_EQ(test_util::ProgressInfo(512 * 1024, 512 * 1024), + upload_progress_values[0]); +} + +TEST_F(DriveUploaderTest, UploadExisting2MB) { + base::FilePath local_path; + std::string data; + ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize( + temp_dir_.path(), 2 * 1024 * 1024, &local_path, &data)); + + DriveApiErrorCode error = DRIVE_OTHER_ERROR; + GURL upload_location; + scoped_ptr<FileResource> entry; + + MockDriveServiceWithUploadExpectation mock_service(local_path, data.size()); + DriveUploader uploader(&mock_service, + base::ThreadTaskRunnerHandle::Get().get()); + std::vector<test_util::ProgressInfo> upload_progress_values; + uploader.UploadExistingFile( + kTestInitiateUploadResourceId, local_path, kTestMimeType, + UploadExistingFileOptions(), + test_util::CreateCopyResultCallback(&error, &upload_location, &entry), + base::Bind(&test_util::AppendProgressCallbackResult, + &upload_progress_values)); + base::RunLoop().RunUntilIdle(); + + // 2MB upload should not be split into multiple chunks. + EXPECT_EQ(1, mock_service.resume_upload_call_count()); + EXPECT_EQ(0, mock_service.multipart_upload_call_count()); + EXPECT_EQ(2 * 1024 * 1024, mock_service.received_bytes()); + EXPECT_EQ(HTTP_SUCCESS, error); + EXPECT_TRUE(upload_location.is_empty()); + ASSERT_TRUE(entry); + EXPECT_EQ(kTestDummyMd5, entry->md5_checksum()); + ASSERT_EQ(1U, upload_progress_values.size()); + EXPECT_EQ(test_util::ProgressInfo(2 * 1024 * 1024, 2 * 1024 * 1024), + upload_progress_values[0]); +} + +TEST_F(DriveUploaderTest, InitiateUploadFail) { + base::FilePath local_path; + std::string data; + ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize( + temp_dir_.path(), 2 * 1024 * 1024, &local_path, &data)); + + DriveApiErrorCode error = HTTP_SUCCESS; + GURL upload_location; + scoped_ptr<FileResource> entry; + + MockDriveServiceNoConnectionAtInitiate mock_service; + DriveUploader uploader(&mock_service, + base::ThreadTaskRunnerHandle::Get().get()); + uploader.UploadExistingFile( + kTestInitiateUploadResourceId, local_path, kTestMimeType, + UploadExistingFileOptions(), + test_util::CreateCopyResultCallback(&error, &upload_location, &entry), + google_apis::ProgressCallback()); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(DRIVE_NO_CONNECTION, error); + EXPECT_TRUE(upload_location.is_empty()); + EXPECT_FALSE(entry); +} + +TEST_F(DriveUploaderTest, MultipartUploadFail) { + base::FilePath local_path; + std::string data; + ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize(temp_dir_.path(), 512 * 1024, + &local_path, &data)); + + DriveApiErrorCode error = HTTP_SUCCESS; + GURL upload_location; + scoped_ptr<FileResource> entry; + + MockDriveServiceNoConnectionAtInitiate mock_service; + DriveUploader uploader(&mock_service, + base::ThreadTaskRunnerHandle::Get().get()); + uploader.UploadExistingFile( + kTestInitiateUploadResourceId, local_path, kTestMimeType, + UploadExistingFileOptions(), + test_util::CreateCopyResultCallback(&error, &upload_location, &entry), + google_apis::ProgressCallback()); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(DRIVE_NO_CONNECTION, error); + EXPECT_TRUE(upload_location.is_empty()); + EXPECT_FALSE(entry); +} + +TEST_F(DriveUploaderTest, InitiateUploadNoConflict) { + base::FilePath local_path; + std::string data; + ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize( + temp_dir_.path(), 512 * 1024, &local_path, &data)); + + DriveApiErrorCode error = DRIVE_OTHER_ERROR; + GURL upload_location; + scoped_ptr<FileResource> entry; + + MockDriveServiceWithUploadExpectation mock_service(local_path, data.size()); + DriveUploader uploader(&mock_service, + base::ThreadTaskRunnerHandle::Get().get()); + UploadExistingFileOptions options; + options.etag = kTestETag; + uploader.UploadExistingFile(kTestInitiateUploadResourceId, + local_path, + kTestMimeType, + options, + test_util::CreateCopyResultCallback( + &error, &upload_location, &entry), + google_apis::ProgressCallback()); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(HTTP_SUCCESS, error); + EXPECT_TRUE(upload_location.is_empty()); +} + +TEST_F(DriveUploaderTest, MultipartUploadConflict) { + base::FilePath local_path; + std::string data; + ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize( + temp_dir_.path(), 512 * 1024, &local_path, &data)); + const std::string kDestinationETag("destination_etag"); + + DriveApiErrorCode error = DRIVE_OTHER_ERROR; + GURL upload_location; + scoped_ptr<FileResource> entry; + + MockDriveServiceWithUploadExpectation mock_service(local_path, data.size()); + DriveUploader uploader(&mock_service, + base::ThreadTaskRunnerHandle::Get().get()); + UploadExistingFileOptions options; + options.etag = kDestinationETag; + uploader.UploadExistingFile(kTestInitiateUploadResourceId, + local_path, + kTestMimeType, + options, + test_util::CreateCopyResultCallback( + &error, &upload_location, &entry), + google_apis::ProgressCallback()); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(HTTP_CONFLICT, error); + EXPECT_TRUE(upload_location.is_empty()); +} + +TEST_F(DriveUploaderTest, InitiateUploadConflict) { + base::FilePath local_path; + std::string data; + ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize( + temp_dir_.path(), 2 * 1024 * 1024, &local_path, &data)); + const std::string kDestinationETag("destination_etag"); + + DriveApiErrorCode error = DRIVE_OTHER_ERROR; + GURL upload_location; + scoped_ptr<FileResource> entry; + + MockDriveServiceWithUploadExpectation mock_service(local_path, data.size()); + DriveUploader uploader(&mock_service, + base::ThreadTaskRunnerHandle::Get().get()); + UploadExistingFileOptions options; + options.etag = kDestinationETag; + uploader.UploadExistingFile( + kTestInitiateUploadResourceId, local_path, kTestMimeType, options, + test_util::CreateCopyResultCallback(&error, &upload_location, &entry), + google_apis::ProgressCallback()); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(HTTP_CONFLICT, error); + EXPECT_TRUE(upload_location.is_empty()); +} + +TEST_F(DriveUploaderTest, ResumeUploadFail) { + base::FilePath local_path; + std::string data; + ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize( + temp_dir_.path(), 2 * 1024 * 1024, &local_path, &data)); + + DriveApiErrorCode error = HTTP_SUCCESS; + GURL upload_location; + scoped_ptr<FileResource> entry; + + MockDriveServiceNoConnectionAtResume mock_service; + DriveUploader uploader(&mock_service, + base::ThreadTaskRunnerHandle::Get().get()); + uploader.UploadExistingFile( + kTestInitiateUploadResourceId, local_path, kTestMimeType, + UploadExistingFileOptions(), + test_util::CreateCopyResultCallback(&error, &upload_location, &entry), + google_apis::ProgressCallback()); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(DRIVE_NO_CONNECTION, error); + EXPECT_EQ(GURL(kTestUploadExistingFileURL), upload_location); +} + +TEST_F(DriveUploaderTest, GetUploadStatusFail) { + base::FilePath local_path; + std::string data; + ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize( + temp_dir_.path(), 2 * 1024 * 1024, &local_path, &data)); + + DriveApiErrorCode error = HTTP_SUCCESS; + GURL upload_location; + scoped_ptr<FileResource> entry; + + MockDriveServiceNoConnectionAtGetUploadStatus mock_service; + DriveUploader uploader(&mock_service, + base::ThreadTaskRunnerHandle::Get().get()); + uploader.ResumeUploadFile(GURL(kTestUploadExistingFileURL), + local_path, + kTestMimeType, + test_util::CreateCopyResultCallback( + &error, &upload_location, &entry), + google_apis::ProgressCallback()); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(DRIVE_NO_CONNECTION, error); + EXPECT_TRUE(upload_location.is_empty()); +} + +TEST_F(DriveUploaderTest, NonExistingSourceFile) { + DriveApiErrorCode error = DRIVE_OTHER_ERROR; + GURL upload_location; + scoped_ptr<FileResource> entry; + + DriveUploader uploader(NULL, // NULL, the service won't be used. + base::ThreadTaskRunnerHandle::Get().get()); + uploader.UploadExistingFile( + kTestInitiateUploadResourceId, + temp_dir_.path().AppendASCII("_this_path_should_not_exist_"), + kTestMimeType, UploadExistingFileOptions(), + test_util::CreateCopyResultCallback(&error, &upload_location, &entry), + google_apis::ProgressCallback()); + base::RunLoop().RunUntilIdle(); + + // Should return failure without doing any attempt to connect to the server. + EXPECT_EQ(HTTP_NOT_FOUND, error); + EXPECT_TRUE(upload_location.is_empty()); +} + +TEST_F(DriveUploaderTest, ResumeUpload) { + base::FilePath local_path; + std::string data; + ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize( + temp_dir_.path(), 1024 * 1024, &local_path, &data)); + + DriveApiErrorCode error = DRIVE_OTHER_ERROR; + GURL upload_location; + scoped_ptr<FileResource> entry; + + MockDriveServiceWithUploadExpectation mock_service(local_path, data.size()); + DriveUploader uploader(&mock_service, + base::ThreadTaskRunnerHandle::Get().get()); + // Emulate the situation that the only first part is successfully uploaded, + // but not the latter half. + mock_service.set_received_bytes(512 * 1024); + + std::vector<test_util::ProgressInfo> upload_progress_values; + uploader.ResumeUploadFile( + GURL(kTestUploadExistingFileURL), + local_path, + kTestMimeType, + test_util::CreateCopyResultCallback( + &error, &upload_location, &entry), + base::Bind(&test_util::AppendProgressCallbackResult, + &upload_progress_values)); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(1, mock_service.resume_upload_call_count()); + EXPECT_EQ(1024 * 1024, mock_service.received_bytes()); + EXPECT_EQ(HTTP_SUCCESS, error); + EXPECT_TRUE(upload_location.is_empty()); + ASSERT_TRUE(entry); + EXPECT_EQ(kTestDummyMd5, entry->md5_checksum()); + ASSERT_EQ(1U, upload_progress_values.size()); + EXPECT_EQ(test_util::ProgressInfo(1024 * 1024, 1024 * 1024), + upload_progress_values[0]); +} + +class MockDriveServiceForBatchProcessing : public DummyDriveService { + public: + struct UploadFileInfo { + enum { NEW_FILE, EXISTING_FILE } type; + std::string content_type; + uint64 content_length; + std::string parent_resource_id; + std::string resource_id; + std::string title; + base::FilePath local_file_path; + google_apis::FileResourceCallback callback; + google_apis::ProgressCallback progress_callback; + }; + + class BatchRequestConfigurator : public BatchRequestConfiguratorInterface { + public: + explicit BatchRequestConfigurator( + MockDriveServiceForBatchProcessing* service) + : service(service) {} + + 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 { + UploadFileInfo info; + info.type = UploadFileInfo::NEW_FILE; + info.content_type = content_type; + info.content_length = content_length; + info.parent_resource_id = parent_resource_id; + info.title = title; + info.local_file_path = local_file_path; + info.callback = callback; + info.progress_callback = progress_callback; + service->files.push_back(info); + return CancelCallback(); + } + + 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 { + UploadFileInfo info; + info.type = UploadFileInfo::EXISTING_FILE; + info.content_type = content_type; + info.content_length = content_length; + info.resource_id = resource_id; + info.local_file_path = local_file_path; + info.callback = callback; + info.progress_callback = progress_callback; + service->files.push_back(info); + return CancelCallback(); + } + + void Commit() override { + ASSERT_FALSE(service->committed); + service->committed = true; + for (const auto& file : service->files) { + SendMultipartUploadResult(HTTP_SUCCESS, file.content_length, + file.callback, file.progress_callback); + } + } + + private: + MockDriveServiceForBatchProcessing* service; + }; + + public: + scoped_ptr<BatchRequestConfiguratorInterface> StartBatchRequest() override { + committed = false; + return scoped_ptr<BatchRequestConfiguratorInterface>( + new BatchRequestConfigurator(this)); + } + + std::vector<UploadFileInfo> files; + bool committed; +}; + +TEST_F(DriveUploaderTest, BatchProcessing) { + // Preapre test file. + const size_t kTestFileSize = 1024 * 512; + base::FilePath local_path; + std::string data; + ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize( + temp_dir_.path(), kTestFileSize, &local_path, &data)); + + // Prepare test target. + MockDriveServiceForBatchProcessing service; + DriveUploader uploader(&service, base::ThreadTaskRunnerHandle::Get().get()); + + struct { + DriveApiErrorCode error; + GURL resume_url; + scoped_ptr<FileResource> file; + UploadCompletionCallback callback() { + return test_util::CreateCopyResultCallback(&error, &resume_url, &file); + } + } results[2]; + + uploader.StartBatchProcessing(); + uploader.UploadNewFile("parent_resource_id", local_path, "title", + kTestMimeType, UploadNewFileOptions(), + results[0].callback(), + google_apis::ProgressCallback()); + uploader.UploadExistingFile( + "resource_id", local_path, kTestMimeType, UploadExistingFileOptions(), + results[1].callback(), google_apis::ProgressCallback()); + uploader.StopBatchProcessing(); + base::RunLoop().RunUntilIdle(); + + ASSERT_EQ(2u, service.files.size()); + EXPECT_TRUE(service.committed); + + EXPECT_EQ(MockDriveServiceForBatchProcessing::UploadFileInfo::NEW_FILE, + service.files[0].type); + EXPECT_EQ(kTestMimeType, service.files[0].content_type); + EXPECT_EQ(kTestFileSize, service.files[0].content_length); + EXPECT_EQ("parent_resource_id", service.files[0].parent_resource_id); + EXPECT_EQ("", service.files[0].resource_id); + EXPECT_EQ("title", service.files[0].title); + EXPECT_EQ(local_path.value(), service.files[0].local_file_path.value()); + + EXPECT_EQ(MockDriveServiceForBatchProcessing::UploadFileInfo::EXISTING_FILE, + service.files[1].type); + EXPECT_EQ(kTestMimeType, service.files[1].content_type); + EXPECT_EQ(kTestFileSize, service.files[1].content_length); + EXPECT_EQ("", service.files[1].parent_resource_id); + EXPECT_EQ("resource_id", service.files[1].resource_id); + EXPECT_EQ("", service.files[1].title); + EXPECT_EQ(local_path.value(), service.files[1].local_file_path.value()); + + EXPECT_EQ(HTTP_SUCCESS, results[0].error); + EXPECT_TRUE(results[0].resume_url.is_empty()); + EXPECT_TRUE(results[0].file); + + EXPECT_EQ(HTTP_SUCCESS, results[1].error); + EXPECT_TRUE(results[1].resume_url.is_empty()); + EXPECT_TRUE(results[1].file); +} + +TEST_F(DriveUploaderTest, BatchProcessingWithError) { + // Prepare test target. + MockDriveServiceForBatchProcessing service; + DriveUploader uploader(&service, base::ThreadTaskRunnerHandle::Get().get()); + + struct { + DriveApiErrorCode error; + GURL resume_url; + scoped_ptr<FileResource> file; + UploadCompletionCallback callback() { + return test_util::CreateCopyResultCallback(&error, &resume_url, &file); + } + } results[2]; + + uploader.StartBatchProcessing(); + uploader.UploadNewFile("parent_resource_id", + base::FilePath(FILE_PATH_LITERAL("/path/non_exists")), + "title", kTestMimeType, UploadNewFileOptions(), + results[0].callback(), + google_apis::ProgressCallback()); + uploader.UploadExistingFile( + "resource_id", base::FilePath(FILE_PATH_LITERAL("/path/non_exists")), + kTestMimeType, UploadExistingFileOptions(), results[1].callback(), + google_apis::ProgressCallback()); + uploader.StopBatchProcessing(); + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(0u, service.files.size()); + EXPECT_TRUE(service.committed); + + EXPECT_EQ(HTTP_NOT_FOUND, results[0].error); + EXPECT_TRUE(results[0].resume_url.is_empty()); + EXPECT_FALSE(results[0].file); + + EXPECT_EQ(HTTP_NOT_FOUND, results[1].error); + EXPECT_TRUE(results[1].resume_url.is_empty()); + EXPECT_FALSE(results[1].file); +} +} // namespace drive |