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.
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"
32 namespace google_apis
{
36 const char kTestUserAgent
[] = "test-user-agent";
37 const char kTestETag
[] = "test_etag";
38 const char kTestDownloadPathPrefix
[] = "/download/";
40 class GDataWapiRequestsTest
: public testing::Test
{
42 GDataWapiRequestsTest()
43 : test_server_(message_loop_
.message_loop_proxy()) {
46 virtual void SetUp() OVERRIDE
{
47 request_context_getter_
= new net::TestURLRequestContextGetter(
48 message_loop_
.message_loop_proxy());
50 request_sender_
.reset(new RequestSender(new DummyAuthService
,
51 request_context_getter_
.get(),
52 message_loop_
.message_loop_proxy(),
55 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
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)));
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
)));
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
;
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
>();
105 if (!test_util::RemovePrefix(absolute_url
.path(),
106 "/feeds/default/private/full",
108 return scoped_ptr
<net::test_server::HttpResponse
>();
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
>();
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
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
>();
160 return scoped_ptr
<net::test_server::HttpResponse
>();
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
;
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
>();
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") ==
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
);
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
);
188 // Write back it as the content of the result.
190 base::JSONWriter::Write(parsed_content
.get(), &content
);
191 result
->set_content(content
);
194 return result
.PassAs
<net::test_server::HttpResponse
>();
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
;
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
);
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
>();
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
>();
229 http_response
->set_code(net::HTTP_OK
);
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");
237 return scoped_ptr
<net::test_server::HttpResponse
>();
239 http_response
->AddCustomHeader("Location", upload_url
.spec());
240 return http_response
.PassAs
<net::test_server::HttpResponse
>();
243 return scoped_ptr
<net::test_server::HttpResponse
>();
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
;
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
>();
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
264 if (absolute_url
.path() == "/upload_new_file")
265 response
->set_code(net::HTTP_CREATED
);
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
>();
275 int64 start_position
= 0;
276 int64 end_position
= 0;
277 if (!test_util::ParseContentRangeHeader(iter
->second
,
281 return scoped_ptr
<net::test_server::HttpResponse
>();
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;
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(
295 "bytes=0-" + base::Int64ToString(received_bytes_
- 1));
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));
302 return response
.PassAs
<net::test_server::HttpResponse
>();
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
;
310 const GURL absolute_url
= test_server_
.GetURL(request
.relative_url
);
312 if (!test_util::RemovePrefix(absolute_url
.path(),
313 kTestDownloadPathPrefix
,
315 return scoped_ptr
<net::test_server::HttpResponse
>();
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
>();
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_
;
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
339 int64 received_bytes_
;
340 int64 content_length_
;
342 // The incoming HTTP request is saved so tests can verify the request
343 // parameters like HTTP method (ex. some requests should use DELETE
345 net::test_server::HttpRequest http_request_
;
350 TEST_F(GDataWapiRequestsTest
, GetResourceListRequest_DefaultFeed
) {
351 GDataErrorCode result_code
= GDATA_OTHER_ERROR
;
352 scoped_ptr
<ResourceList
> result_data
;
355 base::RunLoop run_loop
;
356 GetResourceListRequest
* request
= new GetResourceListRequest(
357 request_sender_
.get(),
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(
365 test_util::CreateCopyResultCallback(&result_code
, &result_data
)));
366 request_sender_
->StartRequestWithRetry(request
);
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
);
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());
384 TEST_F(GDataWapiRequestsTest
, GetResourceListRequest_ValidFeed
) {
385 GDataErrorCode result_code
= GDATA_OTHER_ERROR
;
386 scoped_ptr
<ResourceList
> result_data
;
389 base::RunLoop run_loop
;
390 GetResourceListRequest
* request
= new GetResourceListRequest(
391 request_sender_
.get(),
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(
399 test_util::CreateCopyResultCallback(&result_code
, &result_data
)));
400 request_sender_
->StartRequestWithRetry(request
);
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
);
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());
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
;
424 base::RunLoop run_loop
;
425 GetResourceListRequest
* request
= new GetResourceListRequest(
426 request_sender_
.get(),
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(
434 test_util::CreateCopyResultCallback(&result_code
, &result_data
)));
435 request_sender_
->StartRequestWithRetry(request
);
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
);
447 TEST_F(GDataWapiRequestsTest
, SearchByTitleRequest
) {
448 GDataErrorCode result_code
= GDATA_OTHER_ERROR
;
449 scoped_ptr
<ResourceList
> result_data
;
452 base::RunLoop run_loop
;
453 SearchByTitleRequest
* request
= new SearchByTitleRequest(
454 request_sender_
.get(),
457 std::string(), // directory resource id
458 test_util::CreateQuitCallback(
460 test_util::CreateCopyResultCallback(&result_code
, &result_data
)));
461 request_sender_
->StartRequestWithRetry(request
);
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
);
474 TEST_F(GDataWapiRequestsTest
, GetResourceEntryRequest_ValidResourceId
) {
475 GDataErrorCode result_code
= GDATA_OTHER_ERROR
;
476 scoped_ptr
<base::Value
> result_data
;
479 base::RunLoop run_loop
;
480 GetResourceEntryRequest
* request
= new GetResourceEntryRequest(
481 request_sender_
.get(),
483 "file:2_file_resource_id", // resource ID
484 GURL(), // embed origin
485 test_util::CreateQuitCallback(
487 test_util::CreateCopyResultCallback(&result_code
, &result_data
)));
488 request_sender_
->StartRequestWithRetry(request
);
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"),
502 TEST_F(GDataWapiRequestsTest
, GetResourceEntryRequest_InvalidResourceId
) {
503 GDataErrorCode result_code
= GDATA_OTHER_ERROR
;
504 scoped_ptr
<base::Value
> result_data
;
507 base::RunLoop run_loop
;
508 GetResourceEntryRequest
* request
= new GetResourceEntryRequest(
509 request_sender_
.get(),
511 "<invalid>", // resource ID
512 GURL(), // embed origin
513 test_util::CreateQuitCallback(
515 test_util::CreateCopyResultCallback(&result_code
, &result_data
)));
516 request_sender_
->StartRequestWithRetry(request
);
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"
524 http_request_
.relative_url
);
525 ASSERT_FALSE(result_data
);
528 TEST_F(GDataWapiRequestsTest
, GetAccountMetadataRequest
) {
529 GDataErrorCode result_code
= GDATA_OTHER_ERROR
;
530 scoped_ptr
<AccountMetadata
> result_data
;
533 base::RunLoop run_loop
;
534 GetAccountMetadataRequest
* request
= new GetAccountMetadataRequest(
535 request_sender_
.get(),
537 test_util::CreateQuitCallback(
539 test_util::CreateCopyResultCallback(&result_code
, &result_data
)),
540 true); // Include installed apps.
541 request_sender_
->StartRequestWithRetry(request
);
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
);
551 scoped_ptr
<AccountMetadata
> expected(
552 AccountMetadata::CreateFrom(
553 *test_util::LoadJSONFile("gdata/account_metadata.json")));
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());
563 // Sanity check for installed apps.
564 EXPECT_EQ(expected
->installed_apps().size(),
565 result_data
->installed_apps().size());
568 TEST_F(GDataWapiRequestsTest
,
569 GetAccountMetadataRequestWithoutInstalledApps
) {
570 GDataErrorCode result_code
= GDATA_OTHER_ERROR
;
571 scoped_ptr
<AccountMetadata
> result_data
;
574 base::RunLoop run_loop
;
575 GetAccountMetadataRequest
* request
= new GetAccountMetadataRequest(
576 request_sender_
.get(),
578 test_util::CreateQuitCallback(
580 test_util::CreateCopyResultCallback(&result_code
, &result_data
)),
581 false); // Exclude installed apps.
582 request_sender_
->StartRequestWithRetry(request
);
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
);
591 scoped_ptr
<AccountMetadata
> expected(
592 AccountMetadata::CreateFrom(
593 *test_util::LoadJSONFile("gdata/account_metadata.json")));
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());
603 // Installed apps shouldn't be included.
604 EXPECT_EQ(0U, result_data
->installed_apps().size());
607 TEST_F(GDataWapiRequestsTest
, DeleteResourceRequest
) {
608 GDataErrorCode result_code
= GDATA_OTHER_ERROR
;
611 base::RunLoop run_loop
;
612 DeleteResourceRequest
* request
= new DeleteResourceRequest(
613 request_sender_
.get(),
615 test_util::CreateQuitCallback(
617 test_util::CreateCopyResultCallback(&result_code
)),
618 "file:2_file_resource_id",
621 request_sender_
->StartRequestWithRetry(request
);
625 EXPECT_EQ(HTTP_SUCCESS
, result_code
);
626 EXPECT_EQ(net::test_server::METHOD_DELETE
, http_request_
.method
);
628 "/feeds/default/private/full/file%3A2_file_resource_id?v=3&alt=json"
630 http_request_
.relative_url
);
631 EXPECT_EQ("*", http_request_
.headers
["If-Match"]);
634 TEST_F(GDataWapiRequestsTest
, DeleteResourceRequestWithETag
) {
635 GDataErrorCode result_code
= GDATA_OTHER_ERROR
;
638 base::RunLoop run_loop
;
639 DeleteResourceRequest
* request
= new DeleteResourceRequest(
640 request_sender_
.get(),
642 test_util::CreateQuitCallback(
644 test_util::CreateCopyResultCallback(&result_code
)),
645 "file:2_file_resource_id",
648 request_sender_
->StartRequestWithRetry(request
);
652 EXPECT_EQ(HTTP_SUCCESS
, result_code
);
653 EXPECT_EQ(net::test_server::METHOD_DELETE
, http_request_
.method
);
655 "/feeds/default/private/full/file%3A2_file_resource_id?v=3&alt=json"
657 http_request_
.relative_url
);
658 EXPECT_EQ("etag", http_request_
.headers
["If-Match"]);
661 TEST_F(GDataWapiRequestsTest
, CreateDirectoryRequest
) {
662 GDataErrorCode result_code
= GDATA_OTHER_ERROR
;
663 scoped_ptr
<base::Value
> result_data
;
665 // Create "new directory" in the root directory.
667 base::RunLoop run_loop
;
668 CreateDirectoryRequest
* request
= new CreateDirectoryRequest(
669 request_sender_
.get(),
671 test_util::CreateQuitCallback(
673 test_util::CreateCopyResultCallback(&result_code
, &result_data
)),
677 request_sender_
->StartRequestWithRetry(request
);
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"
685 http_request_
.relative_url
);
686 EXPECT_EQ("application/atom+xml", http_request_
.headers
["Content-Type"]);
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"
695 http_request_
.content
);
698 TEST_F(GDataWapiRequestsTest
, CopyHostedDocumentRequest
) {
699 GDataErrorCode result_code
= GDATA_OTHER_ERROR
;
700 scoped_ptr
<base::Value
> result_data
;
702 // Copy a document with a new name "New Document".
704 base::RunLoop run_loop
;
705 CopyHostedDocumentRequest
* request
= new CopyHostedDocumentRequest(
706 request_sender_
.get(),
708 test_util::CreateQuitCallback(
710 test_util::CreateCopyResultCallback(&result_code
, &result_data
)),
711 "document:5_document_resource_id", // source resource ID
714 request_sender_
->StartRequestWithRetry(request
);
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"]);
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"
730 http_request_
.content
);
733 TEST_F(GDataWapiRequestsTest
, RenameResourceRequest
) {
734 GDataErrorCode result_code
= GDATA_OTHER_ERROR
;
736 // Rename a file with a new name "New File".
738 base::RunLoop run_loop
;
739 RenameResourceRequest
* request
= new RenameResourceRequest(
740 request_sender_
.get(),
742 test_util::CreateQuitCallback(
744 test_util::CreateCopyResultCallback(&result_code
)),
745 "file:2_file_resource_id",
748 request_sender_
->StartRequestWithRetry(request
);
752 EXPECT_EQ(HTTP_SUCCESS
, result_code
);
753 EXPECT_EQ(net::test_server::METHOD_PUT
, http_request_
.method
);
755 "/feeds/default/private/full/file%3A2_file_resource_id?v=3&alt=json"
757 http_request_
.relative_url
);
758 EXPECT_EQ("application/atom+xml", http_request_
.headers
["Content-Type"]);
759 EXPECT_EQ("*", http_request_
.headers
["If-Match"]);
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"
766 http_request_
.content
);
769 TEST_F(GDataWapiRequestsTest
, AuthorizeAppRequest_ValidFeed
) {
770 GDataErrorCode result_code
= GDATA_OTHER_ERROR
;
773 // Authorize an app with APP_ID to access to a document.
775 base::RunLoop run_loop
;
776 AuthorizeAppRequest
* request
= new AuthorizeAppRequest(
777 request_sender_
.get(),
779 test_util::CreateQuitCallback(
781 test_util::CreateCopyResultCallback(&result_code
, &result_data
)),
782 "file:2_file_resource_id",
785 request_sender_
->StartRequestWithRetry(request
);
789 EXPECT_EQ(HTTP_SUCCESS
, result_code
);
790 EXPECT_EQ(GURL("https://entry1_open_with_link/"), result_data
);
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"]);
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"
805 http_request_
.content
);
808 TEST_F(GDataWapiRequestsTest
, AuthorizeAppRequest_NotFound
) {
809 GDataErrorCode result_code
= GDATA_OTHER_ERROR
;
812 // Authorize an app with APP_ID to access to a document.
814 base::RunLoop run_loop
;
815 AuthorizeAppRequest
* request
= new AuthorizeAppRequest(
816 request_sender_
.get(),
818 test_util::CreateQuitCallback(
820 test_util::CreateCopyResultCallback(&result_code
, &result_data
)),
821 "file:2_file_resource_id",
822 "unauthorized_app_id");
824 request_sender_
->StartRequestWithRetry(request
);
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"]);
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"
842 http_request_
.content
);
845 TEST_F(GDataWapiRequestsTest
, AuthorizeAppRequest_InvalidFeed
) {
846 GDataErrorCode result_code
= GDATA_OTHER_ERROR
;
849 // Authorize an app with APP_ID to access to a document but an invalid feed.
851 base::RunLoop run_loop
;
852 AuthorizeAppRequest
* request
= new AuthorizeAppRequest(
853 request_sender_
.get(),
855 test_util::CreateQuitCallback(
857 test_util::CreateCopyResultCallback(&result_code
, &result_data
)),
858 "invalid_resource_id",
861 request_sender_
->StartRequestWithRetry(request
);
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"]);
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"
879 http_request_
.content
);
882 TEST_F(GDataWapiRequestsTest
, AddResourceToDirectoryRequest
) {
883 GDataErrorCode result_code
= GDATA_OTHER_ERROR
;
885 // Add a file to the root directory.
887 base::RunLoop run_loop
;
888 AddResourceToDirectoryRequest
* request
=
889 new AddResourceToDirectoryRequest(
890 request_sender_
.get(),
892 test_util::CreateQuitCallback(
894 test_util::CreateCopyResultCallback(&result_code
)),
896 "file:2_file_resource_id");
898 request_sender_
->StartRequestWithRetry(request
);
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"
906 http_request_
.relative_url
);
907 EXPECT_EQ("application/atom+xml", http_request_
.headers
["Content-Type"]);
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"
915 test_server_
.base_url().spec().c_str()),
916 http_request_
.content
);
919 TEST_F(GDataWapiRequestsTest
, RemoveResourceFromDirectoryRequest
) {
920 GDataErrorCode result_code
= GDATA_OTHER_ERROR
;
922 // Remove a file from the root directory.
924 base::RunLoop run_loop
;
925 RemoveResourceFromDirectoryRequest
* request
=
926 new RemoveResourceFromDirectoryRequest(
927 request_sender_
.get(),
929 test_util::CreateQuitCallback(
931 test_util::CreateCopyResultCallback(&result_code
)),
933 "file:2_file_resource_id");
935 request_sender_
->StartRequestWithRetry(request
);
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
);
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
));
957 GDataErrorCode result_code
= GDATA_OTHER_ERROR
;
960 // 1) Get the upload URL for uploading a new file.
962 base::RunLoop run_loop
;
963 InitiateUploadNewFileRequest
* initiate_request
=
964 new InitiateUploadNewFileRequest(
965 request_sender_
.get(),
967 test_util::CreateQuitCallback(
969 test_util::CreateCopyResultCallback(&result_code
, &upload_url
)),
971 kUploadContent
.size(),
974 request_sender_
->StartRequestWithRetry(initiate_request
);
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.
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"]);
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"
997 http_request_
.content
);
999 // 2) Upload the content to the upload URL.
1000 UploadRangeResponse response
;
1001 scoped_ptr
<ResourceEntry
> new_entry
;
1004 base::RunLoop run_loop
;
1005 ResumeUploadRequest
* resume_request
= new ResumeUploadRequest(
1006 request_sender_
.get(),
1007 test_util::CreateQuitCallback(
1009 test_util::CreateCopyResultCallback(&response
, &new_entry
)),
1012 0, // start_position
1013 kUploadContent
.size(), // end_position (exclusive)
1014 kUploadContent
.size(), // content_length,
1015 "text/plain", // content_type
1018 request_sender_
->StartRequestWithRetry(resume_request
);
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
);
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
);
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
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
));
1057 GDataErrorCode result_code
= GDATA_OTHER_ERROR
;
1060 // 1) Get the upload URL for uploading a new file.
1062 base::RunLoop run_loop
;
1063 InitiateUploadNewFileRequest
* initiate_request
=
1064 new InitiateUploadNewFileRequest(
1065 request_sender_
.get(),
1067 test_util::CreateQuitCallback(
1069 test_util::CreateCopyResultCallback(&result_code
, &upload_url
)),
1071 kUploadContent
.size(),
1074 request_sender_
->StartRequestWithRetry(initiate_request
);
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.
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"]);
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"
1097 http_request_
.content
);
1099 // 2) Before sending any data, check the current status.
1100 // This is an edge case test for GetUploadStatusRequest
1101 // (UploadRangeRequestBase).
1103 UploadRangeResponse response
;
1104 scoped_ptr
<ResourceEntry
> new_entry
;
1106 // Check the response by GetUploadStatusRequest.
1108 base::RunLoop run_loop
;
1109 GetUploadStatusRequest
* get_upload_status_request
=
1110 new GetUploadStatusRequest(
1111 request_sender_
.get(),
1112 test_util::CreateQuitCallback(
1114 test_util::CreateCopyResultCallback(&response
, &new_entry
)),
1116 kUploadContent
.size());
1117 request_sender_
->StartRequestWithRetry(get_upload_status_request
);
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());
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
);
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
);
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();
1151 UploadRangeResponse response
;
1152 scoped_ptr
<ResourceEntry
> new_entry
;
1155 base::RunLoop run_loop
;
1156 ResumeUploadRequest
* resume_request
= new ResumeUploadRequest(
1157 request_sender_
.get(),
1158 test_util::CreateQuitCallback(
1160 test_util::CreateCopyResultCallback(&response
, &new_entry
)),
1165 kUploadContent
.size(), // content_length,
1166 "text/plain", // content_type
1168 request_sender_
->StartRequestWithRetry(resume_request
);
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
);
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
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.
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
);
1202 // Check the response by GetUploadStatusRequest.
1204 base::RunLoop run_loop
;
1205 GetUploadStatusRequest
* get_upload_status_request
=
1206 new GetUploadStatusRequest(
1207 request_sender_
.get(),
1208 test_util::CreateQuitCallback(
1210 test_util::CreateCopyResultCallback(&response
, &new_entry
)),
1212 kUploadContent
.size());
1213 request_sender_
->StartRequestWithRetry(get_upload_status_request
);
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());
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
);
1234 EXPECT_EQ(kUploadContent
.size(), num_bytes_consumed
);
1237 // This test exercises InitiateUploadNewFileRequest and ResumeUploadRequest
1238 // for a scenario of uploading a new *empty* file.
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
));
1248 GDataErrorCode result_code
= GDATA_OTHER_ERROR
;
1251 // 1) Get the upload URL for uploading a new file.
1253 base::RunLoop run_loop
;
1254 InitiateUploadNewFileRequest
* initiate_request
=
1255 new InitiateUploadNewFileRequest(
1256 request_sender_
.get(),
1258 test_util::CreateQuitCallback(
1260 test_util::CreateCopyResultCallback(&result_code
, &upload_url
)),
1262 kUploadContent
.size(),
1265 request_sender_
->StartRequestWithRetry(initiate_request
);
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.
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"]);
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"
1288 http_request_
.content
);
1290 // 2) Upload the content to the upload URL.
1291 UploadRangeResponse response
;
1292 scoped_ptr
<ResourceEntry
> new_entry
;
1295 base::RunLoop run_loop
;
1296 ResumeUploadRequest
* resume_request
= new ResumeUploadRequest(
1297 request_sender_
.get(),
1298 test_util::CreateQuitCallback(
1300 test_util::CreateCopyResultCallback(&response
, &new_entry
)),
1303 0, // start_position
1304 kUploadContent
.size(), // end_position (exclusive)
1305 kUploadContent
.size(), // content_length,
1306 "text/plain", // content_type
1308 request_sender_
->StartRequestWithRetry(resume_request
);
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
);
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
);
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
));
1338 GDataErrorCode result_code
= GDATA_OTHER_ERROR
;
1341 // 1) Get the upload URL for uploading an existing file.
1343 base::RunLoop run_loop
;
1344 InitiateUploadExistingFileRequest
* initiate_request
=
1345 new InitiateUploadExistingFileRequest(
1346 request_sender_
.get(),
1348 test_util::CreateQuitCallback(
1350 test_util::CreateCopyResultCallback(&result_code
, &upload_url
)),
1352 kUploadContent
.size(),
1354 std::string() /* etag */);
1355 request_sender_
->StartRequestWithRetry(initiate_request
);
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
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
1375 EXPECT_TRUE(http_request_
.has_content
);
1376 EXPECT_EQ("", http_request_
.content
);
1377 EXPECT_EQ("*", http_request_
.headers
["If-Match"]);
1379 // 2) Upload the content to the upload URL.
1380 UploadRangeResponse response
;
1381 scoped_ptr
<ResourceEntry
> new_entry
;
1384 base::RunLoop run_loop
;
1385 ResumeUploadRequest
* resume_request
= new ResumeUploadRequest(
1386 request_sender_
.get(),
1387 test_util::CreateQuitCallback(
1389 test_util::CreateCopyResultCallback(&response
, &new_entry
)),
1392 0, // start_position
1393 kUploadContent
.size(), // end_position (exclusive)
1394 kUploadContent
.size(), // content_length,
1395 "text/plain", // content_type
1398 request_sender_
->StartRequestWithRetry(resume_request
);
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
);
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
);
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
));
1430 GDataErrorCode result_code
= GDATA_OTHER_ERROR
;
1433 // 1) Get the upload URL for uploading an existing file.
1435 base::RunLoop run_loop
;
1436 InitiateUploadExistingFileRequest
* initiate_request
=
1437 new InitiateUploadExistingFileRequest(
1438 request_sender_
.get(),
1440 test_util::CreateQuitCallback(
1442 test_util::CreateCopyResultCallback(&result_code
, &upload_url
)),
1444 kUploadContent
.size(),
1447 request_sender_
->StartRequestWithRetry(initiate_request
);
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
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
1467 EXPECT_TRUE(http_request_
.has_content
);
1468 EXPECT_EQ("", http_request_
.content
);
1469 EXPECT_EQ(kTestETag
, http_request_
.headers
["If-Match"]);
1471 // 2) Upload the content to the upload URL.
1472 UploadRangeResponse response
;
1473 scoped_ptr
<ResourceEntry
> new_entry
;
1476 base::RunLoop run_loop
;
1477 ResumeUploadRequest
* resume_request
= new ResumeUploadRequest(
1478 request_sender_
.get(),
1479 test_util::CreateQuitCallback(
1481 test_util::CreateCopyResultCallback(&response
, &new_entry
)),
1484 0, // start_position
1485 kUploadContent
.size(), // end_position (exclusive)
1486 kUploadContent
.size(), // content_length,
1487 "text/plain", // content_type
1489 request_sender_
->StartRequestWithRetry(resume_request
);
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
);
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
);
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
;
1522 base::RunLoop run_loop
;
1523 InitiateUploadExistingFileRequest
* initiate_request
=
1524 new InitiateUploadExistingFileRequest(
1525 request_sender_
.get(),
1527 test_util::CreateQuitCallback(
1529 test_util::CreateCopyResultCallback(&result_code
, &upload_url
)),
1531 kUploadContent
.size(),
1534 request_sender_
->StartRequestWithRetry(initiate_request
);
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
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
1553 EXPECT_TRUE(http_request_
.has_content
);
1554 EXPECT_EQ("", http_request_
.content
);
1555 EXPECT_EQ(kWrongETag
, http_request_
.headers
["If-Match"]);
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");
1564 GDataErrorCode result_code
= GDATA_OTHER_ERROR
;
1565 base::FilePath temp_file
;
1567 base::RunLoop run_loop
;
1568 DownloadFileRequest
* request
= new DownloadFileRequest(
1569 request_sender_
.get(),
1571 test_util::CreateQuitCallback(
1573 test_util::CreateCopyResultCallback(&result_code
, &temp_file
)),
1574 GetContentCallback(),
1576 kTestIdWithTypeLabel
,
1577 kDownloadedFilePath
);
1578 request_sender_
->StartRequestWithRetry(request
);
1582 std::string contents
;
1583 base::ReadFileToString(temp_file
, &contents
);
1584 base::DeleteFile(temp_file
, false);
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
);
1591 const std::string expected_contents
= kTestId
+ kTestId
+ kTestId
;
1592 EXPECT_EQ(expected_contents
, contents
);
1595 } // namespace google_apis