Home | History | Annotate | Download | only in google_apis
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <algorithm>
      6 #include <map>
      7 
      8 #include "base/bind.h"
      9 #include "base/file_util.h"
     10 #include "base/files/file_path.h"
     11 #include "base/files/scoped_temp_dir.h"
     12 #include "base/json/json_reader.h"
     13 #include "base/json/json_writer.h"
     14 #include "base/message_loop/message_loop.h"
     15 #include "base/run_loop.h"
     16 #include "base/strings/string_number_conversions.h"
     17 #include "base/strings/stringprintf.h"
     18 #include "base/values.h"
     19 #include "chrome/browser/google_apis/dummy_auth_service.h"
     20 #include "chrome/browser/google_apis/gdata_wapi_parser.h"
     21 #include "chrome/browser/google_apis/gdata_wapi_requests.h"
     22 #include "chrome/browser/google_apis/gdata_wapi_url_generator.h"
     23 #include "chrome/browser/google_apis/request_sender.h"
     24 #include "chrome/browser/google_apis/test_util.h"
     25 #include "net/base/escape.h"
     26 #include "net/test/embedded_test_server/embedded_test_server.h"
     27 #include "net/test/embedded_test_server/http_request.h"
     28 #include "net/test/embedded_test_server/http_response.h"
     29 #include "net/url_request/url_request_test_util.h"
     30 #include "testing/gtest/include/gtest/gtest.h"
     31 
     32 namespace google_apis {
     33 
     34 namespace {
     35 
     36 const char kTestUserAgent[] = "test-user-agent";
     37 const char kTestETag[] = "test_etag";
     38 const char kTestDownloadPathPrefix[] = "/download/";
     39 
     40 class GDataWapiRequestsTest : public testing::Test {
     41  public:
     42   GDataWapiRequestsTest()
     43       : test_server_(message_loop_.message_loop_proxy()) {
     44   }
     45 
     46   virtual void SetUp() OVERRIDE {
     47     request_context_getter_ = new net::TestURLRequestContextGetter(
     48         message_loop_.message_loop_proxy());
     49 
     50     request_sender_.reset(new RequestSender(new DummyAuthService,
     51                                             request_context_getter_.get(),
     52                                             message_loop_.message_loop_proxy(),
     53                                             kTestUserAgent));
     54 
     55     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     56 
     57     ASSERT_TRUE(test_server_.InitializeAndWaitUntilReady());
     58     test_server_.RegisterRequestHandler(
     59         base::Bind(&test_util::HandleDownloadFileRequest,
     60                    test_server_.base_url(),
     61                    base::Unretained(&http_request_)));
     62     test_server_.RegisterRequestHandler(
     63         base::Bind(&GDataWapiRequestsTest::HandleResourceFeedRequest,
     64                    base::Unretained(this)));
     65     test_server_.RegisterRequestHandler(
     66         base::Bind(&GDataWapiRequestsTest::HandleMetadataRequest,
     67                    base::Unretained(this)));
     68     test_server_.RegisterRequestHandler(
     69         base::Bind(&GDataWapiRequestsTest::HandleCreateSessionRequest,
     70                    base::Unretained(this)));
     71     test_server_.RegisterRequestHandler(
     72         base::Bind(&GDataWapiRequestsTest::HandleUploadRequest,
     73                    base::Unretained(this)));
     74     test_server_.RegisterRequestHandler(
     75         base::Bind(&GDataWapiRequestsTest::HandleDownloadRequest,
     76                    base::Unretained(this)));
     77 
     78     GURL test_base_url = test_util::GetBaseUrlForTesting(test_server_.port());
     79     url_generator_.reset(new GDataWapiUrlGenerator(
     80         test_base_url, test_base_url.Resolve(kTestDownloadPathPrefix)));
     81 
     82     received_bytes_ = 0;
     83     content_length_ = 0;
     84   }
     85 
     86  protected:
     87   // Handles a request for fetching a resource feed.
     88   scoped_ptr<net::test_server::HttpResponse> HandleResourceFeedRequest(
     89       const net::test_server::HttpRequest& request) {
     90     http_request_ = request;
     91 
     92     const GURL absolute_url = test_server_.GetURL(request.relative_url);
     93     std::string remaining_path;
     94     if (absolute_url.path() == "/feeds/default/private/full" &&
     95         request.method == net::test_server::METHOD_POST) {
     96       // This is a request for copying a document.
     97       // TODO(satorux): we should generate valid JSON data for the newly
     98       // copied document but for now, just return "file_entry.json"
     99       scoped_ptr<net::test_server::BasicHttpResponse> result(
    100           test_util::CreateHttpResponseFromFile(
    101               test_util::GetTestFilePath("gdata/file_entry.json")));
    102       return result.PassAs<net::test_server::HttpResponse>();
    103     }
    104 
    105     if (!test_util::RemovePrefix(absolute_url.path(),
    106                                  "/feeds/default/private/full",
    107                                  &remaining_path)) {
    108       return scoped_ptr<net::test_server::HttpResponse>();
    109     }
    110 
    111     if (remaining_path.empty()) {
    112       // Process the default feed.
    113       scoped_ptr<net::test_server::BasicHttpResponse> result(
    114           test_util::CreateHttpResponseFromFile(
    115               test_util::GetTestFilePath("gdata/root_feed.json")));
    116       return result.PassAs<net::test_server::HttpResponse>();
    117     } else {
    118       // Process a feed for a single resource ID.
    119       const std::string resource_id = net::UnescapeURLComponent(
    120           remaining_path.substr(1), net::UnescapeRule::URL_SPECIAL_CHARS);
    121       if (resource_id == "file:2_file_resource_id") {
    122         scoped_ptr<net::test_server::BasicHttpResponse> result(
    123             test_util::CreateHttpResponseFromFile(
    124                 test_util::GetTestFilePath("gdata/file_entry.json")));
    125         return result.PassAs<net::test_server::HttpResponse>();
    126       } else if (resource_id == "folder:root/contents" &&
    127                  request.method == net::test_server::METHOD_POST) {
    128         // This is a request for creating a directory in the root directory.
    129         // TODO(satorux): we should generate valid JSON data for the newly
    130         // created directory but for now, just return "directory_entry.json"
    131         scoped_ptr<net::test_server::BasicHttpResponse> result(
    132             test_util::CreateHttpResponseFromFile(
    133                 test_util::GetTestFilePath(
    134                     "gdata/directory_entry.json")));
    135         return result.PassAs<net::test_server::HttpResponse>();
    136       } else if (resource_id ==
    137                  "folder:root/contents/file:2_file_resource_id" &&
    138                  request.method == net::test_server::METHOD_DELETE) {
    139         // This is a request for deleting a file from the root directory.
    140         // TODO(satorux): Investigate what's returned from the server, and
    141         // copy it. For now, just return a random file, as the contents don't
    142         // matter.
    143         scoped_ptr<net::test_server::BasicHttpResponse> result(
    144             test_util::CreateHttpResponseFromFile(
    145                 test_util::GetTestFilePath("gdata/testfile.txt")));
    146         return result.PassAs<net::test_server::HttpResponse>();
    147       } else if (resource_id == "invalid_resource_id") {
    148         // Check if this is an authorization request for an app.
    149         // This emulates to return invalid formatted result from the server.
    150         if (request.method == net::test_server::METHOD_PUT &&
    151             request.content.find("<docs:authorizedApp>") != std::string::npos) {
    152           scoped_ptr<net::test_server::BasicHttpResponse> result(
    153               test_util::CreateHttpResponseFromFile(
    154                   test_util::GetTestFilePath("gdata/testfile.txt")));
    155           return result.PassAs<net::test_server::HttpResponse>();
    156         }
    157       }
    158     }
    159 
    160     return scoped_ptr<net::test_server::HttpResponse>();
    161   }
    162 
    163   // Handles a request for fetching a metadata feed.
    164   scoped_ptr<net::test_server::HttpResponse> HandleMetadataRequest(
    165       const net::test_server::HttpRequest& request) {
    166     http_request_ = request;
    167 
    168     const GURL absolute_url = test_server_.GetURL(request.relative_url);
    169     if (absolute_url.path() != "/feeds/metadata/default")
    170       return scoped_ptr<net::test_server::HttpResponse>();
    171 
    172     scoped_ptr<net::test_server::BasicHttpResponse> result(
    173         test_util::CreateHttpResponseFromFile(
    174             test_util::GetTestFilePath(
    175                 "gdata/account_metadata.json")));
    176     if (absolute_url.query().find("include-installed-apps=true") ==
    177         string::npos) {
    178       // Exclude the list of installed apps.
    179       scoped_ptr<base::Value> parsed_content(
    180           base::JSONReader::Read(result->content(), base::JSON_PARSE_RFC));
    181       CHECK(parsed_content);
    182 
    183       // Remove the install apps node.
    184       base::DictionaryValue* dictionary_value;
    185       CHECK(parsed_content->GetAsDictionary(&dictionary_value));
    186       dictionary_value->Remove("entry.docs$installedApp", NULL);
    187 
    188       // Write back it as the content of the result.
    189       std::string content;
    190       base::JSONWriter::Write(parsed_content.get(), &content);
    191       result->set_content(content);
    192     }
    193 
    194     return result.PassAs<net::test_server::HttpResponse>();
    195   }
    196 
    197   // Handles a request for creating a session for uploading.
    198   scoped_ptr<net::test_server::HttpResponse> HandleCreateSessionRequest(
    199       const net::test_server::HttpRequest& request) {
    200     http_request_ = request;
    201 
    202     const GURL absolute_url = test_server_.GetURL(request.relative_url);
    203     if (StartsWithASCII(absolute_url.path(),
    204                         "/feeds/upload/create-session/default/private/full",
    205                         true)) {  // case sensitive
    206       // This is an initiating upload URL.
    207       scoped_ptr<net::test_server::BasicHttpResponse> http_response(
    208           new net::test_server::BasicHttpResponse);
    209 
    210       // Check an ETag.
    211       std::map<std::string, std::string>::const_iterator found =
    212           request.headers.find("If-Match");
    213       if (found != request.headers.end() &&
    214           found->second != "*" &&
    215           found->second != kTestETag) {
    216         http_response->set_code(net::HTTP_PRECONDITION_FAILED);
    217         return http_response.PassAs<net::test_server::HttpResponse>();
    218       }
    219 
    220       // Check if the X-Upload-Content-Length is present. If yes, store the
    221       // length of the file.
    222       found = request.headers.find("X-Upload-Content-Length");
    223       if (found == request.headers.end() ||
    224           !base::StringToInt64(found->second, &content_length_)) {
    225         return scoped_ptr<net::test_server::HttpResponse>();
    226       }
    227       received_bytes_ = 0;
    228 
    229       http_response->set_code(net::HTTP_OK);
    230       GURL upload_url;
    231       // POST is used for a new file, and PUT is used for an existing file.
    232       if (request.method == net::test_server::METHOD_POST) {
    233         upload_url = test_server_.GetURL("/upload_new_file");
    234       } else if (request.method == net::test_server::METHOD_PUT) {
    235         upload_url = test_server_.GetURL("/upload_existing_file");
    236       } else {
    237         return scoped_ptr<net::test_server::HttpResponse>();
    238       }
    239       http_response->AddCustomHeader("Location", upload_url.spec());
    240       return http_response.PassAs<net::test_server::HttpResponse>();
    241     }
    242 
    243     return scoped_ptr<net::test_server::HttpResponse>();
    244   }
    245 
    246   // Handles a request for uploading content.
    247   scoped_ptr<net::test_server::HttpResponse> HandleUploadRequest(
    248       const net::test_server::HttpRequest& request) {
    249     http_request_ = request;
    250 
    251     const GURL absolute_url = test_server_.GetURL(request.relative_url);
    252     if (absolute_url.path() != "/upload_new_file" &&
    253         absolute_url.path() != "/upload_existing_file") {
    254       return scoped_ptr<net::test_server::HttpResponse>();
    255     }
    256 
    257     // TODO(satorux): We should create a correct JSON data for the uploaded
    258     // file, but for now, just return file_entry.json.
    259     scoped_ptr<net::test_server::BasicHttpResponse> response =
    260         test_util::CreateHttpResponseFromFile(
    261             test_util::GetTestFilePath("gdata/file_entry.json"));
    262     // response.code() is set to SUCCESS. Change it to CREATED if it's a new
    263     // file.
    264     if (absolute_url.path() == "/upload_new_file")
    265       response->set_code(net::HTTP_CREATED);
    266 
    267     // Check if the Content-Range header is present. This must be present if
    268     // the request body is not empty.
    269     if (!request.content.empty()) {
    270       std::map<std::string, std::string>::const_iterator iter =
    271           request.headers.find("Content-Range");
    272       if (iter == request.headers.end())
    273         return scoped_ptr<net::test_server::HttpResponse>();
    274       int64 length = 0;
    275       int64 start_position = 0;
    276       int64 end_position = 0;
    277       if (!test_util::ParseContentRangeHeader(iter->second,
    278                                               &start_position,
    279                                               &end_position,
    280                                               &length)) {
    281         return scoped_ptr<net::test_server::HttpResponse>();
    282       }
    283       EXPECT_EQ(start_position, received_bytes_);
    284       EXPECT_EQ(length, content_length_);
    285       // end_position is inclusive, but so +1 to change the range to byte size.
    286       received_bytes_ = end_position + 1;
    287     }
    288 
    289     // Add Range header to the response, based on the values of
    290     // Content-Range header in the request.
    291     // The header is annotated only when at least one byte is received.
    292     if (received_bytes_ > 0) {
    293       response->AddCustomHeader(
    294           "Range",
    295           "bytes=0-" + base::Int64ToString(received_bytes_ - 1));
    296     }
    297 
    298     // Change the code to RESUME_INCOMPLETE if upload is not complete.
    299     if (received_bytes_ < content_length_)
    300       response->set_code(static_cast<net::HttpStatusCode>(308));
    301 
    302     return response.PassAs<net::test_server::HttpResponse>();
    303   }
    304 
    305   // Handles a request for downloading a file.
    306   scoped_ptr<net::test_server::HttpResponse> HandleDownloadRequest(
    307       const net::test_server::HttpRequest& request) {
    308     http_request_ = request;
    309 
    310     const GURL absolute_url = test_server_.GetURL(request.relative_url);
    311     std::string id;
    312     if (!test_util::RemovePrefix(absolute_url.path(),
    313                                  kTestDownloadPathPrefix,
    314                                  &id)) {
    315       return scoped_ptr<net::test_server::HttpResponse>();
    316     }
    317 
    318     // For testing, returns a text with |id| repeated 3 times.
    319     scoped_ptr<net::test_server::BasicHttpResponse> response(
    320         new net::test_server::BasicHttpResponse);
    321     response->set_code(net::HTTP_OK);
    322     response->set_content(id + id + id);
    323     response->set_content_type("text/plain");
    324     return response.PassAs<net::test_server::HttpResponse>();
    325   }
    326 
    327   base::MessageLoopForIO message_loop_;  // Test server needs IO thread.
    328   net::test_server::EmbeddedTestServer test_server_;
    329   scoped_ptr<RequestSender> request_sender_;
    330   scoped_ptr<GDataWapiUrlGenerator> url_generator_;
    331   scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
    332   base::ScopedTempDir temp_dir_;
    333 
    334   // These fields are used to keep the current upload state during a
    335   // test case. These values are updated by the request from
    336   // ResumeUploadRequest, and used to construct the response for
    337   // both ResumeUploadRequest and GetUploadStatusRequest, to emulate
    338   // the WAPI server.
    339   int64 received_bytes_;
    340   int64 content_length_;
    341 
    342   // The incoming HTTP request is saved so tests can verify the request
    343   // parameters like HTTP method (ex. some requests should use DELETE
    344   // instead of GET).
    345   net::test_server::HttpRequest http_request_;
    346 };
    347 
    348 }  // namespace
    349 
    350 TEST_F(GDataWapiRequestsTest, GetResourceListRequest_DefaultFeed) {
    351   GDataErrorCode result_code = GDATA_OTHER_ERROR;
    352   scoped_ptr<ResourceList> result_data;
    353 
    354   {
    355     base::RunLoop run_loop;
    356     GetResourceListRequest* request = new GetResourceListRequest(
    357         request_sender_.get(),
    358         *url_generator_,
    359         GURL(),         // Pass an empty URL to use the default feed
    360         0,              // start changestamp
    361         std::string(),  // search string
    362         std::string(),  // directory resource ID
    363         test_util::CreateQuitCallback(
    364             &run_loop,
    365             test_util::CreateCopyResultCallback(&result_code, &result_data)));
    366     request_sender_->StartRequestWithRetry(request);
    367     run_loop.Run();
    368   }
    369 
    370   EXPECT_EQ(HTTP_SUCCESS, result_code);
    371   EXPECT_EQ(net::test_server::METHOD_GET, http_request_.method);
    372   EXPECT_EQ("/feeds/default/private/full?v=3&alt=json&showroot=true&"
    373             "showfolders=true&include-shared=true&max-results=500",
    374             http_request_.relative_url);
    375 
    376   // Sanity check of the result.
    377   scoped_ptr<ResourceList> expected(
    378       ResourceList::ExtractAndParse(
    379           *test_util::LoadJSONFile("gdata/root_feed.json")));
    380   ASSERT_TRUE(result_data);
    381   EXPECT_EQ(expected->title(), result_data->title());
    382 }
    383 
    384 TEST_F(GDataWapiRequestsTest, GetResourceListRequest_ValidFeed) {
    385   GDataErrorCode result_code = GDATA_OTHER_ERROR;
    386   scoped_ptr<ResourceList> result_data;
    387 
    388   {
    389     base::RunLoop run_loop;
    390     GetResourceListRequest* request = new GetResourceListRequest(
    391         request_sender_.get(),
    392         *url_generator_,
    393         test_server_.GetURL("/files/gdata/root_feed.json"),
    394         0,              // start changestamp
    395         std::string(),  // search string
    396         std::string(),  // directory resource ID
    397         test_util::CreateQuitCallback(
    398             &run_loop,
    399             test_util::CreateCopyResultCallback(&result_code, &result_data)));
    400     request_sender_->StartRequestWithRetry(request);
    401     run_loop.Run();
    402   }
    403 
    404   EXPECT_EQ(HTTP_SUCCESS, result_code);
    405   EXPECT_EQ(net::test_server::METHOD_GET, http_request_.method);
    406   EXPECT_EQ("/files/gdata/root_feed.json?v=3&alt=json&showroot=true&"
    407             "showfolders=true&include-shared=true&max-results=500",
    408             http_request_.relative_url);
    409 
    410   scoped_ptr<ResourceList> expected(
    411       ResourceList::ExtractAndParse(
    412           *test_util::LoadJSONFile("gdata/root_feed.json")));
    413   ASSERT_TRUE(result_data);
    414   EXPECT_EQ(expected->title(), result_data->title());
    415 }
    416 
    417 TEST_F(GDataWapiRequestsTest, GetResourceListRequest_InvalidFeed) {
    418   // testfile.txt exists but the response is not JSON, so it should
    419   // emit a parse error instead.
    420   GDataErrorCode result_code = GDATA_OTHER_ERROR;
    421   scoped_ptr<ResourceList> result_data;
    422 
    423   {
    424     base::RunLoop run_loop;
    425     GetResourceListRequest* request = new GetResourceListRequest(
    426         request_sender_.get(),
    427         *url_generator_,
    428         test_server_.GetURL("/files/gdata/testfile.txt"),
    429         0,              // start changestamp
    430         std::string(),  // search string
    431         std::string(),  // directory resource ID
    432         test_util::CreateQuitCallback(
    433             &run_loop,
    434             test_util::CreateCopyResultCallback(&result_code, &result_data)));
    435     request_sender_->StartRequestWithRetry(request);
    436     run_loop.Run();
    437   }
    438 
    439   EXPECT_EQ(GDATA_PARSE_ERROR, result_code);
    440   EXPECT_EQ(net::test_server::METHOD_GET, http_request_.method);
    441   EXPECT_EQ("/files/gdata/testfile.txt?v=3&alt=json&showroot=true&"
    442             "showfolders=true&include-shared=true&max-results=500",
    443             http_request_.relative_url);
    444   EXPECT_FALSE(result_data);
    445 }
    446 
    447 TEST_F(GDataWapiRequestsTest, SearchByTitleRequest) {
    448   GDataErrorCode result_code = GDATA_OTHER_ERROR;
    449   scoped_ptr<ResourceList> result_data;
    450 
    451   {
    452     base::RunLoop run_loop;
    453     SearchByTitleRequest* request = new SearchByTitleRequest(
    454         request_sender_.get(),
    455         *url_generator_,
    456         "search-title",
    457         std::string(),  // directory resource id
    458         test_util::CreateQuitCallback(
    459             &run_loop,
    460             test_util::CreateCopyResultCallback(&result_code, &result_data)));
    461     request_sender_->StartRequestWithRetry(request);
    462     run_loop.Run();
    463   }
    464 
    465   EXPECT_EQ(HTTP_SUCCESS, result_code);
    466   EXPECT_EQ(net::test_server::METHOD_GET, http_request_.method);
    467   EXPECT_EQ("/feeds/default/private/full?v=3&alt=json&showroot=true&"
    468             "showfolders=true&include-shared=true&max-results=500"
    469             "&title=search-title&title-exact=true",
    470             http_request_.relative_url);
    471   EXPECT_TRUE(result_data);
    472 }
    473 
    474 TEST_F(GDataWapiRequestsTest, GetResourceEntryRequest_ValidResourceId) {
    475   GDataErrorCode result_code = GDATA_OTHER_ERROR;
    476   scoped_ptr<base::Value> result_data;
    477 
    478   {
    479     base::RunLoop run_loop;
    480     GetResourceEntryRequest* request = new GetResourceEntryRequest(
    481         request_sender_.get(),
    482         *url_generator_,
    483         "file:2_file_resource_id",  // resource ID
    484         GURL(),  // embed origin
    485         test_util::CreateQuitCallback(
    486             &run_loop,
    487             test_util::CreateCopyResultCallback(&result_code, &result_data)));
    488     request_sender_->StartRequestWithRetry(request);
    489     run_loop.Run();
    490   }
    491 
    492   EXPECT_EQ(HTTP_SUCCESS, result_code);
    493   EXPECT_EQ(net::test_server::METHOD_GET, http_request_.method);
    494   EXPECT_EQ("/feeds/default/private/full/file%3A2_file_resource_id"
    495             "?v=3&alt=json&showroot=true",
    496             http_request_.relative_url);
    497   EXPECT_TRUE(test_util::VerifyJsonData(
    498       test_util::GetTestFilePath("gdata/file_entry.json"),
    499       result_data.get()));
    500 }
    501 
    502 TEST_F(GDataWapiRequestsTest, GetResourceEntryRequest_InvalidResourceId) {
    503   GDataErrorCode result_code = GDATA_OTHER_ERROR;
    504   scoped_ptr<base::Value> result_data;
    505 
    506   {
    507     base::RunLoop run_loop;
    508     GetResourceEntryRequest* request = new GetResourceEntryRequest(
    509         request_sender_.get(),
    510         *url_generator_,
    511         "<invalid>",  // resource ID
    512         GURL(),  // embed origin
    513         test_util::CreateQuitCallback(
    514             &run_loop,
    515             test_util::CreateCopyResultCallback(&result_code, &result_data)));
    516     request_sender_->StartRequestWithRetry(request);
    517     run_loop.Run();
    518   }
    519 
    520   EXPECT_EQ(HTTP_NOT_FOUND, result_code);
    521   EXPECT_EQ(net::test_server::METHOD_GET, http_request_.method);
    522   EXPECT_EQ("/feeds/default/private/full/%3Cinvalid%3E?v=3&alt=json"
    523             "&showroot=true",
    524             http_request_.relative_url);
    525   ASSERT_FALSE(result_data);
    526 }
    527 
    528 TEST_F(GDataWapiRequestsTest, GetAccountMetadataRequest) {
    529   GDataErrorCode result_code = GDATA_OTHER_ERROR;
    530   scoped_ptr<AccountMetadata> result_data;
    531 
    532   {
    533     base::RunLoop run_loop;
    534     GetAccountMetadataRequest* request = new GetAccountMetadataRequest(
    535         request_sender_.get(),
    536         *url_generator_,
    537         test_util::CreateQuitCallback(
    538             &run_loop,
    539             test_util::CreateCopyResultCallback(&result_code, &result_data)),
    540         true);  // Include installed apps.
    541     request_sender_->StartRequestWithRetry(request);
    542     run_loop.Run();
    543   }
    544 
    545   EXPECT_EQ(HTTP_SUCCESS, result_code);
    546   EXPECT_EQ(net::test_server::METHOD_GET, http_request_.method);
    547   EXPECT_EQ("/feeds/metadata/default?v=3&alt=json&showroot=true"
    548             "&include-installed-apps=true",
    549             http_request_.relative_url);
    550 
    551   scoped_ptr<AccountMetadata> expected(
    552       AccountMetadata::CreateFrom(
    553           *test_util::LoadJSONFile("gdata/account_metadata.json")));
    554 
    555   ASSERT_TRUE(result_data.get());
    556   EXPECT_EQ(expected->largest_changestamp(),
    557             result_data->largest_changestamp());
    558   EXPECT_EQ(expected->quota_bytes_total(),
    559             result_data->quota_bytes_total());
    560   EXPECT_EQ(expected->quota_bytes_used(),
    561             result_data->quota_bytes_used());
    562 
    563   // Sanity check for installed apps.
    564   EXPECT_EQ(expected->installed_apps().size(),
    565             result_data->installed_apps().size());
    566 }
    567 
    568 TEST_F(GDataWapiRequestsTest,
    569        GetAccountMetadataRequestWithoutInstalledApps) {
    570   GDataErrorCode result_code = GDATA_OTHER_ERROR;
    571   scoped_ptr<AccountMetadata> result_data;
    572 
    573   {
    574     base::RunLoop run_loop;
    575     GetAccountMetadataRequest* request = new GetAccountMetadataRequest(
    576         request_sender_.get(),
    577         *url_generator_,
    578         test_util::CreateQuitCallback(
    579             &run_loop,
    580             test_util::CreateCopyResultCallback(&result_code, &result_data)),
    581         false);  // Exclude installed apps.
    582     request_sender_->StartRequestWithRetry(request);
    583     run_loop.Run();
    584   }
    585 
    586   EXPECT_EQ(HTTP_SUCCESS, result_code);
    587   EXPECT_EQ(net::test_server::METHOD_GET, http_request_.method);
    588   EXPECT_EQ("/feeds/metadata/default?v=3&alt=json&showroot=true",
    589             http_request_.relative_url);
    590 
    591   scoped_ptr<AccountMetadata> expected(
    592       AccountMetadata::CreateFrom(
    593           *test_util::LoadJSONFile("gdata/account_metadata.json")));
    594 
    595   ASSERT_TRUE(result_data.get());
    596   EXPECT_EQ(expected->largest_changestamp(),
    597             result_data->largest_changestamp());
    598   EXPECT_EQ(expected->quota_bytes_total(),
    599             result_data->quota_bytes_total());
    600   EXPECT_EQ(expected->quota_bytes_used(),
    601             result_data->quota_bytes_used());
    602 
    603   // Installed apps shouldn't be included.
    604   EXPECT_EQ(0U, result_data->installed_apps().size());
    605 }
    606 
    607 TEST_F(GDataWapiRequestsTest, DeleteResourceRequest) {
    608   GDataErrorCode result_code = GDATA_OTHER_ERROR;
    609 
    610   {
    611     base::RunLoop run_loop;
    612     DeleteResourceRequest* request = new DeleteResourceRequest(
    613         request_sender_.get(),
    614         *url_generator_,
    615         test_util::CreateQuitCallback(
    616             &run_loop,
    617             test_util::CreateCopyResultCallback(&result_code)),
    618         "file:2_file_resource_id",
    619         std::string());
    620 
    621     request_sender_->StartRequestWithRetry(request);
    622     run_loop.Run();
    623   }
    624 
    625   EXPECT_EQ(HTTP_SUCCESS, result_code);
    626   EXPECT_EQ(net::test_server::METHOD_DELETE, http_request_.method);
    627   EXPECT_EQ(
    628       "/feeds/default/private/full/file%3A2_file_resource_id?v=3&alt=json"
    629       "&showroot=true",
    630       http_request_.relative_url);
    631   EXPECT_EQ("*", http_request_.headers["If-Match"]);
    632 }
    633 
    634 TEST_F(GDataWapiRequestsTest, DeleteResourceRequestWithETag) {
    635   GDataErrorCode result_code = GDATA_OTHER_ERROR;
    636 
    637   {
    638     base::RunLoop run_loop;
    639     DeleteResourceRequest* request = new DeleteResourceRequest(
    640         request_sender_.get(),
    641         *url_generator_,
    642         test_util::CreateQuitCallback(
    643             &run_loop,
    644             test_util::CreateCopyResultCallback(&result_code)),
    645         "file:2_file_resource_id",
    646         "etag");
    647 
    648     request_sender_->StartRequestWithRetry(request);
    649     run_loop.Run();
    650   }
    651 
    652   EXPECT_EQ(HTTP_SUCCESS, result_code);
    653   EXPECT_EQ(net::test_server::METHOD_DELETE, http_request_.method);
    654   EXPECT_EQ(
    655       "/feeds/default/private/full/file%3A2_file_resource_id?v=3&alt=json"
    656       "&showroot=true",
    657       http_request_.relative_url);
    658   EXPECT_EQ("etag", http_request_.headers["If-Match"]);
    659 }
    660 
    661 TEST_F(GDataWapiRequestsTest, CreateDirectoryRequest) {
    662   GDataErrorCode result_code = GDATA_OTHER_ERROR;
    663   scoped_ptr<base::Value> result_data;
    664 
    665   // Create "new directory" in the root directory.
    666   {
    667     base::RunLoop run_loop;
    668     CreateDirectoryRequest* request = new CreateDirectoryRequest(
    669         request_sender_.get(),
    670         *url_generator_,
    671         test_util::CreateQuitCallback(
    672             &run_loop,
    673             test_util::CreateCopyResultCallback(&result_code, &result_data)),
    674         "folder:root",
    675         "new directory");
    676 
    677     request_sender_->StartRequestWithRetry(request);
    678     run_loop.Run();
    679   }
    680 
    681   EXPECT_EQ(HTTP_SUCCESS, result_code);
    682   EXPECT_EQ(net::test_server::METHOD_POST, http_request_.method);
    683   EXPECT_EQ("/feeds/default/private/full/folder%3Aroot/contents?v=3&alt=json"
    684             "&showroot=true",
    685             http_request_.relative_url);
    686   EXPECT_EQ("application/atom+xml", http_request_.headers["Content-Type"]);
    687 
    688   EXPECT_TRUE(http_request_.has_content);
    689   EXPECT_EQ("<?xml version=\"1.0\"?>\n"
    690             "<entry xmlns=\"http://www.w3.org/2005/Atom\">\n"
    691             " <category scheme=\"http://schemas.google.com/g/2005#kind\" "
    692             "term=\"http://schemas.google.com/docs/2007#folder\"/>\n"
    693             " <title>new directory</title>\n"
    694             "</entry>\n",
    695             http_request_.content);
    696 }
    697 
    698 TEST_F(GDataWapiRequestsTest, CopyHostedDocumentRequest) {
    699   GDataErrorCode result_code = GDATA_OTHER_ERROR;
    700   scoped_ptr<base::Value> result_data;
    701 
    702   // Copy a document with a new name "New Document".
    703   {
    704     base::RunLoop run_loop;
    705     CopyHostedDocumentRequest* request = new CopyHostedDocumentRequest(
    706         request_sender_.get(),
    707         *url_generator_,
    708         test_util::CreateQuitCallback(
    709             &run_loop,
    710             test_util::CreateCopyResultCallback(&result_code, &result_data)),
    711         "document:5_document_resource_id",  // source resource ID
    712         "New Document");
    713 
    714     request_sender_->StartRequestWithRetry(request);
    715     run_loop.Run();
    716   }
    717 
    718   EXPECT_EQ(HTTP_SUCCESS, result_code);
    719   EXPECT_EQ(net::test_server::METHOD_POST, http_request_.method);
    720   EXPECT_EQ("/feeds/default/private/full?v=3&alt=json&showroot=true",
    721             http_request_.relative_url);
    722   EXPECT_EQ("application/atom+xml", http_request_.headers["Content-Type"]);
    723 
    724   EXPECT_TRUE(http_request_.has_content);
    725   EXPECT_EQ("<?xml version=\"1.0\"?>\n"
    726             "<entry xmlns=\"http://www.w3.org/2005/Atom\">\n"
    727             " <id>document:5_document_resource_id</id>\n"
    728             " <title>New Document</title>\n"
    729             "</entry>\n",
    730             http_request_.content);
    731 }
    732 
    733 TEST_F(GDataWapiRequestsTest, RenameResourceRequest) {
    734   GDataErrorCode result_code = GDATA_OTHER_ERROR;
    735 
    736   // Rename a file with a new name "New File".
    737   {
    738     base::RunLoop run_loop;
    739     RenameResourceRequest* request = new RenameResourceRequest(
    740         request_sender_.get(),
    741         *url_generator_,
    742         test_util::CreateQuitCallback(
    743             &run_loop,
    744             test_util::CreateCopyResultCallback(&result_code)),
    745         "file:2_file_resource_id",
    746         "New File");
    747 
    748     request_sender_->StartRequestWithRetry(request);
    749     run_loop.Run();
    750   }
    751 
    752   EXPECT_EQ(HTTP_SUCCESS, result_code);
    753   EXPECT_EQ(net::test_server::METHOD_PUT, http_request_.method);
    754   EXPECT_EQ(
    755       "/feeds/default/private/full/file%3A2_file_resource_id?v=3&alt=json"
    756       "&showroot=true",
    757       http_request_.relative_url);
    758   EXPECT_EQ("application/atom+xml", http_request_.headers["Content-Type"]);
    759   EXPECT_EQ("*", http_request_.headers["If-Match"]);
    760 
    761   EXPECT_TRUE(http_request_.has_content);
    762   EXPECT_EQ("<?xml version=\"1.0\"?>\n"
    763             "<entry xmlns=\"http://www.w3.org/2005/Atom\">\n"
    764             " <title>New File</title>\n"
    765             "</entry>\n",
    766             http_request_.content);
    767 }
    768 
    769 TEST_F(GDataWapiRequestsTest, AuthorizeAppRequest_ValidFeed) {
    770   GDataErrorCode result_code = GDATA_OTHER_ERROR;
    771   GURL result_data;
    772 
    773   // Authorize an app with APP_ID to access to a document.
    774   {
    775     base::RunLoop run_loop;
    776     AuthorizeAppRequest* request = new AuthorizeAppRequest(
    777         request_sender_.get(),
    778         *url_generator_,
    779         test_util::CreateQuitCallback(
    780             &run_loop,
    781             test_util::CreateCopyResultCallback(&result_code, &result_data)),
    782         "file:2_file_resource_id",
    783         "the_app_id");
    784 
    785     request_sender_->StartRequestWithRetry(request);
    786     run_loop.Run();
    787   }
    788 
    789   EXPECT_EQ(HTTP_SUCCESS, result_code);
    790   EXPECT_EQ(GURL("https://entry1_open_with_link/"), result_data);
    791 
    792   EXPECT_EQ(net::test_server::METHOD_PUT, http_request_.method);
    793   EXPECT_EQ("/feeds/default/private/full/file%3A2_file_resource_id"
    794             "?v=3&alt=json&showroot=true",
    795             http_request_.relative_url);
    796   EXPECT_EQ("application/atom+xml", http_request_.headers["Content-Type"]);
    797   EXPECT_EQ("*", http_request_.headers["If-Match"]);
    798 
    799   EXPECT_TRUE(http_request_.has_content);
    800   EXPECT_EQ("<?xml version=\"1.0\"?>\n"
    801             "<entry xmlns=\"http://www.w3.org/2005/Atom\" "
    802             "xmlns:docs=\"http://schemas.google.com/docs/2007\">\n"
    803             " <docs:authorizedApp>the_app_id</docs:authorizedApp>\n"
    804             "</entry>\n",
    805             http_request_.content);
    806 }
    807 
    808 TEST_F(GDataWapiRequestsTest, AuthorizeAppRequest_NotFound) {
    809   GDataErrorCode result_code = GDATA_OTHER_ERROR;
    810   GURL result_data;
    811 
    812   // Authorize an app with APP_ID to access to a document.
    813   {
    814     base::RunLoop run_loop;
    815     AuthorizeAppRequest* request = new AuthorizeAppRequest(
    816         request_sender_.get(),
    817         *url_generator_,
    818         test_util::CreateQuitCallback(
    819             &run_loop,
    820             test_util::CreateCopyResultCallback(&result_code, &result_data)),
    821         "file:2_file_resource_id",
    822         "unauthorized_app_id");
    823 
    824     request_sender_->StartRequestWithRetry(request);
    825     run_loop.Run();
    826   }
    827 
    828   EXPECT_EQ(GDATA_OTHER_ERROR, result_code);
    829   EXPECT_EQ(net::test_server::METHOD_PUT, http_request_.method);
    830   EXPECT_EQ("/feeds/default/private/full/file%3A2_file_resource_id"
    831             "?v=3&alt=json&showroot=true",
    832             http_request_.relative_url);
    833   EXPECT_EQ("application/atom+xml", http_request_.headers["Content-Type"]);
    834   EXPECT_EQ("*", http_request_.headers["If-Match"]);
    835 
    836   EXPECT_TRUE(http_request_.has_content);
    837   EXPECT_EQ("<?xml version=\"1.0\"?>\n"
    838             "<entry xmlns=\"http://www.w3.org/2005/Atom\" "
    839             "xmlns:docs=\"http://schemas.google.com/docs/2007\">\n"
    840             " <docs:authorizedApp>unauthorized_app_id</docs:authorizedApp>\n"
    841             "</entry>\n",
    842             http_request_.content);
    843 }
    844 
    845 TEST_F(GDataWapiRequestsTest, AuthorizeAppRequest_InvalidFeed) {
    846   GDataErrorCode result_code = GDATA_OTHER_ERROR;
    847   GURL result_data;
    848 
    849   // Authorize an app with APP_ID to access to a document but an invalid feed.
    850   {
    851     base::RunLoop run_loop;
    852     AuthorizeAppRequest* request = new AuthorizeAppRequest(
    853         request_sender_.get(),
    854         *url_generator_,
    855         test_util::CreateQuitCallback(
    856             &run_loop,
    857             test_util::CreateCopyResultCallback(&result_code, &result_data)),
    858         "invalid_resource_id",
    859         "APP_ID");
    860 
    861     request_sender_->StartRequestWithRetry(request);
    862     run_loop.Run();
    863   }
    864 
    865   EXPECT_EQ(GDATA_PARSE_ERROR, result_code);
    866   EXPECT_EQ(net::test_server::METHOD_PUT, http_request_.method);
    867   EXPECT_EQ("/feeds/default/private/full/invalid_resource_id"
    868             "?v=3&alt=json&showroot=true",
    869             http_request_.relative_url);
    870   EXPECT_EQ("application/atom+xml", http_request_.headers["Content-Type"]);
    871   EXPECT_EQ("*", http_request_.headers["If-Match"]);
    872 
    873   EXPECT_TRUE(http_request_.has_content);
    874   EXPECT_EQ("<?xml version=\"1.0\"?>\n"
    875             "<entry xmlns=\"http://www.w3.org/2005/Atom\" "
    876             "xmlns:docs=\"http://schemas.google.com/docs/2007\">\n"
    877             " <docs:authorizedApp>APP_ID</docs:authorizedApp>\n"
    878             "</entry>\n",
    879             http_request_.content);
    880 }
    881 
    882 TEST_F(GDataWapiRequestsTest, AddResourceToDirectoryRequest) {
    883   GDataErrorCode result_code = GDATA_OTHER_ERROR;
    884 
    885   // Add a file to the root directory.
    886   {
    887     base::RunLoop run_loop;
    888     AddResourceToDirectoryRequest* request =
    889         new AddResourceToDirectoryRequest(
    890             request_sender_.get(),
    891             *url_generator_,
    892             test_util::CreateQuitCallback(
    893                 &run_loop,
    894                 test_util::CreateCopyResultCallback(&result_code)),
    895             "folder:root",
    896             "file:2_file_resource_id");
    897 
    898     request_sender_->StartRequestWithRetry(request);
    899     run_loop.Run();
    900   }
    901 
    902   EXPECT_EQ(HTTP_SUCCESS, result_code);
    903   EXPECT_EQ(net::test_server::METHOD_POST, http_request_.method);
    904   EXPECT_EQ("/feeds/default/private/full/folder%3Aroot/contents?v=3&alt=json"
    905             "&showroot=true",
    906             http_request_.relative_url);
    907   EXPECT_EQ("application/atom+xml", http_request_.headers["Content-Type"]);
    908 
    909   EXPECT_TRUE(http_request_.has_content);
    910   EXPECT_EQ(base::StringPrintf("<?xml version=\"1.0\"?>\n"
    911                                "<entry xmlns=\"http://www.w3.org/2005/Atom\">\n"
    912                                " <id>%sfeeds/default/private/full/"
    913                                "file%%3A2_file_resource_id</id>\n"
    914                                "</entry>\n",
    915                                test_server_.base_url().spec().c_str()),
    916             http_request_.content);
    917 }
    918 
    919 TEST_F(GDataWapiRequestsTest, RemoveResourceFromDirectoryRequest) {
    920   GDataErrorCode result_code = GDATA_OTHER_ERROR;
    921 
    922   // Remove a file from the root directory.
    923   {
    924     base::RunLoop run_loop;
    925     RemoveResourceFromDirectoryRequest* request =
    926         new RemoveResourceFromDirectoryRequest(
    927             request_sender_.get(),
    928             *url_generator_,
    929             test_util::CreateQuitCallback(
    930                 &run_loop,
    931                 test_util::CreateCopyResultCallback(&result_code)),
    932             "folder:root",
    933             "file:2_file_resource_id");
    934 
    935     request_sender_->StartRequestWithRetry(request);
    936     run_loop.Run();
    937   }
    938 
    939   EXPECT_EQ(HTTP_SUCCESS, result_code);
    940   // DELETE method should be used, without the body content.
    941   EXPECT_EQ(net::test_server::METHOD_DELETE, http_request_.method);
    942   EXPECT_EQ("/feeds/default/private/full/folder%3Aroot/contents/"
    943             "file%3A2_file_resource_id?v=3&alt=json&showroot=true",
    944             http_request_.relative_url);
    945   EXPECT_EQ("*", http_request_.headers["If-Match"]);
    946   EXPECT_FALSE(http_request_.has_content);
    947 }
    948 
    949 // This test exercises InitiateUploadNewFileRequest and
    950 // ResumeUploadRequest for a scenario of uploading a new file.
    951 TEST_F(GDataWapiRequestsTest, UploadNewFile) {
    952   const std::string kUploadContent = "hello";
    953   const base::FilePath kTestFilePath =
    954       temp_dir_.path().AppendASCII("upload_file.txt");
    955   ASSERT_TRUE(test_util::WriteStringToFile(kTestFilePath, kUploadContent));
    956 
    957   GDataErrorCode result_code = GDATA_OTHER_ERROR;
    958   GURL upload_url;
    959 
    960   // 1) Get the upload URL for uploading a new file.
    961   {
    962     base::RunLoop run_loop;
    963     InitiateUploadNewFileRequest* initiate_request =
    964         new InitiateUploadNewFileRequest(
    965             request_sender_.get(),
    966             *url_generator_,
    967             test_util::CreateQuitCallback(
    968                 &run_loop,
    969                 test_util::CreateCopyResultCallback(&result_code, &upload_url)),
    970             "text/plain",
    971             kUploadContent.size(),
    972             "folder:id",
    973             "New file");
    974     request_sender_->StartRequestWithRetry(initiate_request);
    975     run_loop.Run();
    976   }
    977 
    978   EXPECT_EQ(HTTP_SUCCESS, result_code);
    979   EXPECT_EQ(test_server_.GetURL("/upload_new_file"), upload_url);
    980   EXPECT_EQ(net::test_server::METHOD_POST, http_request_.method);
    981   // convert=false should be passed as files should be uploaded as-is.
    982   EXPECT_EQ(
    983       "/feeds/upload/create-session/default/private/full/folder%3Aid/contents"
    984       "?convert=false&v=3&alt=json&showroot=true",
    985       http_request_.relative_url);
    986   EXPECT_EQ("text/plain", http_request_.headers["X-Upload-Content-Type"]);
    987   EXPECT_EQ("application/atom+xml", http_request_.headers["Content-Type"]);
    988   EXPECT_EQ(base::Int64ToString(kUploadContent.size()),
    989             http_request_.headers["X-Upload-Content-Length"]);
    990 
    991   EXPECT_TRUE(http_request_.has_content);
    992   EXPECT_EQ("<?xml version=\"1.0\"?>\n"
    993             "<entry xmlns=\"http://www.w3.org/2005/Atom\" "
    994             "xmlns:docs=\"http://schemas.google.com/docs/2007\">\n"
    995             " <title>New file</title>\n"
    996             "</entry>\n",
    997             http_request_.content);
    998 
    999   // 2) Upload the content to the upload URL.
   1000   UploadRangeResponse response;
   1001   scoped_ptr<ResourceEntry> new_entry;
   1002 
   1003   {
   1004     base::RunLoop run_loop;
   1005     ResumeUploadRequest* resume_request = new ResumeUploadRequest(
   1006         request_sender_.get(),
   1007         test_util::CreateQuitCallback(
   1008             &run_loop,
   1009             test_util::CreateCopyResultCallback(&response, &new_entry)),
   1010         ProgressCallback(),
   1011         upload_url,
   1012         0,  // start_position
   1013         kUploadContent.size(),  // end_position (exclusive)
   1014         kUploadContent.size(),  // content_length,
   1015         "text/plain",  // content_type
   1016         kTestFilePath);
   1017 
   1018     request_sender_->StartRequestWithRetry(resume_request);
   1019     run_loop.Run();
   1020   }
   1021 
   1022   // METHOD_PUT should be used to upload data.
   1023   EXPECT_EQ(net::test_server::METHOD_PUT, http_request_.method);
   1024   // Request should go to the upload URL.
   1025   EXPECT_EQ(upload_url.path(), http_request_.relative_url);
   1026   // Content-Range header should be added.
   1027   EXPECT_EQ("bytes 0-" +
   1028             base::Int64ToString(kUploadContent.size() -1) + "/" +
   1029             base::Int64ToString(kUploadContent.size()),
   1030             http_request_.headers["Content-Range"]);
   1031   // The upload content should be set in the HTTP request.
   1032   EXPECT_TRUE(http_request_.has_content);
   1033   EXPECT_EQ(kUploadContent, http_request_.content);
   1034 
   1035   // Check the response.
   1036   EXPECT_EQ(HTTP_CREATED, response.code);  // Because it's a new file
   1037   // The start and end positions should be set to -1, if an upload is complete.
   1038   EXPECT_EQ(-1, response.start_position_received);
   1039   EXPECT_EQ(-1, response.end_position_received);
   1040 }
   1041 
   1042 // This test exercises InitiateUploadNewFileRequest and ResumeUploadRequest
   1043 // for a scenario of uploading a new *large* file, which requires multiple
   1044 // requests of ResumeUploadRequest. GetUploadRequest is also tested in this
   1045 // test case.
   1046 TEST_F(GDataWapiRequestsTest, UploadNewLargeFile) {
   1047   const size_t kMaxNumBytes = 10;
   1048   // This is big enough to cause multiple requests of ResumeUploadRequest
   1049   // as we are going to send at most kMaxNumBytes at a time.
   1050   // So, sending "kMaxNumBytes * 2 + 1" bytes ensures three
   1051   // ResumeUploadRequests, which are start, middle and last requests.
   1052   const std::string kUploadContent(kMaxNumBytes * 2 + 1, 'a');
   1053   const base::FilePath kTestFilePath =
   1054       temp_dir_.path().AppendASCII("upload_file.txt");
   1055   ASSERT_TRUE(test_util::WriteStringToFile(kTestFilePath, kUploadContent));
   1056 
   1057   GDataErrorCode result_code = GDATA_OTHER_ERROR;
   1058   GURL upload_url;
   1059 
   1060   // 1) Get the upload URL for uploading a new file.
   1061   {
   1062     base::RunLoop run_loop;
   1063     InitiateUploadNewFileRequest* initiate_request =
   1064         new InitiateUploadNewFileRequest(
   1065             request_sender_.get(),
   1066             *url_generator_,
   1067             test_util::CreateQuitCallback(
   1068                 &run_loop,
   1069                 test_util::CreateCopyResultCallback(&result_code, &upload_url)),
   1070             "text/plain",
   1071             kUploadContent.size(),
   1072             "folder:id",
   1073             "New file");
   1074     request_sender_->StartRequestWithRetry(initiate_request);
   1075     run_loop.Run();
   1076   }
   1077 
   1078   EXPECT_EQ(HTTP_SUCCESS, result_code);
   1079   EXPECT_EQ(test_server_.GetURL("/upload_new_file"), upload_url);
   1080   EXPECT_EQ(net::test_server::METHOD_POST, http_request_.method);
   1081   // convert=false should be passed as files should be uploaded as-is.
   1082   EXPECT_EQ(
   1083       "/feeds/upload/create-session/default/private/full/folder%3Aid/contents"
   1084       "?convert=false&v=3&alt=json&showroot=true",
   1085       http_request_.relative_url);
   1086   EXPECT_EQ("text/plain", http_request_.headers["X-Upload-Content-Type"]);
   1087   EXPECT_EQ("application/atom+xml", http_request_.headers["Content-Type"]);
   1088   EXPECT_EQ(base::Int64ToString(kUploadContent.size()),
   1089             http_request_.headers["X-Upload-Content-Length"]);
   1090 
   1091   EXPECT_TRUE(http_request_.has_content);
   1092   EXPECT_EQ("<?xml version=\"1.0\"?>\n"
   1093             "<entry xmlns=\"http://www.w3.org/2005/Atom\" "
   1094             "xmlns:docs=\"http://schemas.google.com/docs/2007\">\n"
   1095             " <title>New file</title>\n"
   1096             "</entry>\n",
   1097             http_request_.content);
   1098 
   1099   // 2) Before sending any data, check the current status.
   1100   // This is an edge case test for GetUploadStatusRequest
   1101   // (UploadRangeRequestBase).
   1102   {
   1103     UploadRangeResponse response;
   1104     scoped_ptr<ResourceEntry> new_entry;
   1105 
   1106     // Check the response by GetUploadStatusRequest.
   1107     {
   1108       base::RunLoop run_loop;
   1109       GetUploadStatusRequest* get_upload_status_request =
   1110           new GetUploadStatusRequest(
   1111               request_sender_.get(),
   1112               test_util::CreateQuitCallback(
   1113                   &run_loop,
   1114                   test_util::CreateCopyResultCallback(&response, &new_entry)),
   1115               upload_url,
   1116               kUploadContent.size());
   1117       request_sender_->StartRequestWithRetry(get_upload_status_request);
   1118       run_loop.Run();
   1119     }
   1120 
   1121     // METHOD_PUT should be used to upload data.
   1122     EXPECT_EQ(net::test_server::METHOD_PUT, http_request_.method);
   1123     // Request should go to the upload URL.
   1124     EXPECT_EQ(upload_url.path(), http_request_.relative_url);
   1125     // Content-Range header should be added.
   1126     EXPECT_EQ("bytes */" + base::Int64ToString(kUploadContent.size()),
   1127               http_request_.headers["Content-Range"]);
   1128     EXPECT_TRUE(http_request_.has_content);
   1129     EXPECT_TRUE(http_request_.content.empty());
   1130 
   1131     // Check the response.
   1132     EXPECT_EQ(HTTP_RESUME_INCOMPLETE, response.code);
   1133     EXPECT_EQ(0, response.start_position_received);
   1134     EXPECT_EQ(0, response.end_position_received);
   1135   }
   1136 
   1137   // 3) Upload the content to the upload URL with multiple requests.
   1138   size_t num_bytes_consumed = 0;
   1139   for (size_t start_position = 0; start_position < kUploadContent.size();
   1140        start_position += kMaxNumBytes) {
   1141     SCOPED_TRACE(testing::Message("start_position: ") << start_position);
   1142 
   1143     // The payload is at most kMaxNumBytes.
   1144     const size_t remaining_size = kUploadContent.size() - start_position;
   1145     const std::string payload = kUploadContent.substr(
   1146         start_position, std::min(kMaxNumBytes, remaining_size));
   1147     num_bytes_consumed += payload.size();
   1148     // The end position is exclusive.
   1149     const size_t end_position = start_position + payload.size();
   1150 
   1151     UploadRangeResponse response;
   1152     scoped_ptr<ResourceEntry> new_entry;
   1153 
   1154     {
   1155       base::RunLoop run_loop;
   1156       ResumeUploadRequest* resume_request = new ResumeUploadRequest(
   1157           request_sender_.get(),
   1158           test_util::CreateQuitCallback(
   1159               &run_loop,
   1160               test_util::CreateCopyResultCallback(&response, &new_entry)),
   1161           ProgressCallback(),
   1162           upload_url,
   1163           start_position,
   1164           end_position,
   1165           kUploadContent.size(),  // content_length,
   1166           "text/plain",  // content_type
   1167           kTestFilePath);
   1168       request_sender_->StartRequestWithRetry(resume_request);
   1169       run_loop.Run();
   1170     }
   1171 
   1172     // METHOD_PUT should be used to upload data.
   1173     EXPECT_EQ(net::test_server::METHOD_PUT, http_request_.method);
   1174     // Request should go to the upload URL.
   1175     EXPECT_EQ(upload_url.path(), http_request_.relative_url);
   1176     // Content-Range header should be added.
   1177     EXPECT_EQ("bytes " +
   1178               base::Int64ToString(start_position) + "-" +
   1179               base::Int64ToString(end_position - 1) + "/" +
   1180               base::Int64ToString(kUploadContent.size()),
   1181               http_request_.headers["Content-Range"]);
   1182     // The upload content should be set in the HTTP request.
   1183     EXPECT_TRUE(http_request_.has_content);
   1184     EXPECT_EQ(payload, http_request_.content);
   1185 
   1186     // Check the response.
   1187     if (payload.size() == remaining_size) {
   1188       EXPECT_EQ(HTTP_CREATED, response.code);  // Because it's a new file.
   1189       // The start and end positions should be set to -1, if an upload is
   1190       // complete.
   1191       EXPECT_EQ(-1, response.start_position_received);
   1192       EXPECT_EQ(-1, response.end_position_received);
   1193       // The upload process is completed, so exit from the loop.
   1194       break;
   1195     }
   1196 
   1197     EXPECT_EQ(HTTP_RESUME_INCOMPLETE, response.code);
   1198     EXPECT_EQ(0, response.start_position_received);
   1199     EXPECT_EQ(static_cast<int64>(end_position),
   1200               response.end_position_received);
   1201 
   1202     // Check the response by GetUploadStatusRequest.
   1203     {
   1204       base::RunLoop run_loop;
   1205       GetUploadStatusRequest* get_upload_status_request =
   1206           new GetUploadStatusRequest(
   1207               request_sender_.get(),
   1208               test_util::CreateQuitCallback(
   1209                   &run_loop,
   1210                   test_util::CreateCopyResultCallback(&response, &new_entry)),
   1211               upload_url,
   1212               kUploadContent.size());
   1213       request_sender_->StartRequestWithRetry(get_upload_status_request);
   1214       run_loop.Run();
   1215     }
   1216 
   1217     // METHOD_PUT should be used to upload data.
   1218     EXPECT_EQ(net::test_server::METHOD_PUT, http_request_.method);
   1219     // Request should go to the upload URL.
   1220     EXPECT_EQ(upload_url.path(), http_request_.relative_url);
   1221     // Content-Range header should be added.
   1222     EXPECT_EQ("bytes */" + base::Int64ToString(kUploadContent.size()),
   1223               http_request_.headers["Content-Range"]);
   1224     EXPECT_TRUE(http_request_.has_content);
   1225     EXPECT_TRUE(http_request_.content.empty());
   1226 
   1227     // Check the response.
   1228     EXPECT_EQ(HTTP_RESUME_INCOMPLETE, response.code);
   1229     EXPECT_EQ(0, response.start_position_received);
   1230     EXPECT_EQ(static_cast<int64>(end_position),
   1231               response.end_position_received);
   1232   }
   1233 
   1234   EXPECT_EQ(kUploadContent.size(), num_bytes_consumed);
   1235 }
   1236 
   1237 // This test exercises InitiateUploadNewFileRequest and ResumeUploadRequest
   1238 // for a scenario of uploading a new *empty* file.
   1239 //
   1240 // The test is almost identical to UploadNewFile. The only difference is the
   1241 // expectation for the Content-Range header.
   1242 TEST_F(GDataWapiRequestsTest, UploadNewEmptyFile) {
   1243   const std::string kUploadContent;
   1244   const base::FilePath kTestFilePath =
   1245       temp_dir_.path().AppendASCII("empty_file.txt");
   1246   ASSERT_TRUE(test_util::WriteStringToFile(kTestFilePath, kUploadContent));
   1247 
   1248   GDataErrorCode result_code = GDATA_OTHER_ERROR;
   1249   GURL upload_url;
   1250 
   1251   // 1) Get the upload URL for uploading a new file.
   1252   {
   1253     base::RunLoop run_loop;
   1254     InitiateUploadNewFileRequest* initiate_request =
   1255         new InitiateUploadNewFileRequest(
   1256             request_sender_.get(),
   1257             *url_generator_,
   1258             test_util::CreateQuitCallback(
   1259                 &run_loop,
   1260                 test_util::CreateCopyResultCallback(&result_code, &upload_url)),
   1261             "text/plain",
   1262             kUploadContent.size(),
   1263             "folder:id",
   1264             "New file");
   1265     request_sender_->StartRequestWithRetry(initiate_request);
   1266     run_loop.Run();
   1267   }
   1268 
   1269   EXPECT_EQ(HTTP_SUCCESS, result_code);
   1270   EXPECT_EQ(test_server_.GetURL("/upload_new_file"), upload_url);
   1271   EXPECT_EQ(net::test_server::METHOD_POST, http_request_.method);
   1272   // convert=false should be passed as files should be uploaded as-is.
   1273   EXPECT_EQ(
   1274       "/feeds/upload/create-session/default/private/full/folder%3Aid/contents"
   1275       "?convert=false&v=3&alt=json&showroot=true",
   1276       http_request_.relative_url);
   1277   EXPECT_EQ("text/plain", http_request_.headers["X-Upload-Content-Type"]);
   1278   EXPECT_EQ("application/atom+xml", http_request_.headers["Content-Type"]);
   1279   EXPECT_EQ(base::Int64ToString(kUploadContent.size()),
   1280             http_request_.headers["X-Upload-Content-Length"]);
   1281 
   1282   EXPECT_TRUE(http_request_.has_content);
   1283   EXPECT_EQ("<?xml version=\"1.0\"?>\n"
   1284             "<entry xmlns=\"http://www.w3.org/2005/Atom\" "
   1285             "xmlns:docs=\"http://schemas.google.com/docs/2007\">\n"
   1286             " <title>New file</title>\n"
   1287             "</entry>\n",
   1288             http_request_.content);
   1289 
   1290   // 2) Upload the content to the upload URL.
   1291   UploadRangeResponse response;
   1292   scoped_ptr<ResourceEntry> new_entry;
   1293 
   1294   {
   1295     base::RunLoop run_loop;
   1296     ResumeUploadRequest* resume_request = new ResumeUploadRequest(
   1297         request_sender_.get(),
   1298         test_util::CreateQuitCallback(
   1299             &run_loop,
   1300             test_util::CreateCopyResultCallback(&response, &new_entry)),
   1301         ProgressCallback(),
   1302         upload_url,
   1303         0,  // start_position
   1304         kUploadContent.size(),  // end_position (exclusive)
   1305         kUploadContent.size(),  // content_length,
   1306         "text/plain",  // content_type
   1307         kTestFilePath);
   1308     request_sender_->StartRequestWithRetry(resume_request);
   1309     run_loop.Run();
   1310   }
   1311 
   1312   // METHOD_PUT should be used to upload data.
   1313   EXPECT_EQ(net::test_server::METHOD_PUT, http_request_.method);
   1314   // Request should go to the upload URL.
   1315   EXPECT_EQ(upload_url.path(), http_request_.relative_url);
   1316   // Content-Range header should not exit if the content is empty.
   1317   // We should not generate the header with an invalid value "bytes 0--1/0".
   1318   EXPECT_EQ(0U, http_request_.headers.count("Content-Range"));
   1319   // The upload content should be set in the HTTP request.
   1320   EXPECT_TRUE(http_request_.has_content);
   1321   EXPECT_EQ(kUploadContent, http_request_.content);
   1322 
   1323   // Check the response.
   1324   EXPECT_EQ(HTTP_CREATED, response.code);  // Because it's a new file.
   1325   // The start and end positions should be set to -1, if an upload is complete.
   1326   EXPECT_EQ(-1, response.start_position_received);
   1327   EXPECT_EQ(-1, response.end_position_received);
   1328 }
   1329 
   1330 // This test exercises InitiateUploadExistingFileRequest and
   1331 // ResumeUploadRequest for a scenario of updating an existing file.
   1332 TEST_F(GDataWapiRequestsTest, UploadExistingFile) {
   1333   const std::string kUploadContent = "hello";
   1334   const base::FilePath kTestFilePath =
   1335       temp_dir_.path().AppendASCII("upload_file.txt");
   1336   ASSERT_TRUE(test_util::WriteStringToFile(kTestFilePath, kUploadContent));
   1337 
   1338   GDataErrorCode result_code = GDATA_OTHER_ERROR;
   1339   GURL upload_url;
   1340 
   1341   // 1) Get the upload URL for uploading an existing file.
   1342   {
   1343     base::RunLoop run_loop;
   1344     InitiateUploadExistingFileRequest* initiate_request =
   1345         new InitiateUploadExistingFileRequest(
   1346             request_sender_.get(),
   1347             *url_generator_,
   1348             test_util::CreateQuitCallback(
   1349                 &run_loop,
   1350                 test_util::CreateCopyResultCallback(&result_code, &upload_url)),
   1351             "text/plain",
   1352             kUploadContent.size(),
   1353             "file:foo",
   1354             std::string() /* etag */);
   1355     request_sender_->StartRequestWithRetry(initiate_request);
   1356     run_loop.Run();
   1357   }
   1358 
   1359   EXPECT_EQ(HTTP_SUCCESS, result_code);
   1360   EXPECT_EQ(test_server_.GetURL("/upload_existing_file"), upload_url);
   1361   // For updating an existing file, METHOD_PUT should be used.
   1362   EXPECT_EQ(net::test_server::METHOD_PUT, http_request_.method);
   1363   // convert=false should be passed as files should be uploaded as-is.
   1364   EXPECT_EQ("/feeds/upload/create-session/default/private/full/file%3Afoo"
   1365             "?convert=false&v=3&alt=json&showroot=true",
   1366             http_request_.relative_url);
   1367   // Even though the body is empty, the content type should be set to
   1368   // "text/plain".
   1369   EXPECT_EQ("text/plain", http_request_.headers["Content-Type"]);
   1370   EXPECT_EQ("text/plain", http_request_.headers["X-Upload-Content-Type"]);
   1371   EXPECT_EQ(base::Int64ToString(kUploadContent.size()),
   1372             http_request_.headers["X-Upload-Content-Length"]);
   1373   // For updating an existing file, an empty body should be attached (PUT
   1374   // requires a body)
   1375   EXPECT_TRUE(http_request_.has_content);
   1376   EXPECT_EQ("", http_request_.content);
   1377   EXPECT_EQ("*", http_request_.headers["If-Match"]);
   1378 
   1379   // 2) Upload the content to the upload URL.
   1380   UploadRangeResponse response;
   1381   scoped_ptr<ResourceEntry> new_entry;
   1382 
   1383   {
   1384     base::RunLoop run_loop;
   1385     ResumeUploadRequest* resume_request = new ResumeUploadRequest(
   1386         request_sender_.get(),
   1387         test_util::CreateQuitCallback(
   1388             &run_loop,
   1389             test_util::CreateCopyResultCallback(&response, &new_entry)),
   1390         ProgressCallback(),
   1391         upload_url,
   1392         0,  // start_position
   1393         kUploadContent.size(),  // end_position (exclusive)
   1394         kUploadContent.size(),  // content_length,
   1395         "text/plain",  // content_type
   1396         kTestFilePath);
   1397 
   1398     request_sender_->StartRequestWithRetry(resume_request);
   1399     run_loop.Run();
   1400   }
   1401 
   1402   // METHOD_PUT should be used to upload data.
   1403   EXPECT_EQ(net::test_server::METHOD_PUT, http_request_.method);
   1404   // Request should go to the upload URL.
   1405   EXPECT_EQ(upload_url.path(), http_request_.relative_url);
   1406   // Content-Range header should be added.
   1407   EXPECT_EQ("bytes 0-" +
   1408             base::Int64ToString(kUploadContent.size() -1) + "/" +
   1409             base::Int64ToString(kUploadContent.size()),
   1410             http_request_.headers["Content-Range"]);
   1411   // The upload content should be set in the HTTP request.
   1412   EXPECT_TRUE(http_request_.has_content);
   1413   EXPECT_EQ(kUploadContent, http_request_.content);
   1414 
   1415   // Check the response.
   1416   EXPECT_EQ(HTTP_SUCCESS, response.code);  // Because it's an existing file.
   1417   // The start and end positions should be set to -1, if an upload is complete.
   1418   EXPECT_EQ(-1, response.start_position_received);
   1419   EXPECT_EQ(-1, response.end_position_received);
   1420 }
   1421 
   1422 // This test exercises InitiateUploadExistingFileRequest and
   1423 // ResumeUploadRequest for a scenario of updating an existing file.
   1424 TEST_F(GDataWapiRequestsTest, UploadExistingFileWithETag) {
   1425   const std::string kUploadContent = "hello";
   1426   const base::FilePath kTestFilePath =
   1427       temp_dir_.path().AppendASCII("upload_file.txt");
   1428   ASSERT_TRUE(test_util::WriteStringToFile(kTestFilePath, kUploadContent));
   1429 
   1430   GDataErrorCode result_code = GDATA_OTHER_ERROR;
   1431   GURL upload_url;
   1432 
   1433   // 1) Get the upload URL for uploading an existing file.
   1434   {
   1435     base::RunLoop run_loop;
   1436     InitiateUploadExistingFileRequest* initiate_request =
   1437         new InitiateUploadExistingFileRequest(
   1438             request_sender_.get(),
   1439             *url_generator_,
   1440             test_util::CreateQuitCallback(
   1441                 &run_loop,
   1442                 test_util::CreateCopyResultCallback(&result_code, &upload_url)),
   1443             "text/plain",
   1444             kUploadContent.size(),
   1445             "file:foo",
   1446             kTestETag);
   1447     request_sender_->StartRequestWithRetry(initiate_request);
   1448     run_loop.Run();
   1449   }
   1450 
   1451   EXPECT_EQ(HTTP_SUCCESS, result_code);
   1452   EXPECT_EQ(test_server_.GetURL("/upload_existing_file"), upload_url);
   1453   // For updating an existing file, METHOD_PUT should be used.
   1454   EXPECT_EQ(net::test_server::METHOD_PUT, http_request_.method);
   1455   // convert=false should be passed as files should be uploaded as-is.
   1456   EXPECT_EQ("/feeds/upload/create-session/default/private/full/file%3Afoo"
   1457             "?convert=false&v=3&alt=json&showroot=true",
   1458             http_request_.relative_url);
   1459   // Even though the body is empty, the content type should be set to
   1460   // "text/plain".
   1461   EXPECT_EQ("text/plain", http_request_.headers["Content-Type"]);
   1462   EXPECT_EQ("text/plain", http_request_.headers["X-Upload-Content-Type"]);
   1463   EXPECT_EQ(base::Int64ToString(kUploadContent.size()),
   1464             http_request_.headers["X-Upload-Content-Length"]);
   1465   // For updating an existing file, an empty body should be attached (PUT
   1466   // requires a body)
   1467   EXPECT_TRUE(http_request_.has_content);
   1468   EXPECT_EQ("", http_request_.content);
   1469   EXPECT_EQ(kTestETag, http_request_.headers["If-Match"]);
   1470 
   1471   // 2) Upload the content to the upload URL.
   1472   UploadRangeResponse response;
   1473   scoped_ptr<ResourceEntry> new_entry;
   1474 
   1475   {
   1476     base::RunLoop run_loop;
   1477     ResumeUploadRequest* resume_request = new ResumeUploadRequest(
   1478         request_sender_.get(),
   1479         test_util::CreateQuitCallback(
   1480             &run_loop,
   1481             test_util::CreateCopyResultCallback(&response, &new_entry)),
   1482         ProgressCallback(),
   1483         upload_url,
   1484         0,  // start_position
   1485         kUploadContent.size(),  // end_position (exclusive)
   1486         kUploadContent.size(),  // content_length,
   1487         "text/plain",  // content_type
   1488         kTestFilePath);
   1489     request_sender_->StartRequestWithRetry(resume_request);
   1490     run_loop.Run();
   1491   }
   1492 
   1493   // METHOD_PUT should be used to upload data.
   1494   EXPECT_EQ(net::test_server::METHOD_PUT, http_request_.method);
   1495   // Request should go to the upload URL.
   1496   EXPECT_EQ(upload_url.path(), http_request_.relative_url);
   1497   // Content-Range header should be added.
   1498   EXPECT_EQ("bytes 0-" +
   1499             base::Int64ToString(kUploadContent.size() -1) + "/" +
   1500             base::Int64ToString(kUploadContent.size()),
   1501             http_request_.headers["Content-Range"]);
   1502   // The upload content should be set in the HTTP request.
   1503   EXPECT_TRUE(http_request_.has_content);
   1504   EXPECT_EQ(kUploadContent, http_request_.content);
   1505 
   1506   // Check the response.
   1507   EXPECT_EQ(HTTP_SUCCESS, response.code);  // Because it's an existing file.
   1508   // The start and end positions should be set to -1, if an upload is complete.
   1509   EXPECT_EQ(-1, response.start_position_received);
   1510   EXPECT_EQ(-1, response.end_position_received);
   1511 }
   1512 
   1513 // This test exercises InitiateUploadExistingFileRequest for a scenario of
   1514 // confliction on updating an existing file.
   1515 TEST_F(GDataWapiRequestsTest, UploadExistingFileWithETagConflict) {
   1516   const std::string kUploadContent = "hello";
   1517   const std::string kWrongETag = "wrong_etag";
   1518   GDataErrorCode result_code = GDATA_OTHER_ERROR;
   1519   GURL upload_url;
   1520 
   1521   {
   1522     base::RunLoop run_loop;
   1523     InitiateUploadExistingFileRequest* initiate_request =
   1524         new InitiateUploadExistingFileRequest(
   1525             request_sender_.get(),
   1526             *url_generator_,
   1527             test_util::CreateQuitCallback(
   1528                 &run_loop,
   1529                 test_util::CreateCopyResultCallback(&result_code, &upload_url)),
   1530             "text/plain",
   1531             kUploadContent.size(),
   1532             "file:foo",
   1533             kWrongETag);
   1534     request_sender_->StartRequestWithRetry(initiate_request);
   1535     run_loop.Run();
   1536   }
   1537 
   1538   EXPECT_EQ(HTTP_PRECONDITION, result_code);
   1539   // For updating an existing file, METHOD_PUT should be used.
   1540   EXPECT_EQ(net::test_server::METHOD_PUT, http_request_.method);
   1541   // convert=false should be passed as files should be uploaded as-is.
   1542   EXPECT_EQ("/feeds/upload/create-session/default/private/full/file%3Afoo"
   1543             "?convert=false&v=3&alt=json&showroot=true",
   1544             http_request_.relative_url);
   1545   // Even though the body is empty, the content type should be set to
   1546   // "text/plain".
   1547   EXPECT_EQ("text/plain", http_request_.headers["Content-Type"]);
   1548   EXPECT_EQ("text/plain", http_request_.headers["X-Upload-Content-Type"]);
   1549   EXPECT_EQ(base::Int64ToString(kUploadContent.size()),
   1550             http_request_.headers["X-Upload-Content-Length"]);
   1551   // For updating an existing file, an empty body should be attached (PUT
   1552   // requires a body)
   1553   EXPECT_TRUE(http_request_.has_content);
   1554   EXPECT_EQ("", http_request_.content);
   1555   EXPECT_EQ(kWrongETag, http_request_.headers["If-Match"]);
   1556 }
   1557 
   1558 TEST_F(GDataWapiRequestsTest, DownloadFileRequest) {
   1559   const base::FilePath kDownloadedFilePath =
   1560       temp_dir_.path().AppendASCII("cache_file");
   1561   const std::string kTestIdWithTypeLabel("file:dummyId");
   1562   const std::string kTestId("dummyId");
   1563 
   1564   GDataErrorCode result_code = GDATA_OTHER_ERROR;
   1565   base::FilePath temp_file;
   1566   {
   1567     base::RunLoop run_loop;
   1568     DownloadFileRequest* request = new DownloadFileRequest(
   1569         request_sender_.get(),
   1570         *url_generator_,
   1571         test_util::CreateQuitCallback(
   1572             &run_loop,
   1573             test_util::CreateCopyResultCallback(&result_code, &temp_file)),
   1574         GetContentCallback(),
   1575         ProgressCallback(),
   1576         kTestIdWithTypeLabel,
   1577         kDownloadedFilePath);
   1578     request_sender_->StartRequestWithRetry(request);
   1579     run_loop.Run();
   1580   }
   1581 
   1582   std::string contents;
   1583   file_util::ReadFileToString(temp_file, &contents);
   1584   base::DeleteFile(temp_file, false);
   1585 
   1586   EXPECT_EQ(HTTP_SUCCESS, result_code);
   1587   EXPECT_EQ(net::test_server::METHOD_GET, http_request_.method);
   1588   EXPECT_EQ(kTestDownloadPathPrefix + kTestId, http_request_.relative_url);
   1589   EXPECT_EQ(kDownloadedFilePath, temp_file);
   1590 
   1591   const std::string expected_contents = kTestId + kTestId + kTestId;
   1592   EXPECT_EQ(expected_contents, contents);
   1593 }
   1594 
   1595 }  // namespace google_apis
   1596