drive_uploader_unittest.cc 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963
  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. #include "components/drive/drive_uploader.h"
  5. #include <stddef.h>
  6. #include <stdint.h>
  7. #include <algorithm>
  8. #include <memory>
  9. #include <string>
  10. #include <utility>
  11. #include <vector>
  12. #include "base/bind.h"
  13. #include "base/files/scoped_temp_dir.h"
  14. #include "base/memory/raw_ptr.h"
  15. #include "base/run_loop.h"
  16. #include "base/test/task_environment.h"
  17. #include "base/threading/thread_task_runner_handle.h"
  18. #include "base/values.h"
  19. #include "components/drive/service/dummy_drive_service.h"
  20. #include "google_apis/common/test_util.h"
  21. #include "google_apis/drive/drive_api_parser.h"
  22. #include "testing/gtest/include/gtest/gtest.h"
  23. using google_apis::ApiErrorCode;
  24. using google_apis::CancelCallbackOnce;
  25. using google_apis::FileResource;
  26. using google_apis::HTTP_CONFLICT;
  27. using google_apis::HTTP_CREATED;
  28. using google_apis::HTTP_NOT_FOUND;
  29. using google_apis::HTTP_PRECONDITION;
  30. using google_apis::HTTP_RESUME_INCOMPLETE;
  31. using google_apis::HTTP_SUCCESS;
  32. using google_apis::InitiateUploadCallback;
  33. using google_apis::NO_CONNECTION;
  34. using google_apis::OTHER_ERROR;
  35. using google_apis::ProgressCallback;
  36. using google_apis::UploadRangeResponse;
  37. using google_apis::drive::UploadRangeCallback;
  38. namespace test_util = google_apis::test_util;
  39. namespace drive {
  40. namespace {
  41. const char kTestDummyMd5[] = "dummy_md5";
  42. const char kTestDocumentTitle[] = "Hello world";
  43. const char kTestInitiateUploadParentResourceId[] = "parent_resource_id";
  44. const char kTestInitiateUploadResourceId[] = "resource_id";
  45. const char kTestMimeType[] = "text/plain";
  46. const char kTestUploadNewFileURL[] = "http://test/upload_location/new_file";
  47. const char kTestUploadExistingFileURL[] =
  48. "http://test/upload_location/existing_file";
  49. const int64_t kUploadChunkSize = 1024 * 1024 * 1024;
  50. const char kTestETag[] = "test_etag";
  51. CancelCallbackOnce SendMultipartUploadResult(
  52. ApiErrorCode response_code,
  53. int64_t content_length,
  54. google_apis::FileResourceCallback callback,
  55. google_apis::ProgressCallback progress_callback) {
  56. // Callback progress
  57. if (!progress_callback.is_null()) {
  58. // For the testing purpose, it always notifies the progress at the end of
  59. // whole file uploading.
  60. base::ThreadTaskRunnerHandle::Get()->PostTask(
  61. FROM_HERE,
  62. base::BindOnce(progress_callback, content_length, content_length));
  63. }
  64. // MultipartUploadXXXFile is an asynchronous function, so don't callback
  65. // directly.
  66. auto entry = std::make_unique<FileResource>();
  67. entry->set_md5_checksum(kTestDummyMd5);
  68. base::ThreadTaskRunnerHandle::Get()->PostTask(
  69. FROM_HERE,
  70. base::BindOnce(std::move(callback), response_code, std::move(entry)));
  71. return CancelCallbackOnce();
  72. }
  73. // Mock DriveService that verifies if the uploaded content matches the preset
  74. // expectation.
  75. class MockDriveServiceWithUploadExpectation : public DummyDriveService {
  76. public:
  77. // Sets up an expected upload content. InitiateUpload and ResumeUpload will
  78. // verify that the specified data is correctly uploaded.
  79. MockDriveServiceWithUploadExpectation(
  80. const base::FilePath& expected_upload_file,
  81. int64_t expected_content_length)
  82. : expected_upload_file_(expected_upload_file),
  83. expected_content_length_(expected_content_length),
  84. received_bytes_(0),
  85. resume_upload_call_count_(0),
  86. multipart_upload_call_count_(0) {}
  87. int64_t received_bytes() const { return received_bytes_; }
  88. void set_received_bytes(int64_t received_bytes) {
  89. received_bytes_ = received_bytes;
  90. }
  91. int64_t resume_upload_call_count() const { return resume_upload_call_count_; }
  92. int64_t multipart_upload_call_count() const {
  93. return multipart_upload_call_count_;
  94. }
  95. private:
  96. // DriveServiceInterface overrides.
  97. // Handles a request for obtaining an upload location URL.
  98. CancelCallbackOnce InitiateUploadNewFile(
  99. const std::string& content_type,
  100. int64_t content_length,
  101. const std::string& parent_resource_id,
  102. const std::string& title,
  103. const UploadNewFileOptions& options,
  104. InitiateUploadCallback callback) override {
  105. EXPECT_EQ(kTestDocumentTitle, title);
  106. EXPECT_EQ(kTestMimeType, content_type);
  107. EXPECT_EQ(expected_content_length_, content_length);
  108. EXPECT_EQ(kTestInitiateUploadParentResourceId, parent_resource_id);
  109. // Calls back the upload URL for subsequent ResumeUpload requests.
  110. // InitiateUpload is an asynchronous function, so don't callback directly.
  111. base::ThreadTaskRunnerHandle::Get()->PostTask(
  112. FROM_HERE, base::BindOnce(std::move(callback), HTTP_SUCCESS,
  113. GURL(kTestUploadNewFileURL)));
  114. return CancelCallbackOnce();
  115. }
  116. CancelCallbackOnce InitiateUploadExistingFile(
  117. const std::string& content_type,
  118. int64_t content_length,
  119. const std::string& resource_id,
  120. const UploadExistingFileOptions& options,
  121. InitiateUploadCallback callback) override {
  122. EXPECT_EQ(kTestMimeType, content_type);
  123. EXPECT_EQ(expected_content_length_, content_length);
  124. EXPECT_EQ(kTestInitiateUploadResourceId, resource_id);
  125. if (!options.etag.empty() && options.etag != kTestETag) {
  126. base::ThreadTaskRunnerHandle::Get()->PostTask(
  127. FROM_HERE,
  128. base::BindOnce(std::move(callback), HTTP_PRECONDITION, GURL()));
  129. return CancelCallbackOnce();
  130. }
  131. // Calls back the upload URL for subsequent ResumeUpload requests.
  132. // InitiateUpload is an asynchronous function, so don't callback directly.
  133. base::ThreadTaskRunnerHandle::Get()->PostTask(
  134. FROM_HERE, base::BindOnce(std::move(callback), HTTP_SUCCESS,
  135. GURL(kTestUploadExistingFileURL)));
  136. return CancelCallbackOnce();
  137. }
  138. // Handles a request for uploading a chunk of bytes.
  139. CancelCallbackOnce ResumeUpload(const GURL& upload_location,
  140. int64_t start_position,
  141. int64_t end_position,
  142. int64_t content_length,
  143. const std::string& content_type,
  144. const base::FilePath& local_file_path,
  145. UploadRangeCallback callback,
  146. ProgressCallback progress_callback) override {
  147. // The upload range should start from the current first unreceived byte.
  148. EXPECT_EQ(received_bytes_, start_position);
  149. EXPECT_EQ(expected_upload_file_, local_file_path);
  150. // The upload data must be split into 512KB chunks.
  151. const int64_t expected_chunk_end =
  152. std::min(received_bytes_ + kUploadChunkSize, expected_content_length_);
  153. EXPECT_EQ(expected_chunk_end, end_position);
  154. // The upload URL returned by InitiateUpload() must be used.
  155. EXPECT_TRUE(upload_location == kTestUploadNewFileURL ||
  156. upload_location == kTestUploadExistingFileURL);
  157. // Other parameters should be the exact values passed to DriveUploader.
  158. EXPECT_EQ(expected_content_length_, content_length);
  159. EXPECT_EQ(kTestMimeType, content_type);
  160. // Update the internal status of the current upload session.
  161. resume_upload_call_count_++;
  162. received_bytes_ = end_position;
  163. // Callback progress
  164. if (!progress_callback.is_null()) {
  165. // For the testing purpose, it always notifies the progress at the end of
  166. // each chunk uploading.
  167. int64_t chunk_size = end_position - start_position;
  168. base::ThreadTaskRunnerHandle::Get()->PostTask(
  169. FROM_HERE, base::BindOnce(progress_callback, chunk_size, chunk_size));
  170. }
  171. SendUploadRangeResponse(upload_location, std::move(callback));
  172. return CancelCallbackOnce();
  173. }
  174. // Handles a request to fetch the current upload status.
  175. CancelCallbackOnce GetUploadStatus(const GURL& upload_location,
  176. int64_t content_length,
  177. UploadRangeCallback callback) override {
  178. EXPECT_EQ(expected_content_length_, content_length);
  179. // The upload URL returned by InitiateUpload() must be used.
  180. EXPECT_TRUE(upload_location == kTestUploadNewFileURL ||
  181. upload_location == kTestUploadExistingFileURL);
  182. SendUploadRangeResponse(upload_location, std::move(callback));
  183. return CancelCallbackOnce();
  184. }
  185. // Runs |callback| with the current upload status.
  186. void SendUploadRangeResponse(const GURL& upload_location,
  187. UploadRangeCallback callback) {
  188. // Callback with response.
  189. UploadRangeResponse response;
  190. std::unique_ptr<FileResource> entry;
  191. if (received_bytes_ == expected_content_length_) {
  192. ApiErrorCode response_code = upload_location == kTestUploadNewFileURL
  193. ? HTTP_CREATED
  194. : HTTP_SUCCESS;
  195. response = UploadRangeResponse(response_code, -1, -1);
  196. entry = std::make_unique<FileResource>();
  197. entry->set_md5_checksum(kTestDummyMd5);
  198. } else {
  199. response =
  200. UploadRangeResponse(HTTP_RESUME_INCOMPLETE, 0, received_bytes_);
  201. }
  202. // ResumeUpload is an asynchronous function, so don't callback directly.
  203. base::ThreadTaskRunnerHandle::Get()->PostTask(
  204. FROM_HERE,
  205. base::BindOnce(std::move(callback), response, std::move(entry)));
  206. }
  207. CancelCallbackOnce MultipartUploadNewFile(
  208. const std::string& content_type,
  209. int64_t content_length,
  210. const std::string& parent_resource_id,
  211. const std::string& title,
  212. const base::FilePath& local_file_path,
  213. const UploadNewFileOptions& options,
  214. google_apis::FileResourceCallback callback,
  215. google_apis::ProgressCallback progress_callback) override {
  216. EXPECT_EQ(kTestMimeType, content_type);
  217. EXPECT_EQ(expected_content_length_, content_length);
  218. EXPECT_EQ(kTestInitiateUploadParentResourceId, parent_resource_id);
  219. EXPECT_EQ(kTestDocumentTitle, title);
  220. EXPECT_EQ(expected_upload_file_, local_file_path);
  221. received_bytes_ = content_length;
  222. multipart_upload_call_count_++;
  223. return SendMultipartUploadResult(HTTP_CREATED, content_length,
  224. std::move(callback), progress_callback);
  225. }
  226. CancelCallbackOnce MultipartUploadExistingFile(
  227. const std::string& content_type,
  228. int64_t content_length,
  229. const std::string& resource_id,
  230. const base::FilePath& local_file_path,
  231. const UploadExistingFileOptions& options,
  232. google_apis::FileResourceCallback callback,
  233. google_apis::ProgressCallback progress_callback) override {
  234. EXPECT_EQ(kTestMimeType, content_type);
  235. EXPECT_EQ(expected_content_length_, content_length);
  236. EXPECT_EQ(kTestInitiateUploadResourceId, resource_id);
  237. EXPECT_EQ(expected_upload_file_, local_file_path);
  238. if (!options.etag.empty() && options.etag != kTestETag) {
  239. base::ThreadTaskRunnerHandle::Get()->PostTask(
  240. FROM_HERE,
  241. base::BindOnce(std::move(callback), HTTP_PRECONDITION, nullptr));
  242. return CancelCallbackOnce();
  243. }
  244. received_bytes_ = content_length;
  245. multipart_upload_call_count_++;
  246. return SendMultipartUploadResult(HTTP_SUCCESS, content_length,
  247. std::move(callback), progress_callback);
  248. }
  249. const base::FilePath expected_upload_file_;
  250. const int64_t expected_content_length_;
  251. int64_t received_bytes_;
  252. int64_t resume_upload_call_count_;
  253. int64_t multipart_upload_call_count_;
  254. };
  255. // Mock DriveService that returns a failure at InitiateUpload().
  256. class MockDriveServiceNoConnectionAtInitiate : public DummyDriveService {
  257. // Returns error.
  258. CancelCallbackOnce InitiateUploadNewFile(
  259. const std::string& content_type,
  260. int64_t content_length,
  261. const std::string& parent_resource_id,
  262. const std::string& title,
  263. const UploadNewFileOptions& options,
  264. InitiateUploadCallback callback) override {
  265. base::ThreadTaskRunnerHandle::Get()->PostTask(
  266. FROM_HERE, base::BindOnce(std::move(callback), NO_CONNECTION, GURL()));
  267. return CancelCallbackOnce();
  268. }
  269. CancelCallbackOnce InitiateUploadExistingFile(
  270. const std::string& content_type,
  271. int64_t content_length,
  272. const std::string& resource_id,
  273. const UploadExistingFileOptions& options,
  274. InitiateUploadCallback callback) override {
  275. base::ThreadTaskRunnerHandle::Get()->PostTask(
  276. FROM_HERE, base::BindOnce(std::move(callback), NO_CONNECTION, GURL()));
  277. return CancelCallbackOnce();
  278. }
  279. // Should not be used.
  280. CancelCallbackOnce ResumeUpload(const GURL& upload_url,
  281. int64_t start_position,
  282. int64_t end_position,
  283. int64_t content_length,
  284. const std::string& content_type,
  285. const base::FilePath& local_file_path,
  286. UploadRangeCallback callback,
  287. ProgressCallback progress_callback) override {
  288. NOTREACHED();
  289. return CancelCallbackOnce();
  290. }
  291. CancelCallbackOnce MultipartUploadNewFile(
  292. const std::string& content_type,
  293. int64_t content_length,
  294. const std::string& parent_resource_id,
  295. const std::string& title,
  296. const base::FilePath& local_file_path,
  297. const UploadNewFileOptions& options,
  298. google_apis::FileResourceCallback callback,
  299. google_apis::ProgressCallback progress_callback) override {
  300. base::ThreadTaskRunnerHandle::Get()->PostTask(
  301. FROM_HERE, base::BindOnce(std::move(callback), NO_CONNECTION, nullptr));
  302. return CancelCallbackOnce();
  303. }
  304. CancelCallbackOnce MultipartUploadExistingFile(
  305. const std::string& content_type,
  306. int64_t content_length,
  307. const std::string& resource_id,
  308. const base::FilePath& local_file_path,
  309. const UploadExistingFileOptions& options,
  310. google_apis::FileResourceCallback callback,
  311. google_apis::ProgressCallback progress_callback) override {
  312. base::ThreadTaskRunnerHandle::Get()->PostTask(
  313. FROM_HERE, base::BindOnce(std::move(callback), NO_CONNECTION, nullptr));
  314. return CancelCallbackOnce();
  315. }
  316. };
  317. // Mock DriveService that returns a failure at ResumeUpload().
  318. class MockDriveServiceNoConnectionAtResume : public DummyDriveService {
  319. // Succeeds and returns an upload location URL.
  320. CancelCallbackOnce InitiateUploadNewFile(
  321. const std::string& content_type,
  322. int64_t content_length,
  323. const std::string& parent_resource_id,
  324. const std::string& title,
  325. const UploadNewFileOptions& options,
  326. InitiateUploadCallback callback) override {
  327. base::ThreadTaskRunnerHandle::Get()->PostTask(
  328. FROM_HERE, base::BindOnce(std::move(callback), HTTP_SUCCESS,
  329. GURL(kTestUploadNewFileURL)));
  330. return CancelCallbackOnce();
  331. }
  332. CancelCallbackOnce InitiateUploadExistingFile(
  333. const std::string& content_type,
  334. int64_t content_length,
  335. const std::string& resource_id,
  336. const UploadExistingFileOptions& options,
  337. InitiateUploadCallback callback) override {
  338. base::ThreadTaskRunnerHandle::Get()->PostTask(
  339. FROM_HERE, base::BindOnce(std::move(callback), HTTP_SUCCESS,
  340. GURL(kTestUploadExistingFileURL)));
  341. return CancelCallbackOnce();
  342. }
  343. // Returns error.
  344. CancelCallbackOnce ResumeUpload(const GURL& upload_url,
  345. int64_t start_position,
  346. int64_t end_position,
  347. int64_t content_length,
  348. const std::string& content_type,
  349. const base::FilePath& local_file_path,
  350. UploadRangeCallback callback,
  351. ProgressCallback progress_callback) override {
  352. base::ThreadTaskRunnerHandle::Get()->PostTask(
  353. FROM_HERE,
  354. base::BindOnce(std::move(callback),
  355. UploadRangeResponse(NO_CONNECTION, -1, -1), nullptr));
  356. return CancelCallbackOnce();
  357. }
  358. };
  359. // Mock DriveService that returns a failure at GetUploadStatus().
  360. class MockDriveServiceNoConnectionAtGetUploadStatus : public DummyDriveService {
  361. // Returns error.
  362. CancelCallbackOnce GetUploadStatus(const GURL& upload_url,
  363. int64_t content_length,
  364. UploadRangeCallback callback) override {
  365. base::ThreadTaskRunnerHandle::Get()->PostTask(
  366. FROM_HERE,
  367. base::BindOnce(std::move(callback),
  368. UploadRangeResponse(NO_CONNECTION, -1, -1), nullptr));
  369. return CancelCallbackOnce();
  370. }
  371. };
  372. class DriveUploaderTest : public testing::Test {
  373. public:
  374. void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
  375. protected:
  376. base::test::SingleThreadTaskEnvironment task_environment_;
  377. base::ScopedTempDir temp_dir_;
  378. };
  379. } // namespace
  380. TEST_F(DriveUploaderTest, UploadExisting0KB) {
  381. base::FilePath local_path;
  382. std::string data;
  383. ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize(temp_dir_.GetPath(), 0,
  384. &local_path, &data));
  385. ApiErrorCode error = OTHER_ERROR;
  386. GURL upload_location;
  387. std::unique_ptr<FileResource> entry;
  388. MockDriveServiceWithUploadExpectation mock_service(local_path, data.size());
  389. DriveUploader uploader(&mock_service,
  390. base::ThreadTaskRunnerHandle::Get().get(),
  391. mojo::NullRemote());
  392. std::vector<test_util::ProgressInfo> upload_progress_values;
  393. uploader.UploadExistingFile(
  394. kTestInitiateUploadResourceId, local_path, kTestMimeType,
  395. UploadExistingFileOptions(),
  396. test_util::CreateCopyResultCallback(&error, &upload_location, &entry),
  397. base::BindRepeating(&test_util::AppendProgressCallbackResult,
  398. &upload_progress_values));
  399. base::RunLoop().RunUntilIdle();
  400. EXPECT_EQ(0, mock_service.resume_upload_call_count());
  401. EXPECT_EQ(1, mock_service.multipart_upload_call_count());
  402. EXPECT_EQ(0, mock_service.received_bytes());
  403. EXPECT_EQ(HTTP_SUCCESS, error);
  404. EXPECT_TRUE(upload_location.is_empty());
  405. ASSERT_TRUE(entry);
  406. EXPECT_EQ(kTestDummyMd5, entry->md5_checksum());
  407. ASSERT_EQ(1U, upload_progress_values.size());
  408. EXPECT_EQ(test_util::ProgressInfo(0, 0), upload_progress_values[0]);
  409. }
  410. TEST_F(DriveUploaderTest, UploadExisting512KB) {
  411. base::FilePath local_path;
  412. std::string data;
  413. ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize(
  414. temp_dir_.GetPath(), 512 * 1024, &local_path, &data));
  415. ApiErrorCode error = OTHER_ERROR;
  416. GURL upload_location;
  417. std::unique_ptr<FileResource> entry;
  418. MockDriveServiceWithUploadExpectation mock_service(local_path, data.size());
  419. DriveUploader uploader(&mock_service,
  420. base::ThreadTaskRunnerHandle::Get().get(),
  421. mojo::NullRemote());
  422. std::vector<test_util::ProgressInfo> upload_progress_values;
  423. uploader.UploadExistingFile(
  424. kTestInitiateUploadResourceId, local_path, kTestMimeType,
  425. UploadExistingFileOptions(),
  426. test_util::CreateCopyResultCallback(&error, &upload_location, &entry),
  427. base::BindRepeating(&test_util::AppendProgressCallbackResult,
  428. &upload_progress_values));
  429. base::RunLoop().RunUntilIdle();
  430. // 512KB upload should be uploaded as multipart body.
  431. EXPECT_EQ(0, mock_service.resume_upload_call_count());
  432. EXPECT_EQ(1, mock_service.multipart_upload_call_count());
  433. EXPECT_EQ(512 * 1024, mock_service.received_bytes());
  434. EXPECT_EQ(HTTP_SUCCESS, error);
  435. EXPECT_TRUE(upload_location.is_empty());
  436. ASSERT_TRUE(entry);
  437. EXPECT_EQ(kTestDummyMd5, entry->md5_checksum());
  438. ASSERT_EQ(1U, upload_progress_values.size());
  439. EXPECT_EQ(test_util::ProgressInfo(512 * 1024, 512 * 1024),
  440. upload_progress_values[0]);
  441. }
  442. TEST_F(DriveUploaderTest, UploadExisting2MB) {
  443. base::FilePath local_path;
  444. std::string data;
  445. ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize(
  446. temp_dir_.GetPath(), 2 * 1024 * 1024, &local_path, &data));
  447. ApiErrorCode error = OTHER_ERROR;
  448. GURL upload_location;
  449. std::unique_ptr<FileResource> entry;
  450. MockDriveServiceWithUploadExpectation mock_service(local_path, data.size());
  451. DriveUploader uploader(&mock_service,
  452. base::ThreadTaskRunnerHandle::Get().get(),
  453. mojo::NullRemote());
  454. std::vector<test_util::ProgressInfo> upload_progress_values;
  455. uploader.UploadExistingFile(
  456. kTestInitiateUploadResourceId, local_path, kTestMimeType,
  457. UploadExistingFileOptions(),
  458. test_util::CreateCopyResultCallback(&error, &upload_location, &entry),
  459. base::BindRepeating(&test_util::AppendProgressCallbackResult,
  460. &upload_progress_values));
  461. base::RunLoop().RunUntilIdle();
  462. // 2MB upload should not be split into multiple chunks.
  463. EXPECT_EQ(1, mock_service.resume_upload_call_count());
  464. EXPECT_EQ(0, mock_service.multipart_upload_call_count());
  465. EXPECT_EQ(2 * 1024 * 1024, mock_service.received_bytes());
  466. EXPECT_EQ(HTTP_SUCCESS, error);
  467. EXPECT_TRUE(upload_location.is_empty());
  468. ASSERT_TRUE(entry);
  469. EXPECT_EQ(kTestDummyMd5, entry->md5_checksum());
  470. ASSERT_EQ(1U, upload_progress_values.size());
  471. EXPECT_EQ(test_util::ProgressInfo(2 * 1024 * 1024, 2 * 1024 * 1024),
  472. upload_progress_values[0]);
  473. }
  474. TEST_F(DriveUploaderTest, InitiateUploadFail) {
  475. base::FilePath local_path;
  476. std::string data;
  477. ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize(
  478. temp_dir_.GetPath(), 2 * 1024 * 1024, &local_path, &data));
  479. ApiErrorCode error = HTTP_SUCCESS;
  480. GURL upload_location;
  481. std::unique_ptr<FileResource> entry;
  482. MockDriveServiceNoConnectionAtInitiate mock_service;
  483. DriveUploader uploader(&mock_service,
  484. base::ThreadTaskRunnerHandle::Get().get(),
  485. mojo::NullRemote());
  486. uploader.UploadExistingFile(
  487. kTestInitiateUploadResourceId, local_path, kTestMimeType,
  488. UploadExistingFileOptions(),
  489. test_util::CreateCopyResultCallback(&error, &upload_location, &entry),
  490. google_apis::ProgressCallback());
  491. base::RunLoop().RunUntilIdle();
  492. EXPECT_EQ(NO_CONNECTION, error);
  493. EXPECT_TRUE(upload_location.is_empty());
  494. EXPECT_FALSE(entry);
  495. }
  496. TEST_F(DriveUploaderTest, MultipartUploadFail) {
  497. base::FilePath local_path;
  498. std::string data;
  499. ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize(
  500. temp_dir_.GetPath(), 512 * 1024, &local_path, &data));
  501. ApiErrorCode error = HTTP_SUCCESS;
  502. GURL upload_location;
  503. std::unique_ptr<FileResource> entry;
  504. MockDriveServiceNoConnectionAtInitiate mock_service;
  505. DriveUploader uploader(&mock_service,
  506. base::ThreadTaskRunnerHandle::Get().get(),
  507. mojo::NullRemote());
  508. uploader.UploadExistingFile(
  509. kTestInitiateUploadResourceId, local_path, kTestMimeType,
  510. UploadExistingFileOptions(),
  511. test_util::CreateCopyResultCallback(&error, &upload_location, &entry),
  512. google_apis::ProgressCallback());
  513. base::RunLoop().RunUntilIdle();
  514. EXPECT_EQ(NO_CONNECTION, error);
  515. EXPECT_TRUE(upload_location.is_empty());
  516. EXPECT_FALSE(entry);
  517. }
  518. TEST_F(DriveUploaderTest, InitiateUploadNoConflict) {
  519. base::FilePath local_path;
  520. std::string data;
  521. ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize(
  522. temp_dir_.GetPath(), 512 * 1024, &local_path, &data));
  523. ApiErrorCode error = OTHER_ERROR;
  524. GURL upload_location;
  525. std::unique_ptr<FileResource> entry;
  526. MockDriveServiceWithUploadExpectation mock_service(local_path, data.size());
  527. DriveUploader uploader(&mock_service,
  528. base::ThreadTaskRunnerHandle::Get().get(),
  529. mojo::NullRemote());
  530. UploadExistingFileOptions options;
  531. options.etag = kTestETag;
  532. uploader.UploadExistingFile(
  533. kTestInitiateUploadResourceId, local_path, kTestMimeType, options,
  534. test_util::CreateCopyResultCallback(&error, &upload_location, &entry),
  535. google_apis::ProgressCallback());
  536. base::RunLoop().RunUntilIdle();
  537. EXPECT_EQ(HTTP_SUCCESS, error);
  538. EXPECT_TRUE(upload_location.is_empty());
  539. }
  540. TEST_F(DriveUploaderTest, MultipartUploadConflict) {
  541. base::FilePath local_path;
  542. std::string data;
  543. ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize(
  544. temp_dir_.GetPath(), 512 * 1024, &local_path, &data));
  545. const std::string kDestinationETag("destination_etag");
  546. ApiErrorCode error = OTHER_ERROR;
  547. GURL upload_location;
  548. std::unique_ptr<FileResource> entry;
  549. MockDriveServiceWithUploadExpectation mock_service(local_path, data.size());
  550. DriveUploader uploader(&mock_service,
  551. base::ThreadTaskRunnerHandle::Get().get(),
  552. mojo::NullRemote());
  553. UploadExistingFileOptions options;
  554. options.etag = kDestinationETag;
  555. uploader.UploadExistingFile(
  556. kTestInitiateUploadResourceId, local_path, kTestMimeType, options,
  557. test_util::CreateCopyResultCallback(&error, &upload_location, &entry),
  558. google_apis::ProgressCallback());
  559. base::RunLoop().RunUntilIdle();
  560. EXPECT_EQ(HTTP_CONFLICT, error);
  561. EXPECT_TRUE(upload_location.is_empty());
  562. }
  563. TEST_F(DriveUploaderTest, InitiateUploadConflict) {
  564. base::FilePath local_path;
  565. std::string data;
  566. ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize(
  567. temp_dir_.GetPath(), 2 * 1024 * 1024, &local_path, &data));
  568. const std::string kDestinationETag("destination_etag");
  569. ApiErrorCode error = OTHER_ERROR;
  570. GURL upload_location;
  571. std::unique_ptr<FileResource> entry;
  572. MockDriveServiceWithUploadExpectation mock_service(local_path, data.size());
  573. DriveUploader uploader(&mock_service,
  574. base::ThreadTaskRunnerHandle::Get().get(),
  575. mojo::NullRemote());
  576. UploadExistingFileOptions options;
  577. options.etag = kDestinationETag;
  578. uploader.UploadExistingFile(
  579. kTestInitiateUploadResourceId, local_path, kTestMimeType, options,
  580. test_util::CreateCopyResultCallback(&error, &upload_location, &entry),
  581. google_apis::ProgressCallback());
  582. base::RunLoop().RunUntilIdle();
  583. EXPECT_EQ(HTTP_CONFLICT, error);
  584. EXPECT_TRUE(upload_location.is_empty());
  585. }
  586. TEST_F(DriveUploaderTest, ResumeUploadFail) {
  587. base::FilePath local_path;
  588. std::string data;
  589. ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize(
  590. temp_dir_.GetPath(), 2 * 1024 * 1024, &local_path, &data));
  591. ApiErrorCode error = HTTP_SUCCESS;
  592. GURL upload_location;
  593. std::unique_ptr<FileResource> entry;
  594. MockDriveServiceNoConnectionAtResume mock_service;
  595. DriveUploader uploader(&mock_service,
  596. base::ThreadTaskRunnerHandle::Get().get(),
  597. mojo::NullRemote());
  598. uploader.UploadExistingFile(
  599. kTestInitiateUploadResourceId, local_path, kTestMimeType,
  600. UploadExistingFileOptions(),
  601. test_util::CreateCopyResultCallback(&error, &upload_location, &entry),
  602. google_apis::ProgressCallback());
  603. base::RunLoop().RunUntilIdle();
  604. EXPECT_EQ(NO_CONNECTION, error);
  605. EXPECT_EQ(GURL(kTestUploadExistingFileURL), upload_location);
  606. }
  607. TEST_F(DriveUploaderTest, GetUploadStatusFail) {
  608. base::FilePath local_path;
  609. std::string data;
  610. ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize(
  611. temp_dir_.GetPath(), 2 * 1024 * 1024, &local_path, &data));
  612. ApiErrorCode error = HTTP_SUCCESS;
  613. GURL upload_location;
  614. std::unique_ptr<FileResource> entry;
  615. MockDriveServiceNoConnectionAtGetUploadStatus mock_service;
  616. DriveUploader uploader(&mock_service,
  617. base::ThreadTaskRunnerHandle::Get().get(),
  618. mojo::NullRemote());
  619. uploader.ResumeUploadFile(
  620. GURL(kTestUploadExistingFileURL), local_path, kTestMimeType,
  621. test_util::CreateCopyResultCallback(&error, &upload_location, &entry),
  622. google_apis::ProgressCallback());
  623. base::RunLoop().RunUntilIdle();
  624. EXPECT_EQ(NO_CONNECTION, error);
  625. EXPECT_TRUE(upload_location.is_empty());
  626. }
  627. TEST_F(DriveUploaderTest, NonExistingSourceFile) {
  628. ApiErrorCode error = OTHER_ERROR;
  629. GURL upload_location;
  630. std::unique_ptr<FileResource> entry;
  631. DriveUploader uploader(nullptr, // nullptr, the service won't be used.
  632. base::ThreadTaskRunnerHandle::Get().get(),
  633. mojo::NullRemote());
  634. uploader.UploadExistingFile(
  635. kTestInitiateUploadResourceId,
  636. temp_dir_.GetPath().AppendASCII("_this_path_should_not_exist_"),
  637. kTestMimeType, UploadExistingFileOptions(),
  638. test_util::CreateCopyResultCallback(&error, &upload_location, &entry),
  639. google_apis::ProgressCallback());
  640. base::RunLoop().RunUntilIdle();
  641. // Should return failure without doing any attempt to connect to the server.
  642. EXPECT_EQ(HTTP_NOT_FOUND, error);
  643. EXPECT_TRUE(upload_location.is_empty());
  644. }
  645. TEST_F(DriveUploaderTest, ResumeUpload) {
  646. base::FilePath local_path;
  647. std::string data;
  648. ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize(
  649. temp_dir_.GetPath(), 1024 * 1024, &local_path, &data));
  650. ApiErrorCode error = OTHER_ERROR;
  651. GURL upload_location;
  652. std::unique_ptr<FileResource> entry;
  653. MockDriveServiceWithUploadExpectation mock_service(local_path, data.size());
  654. DriveUploader uploader(&mock_service,
  655. base::ThreadTaskRunnerHandle::Get().get(),
  656. mojo::NullRemote());
  657. // Emulate the situation that the only first part is successfully uploaded,
  658. // but not the latter half.
  659. mock_service.set_received_bytes(512 * 1024);
  660. std::vector<test_util::ProgressInfo> upload_progress_values;
  661. uploader.ResumeUploadFile(
  662. GURL(kTestUploadExistingFileURL), local_path, kTestMimeType,
  663. test_util::CreateCopyResultCallback(&error, &upload_location, &entry),
  664. base::BindRepeating(&test_util::AppendProgressCallbackResult,
  665. &upload_progress_values));
  666. base::RunLoop().RunUntilIdle();
  667. EXPECT_EQ(1, mock_service.resume_upload_call_count());
  668. EXPECT_EQ(1024 * 1024, mock_service.received_bytes());
  669. EXPECT_EQ(HTTP_SUCCESS, error);
  670. EXPECT_TRUE(upload_location.is_empty());
  671. ASSERT_TRUE(entry);
  672. EXPECT_EQ(kTestDummyMd5, entry->md5_checksum());
  673. ASSERT_EQ(1U, upload_progress_values.size());
  674. EXPECT_EQ(test_util::ProgressInfo(1024 * 1024, 1024 * 1024),
  675. upload_progress_values[0]);
  676. }
  677. class MockDriveServiceForBatchProcessing : public DummyDriveService {
  678. public:
  679. struct UploadFileInfo {
  680. enum { NEW_FILE, EXISTING_FILE } type;
  681. std::string content_type;
  682. uint64_t content_length;
  683. std::string parent_resource_id;
  684. std::string resource_id;
  685. std::string title;
  686. base::FilePath local_file_path;
  687. google_apis::FileResourceCallback callback;
  688. google_apis::ProgressCallback progress_callback;
  689. };
  690. class BatchRequestConfigurator : public BatchRequestConfiguratorInterface {
  691. public:
  692. explicit BatchRequestConfigurator(
  693. MockDriveServiceForBatchProcessing* service)
  694. : service(service) {}
  695. CancelCallbackOnce MultipartUploadNewFile(
  696. const std::string& content_type,
  697. int64_t content_length,
  698. const std::string& parent_resource_id,
  699. const std::string& title,
  700. const base::FilePath& local_file_path,
  701. const UploadNewFileOptions& options,
  702. google_apis::FileResourceCallback callback,
  703. google_apis::ProgressCallback progress_callback) override {
  704. UploadFileInfo info;
  705. info.type = UploadFileInfo::NEW_FILE;
  706. info.content_type = content_type;
  707. info.content_length = content_length;
  708. info.parent_resource_id = parent_resource_id;
  709. info.title = title;
  710. info.local_file_path = local_file_path;
  711. info.callback = std::move(callback);
  712. info.progress_callback = progress_callback;
  713. service->files.push_back(std::move(info));
  714. return CancelCallbackOnce();
  715. }
  716. CancelCallbackOnce MultipartUploadExistingFile(
  717. const std::string& content_type,
  718. int64_t content_length,
  719. const std::string& resource_id,
  720. const base::FilePath& local_file_path,
  721. const UploadExistingFileOptions& options,
  722. google_apis::FileResourceCallback callback,
  723. google_apis::ProgressCallback progress_callback) override {
  724. UploadFileInfo info;
  725. info.type = UploadFileInfo::EXISTING_FILE;
  726. info.content_type = content_type;
  727. info.content_length = content_length;
  728. info.resource_id = resource_id;
  729. info.local_file_path = local_file_path;
  730. info.callback = std::move(callback);
  731. info.progress_callback = progress_callback;
  732. service->files.push_back(std::move(info));
  733. return CancelCallbackOnce();
  734. }
  735. void Commit() override {
  736. ASSERT_FALSE(service->committed);
  737. service->committed = true;
  738. for (auto& file : service->files) {
  739. SendMultipartUploadResult(HTTP_SUCCESS, file.content_length,
  740. std::move(file.callback),
  741. file.progress_callback);
  742. }
  743. }
  744. private:
  745. raw_ptr<MockDriveServiceForBatchProcessing> service;
  746. };
  747. public:
  748. std::unique_ptr<BatchRequestConfiguratorInterface> StartBatchRequest()
  749. override {
  750. committed = false;
  751. return std::unique_ptr<BatchRequestConfiguratorInterface>(
  752. new BatchRequestConfigurator(this));
  753. }
  754. std::vector<UploadFileInfo> files;
  755. bool committed;
  756. };
  757. TEST_F(DriveUploaderTest, BatchProcessing) {
  758. // Preapre test file.
  759. const size_t kTestFileSize = 1024 * 512;
  760. base::FilePath local_path;
  761. std::string data;
  762. ASSERT_TRUE(test_util::CreateFileOfSpecifiedSize(
  763. temp_dir_.GetPath(), kTestFileSize, &local_path, &data));
  764. // Prepare test target.
  765. MockDriveServiceForBatchProcessing service;
  766. DriveUploader uploader(&service, base::ThreadTaskRunnerHandle::Get().get(),
  767. mojo::NullRemote());
  768. struct {
  769. ApiErrorCode error;
  770. GURL resume_url;
  771. std::unique_ptr<FileResource> file;
  772. UploadCompletionCallback callback() {
  773. return test_util::CreateCopyResultCallback(&error, &resume_url, &file);
  774. }
  775. } results[2];
  776. uploader.StartBatchProcessing();
  777. uploader.UploadNewFile("parent_resource_id", local_path, "title",
  778. kTestMimeType, UploadNewFileOptions(),
  779. results[0].callback(),
  780. google_apis::ProgressCallback());
  781. uploader.UploadExistingFile(
  782. "resource_id", local_path, kTestMimeType, UploadExistingFileOptions(),
  783. results[1].callback(), google_apis::ProgressCallback());
  784. uploader.StopBatchProcessing();
  785. base::RunLoop().RunUntilIdle();
  786. ASSERT_EQ(2u, service.files.size());
  787. EXPECT_TRUE(service.committed);
  788. EXPECT_EQ(MockDriveServiceForBatchProcessing::UploadFileInfo::NEW_FILE,
  789. service.files[0].type);
  790. EXPECT_EQ(kTestMimeType, service.files[0].content_type);
  791. EXPECT_EQ(kTestFileSize, service.files[0].content_length);
  792. EXPECT_EQ("parent_resource_id", service.files[0].parent_resource_id);
  793. EXPECT_EQ("", service.files[0].resource_id);
  794. EXPECT_EQ("title", service.files[0].title);
  795. EXPECT_EQ(local_path.value(), service.files[0].local_file_path.value());
  796. EXPECT_EQ(MockDriveServiceForBatchProcessing::UploadFileInfo::EXISTING_FILE,
  797. service.files[1].type);
  798. EXPECT_EQ(kTestMimeType, service.files[1].content_type);
  799. EXPECT_EQ(kTestFileSize, service.files[1].content_length);
  800. EXPECT_EQ("", service.files[1].parent_resource_id);
  801. EXPECT_EQ("resource_id", service.files[1].resource_id);
  802. EXPECT_EQ("", service.files[1].title);
  803. EXPECT_EQ(local_path.value(), service.files[1].local_file_path.value());
  804. EXPECT_EQ(HTTP_SUCCESS, results[0].error);
  805. EXPECT_TRUE(results[0].resume_url.is_empty());
  806. EXPECT_TRUE(results[0].file);
  807. EXPECT_EQ(HTTP_SUCCESS, results[1].error);
  808. EXPECT_TRUE(results[1].resume_url.is_empty());
  809. EXPECT_TRUE(results[1].file);
  810. }
  811. TEST_F(DriveUploaderTest, BatchProcessingWithError) {
  812. // Prepare test target.
  813. MockDriveServiceForBatchProcessing service;
  814. DriveUploader uploader(&service, base::ThreadTaskRunnerHandle::Get().get(),
  815. mojo::NullRemote());
  816. struct {
  817. ApiErrorCode error;
  818. GURL resume_url;
  819. std::unique_ptr<FileResource> file;
  820. UploadCompletionCallback callback() {
  821. return test_util::CreateCopyResultCallback(&error, &resume_url, &file);
  822. }
  823. } results[2];
  824. uploader.StartBatchProcessing();
  825. uploader.UploadNewFile("parent_resource_id",
  826. base::FilePath(FILE_PATH_LITERAL("/path/non_exists")),
  827. "title", kTestMimeType, UploadNewFileOptions(),
  828. results[0].callback(),
  829. google_apis::ProgressCallback());
  830. uploader.UploadExistingFile(
  831. "resource_id", base::FilePath(FILE_PATH_LITERAL("/path/non_exists")),
  832. kTestMimeType, UploadExistingFileOptions(), results[1].callback(),
  833. google_apis::ProgressCallback());
  834. uploader.StopBatchProcessing();
  835. base::RunLoop().RunUntilIdle();
  836. EXPECT_EQ(0u, service.files.size());
  837. EXPECT_TRUE(service.committed);
  838. EXPECT_EQ(HTTP_NOT_FOUND, results[0].error);
  839. EXPECT_TRUE(results[0].resume_url.is_empty());
  840. EXPECT_FALSE(results[0].file);
  841. EXPECT_EQ(HTTP_NOT_FOUND, results[1].error);
  842. EXPECT_TRUE(results[1].resume_url.is_empty());
  843. EXPECT_FALSE(results[1].file);
  844. }
  845. } // namespace drive