diff options
author | hirono <hirono@chromium.org> | 2015-04-29 20:08:27 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-04-30 03:10:03 +0000 |
commit | 35d010b6e1351dfad5a7c8872946d2fef6ad8545 (patch) | |
tree | df75ee5fd63b8cd6bb94b4b2d1d4b2c83e0c70a3 /google_apis | |
parent | d19bbadb7bd8b35bb0d625ae467807f197545698 (diff) | |
download | chromium_src-35d010b6e1351dfad5a7c8872946d2fef6ad8545.zip chromium_src-35d010b6e1351dfad5a7c8872946d2fef6ad8545.tar.gz chromium_src-35d010b6e1351dfad5a7c8872946d2fef6ad8545.tar.bz2 |
Drive: Add response handling to BatchUploadRequst class.
BUG=451917
TEST=DriveApiRequestTest.BatchUploadRequest
Review URL: https://codereview.chromium.org/1081313002
Cr-Commit-Position: refs/heads/master@{#327646}
Diffstat (limited to 'google_apis')
-rw-r--r-- | google_apis/drive/drive_api_requests.cc | 218 | ||||
-rw-r--r-- | google_apis/drive/drive_api_requests.h | 24 | ||||
-rw-r--r-- | google_apis/drive/drive_api_requests_unittest.cc | 237 | ||||
-rw-r--r-- | google_apis/drive/drive_api_url_generator_unittest.cc | 5 |
4 files changed, 403 insertions, 81 deletions
diff --git a/google_apis/drive/drive_api_requests.cc b/google_apis/drive/drive_api_requests.cc index 8c16a03..d770f4b 100644 --- a/google_apis/drive/drive_api_requests.cc +++ b/google_apis/drive/drive_api_requests.cc @@ -9,6 +9,8 @@ #include "base/json/json_writer.h" #include "base/location.h" #include "base/sequenced_task_runner.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" #include "base/strings/stringprintf.h" #include "base/task_runner_util.h" #include "base/values.h" @@ -16,6 +18,7 @@ #include "google_apis/drive/request_util.h" #include "google_apis/drive/time_util.h" #include "net/base/url_util.h" +#include "net/http/http_response_headers.h" namespace google_apis { namespace drive { @@ -36,6 +39,12 @@ const char kBatchUploadHeader[] = "X-Goog-Upload-Protocol: batch"; // Content type of HTTP request. const char kHttpContentType[] = "application/http"; +// Break line in HTTP message. +const char kHttpBr[] = "\r\n"; + +// Mime type of multipart mixed. +const char kMultipartMixedMimeTypePrefix[] = "multipart/mixed; boundary="; + // Parses the JSON value to FileResource instance and runs |callback| on the // UI thread once parsing is done. // This is customized version of ParseJsonAndRun defined above to adapt the @@ -126,8 +135,174 @@ std::string CreateMultipartUploadMetadataJson( return json_string; } +// Splits |string| into lines by |kHttpBr|. +// Each line does not include |kHttpBr|. +void SplitIntoLines(const std::string& string, + std::vector<base::StringPiece>* output) { + const size_t br_size = std::string(kHttpBr).size(); + std::string::const_iterator it = string.begin(); + std::vector<base::StringPiece> lines; + while (true) { + const std::string::const_iterator next_pos = + std::search(it, string.end(), kHttpBr, kHttpBr + br_size); + lines.push_back(base::StringPiece(it, next_pos)); + if (next_pos == string.end()) + break; + it = next_pos + br_size; + } + output->swap(lines); +} + +// Remove transport padding (spaces and tabs at the end of line) from |piece|. +base::StringPiece TrimTransportPadding(const base::StringPiece& piece) { + size_t trim_size = 0; + while (trim_size < piece.size() && + (piece[piece.size() - 1 - trim_size] == ' ' || + piece[piece.size() - 1 - trim_size] == '\t')) { + ++trim_size; + } + return piece.substr(0, piece.size() - trim_size); +} + } // namespace +MultipartHttpResponse::MultipartHttpResponse() : code(HTTP_SUCCESS) { +} + +MultipartHttpResponse::~MultipartHttpResponse() { +} + +// The |response| must be multipart/mixed format that contains child HTTP +// response of drive batch request. +// https://www.ietf.org/rfc/rfc2046.txt +// +// It looks like: +// --Boundary +// Content-type: application/http +// +// HTTP/1.1 200 OK +// Header of child response +// +// Body of child response +// --Boundary +// Content-type: application/http +// +// HTTP/1.1 404 Not Found +// Header of child response +// +// Body of child response +// --Boundary-- +bool ParseMultipartResponse(const std::string& content_type, + const std::string& response, + std::vector<MultipartHttpResponse>* parts) { + if (response.empty()) + return false; + + base::StringPiece content_type_piece(content_type); + if (!content_type_piece.starts_with(kMultipartMixedMimeTypePrefix)) { + return false; + } + content_type_piece.remove_prefix( + base::StringPiece(kMultipartMixedMimeTypePrefix).size()); + + if (content_type_piece.empty()) + return false; + if (content_type_piece[0] == '"') { + if (content_type_piece.size() <= 2 || + content_type_piece[content_type_piece.size() - 1] != '"') { + return false; + } + content_type_piece = + content_type_piece.substr(1, content_type_piece.size() - 2); + } + + std::string boundary; + content_type_piece.CopyToString(&boundary); + const std::string header = "--" + boundary; + const std::string terminator = "--" + boundary + "--"; + + std::vector<base::StringPiece> lines; + SplitIntoLines(response, &lines); + + enum { + STATE_START, + STATE_PART_HEADER, + STATE_PART_HTTP_STATUS_LINE, + STATE_PART_HTTP_HEADER, + STATE_PART_HTTP_BODY + } state = STATE_START; + + const std::string kHttpStatusPrefix = "HTTP/1.1 "; + std::vector<MultipartHttpResponse> responses; + DriveApiErrorCode code = DRIVE_PARSE_ERROR; + std::string body; + for (const auto& line : lines) { + if (state == STATE_PART_HEADER && line.empty()) { + state = STATE_PART_HTTP_STATUS_LINE; + continue; + } + + if (state == STATE_PART_HTTP_STATUS_LINE) { + if (line.starts_with(kHttpStatusPrefix)) { + int int_code; + base::StringToInt( + line.substr(base::StringPiece(kHttpStatusPrefix).size()), + &int_code); + if (int_code > 0) + code = static_cast<DriveApiErrorCode>(int_code); + else + code = DRIVE_PARSE_ERROR; + } else { + code = DRIVE_PARSE_ERROR; + } + state = STATE_PART_HTTP_HEADER; + continue; + } + + if (state == STATE_PART_HTTP_HEADER && line.empty()) { + state = STATE_PART_HTTP_BODY; + body.clear(); + continue; + } + const base::StringPiece chopped_line = TrimTransportPadding(line); + const bool is_new_part = chopped_line == header; + const bool was_last_part = chopped_line == terminator; + if (is_new_part || was_last_part) { + switch (state) { + case STATE_START: + break; + case STATE_PART_HEADER: + case STATE_PART_HTTP_STATUS_LINE: + responses.push_back(MultipartHttpResponse()); + responses.back().code = DRIVE_PARSE_ERROR; + break; + case STATE_PART_HTTP_HEADER: + responses.push_back(MultipartHttpResponse()); + responses.back().code = code; + break; + case STATE_PART_HTTP_BODY: + // Drop the last kHttpBr. + if (!body.empty()) + body.resize(body.size() - 2); + responses.push_back(MultipartHttpResponse()); + responses.back().code = code; + responses.back().body.swap(body); + break; + } + if (is_new_part) + state = STATE_PART_HEADER; + if (was_last_part) + break; + } else if (state == STATE_PART_HTTP_BODY) { + line.AppendToString(&body); + body.append(kHttpBr); + } + } + + parts->swap(responses); + return true; +} + Property::Property() : visibility_(VISIBILITY_PRIVATE) { } @@ -1014,14 +1189,12 @@ void BatchUploadRequest::AddRequest(BatchableRequestBase* request) { DCHECK(GetChildEntry(request) == child_requests_.end()); DCHECK(!committed_); child_requests_.push_back(BatchUploadChildEntry(request)); - request->Prepare( - base::Bind(&BatchUploadRequest::OnChildRequestPrepared, - weak_ptr_factory_.GetWeakPtr(), - request)); + request->Prepare(base::Bind(&BatchUploadRequest::OnChildRequestPrepared, + weak_ptr_factory_.GetWeakPtr(), request)); } -void BatchUploadRequest::OnChildRequestPrepared( - RequestID request_id, DriveApiErrorCode result) { +void BatchUploadRequest::OnChildRequestPrepared(RequestID request_id, + DriveApiErrorCode result) { DCHECK(CalledOnValidThread()); auto const child = GetChildEntry(request_id); DCHECK(child != child_requests_.end()); @@ -1061,8 +1234,8 @@ void BatchUploadRequest::Cancel() { // Obtains corresponding child entry of |request_id|. Returns NULL if the // entry is not found. -std::vector<BatchUploadChildEntry>::iterator -BatchUploadRequest::GetChildEntry(RequestID request_id) { +std::vector<BatchUploadChildEntry>::iterator BatchUploadRequest::GetChildEntry( + RequestID request_id) { for (auto it = child_requests_.begin(); it != child_requests_.end(); ++it) { if (it->request == request_id) return it; @@ -1104,11 +1277,8 @@ void BatchUploadRequest::MayCompletePrepare() { parts.push_back(ContentTypeAndData()); parts.back().type = kHttpContentType; parts.back().data = base::StringPrintf( - kBatchUploadRequestFormat, - method.c_str(), - url.path().c_str(), - url_generator_.GetBatchUploadUrl().host().c_str(), - type.c_str(), + kBatchUploadRequestFormat, method.c_str(), url.path().c_str(), + url_generator_.GetBatchUploadUrl().host().c_str(), type.c_str(), data.c_str()); } @@ -1149,14 +1319,26 @@ void BatchUploadRequest::ProcessURLFetchResults(const net::URLFetcher* source) { return; } - for (auto& child : child_requests_) { - // TODO(hirono): Split the mutlipart result and return the correct code and - // body. - child.request->ProcessURLFetchResults(HTTP_SERVICE_UNAVAILABLE, ""); - // Request deletes itself after processing. + std::string content_type; + source->GetResponseHeaders()->EnumerateHeader( + /* need only first header */ NULL, "Content-Type", &content_type); + + std::vector<MultipartHttpResponse> parts; + if (!ParseMultipartResponse(content_type, response_writer()->data(), + &parts) || + child_requests_.size() != parts.size()) { + RunCallbackOnPrematureFailure(DRIVE_PARSE_ERROR); + sender_->RequestFinished(this); + return; + } + + for (size_t i = 0; i < parts.size(); ++i) { + child_requests_[i].request->ProcessURLFetchResults(parts[i].code, + parts[i].body); } child_requests_.clear(); + sender_->RequestFinished(this); } void BatchUploadRequest::RunCallbackOnPrematureFailure(DriveApiErrorCode code) { diff --git a/google_apis/drive/drive_api_requests.h b/google_apis/drive/drive_api_requests.h index 3aaee36..6017799 100644 --- a/google_apis/drive/drive_api_requests.h +++ b/google_apis/drive/drive_api_requests.h @@ -66,6 +66,21 @@ class Property { // List of properties for a single file or a directory. typedef std::vector<Property> Properties; +// Child response embedded in multipart parent response. +struct MultipartHttpResponse { + MultipartHttpResponse(); + ~MultipartHttpResponse(); + DriveApiErrorCode code; + std::string body; +}; + +// Splits multipart |response| into |parts|. Each part must be HTTP sub-response +// of drive batch request. |content_type| is a value of Content-Type response +// header. Returns true on succcess. +bool ParseMultipartResponse(const std::string& content_type, + const std::string& response, + std::vector<MultipartHttpResponse>* parts); + //============================ DriveApiPartialFieldRequest ==================== // This is base class of the Drive API related requests. All Drive API requests @@ -1135,9 +1150,8 @@ class BatchUploadRequest : public UrlFetchRequestBase { GURL GetURL() const override; net::URLFetcher::RequestType GetRequestType() const override; std::vector<std::string> GetExtraRequestHeaders() const override; - bool GetContentData( - std::string* upload_content_type, - std::string* upload_content) override; + bool GetContentData(std::string* upload_content_type, + std::string* upload_content) override; void ProcessURLFetchResults(const net::URLFetcher* source) override; void RunCallbackOnPrematureFailure(DriveApiErrorCode code) override; @@ -1145,8 +1159,8 @@ class BatchUploadRequest : public UrlFetchRequestBase { typedef void* RequestID; // Obtains corresponding child entry of |request_id|. Returns NULL if the // entry is not found. - std::vector<BatchUploadChildEntry>::iterator - GetChildEntry(RequestID request_id); + std::vector<BatchUploadChildEntry>::iterator GetChildEntry( + RequestID request_id); // Called after child requests' |Prepare| method. void OnChildRequestPrepared(RequestID request_id, DriveApiErrorCode result); diff --git a/google_apis/drive/drive_api_requests_unittest.cc b/google_apis/drive/drive_api_requests_unittest.cc index 52bf104..d83292e 100644 --- a/google_apis/drive/drive_api_requests_unittest.cc +++ b/google_apis/drive/drive_api_requests_unittest.cc @@ -410,6 +410,28 @@ class DriveApiRequestsTest : public testing::Test { scoped_ptr<net::test_server::BasicHttpResponse> response( new net::test_server::BasicHttpResponse); response->set_code(net::HTTP_OK); + response->set_content_type("multipart/mixed; boundary=BOUNDARY"); + response->set_content( + "--BOUNDARY\r\n" + "Content-Type: application/http\r\n" + "\r\n" + "HTTP/1.1 200 OK\r\n" + "Content-Type: application/json; charset=UTF-8\r\n" + "\r\n" + "{\r\n" + " \"kind\": \"drive#file\",\r\n" + " \"id\": \"file_id_1\"\r\n" + "}\r\n" + "\r\n" + "--BOUNDARY\r\n" + "Content-Type: application/http\r\n" + "\r\n" + "HTTP/1.1 503 Service Unavailable\r\n" + "Content-Type: application/json; charset=UTF-8\r\n" + "\r\n" + "{}\r\n" + "\r\n" + "--BOUNDARY--\r\n"); return response.Pass(); } @@ -1916,33 +1938,25 @@ TEST_F(DriveApiRequestsTest, BatchUploadRequest) { ASSERT_TRUE(test_util::WriteStringToFile(kTestFilePath, kTestContent)); // Create batch request. - drive::BatchUploadRequest* const request = new drive::BatchUploadRequest( - request_sender_.get(), *url_generator_); + drive::BatchUploadRequest* const request = + new drive::BatchUploadRequest(request_sender_.get(), *url_generator_); request->SetBoundaryForTesting("OUTERBOUNDARY"); request_sender_->StartRequestWithRetry(request); // Create child request. - DriveApiErrorCode error = DRIVE_OTHER_ERROR; - scoped_ptr<FileResource> file_resource; + DriveApiErrorCode errors[] = {DRIVE_OTHER_ERROR, DRIVE_OTHER_ERROR}; + scoped_ptr<FileResource> file_resources[2]; base::RunLoop run_loop[2]; for (int i = 0; i < 2; ++i) { const FileResourceCallback callback = test_util::CreateQuitCallback( &run_loop[i], - test_util::CreateCopyResultCallback(&error, &file_resource)); + test_util::CreateCopyResultCallback(&errors[i], &file_resources[i])); drive::MultipartUploadNewFileRequest* const child_request = new drive::MultipartUploadNewFileRequest( - request_sender_.get(), - base::StringPrintf("new file title %d", i), - "parent_resource_id", - kTestContentType, - kTestContent.size(), - base::Time(), - base::Time(), - kTestFilePath, - drive::Properties(), - *url_generator_, - callback, - ProgressCallback()); + request_sender_.get(), base::StringPrintf("new file title %d", i), + "parent_resource_id", kTestContentType, kTestContent.size(), + base::Time(), base::Time(), kTestFilePath, drive::Properties(), + *url_generator_, callback, ProgressCallback()); child_request->SetBoundaryForTesting("INNERBOUNDARY"); request->AddRequest(child_request); } @@ -1954,44 +1968,50 @@ TEST_F(DriveApiRequestsTest, BatchUploadRequest) { EXPECT_EQ("batch", http_request_.headers["X-Goog-Upload-Protocol"]); EXPECT_EQ("multipart/mixed; boundary=OUTERBOUNDARY", http_request_.headers["Content-Type"]); - EXPECT_EQ("--OUTERBOUNDARY\n" - "Content-Type: application/http\n" - "\n" - "POST /upload/drive/v2/files HTTP/1.1\n" - "Host: 127.0.0.1\n" - "X-Goog-Upload-Protocol: multipart\n" - "Content-Type: multipart/related; boundary=INNERBOUNDARY\n" - "\n" - "--INNERBOUNDARY\n" - "Content-Type: application/json\n" - "\n" - "{\"parents\":[{\"id\":\"parent_resource_id\"," - "\"kind\":\"drive#fileLink\"}],\"title\":\"new file title 0\"}\n" - "--INNERBOUNDARY\n" - "Content-Type: text/plain\n" - "\n" - "aaaaaaaaaa\n" - "--INNERBOUNDARY--\n" - "--OUTERBOUNDARY\n" - "Content-Type: application/http\n" - "\n" - "POST /upload/drive/v2/files HTTP/1.1\n" - "Host: 127.0.0.1\n" - "X-Goog-Upload-Protocol: multipart\n" - "Content-Type: multipart/related; boundary=INNERBOUNDARY\n" - "\n" - "--INNERBOUNDARY\n" - "Content-Type: application/json\n" - "\n" - "{\"parents\":[{\"id\":\"parent_resource_id\"," - "\"kind\":\"drive#fileLink\"}],\"title\":\"new file title 1\"}\n" - "--INNERBOUNDARY\n" - "Content-Type: text/plain\n" - "\n" - "aaaaaaaaaa\n" - "--INNERBOUNDARY--\n" - "--OUTERBOUNDARY--", - http_request_.content); + EXPECT_EQ( + "--OUTERBOUNDARY\n" + "Content-Type: application/http\n" + "\n" + "POST /upload/drive/v2/files HTTP/1.1\n" + "Host: 127.0.0.1\n" + "X-Goog-Upload-Protocol: multipart\n" + "Content-Type: multipart/related; boundary=INNERBOUNDARY\n" + "\n" + "--INNERBOUNDARY\n" + "Content-Type: application/json\n" + "\n" + "{\"parents\":[{\"id\":\"parent_resource_id\"," + "\"kind\":\"drive#fileLink\"}],\"title\":\"new file title 0\"}\n" + "--INNERBOUNDARY\n" + "Content-Type: text/plain\n" + "\n" + "aaaaaaaaaa\n" + "--INNERBOUNDARY--\n" + "--OUTERBOUNDARY\n" + "Content-Type: application/http\n" + "\n" + "POST /upload/drive/v2/files HTTP/1.1\n" + "Host: 127.0.0.1\n" + "X-Goog-Upload-Protocol: multipart\n" + "Content-Type: multipart/related; boundary=INNERBOUNDARY\n" + "\n" + "--INNERBOUNDARY\n" + "Content-Type: application/json\n" + "\n" + "{\"parents\":[{\"id\":\"parent_resource_id\"," + "\"kind\":\"drive#fileLink\"}],\"title\":\"new file title 1\"}\n" + "--INNERBOUNDARY\n" + "Content-Type: text/plain\n" + "\n" + "aaaaaaaaaa\n" + "--INNERBOUNDARY--\n" + "--OUTERBOUNDARY--", + http_request_.content); + EXPECT_EQ(HTTP_SUCCESS, errors[0]); + ASSERT_TRUE(file_resources[0]); + EXPECT_EQ("file_id_1", file_resources[0]->file_id()); + ASSERT_FALSE(file_resources[1]); + EXPECT_EQ(DRIVE_PARSE_ERROR, errors[1]); } TEST_F(DriveApiRequestsTest, EmptyBatchUploadRequest) { @@ -2000,4 +2020,111 @@ TEST_F(DriveApiRequestsTest, EmptyBatchUploadRequest) { EXPECT_DEATH(request->Commit(), ""); } +TEST(ParseMultipartResponseTest, Empty) { + std::vector<drive::MultipartHttpResponse> parts; + EXPECT_FALSE(drive::ParseMultipartResponse( + "multipart/mixed; boundary=BOUNDARY", "", &parts)); + EXPECT_FALSE(drive::ParseMultipartResponse("multipart/mixed; boundary=", + "CONTENT", &parts)); +} + +TEST(ParseMultipartResponseTest, Basic) { + std::vector<drive::MultipartHttpResponse> parts; + ASSERT_TRUE( + drive::ParseMultipartResponse("multipart/mixed; boundary=BOUNDARY", + "--BOUNDARY\r\n" + "Content-Type: application/http\r\n" + "\r\n" + "HTTP/1.1 200 OK\r\n" + "Header: value\r\n" + "\r\n" + "First line\r\n" + "Second line\r\n" + "--BOUNDARY\r\n" + "Content-Type: application/http\r\n" + "\r\n" + "HTTP/1.1 404 Not Found\r\n" + "Header: value\r\n" + "--BOUNDARY--", + &parts)); + ASSERT_EQ(2u, parts.size()); + EXPECT_EQ(HTTP_SUCCESS, parts[0].code); + EXPECT_EQ("First line\r\nSecond line", parts[0].body); + EXPECT_EQ(HTTP_NOT_FOUND, parts[1].code); + EXPECT_EQ("", parts[1].body); +} + +TEST(ParseMultipartResponseTest, InvalidStatusLine) { + std::vector<drive::MultipartHttpResponse> parts; + ASSERT_TRUE( + drive::ParseMultipartResponse("multipart/mixed; boundary=BOUNDARY", + "--BOUNDARY\r\n" + "Content-Type: application/http\r\n" + "\r\n" + "InvalidStatusLine 200 \r\n" + "Header: value\r\n" + "\r\n" + "{}\r\n" + "--BOUNDARY--", + &parts)); + ASSERT_EQ(1u, parts.size()); + EXPECT_EQ(DRIVE_PARSE_ERROR, parts[0].code); + EXPECT_EQ("{}", parts[0].body); +} + +TEST(ParseMultipartResponseTest, BoundaryInTheBodyAndPreamble) { + std::vector<drive::MultipartHttpResponse> parts; + ASSERT_TRUE( + drive::ParseMultipartResponse("multipart/mixed; boundary=BOUNDARY", + "BOUNDARY\r\n" + "PREUMBLE\r\n" + "--BOUNDARY\r\n" + "Content-Type: application/http\r\n" + "\r\n" + "HTTP/1.1 200 OK\r\n" + "Header: value\r\n" + "\r\n" + "{--BOUNDARY}\r\n" + "--BOUNDARY--", + &parts)); + ASSERT_EQ(1u, parts.size()); + EXPECT_EQ(HTTP_SUCCESS, parts[0].code); + EXPECT_EQ("{--BOUNDARY}", parts[0].body); +} + +TEST(ParseMultipartResponseTest, QuatedBoundary) { + std::vector<drive::MultipartHttpResponse> parts; + ASSERT_TRUE( + drive::ParseMultipartResponse("multipart/mixed; boundary=\"BOUNDARY\"", + "--BOUNDARY\r\n" + "Content-Type: application/http\r\n" + "\r\n" + "HTTP/1.1 200 OK\r\n" + "Header: value\r\n" + "\r\n" + "BODY\r\n" + "--BOUNDARY--", + &parts)); + ASSERT_EQ(1u, parts.size()); + EXPECT_EQ(HTTP_SUCCESS, parts[0].code); + EXPECT_EQ("BODY", parts[0].body); +} + +TEST(ParseMultipartResponseTest, BoundaryWithTransportPadding) { + std::vector<drive::MultipartHttpResponse> parts; + ASSERT_TRUE( + drive::ParseMultipartResponse("multipart/mixed; boundary=BOUNDARY", + "--BOUNDARY \t\r\n" + "Content-Type: application/http\r\n" + "\r\n" + "HTTP/1.1 200 OK\r\n" + "Header: value\r\n" + "\r\n" + "BODY\r\n" + "--BOUNDARY-- \t", + &parts)); + ASSERT_EQ(1u, parts.size()); + EXPECT_EQ(HTTP_SUCCESS, parts[0].code); + EXPECT_EQ("BODY", parts[0].body); +} } // namespace google_apis diff --git a/google_apis/drive/drive_api_url_generator_unittest.cc b/google_apis/drive/drive_api_url_generator_unittest.cc index 92e128e..18290b1 100644 --- a/google_apis/drive/drive_api_url_generator_unittest.cc +++ b/google_apis/drive/drive_api_url_generator_unittest.cc @@ -364,9 +364,8 @@ TEST_F(DriveApiUrlGeneratorTest, GenerateThumbnailUrl) { } TEST_F(DriveApiUrlGeneratorTest, BatchUploadUrl) { - EXPECT_EQ( - "https://www.example.com/upload/drive", - url_generator_.GetBatchUploadUrl().spec()); + EXPECT_EQ("https://www.example.com/upload/drive", + url_generator_.GetBatchUploadUrl().spec()); } } // namespace google_apis |