- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / drive / drive_uploader_unittest.cc
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 "chrome/browser/drive/drive_uploader.h"
6
7 #include <string>
8 #include <vector>
9
10 #include "base/bind.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/run_loop.h"
15 #include "base/values.h"
16 #include "chrome/browser/drive/dummy_drive_service.h"
17 #include "chrome/browser/google_apis/test_util.h"
18 #include "content/public/test/test_browser_thread_bundle.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20
21 using google_apis::CancelCallback;
22 using google_apis::GDataErrorCode;
23 using google_apis::GDATA_NO_CONNECTION;
24 using google_apis::GDATA_OTHER_ERROR;
25 using google_apis::HTTP_CONFLICT;
26 using google_apis::HTTP_CREATED;
27 using google_apis::HTTP_NOT_FOUND;
28 using google_apis::HTTP_PRECONDITION;
29 using google_apis::HTTP_RESUME_INCOMPLETE;
30 using google_apis::HTTP_SUCCESS;
31 using google_apis::InitiateUploadCallback;
32 using google_apis::ProgressCallback;
33 using google_apis::ResourceEntry;
34 using google_apis::UploadRangeCallback;
35 using google_apis::UploadRangeResponse;
36 namespace test_util = google_apis::test_util;
37
38 namespace drive {
39
40 namespace {
41
42 const char kTestDummyId[] = "file:dummy_id";
43 const char kTestDocumentTitle[] = "Hello world";
44 const char kTestInitiateUploadParentResourceId[] = "parent_resource_id";
45 const char kTestInitiateUploadResourceId[] = "resource_id";
46 const char kTestMimeType[] = "text/plain";
47 const char kTestUploadNewFileURL[] = "http://test/upload_location/new_file";
48 const char kTestUploadExistingFileURL[] =
49     "http://test/upload_location/existing_file";
50 const int64 kUploadChunkSize = 512 * 1024;
51 const char kTestETag[] = "test_etag";
52
53 // Mock DriveService that verifies if the uploaded content matches the preset
54 // expectation.
55 class MockDriveServiceWithUploadExpectation : public DummyDriveService {
56  public:
57   // Sets up an expected upload content. InitiateUpload and ResumeUpload will
58   // verify that the specified data is correctly uploaded.
59   MockDriveServiceWithUploadExpectation(
60       const base::FilePath& expected_upload_file,
61       int64 expected_content_length)
62      : expected_upload_file_(expected_upload_file),
63        expected_content_length_(expected_content_length),
64        received_bytes_(0),
65        resume_upload_call_count_(0) {}
66
67   int64 received_bytes() const { return received_bytes_; }
68   void set_received_bytes(int64 received_bytes) {
69     received_bytes_ = received_bytes;
70   }
71
72   int64 resume_upload_call_count() const { return resume_upload_call_count_; }
73
74  private:
75   // DriveServiceInterface overrides.
76   // Handles a request for obtaining an upload location URL.
77   virtual CancelCallback InitiateUploadNewFile(
78       const std::string& content_type,
79       int64 content_length,
80       const std::string& parent_resource_id,
81       const std::string& title,
82       const InitiateUploadCallback& callback) OVERRIDE {
83     EXPECT_EQ(kTestDocumentTitle, title);
84     EXPECT_EQ(kTestMimeType, content_type);
85     EXPECT_EQ(expected_content_length_, content_length);
86     EXPECT_EQ(kTestInitiateUploadParentResourceId, parent_resource_id);
87
88     // Calls back the upload URL for subsequent ResumeUpload requests.
89     // InitiateUpload is an asynchronous function, so don't callback directly.
90     base::MessageLoop::current()->PostTask(FROM_HERE,
91         base::Bind(callback, HTTP_SUCCESS, GURL(kTestUploadNewFileURL)));
92     return CancelCallback();
93   }
94
95   virtual CancelCallback InitiateUploadExistingFile(
96       const std::string& content_type,
97       int64 content_length,
98       const std::string& resource_id,
99       const std::string& etag,
100       const InitiateUploadCallback& callback) OVERRIDE {
101     EXPECT_EQ(kTestMimeType, content_type);
102     EXPECT_EQ(expected_content_length_, content_length);
103     EXPECT_EQ(kTestInitiateUploadResourceId, resource_id);
104
105     if (!etag.empty() && etag != kTestETag) {
106       base::MessageLoop::current()->PostTask(FROM_HERE,
107           base::Bind(callback, HTTP_PRECONDITION, GURL()));
108       return CancelCallback();
109     }
110
111     // Calls back the upload URL for subsequent ResumeUpload requests.
112     // InitiateUpload is an asynchronous function, so don't callback directly.
113     base::MessageLoop::current()->PostTask(FROM_HERE,
114         base::Bind(callback, HTTP_SUCCESS, GURL(kTestUploadExistingFileURL)));
115     return CancelCallback();
116   }
117
118   // Handles a request for uploading a chunk of bytes.
119   virtual CancelCallback ResumeUpload(
120       const GURL& upload_location,
121       int64 start_position,
122       int64 end_position,
123       int64 content_length,
124       const std::string& content_type,
125       const base::FilePath& local_file_path,
126       const UploadRangeCallback& callback,
127       const ProgressCallback& progress_callback) OVERRIDE {
128     // The upload range should start from the current first unreceived byte.
129     EXPECT_EQ(received_bytes_, start_position);
130     EXPECT_EQ(expected_upload_file_, local_file_path);
131
132     // The upload data must be split into 512KB chunks.
133     const int64 expected_chunk_end =
134         std::min(received_bytes_ + kUploadChunkSize, expected_content_length_);
135     EXPECT_EQ(expected_chunk_end, end_position);
136
137     // The upload URL returned by InitiateUpload() must be used.
138     EXPECT_TRUE(GURL(kTestUploadNewFileURL) == upload_location ||
139                 GURL(kTestUploadExistingFileURL) == upload_location);
140
141     // Other parameters should be the exact values passed to DriveUploader.
142     EXPECT_EQ(expected_content_length_, content_length);
143     EXPECT_EQ(kTestMimeType, content_type);
144
145     // Update the internal status of the current upload session.
146     resume_upload_call_count_++;
147     received_bytes_ = end_position;
148
149     // Callback progress
150     if (!progress_callback.is_null()) {
151       // For the testing purpose, it always notifies the progress at the end of
152       // each chunk uploading.
153       int64 chunk_size = end_position - start_position;
154       base::MessageLoop::current()->PostTask(FROM_HERE,
155           base::Bind(progress_callback, chunk_size, chunk_size));
156     }
157
158     SendUploadRangeResponse(upload_location, callback);
159     return CancelCallback();
160   }
161
162   // Handles a request to fetch the current upload status.
163   virtual CancelCallback GetUploadStatus(
164       const GURL& upload_location,
165       int64 content_length,
166       const UploadRangeCallback& callback) OVERRIDE {
167     EXPECT_EQ(expected_content_length_, content_length);
168     // The upload URL returned by InitiateUpload() must be used.
169     EXPECT_TRUE(GURL(kTestUploadNewFileURL) == upload_location ||
170                 GURL(kTestUploadExistingFileURL) == upload_location);
171
172     SendUploadRangeResponse(upload_location, callback);
173     return CancelCallback();
174   }
175
176   // Runs |callback| with the current upload status.
177   void SendUploadRangeResponse(const GURL& upload_location,
178                                const UploadRangeCallback& callback) {
179     // Callback with response.
180     UploadRangeResponse response;
181     scoped_ptr<ResourceEntry> entry;
182     if (received_bytes_ == expected_content_length_) {
183       GDataErrorCode response_code =
184           upload_location == GURL(kTestUploadNewFileURL) ?
185           HTTP_CREATED : HTTP_SUCCESS;
186       response = UploadRangeResponse(response_code, -1, -1);
187
188       base::DictionaryValue dict;
189       dict.Set("id.$t", new base::StringValue(kTestDummyId));
190       entry = ResourceEntry::CreateFrom(dict);
191     } else {
192       response = UploadRangeResponse(
193           HTTP_RESUME_INCOMPLETE, 0, received_bytes_);
194     }
195     // ResumeUpload is an asynchronous function, so don't callback directly.
196     base::MessageLoop::current()->PostTask(FROM_HERE,
197         base::Bind(callback, response, base::Passed(&entry)));
198   }
199
200   const base::FilePath expected_upload_file_;
201   const int64 expected_content_length_;
202   int64 received_bytes_;
203   int64 resume_upload_call_count_;
204 };
205
206 // Mock DriveService that returns a failure at InitiateUpload().
207 class MockDriveServiceNoConnectionAtInitiate : public DummyDriveService {
208   // Returns error.
209   virtual CancelCallback InitiateUploadNewFile(
210       const std::string& content_type,
211       int64 content_length,
212       const std::string& parent_resource_id,
213       const std::string& title,
214       const InitiateUploadCallback& callback) OVERRIDE {
215     base::MessageLoop::current()->PostTask(FROM_HERE,
216         base::Bind(callback, GDATA_NO_CONNECTION, GURL()));
217     return CancelCallback();
218   }
219
220   virtual CancelCallback InitiateUploadExistingFile(
221       const std::string& content_type,
222       int64 content_length,
223       const std::string& resource_id,
224       const std::string& etag,
225       const InitiateUploadCallback& callback) OVERRIDE {
226     base::MessageLoop::current()->PostTask(FROM_HERE,
227         base::Bind(callback, GDATA_NO_CONNECTION, GURL()));
228     return CancelCallback();
229   }
230
231   // Should not be used.
232   virtual CancelCallback ResumeUpload(
233       const GURL& upload_url,
234       int64 start_position,
235       int64 end_position,
236       int64 content_length,
237       const std::string& content_type,
238       const base::FilePath& local_file_path,
239       const UploadRangeCallback& callback,
240       const ProgressCallback& progress_callback) OVERRIDE {
241     NOTREACHED();
242     return CancelCallback();
243   }
244 };
245
246 // Mock DriveService that returns a failure at ResumeUpload().
247 class MockDriveServiceNoConnectionAtResume : public DummyDriveService {
248   // Succeeds and returns an upload location URL.
249   virtual CancelCallback InitiateUploadNewFile(
250       const std::string& content_type,
251       int64 content_length,
252       const std::string& parent_resource_id,
253       const std::string& title,
254       const InitiateUploadCallback& callback) OVERRIDE {
255     base::MessageLoop::current()->PostTask(FROM_HERE,
256         base::Bind(callback, HTTP_SUCCESS, GURL(kTestUploadNewFileURL)));
257     return CancelCallback();
258   }
259
260   virtual CancelCallback InitiateUploadExistingFile(
261       const std::string& content_type,
262       int64 content_length,
263       const std::string& resource_id,
264       const std::string& etag,
265       const InitiateUploadCallback& callback) OVERRIDE {
266     base::MessageLoop::current()->PostTask(FROM_HERE,
267         base::Bind(callback, HTTP_SUCCESS, GURL(kTestUploadExistingFileURL)));
268     return CancelCallback();
269   }
270
271   // Returns error.
272   virtual CancelCallback ResumeUpload(
273       const GURL& upload_url,
274       int64 start_position,
275       int64 end_position,
276       int64 content_length,
277       const std::string& content_type,
278       const base::FilePath& local_file_path,
279       const UploadRangeCallback& callback,
280       const ProgressCallback& progress_callback) OVERRIDE {
281     base::MessageLoop::current()->PostTask(FROM_HERE,
282         base::Bind(callback,
283                    UploadRangeResponse(GDATA_NO_CONNECTION, -1, -1),
284                    base::Passed(scoped_ptr<ResourceEntry>())));
285     return CancelCallback();
286   }
287 };
288
289 // Mock DriveService that returns a failure at GetUploadStatus().
290 class MockDriveServiceNoConnectionAtGetUploadStatus : public DummyDriveService {
291   // Returns error.
292   virtual CancelCallback GetUploadStatus(
293       const GURL& upload_url,
294       int64 content_length,
295       const UploadRangeCallback& callback) OVERRIDE {
296     base::MessageLoop::current()->PostTask(FROM_HERE,
297         base::Bind(callback,
298                    UploadRangeResponse(GDATA_NO_CONNECTION, -1, -1),
299                    base::Passed(scoped_ptr<ResourceEntry>())));
300     return CancelCallback();
301   }
302 };
303
304 class DriveUploaderTest : public testing::Test {
305  public:
306   virtual void SetUp() OVERRIDE {
307     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
308   }
309
310  protected:
311   content::TestBrowserThreadBundle thread_bundle_;
312   base::ScopedTempDir temp_dir_;
313 };
314
315 }  // namespace
316
317 TEST_F(DriveUploaderTest, UploadExisting0KB) {
318   base::FilePath local_path;
319   std::string data;
320   ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize(
321       temp_dir_.path(), 0, &local_path, &data));
322
323   GDataErrorCode error = GDATA_OTHER_ERROR;
324   GURL upload_location;
325   scoped_ptr<ResourceEntry> resource_entry;
326
327   MockDriveServiceWithUploadExpectation mock_service(local_path, data.size());
328   DriveUploader uploader(&mock_service,
329                          base::MessageLoopProxy::current().get());
330   std::vector<test_util::ProgressInfo> upload_progress_values;
331   uploader.UploadExistingFile(
332       kTestInitiateUploadResourceId,
333       local_path,
334       kTestMimeType,
335       std::string(),  // etag
336       test_util::CreateCopyResultCallback(
337           &error, &upload_location, &resource_entry),
338       base::Bind(&test_util::AppendProgressCallbackResult,
339                  &upload_progress_values));
340   base::RunLoop().RunUntilIdle();
341
342   EXPECT_EQ(1, mock_service.resume_upload_call_count());
343   EXPECT_EQ(0, mock_service.received_bytes());
344   EXPECT_EQ(HTTP_SUCCESS, error);
345   EXPECT_TRUE(upload_location.is_empty());
346   ASSERT_TRUE(resource_entry);
347   EXPECT_EQ(kTestDummyId, resource_entry->id());
348   ASSERT_EQ(1U, upload_progress_values.size());
349   EXPECT_EQ(test_util::ProgressInfo(0, 0), upload_progress_values[0]);
350 }
351
352 TEST_F(DriveUploaderTest, UploadExisting512KB) {
353   base::FilePath local_path;
354   std::string data;
355   ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize(
356       temp_dir_.path(), 512 * 1024, &local_path, &data));
357
358   GDataErrorCode error = GDATA_OTHER_ERROR;
359   GURL upload_location;
360   scoped_ptr<ResourceEntry> resource_entry;
361
362   MockDriveServiceWithUploadExpectation mock_service(local_path, data.size());
363   DriveUploader uploader(&mock_service,
364                          base::MessageLoopProxy::current().get());
365   std::vector<test_util::ProgressInfo> upload_progress_values;
366   uploader.UploadExistingFile(
367       kTestInitiateUploadResourceId,
368       local_path,
369       kTestMimeType,
370       std::string(),  // etag
371       test_util::CreateCopyResultCallback(
372           &error, &upload_location, &resource_entry),
373       base::Bind(&test_util::AppendProgressCallbackResult,
374                  &upload_progress_values));
375   base::RunLoop().RunUntilIdle();
376
377   // 512KB upload should not be split into multiple chunks.
378   EXPECT_EQ(1, mock_service.resume_upload_call_count());
379   EXPECT_EQ(512 * 1024, mock_service.received_bytes());
380   EXPECT_EQ(HTTP_SUCCESS, error);
381   EXPECT_TRUE(upload_location.is_empty());
382   ASSERT_TRUE(resource_entry);
383   EXPECT_EQ(kTestDummyId, resource_entry->id());
384   ASSERT_EQ(1U, upload_progress_values.size());
385   EXPECT_EQ(test_util::ProgressInfo(512 * 1024, 512 * 1024),
386             upload_progress_values[0]);
387 }
388
389 TEST_F(DriveUploaderTest, InitiateUploadFail) {
390   base::FilePath local_path;
391   std::string data;
392   ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize(
393       temp_dir_.path(), 512 * 1024, &local_path, &data));
394
395   GDataErrorCode error = HTTP_SUCCESS;
396   GURL upload_location;
397   scoped_ptr<ResourceEntry> resource_entry;
398
399   MockDriveServiceNoConnectionAtInitiate mock_service;
400   DriveUploader uploader(&mock_service,
401                          base::MessageLoopProxy::current().get());
402   uploader.UploadExistingFile(kTestInitiateUploadResourceId,
403                               local_path,
404                               kTestMimeType,
405                               std::string(),  // etag
406                               test_util::CreateCopyResultCallback(
407                                   &error, &upload_location, &resource_entry),
408                               google_apis::ProgressCallback());
409   base::RunLoop().RunUntilIdle();
410
411   EXPECT_EQ(GDATA_NO_CONNECTION, error);
412   EXPECT_TRUE(upload_location.is_empty());
413   EXPECT_FALSE(resource_entry);
414 }
415
416 TEST_F(DriveUploaderTest, InitiateUploadNoConflict) {
417   base::FilePath local_path;
418   std::string data;
419   ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize(
420       temp_dir_.path(), 512 * 1024, &local_path, &data));
421
422   GDataErrorCode error = GDATA_OTHER_ERROR;
423   GURL upload_location;
424   scoped_ptr<ResourceEntry> resource_entry;
425
426   MockDriveServiceWithUploadExpectation mock_service(local_path, data.size());
427   DriveUploader uploader(&mock_service,
428                          base::MessageLoopProxy::current().get());
429   uploader.UploadExistingFile(kTestInitiateUploadResourceId,
430                               local_path,
431                               kTestMimeType,
432                               kTestETag,
433                               test_util::CreateCopyResultCallback(
434                                   &error, &upload_location, &resource_entry),
435                               google_apis::ProgressCallback());
436   base::RunLoop().RunUntilIdle();
437
438   EXPECT_EQ(HTTP_SUCCESS, error);
439   EXPECT_TRUE(upload_location.is_empty());
440 }
441
442 TEST_F(DriveUploaderTest, InitiateUploadConflict) {
443   base::FilePath local_path;
444   std::string data;
445   ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize(
446       temp_dir_.path(), 512 * 1024, &local_path, &data));
447   const std::string kDestinationETag("destination_etag");
448
449   GDataErrorCode error = GDATA_OTHER_ERROR;
450   GURL upload_location;
451   scoped_ptr<ResourceEntry> resource_entry;
452
453   MockDriveServiceWithUploadExpectation mock_service(local_path, data.size());
454   DriveUploader uploader(&mock_service,
455                          base::MessageLoopProxy::current().get());
456   uploader.UploadExistingFile(kTestInitiateUploadResourceId,
457                               local_path,
458                               kTestMimeType,
459                               kDestinationETag,
460                               test_util::CreateCopyResultCallback(
461                                   &error, &upload_location, &resource_entry),
462                               google_apis::ProgressCallback());
463   base::RunLoop().RunUntilIdle();
464
465   EXPECT_EQ(HTTP_CONFLICT, error);
466   EXPECT_TRUE(upload_location.is_empty());
467 }
468
469 TEST_F(DriveUploaderTest, ResumeUploadFail) {
470   base::FilePath local_path;
471   std::string data;
472   ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize(
473       temp_dir_.path(), 512 * 1024, &local_path, &data));
474
475   GDataErrorCode error = HTTP_SUCCESS;
476   GURL upload_location;
477   scoped_ptr<ResourceEntry> resource_entry;
478
479   MockDriveServiceNoConnectionAtResume mock_service;
480   DriveUploader uploader(&mock_service,
481                          base::MessageLoopProxy::current().get());
482   uploader.UploadExistingFile(kTestInitiateUploadResourceId,
483                               local_path,
484                               kTestMimeType,
485                               std::string(),  // etag
486                               test_util::CreateCopyResultCallback(
487                                   &error, &upload_location, &resource_entry),
488                               google_apis::ProgressCallback());
489   base::RunLoop().RunUntilIdle();
490
491   EXPECT_EQ(GDATA_NO_CONNECTION, error);
492   EXPECT_EQ(GURL(kTestUploadExistingFileURL), upload_location);
493 }
494
495 TEST_F(DriveUploaderTest, GetUploadStatusFail) {
496   base::FilePath local_path;
497   std::string data;
498   ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize(
499       temp_dir_.path(), 512 * 1024, &local_path, &data));
500
501   GDataErrorCode error = HTTP_SUCCESS;
502   GURL upload_location;
503   scoped_ptr<ResourceEntry> resource_entry;
504
505   MockDriveServiceNoConnectionAtGetUploadStatus mock_service;
506   DriveUploader uploader(&mock_service,
507                          base::MessageLoopProxy::current().get());
508   uploader.ResumeUploadFile(GURL(kTestUploadExistingFileURL),
509                             local_path,
510                             kTestMimeType,
511                             test_util::CreateCopyResultCallback(
512                                 &error, &upload_location, &resource_entry),
513                             google_apis::ProgressCallback());
514   base::RunLoop().RunUntilIdle();
515
516   EXPECT_EQ(GDATA_NO_CONNECTION, error);
517   EXPECT_TRUE(upload_location.is_empty());
518 }
519
520 TEST_F(DriveUploaderTest, NonExistingSourceFile) {
521   GDataErrorCode error = GDATA_OTHER_ERROR;
522   GURL upload_location;
523   scoped_ptr<ResourceEntry> resource_entry;
524
525   DriveUploader uploader(NULL,  // NULL, the service won't be used.
526                          base::MessageLoopProxy::current().get());
527   uploader.UploadExistingFile(
528       kTestInitiateUploadResourceId,
529       temp_dir_.path().AppendASCII("_this_path_should_not_exist_"),
530       kTestMimeType,
531       std::string(),  // etag
532       test_util::CreateCopyResultCallback(
533           &error, &upload_location, &resource_entry),
534       google_apis::ProgressCallback());
535   base::RunLoop().RunUntilIdle();
536
537   // Should return failure without doing any attempt to connect to the server.
538   EXPECT_EQ(HTTP_NOT_FOUND, error);
539   EXPECT_TRUE(upload_location.is_empty());
540 }
541
542 TEST_F(DriveUploaderTest, ResumeUpload) {
543   base::FilePath local_path;
544   std::string data;
545   ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize(
546       temp_dir_.path(), 1024 * 1024, &local_path, &data));
547
548   GDataErrorCode error = GDATA_OTHER_ERROR;
549   GURL upload_location;
550   scoped_ptr<ResourceEntry> resource_entry;
551
552   MockDriveServiceWithUploadExpectation mock_service(local_path, data.size());
553   DriveUploader uploader(&mock_service,
554                          base::MessageLoopProxy::current().get());
555   // Emulate the situation that the only first part is successfully uploaded,
556   // but not the latter half.
557   mock_service.set_received_bytes(512 * 1024);
558
559   std::vector<test_util::ProgressInfo> upload_progress_values;
560   uploader.ResumeUploadFile(
561       GURL(kTestUploadExistingFileURL),
562       local_path,
563       kTestMimeType,
564       test_util::CreateCopyResultCallback(
565           &error, &upload_location, &resource_entry),
566       base::Bind(&test_util::AppendProgressCallbackResult,
567                  &upload_progress_values));
568   base::RunLoop().RunUntilIdle();
569
570   EXPECT_EQ(1, mock_service.resume_upload_call_count());
571   EXPECT_EQ(1024 * 1024, mock_service.received_bytes());
572   EXPECT_EQ(HTTP_SUCCESS, error);
573   EXPECT_TRUE(upload_location.is_empty());
574   ASSERT_TRUE(resource_entry);
575   EXPECT_EQ(kTestDummyId, resource_entry->id());
576   ASSERT_EQ(1U, upload_progress_values.size());
577   EXPECT_EQ(test_util::ProgressInfo(1024 * 1024, 1024 * 1024),
578             upload_progress_values[0]);
579 }
580
581 }  // namespace drive