| OLD | NEW |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/download/parallel_download_job.h" | 5 #include "content/browser/download/parallel_download_job.h" |
| 6 | 6 |
| 7 #include <utility> | 7 #include <utility> |
| 8 #include <vector> | 8 #include <vector> |
| 9 | 9 |
| 10 #include "base/memory/ptr_util.h" | 10 #include "base/memory/ptr_util.h" |
| (...skipping 13 matching lines...) Expand all Loading... |
| 24 class MockDownloadRequestHandle : public DownloadRequestHandleInterface { | 24 class MockDownloadRequestHandle : public DownloadRequestHandleInterface { |
| 25 public: | 25 public: |
| 26 MOCK_CONST_METHOD0(GetWebContents, WebContents*()); | 26 MOCK_CONST_METHOD0(GetWebContents, WebContents*()); |
| 27 MOCK_CONST_METHOD0(GetDownloadManager, DownloadManager*()); | 27 MOCK_CONST_METHOD0(GetDownloadManager, DownloadManager*()); |
| 28 MOCK_CONST_METHOD0(PauseRequest, void()); | 28 MOCK_CONST_METHOD0(PauseRequest, void()); |
| 29 MOCK_CONST_METHOD0(ResumeRequest, void()); | 29 MOCK_CONST_METHOD0(ResumeRequest, void()); |
| 30 MOCK_CONST_METHOD0(CancelRequest, void()); | 30 MOCK_CONST_METHOD0(CancelRequest, void()); |
| 31 MOCK_CONST_METHOD0(DebugString, std::string()); | 31 MOCK_CONST_METHOD0(DebugString, std::string()); |
| 32 }; | 32 }; |
| 33 | 33 |
| 34 class DownloadWorkerForTest : public DownloadWorker { |
| 35 public: |
| 36 DownloadWorkerForTest(DownloadWorker::Delegate* delegate, |
| 37 int64_t offset, |
| 38 int64_t length) |
| 39 : DownloadWorker(delegate, offset, length) {} |
| 40 |
| 41 // For passing in a mock request handle. |
| 42 void MakeResponseReady() { |
| 43 UrlDownloader::Delegate* delegate = |
| 44 static_cast<UrlDownloader::Delegate*>(this); |
| 45 |
| 46 std::unique_ptr<DownloadCreateInfo> create_info = |
| 47 base::MakeUnique<DownloadCreateInfo>(); |
| 48 std::unique_ptr<MockDownloadRequestHandle> mock_handle = |
| 49 base::MakeUnique<MockDownloadRequestHandle>(); |
| 50 mock_request_handle_ = mock_handle.get(); |
| 51 create_info->request_handle = std::move(mock_handle); |
| 52 delegate->OnUrlDownloaderStarted( |
| 53 std::move(create_info), std::unique_ptr<ByteStreamReader>(), |
| 54 DownloadUrlParameters::OnStartedCallback()); |
| 55 } |
| 56 |
| 57 MockDownloadRequestHandle* mock_request_handle() const { |
| 58 return mock_request_handle_; |
| 59 } |
| 60 |
| 61 private: |
| 62 MockDownloadRequestHandle* mock_request_handle_ = nullptr; |
| 63 }; |
| 64 |
| 34 } // namespace | 65 } // namespace |
| 35 | 66 |
| 36 class ParallelDownloadJobForTest : public ParallelDownloadJob { | 67 class ParallelDownloadJobForTest : public ParallelDownloadJob { |
| 37 public: | 68 public: |
| 38 ParallelDownloadJobForTest( | 69 ParallelDownloadJobForTest( |
| 39 DownloadItemImpl* download_item, | 70 DownloadItemImpl* download_item, |
| 40 std::unique_ptr<DownloadRequestHandleInterface> request_handle, | 71 std::unique_ptr<DownloadRequestHandleInterface> request_handle, |
| 41 const DownloadCreateInfo& create_info, | 72 const DownloadCreateInfo& create_info, |
| 42 int request_count) | 73 int request_count) |
| 43 : ParallelDownloadJob(download_item, | 74 : ParallelDownloadJob(download_item, |
| 44 std::move(request_handle), | 75 std::move(request_handle), |
| 45 create_info), | 76 create_info), |
| 46 request_count_(request_count) {} | 77 request_count_(request_count) {} |
| 47 | 78 |
| 79 const std::vector<DownloadWorkerForTest*>& mock_workers() { |
| 80 return mock_workers_; |
| 81 } |
| 82 |
| 48 void CreateRequest(int64_t offset, int64_t length) override { | 83 void CreateRequest(int64_t offset, int64_t length) override { |
| 49 fake_tasks_.push_back(std::pair<int64_t, int64_t>(offset, length)); | 84 std::unique_ptr<DownloadWorkerForTest> mock_worker = |
| 85 base::MakeUnique<DownloadWorkerForTest>(this, offset, length); |
| 86 mock_workers_.push_back(mock_worker.get()); |
| 87 |
| 88 DCHECK(workers_.find(offset) == workers_.end()); |
| 89 workers_[offset] = std::move(mock_worker); |
| 50 } | 90 } |
| 51 | 91 |
| 52 int GetParallelRequestCount() const override { return request_count_; } | 92 int GetParallelRequestCount() const override { return request_count_; } |
| 53 | 93 |
| 54 std::vector<std::pair<int64_t, int64_t>> fake_tasks_; | 94 private: |
| 95 std::vector<DownloadWorkerForTest*> mock_workers_; |
| 96 int request_count_; |
| 55 | 97 |
| 56 private: | |
| 57 int request_count_; | |
| 58 DISALLOW_COPY_AND_ASSIGN(ParallelDownloadJobForTest); | 98 DISALLOW_COPY_AND_ASSIGN(ParallelDownloadJobForTest); |
| 59 }; | 99 }; |
| 60 | 100 |
| 61 class ParallelDownloadJobTest : public testing::Test { | 101 class ParallelDownloadJobTest : public testing::Test { |
| 62 public: | 102 public: |
| 63 void CreateParallelJob(int64_t offset, | 103 void CreateParallelJob(int64_t offset, |
| 64 int64_t content_length, | 104 int64_t content_length, |
| 65 const DownloadItem::ReceivedSlices& slices, | 105 const DownloadItem::ReceivedSlices& slices, |
| 66 int request_count) { | 106 int request_count) { |
| 67 item_delegate_ = base::MakeUnique<DownloadItemImplDelegate>(); | 107 item_delegate_ = base::MakeUnique<DownloadItemImplDelegate>(); |
| 68 download_item_ = base::MakeUnique<NiceMock<MockDownloadItemImpl>>( | 108 download_item_ = base::MakeUnique<NiceMock<MockDownloadItemImpl>>( |
| 69 item_delegate_.get(), slices); | 109 item_delegate_.get(), slices); |
| 70 DownloadCreateInfo info; | 110 DownloadCreateInfo info; |
| 71 info.offset = offset; | 111 info.offset = offset; |
| 72 info.total_bytes = content_length; | 112 info.total_bytes = content_length; |
| 113 std::unique_ptr<MockDownloadRequestHandle> request_handle = |
| 114 base::MakeUnique<MockDownloadRequestHandle>(); |
| 115 mock_request_handle_ = request_handle.get(); |
| 73 job_ = base::MakeUnique<ParallelDownloadJobForTest>( | 116 job_ = base::MakeUnique<ParallelDownloadJobForTest>( |
| 74 download_item_.get(), base::MakeUnique<MockDownloadRequestHandle>(), | 117 download_item_.get(), std::move(request_handle), info, request_count); |
| 75 info, request_count); | |
| 76 } | 118 } |
| 77 | 119 |
| 78 void DestroyParallelJob() { | 120 void DestroyParallelJob() { |
| 79 job_.reset(); | 121 job_.reset(); |
| 80 download_item_.reset(); | 122 download_item_.reset(); |
| 81 item_delegate_.reset(); | 123 item_delegate_.reset(); |
| 124 mock_request_handle_ = nullptr; |
| 82 } | 125 } |
| 83 | 126 |
| 84 void BuildParallelRequests() { job_->BuildParallelRequests(); } | 127 void BuildParallelRequests() { job_->BuildParallelRequests(); } |
| 85 | 128 |
| 129 // Simulate all workers establishing byte streams. |
| 130 void PushAllByteStream() { |
| 131 for (auto& worker : job_->workers_) |
| 132 job_->OnByteStreamReady(worker.second.get(), |
| 133 std::unique_ptr<ByteStreamReader>()); |
| 134 } |
| 135 |
| 136 void VerifyWorker(int64_t offset, int64_t length) const { |
| 137 EXPECT_TRUE(job_->workers_.find(offset) != job_->workers_.end()); |
| 138 EXPECT_EQ(offset, job_->workers_[offset]->offset()); |
| 139 EXPECT_EQ(length, job_->workers_[offset]->length()); |
| 140 } |
| 141 |
| 86 content::TestBrowserThreadBundle browser_threads_; | 142 content::TestBrowserThreadBundle browser_threads_; |
| 87 std::unique_ptr<DownloadItemImplDelegate> item_delegate_; | 143 std::unique_ptr<DownloadItemImplDelegate> item_delegate_; |
| 88 std::unique_ptr<MockDownloadItemImpl> download_item_; | 144 std::unique_ptr<MockDownloadItemImpl> download_item_; |
| 89 std::unique_ptr<ParallelDownloadJobForTest> job_; | 145 std::unique_ptr<ParallelDownloadJobForTest> job_; |
| 146 // Request handle for the original request. |
| 147 MockDownloadRequestHandle* mock_request_handle_; |
| 90 }; | 148 }; |
| 91 | 149 |
| 92 // Test if parallel requests can be built correctly for a new download. | 150 // Test if parallel requests can be built correctly for a new download. |
| 93 TEST_F(ParallelDownloadJobTest, CreateNewDownloadRequests) { | 151 TEST_F(ParallelDownloadJobTest, CreateNewDownloadRequests) { |
| 94 // Totally 2 requests for 100 bytes. | 152 // Totally 2 requests for 100 bytes. |
| 95 // Original request: Range:0-49, for 50 bytes. | 153 // Original request: Range:0-49, for 50 bytes. |
| 96 // Task 1: Range:50-, for 50 bytes. | 154 // Task 1: Range:50-, for 50 bytes. |
| 97 CreateParallelJob(0, 100, DownloadItem::ReceivedSlices(), 2); | 155 CreateParallelJob(0, 100, DownloadItem::ReceivedSlices(), 2); |
| 98 BuildParallelRequests(); | 156 BuildParallelRequests(); |
| 99 EXPECT_EQ(1, static_cast<int>(job_->fake_tasks_.size())); | 157 EXPECT_EQ(1, static_cast<int>(job_->mock_workers().size())); |
| 100 EXPECT_EQ(50, job_->fake_tasks_[0].first); | 158 VerifyWorker(50, 0); |
| 101 EXPECT_EQ(0, job_->fake_tasks_[0].second); | |
| 102 job_->fake_tasks_.clear(); | |
| 103 DestroyParallelJob(); | 159 DestroyParallelJob(); |
| 104 | 160 |
| 105 // Totally 3 requests for 100 bytes. | 161 // Totally 3 requests for 100 bytes. |
| 106 // Original request: Range:0-32, for 33 bytes. | 162 // Original request: Range:0-32, for 33 bytes. |
| 107 // Task 1: Range:33-65, for 33 bytes. | 163 // Task 1: Range:33-65, for 33 bytes. |
| 108 // Task 2: Range:66-, for 34 bytes. | 164 // Task 2: Range:66-, for 34 bytes. |
| 109 CreateParallelJob(0, 100, DownloadItem::ReceivedSlices(), 3); | 165 CreateParallelJob(0, 100, DownloadItem::ReceivedSlices(), 3); |
| 110 BuildParallelRequests(); | 166 BuildParallelRequests(); |
| 111 EXPECT_EQ(2, static_cast<int>(job_->fake_tasks_.size())); | 167 EXPECT_EQ(2, static_cast<int>(job_->mock_workers().size())); |
| 112 EXPECT_EQ(33, job_->fake_tasks_[0].first); | 168 VerifyWorker(33, 33); |
| 113 EXPECT_EQ(33, job_->fake_tasks_[0].second); | 169 VerifyWorker(66, 0); |
| 114 EXPECT_EQ(66, job_->fake_tasks_[1].first); | |
| 115 EXPECT_EQ(0, job_->fake_tasks_[1].second); | |
| 116 job_->fake_tasks_.clear(); | |
| 117 DestroyParallelJob(); | 170 DestroyParallelJob(); |
| 118 | 171 |
| 119 // Totally 3 requests for 100 bytes. Start from the 17th byte. | 172 // Totally 3 requests for 100 bytes. Start from the 17th byte. |
| 120 // Original request: Range:17-43, for 27 bytes. | 173 // Original request: Range:17-43, for 27 bytes. |
| 121 // Task 1: Range:44-70, for 27 bytes. | 174 // Task 1: Range:44-70, for 27 bytes. |
| 122 // Task 2: Range:71-99, for 29 bytes. | 175 // Task 2: Range:71-99, for 29 bytes. |
| 123 CreateParallelJob(17, 83, DownloadItem::ReceivedSlices(), 3); | 176 CreateParallelJob(17, 83, DownloadItem::ReceivedSlices(), 3); |
| 124 BuildParallelRequests(); | 177 BuildParallelRequests(); |
| 125 EXPECT_EQ(2, static_cast<int>(job_->fake_tasks_.size())); | 178 EXPECT_EQ(2, static_cast<int>(job_->mock_workers().size())); |
| 126 EXPECT_EQ(44, job_->fake_tasks_[0].first); | 179 VerifyWorker(44, 27); |
| 127 EXPECT_EQ(27, job_->fake_tasks_[0].second); | 180 VerifyWorker(71, 0); |
| 128 EXPECT_EQ(71, job_->fake_tasks_[1].first); | |
| 129 EXPECT_EQ(0, job_->fake_tasks_[1].second); | |
| 130 job_->fake_tasks_.clear(); | |
| 131 DestroyParallelJob(); | 181 DestroyParallelJob(); |
| 132 | 182 |
| 133 // Less than 2 requests, do nothing. | 183 // Less than 2 requests, do nothing. |
| 134 CreateParallelJob(0, 100, DownloadItem::ReceivedSlices(), 1); | 184 CreateParallelJob(0, 100, DownloadItem::ReceivedSlices(), 1); |
| 135 BuildParallelRequests(); | 185 BuildParallelRequests(); |
| 136 EXPECT_TRUE(job_->fake_tasks_.empty()); | 186 EXPECT_TRUE(job_->mock_workers().empty()); |
| 137 DestroyParallelJob(); | 187 DestroyParallelJob(); |
| 138 | 188 |
| 139 CreateParallelJob(0, 100, DownloadItem::ReceivedSlices(), 0); | 189 CreateParallelJob(0, 100, DownloadItem::ReceivedSlices(), 0); |
| 140 BuildParallelRequests(); | 190 BuildParallelRequests(); |
| 141 EXPECT_TRUE(job_->fake_tasks_.empty()); | 191 EXPECT_TRUE(job_->mock_workers().empty()); |
| 142 DestroyParallelJob(); | 192 DestroyParallelJob(); |
| 143 | 193 |
| 144 // Content-length is 0, do nothing. | 194 // Content-length is 0, do nothing. |
| 145 CreateParallelJob(100, 0, DownloadItem::ReceivedSlices(), 3); | 195 CreateParallelJob(100, 0, DownloadItem::ReceivedSlices(), 3); |
| 146 BuildParallelRequests(); | 196 BuildParallelRequests(); |
| 147 EXPECT_TRUE(job_->fake_tasks_.empty()); | 197 EXPECT_TRUE(job_->mock_workers().empty()); |
| 148 DestroyParallelJob(); | 198 DestroyParallelJob(); |
| 149 | 199 |
| 150 CreateParallelJob(0, 0, DownloadItem::ReceivedSlices(), 3); | 200 CreateParallelJob(0, 0, DownloadItem::ReceivedSlices(), 3); |
| 151 BuildParallelRequests(); | 201 BuildParallelRequests(); |
| 152 EXPECT_TRUE(job_->fake_tasks_.empty()); | 202 EXPECT_TRUE(job_->mock_workers().empty()); |
| 153 DestroyParallelJob(); | 203 DestroyParallelJob(); |
| 154 | 204 |
| 155 // 2 bytes left for 3 additional requests. Only 1 are built. | 205 // 2 bytes left for 3 additional requests. Only 1 are built. |
| 156 // Original request: Range:98-98, for 1 byte. | 206 // Original request: Range:98-98, for 1 byte. |
| 157 // Task 1: Range:99-, for 1 byte. | 207 // Task 1: Range:99-, for 1 byte. |
| 158 CreateParallelJob(98, 2, DownloadItem::ReceivedSlices(), 4); | 208 CreateParallelJob(98, 2, DownloadItem::ReceivedSlices(), 4); |
| 159 BuildParallelRequests(); | 209 BuildParallelRequests(); |
| 160 EXPECT_EQ(1, static_cast<int>(job_->fake_tasks_.size())); | 210 EXPECT_EQ(1, static_cast<int>(job_->mock_workers().size())); |
| 161 EXPECT_EQ(99, job_->fake_tasks_[0].first); | 211 VerifyWorker(99, 0); |
| 162 EXPECT_EQ(0, job_->fake_tasks_[0].second); | |
| 163 job_->fake_tasks_.clear(); | |
| 164 DestroyParallelJob(); | 212 DestroyParallelJob(); |
| 165 } | 213 } |
| 166 | 214 |
| 215 // Pause, cancel, resume can be called before or after the worker establish |
| 216 // the byte stream. |
| 217 // These tests ensure the states consistency between the job and workers. |
| 218 |
| 219 // Ensure cancel before building the requests will result in workers being |
| 220 // canceled. |
| 221 TEST_F(ParallelDownloadJobTest, EarlyCancelBeforeBuildRequests) { |
| 222 CreateParallelJob(0, 100, DownloadItem::ReceivedSlices(), 2); |
| 223 EXPECT_CALL(*mock_request_handle_, CancelRequest()); |
| 224 |
| 225 // Job is canceled before building parallel requests. |
| 226 job_->Cancel(true); |
| 227 EXPECT_TRUE(job_->is_canceled()); |
| 228 |
| 229 BuildParallelRequests(); |
| 230 VerifyWorker(50, 0); |
| 231 for (auto* worker : job_->mock_workers()) { |
| 232 worker->MakeResponseReady(); |
| 233 EXPECT_CALL(*worker->mock_request_handle(), CancelRequest()); |
| 234 } |
| 235 PushAllByteStream(); |
| 236 DestroyParallelJob(); |
| 237 } |
| 238 |
| 239 // Ensure cancel before adding the byte stream will result in workers being |
| 240 // canceled. |
| 241 TEST_F(ParallelDownloadJobTest, EarlyCancelBeforeByteStreamReady) { |
| 242 CreateParallelJob(0, 100, DownloadItem::ReceivedSlices(), 2); |
| 243 EXPECT_CALL(*mock_request_handle_, CancelRequest()); |
| 244 |
| 245 BuildParallelRequests(); |
| 246 VerifyWorker(50, 0); |
| 247 |
| 248 // Job is canceled after building parallel requests and before byte streams |
| 249 // are added to the file sink. |
| 250 job_->Cancel(true); |
| 251 EXPECT_TRUE(job_->is_canceled()); |
| 252 |
| 253 for (auto* worker : job_->mock_workers()) { |
| 254 worker->MakeResponseReady(); |
| 255 EXPECT_CALL(*worker->mock_request_handle(), CancelRequest()); |
| 256 } |
| 257 PushAllByteStream(); |
| 258 DestroyParallelJob(); |
| 259 } |
| 260 |
| 261 // Ensure pause before building the requests will result in workers being |
| 262 // paused. |
| 263 TEST_F(ParallelDownloadJobTest, EarlyPauseBeforeBuildRequests) { |
| 264 CreateParallelJob(0, 100, DownloadItem::ReceivedSlices(), 2); |
| 265 EXPECT_CALL(*mock_request_handle_, PauseRequest()); |
| 266 |
| 267 // Job is paused before building parallel requests. |
| 268 job_->Pause(); |
| 269 EXPECT_TRUE(job_->is_paused()); |
| 270 |
| 271 BuildParallelRequests(); |
| 272 VerifyWorker(50, 0); |
| 273 for (auto* worker : job_->mock_workers()) { |
| 274 worker->MakeResponseReady(); |
| 275 EXPECT_CALL(*worker->mock_request_handle(), PauseRequest()); |
| 276 // MockDownloadItemImpl doesn't have DownloadFile, so the worker will call |
| 277 // cancel after failed to add the byte stream to file sink. |
| 278 // TODO(xingliu): Consider pass a mock download file into this test. |
| 279 EXPECT_CALL(*worker->mock_request_handle(), CancelRequest()); |
| 280 } |
| 281 |
| 282 PushAllByteStream(); |
| 283 DestroyParallelJob(); |
| 284 } |
| 285 |
| 286 // Ensure pause before adding the byte stream will result in workers being |
| 287 // paused. |
| 288 TEST_F(ParallelDownloadJobTest, EarlyPauseBeforeByteStreamReady) { |
| 289 CreateParallelJob(0, 100, DownloadItem::ReceivedSlices(), 2); |
| 290 EXPECT_CALL(*mock_request_handle_, PauseRequest()); |
| 291 |
| 292 BuildParallelRequests(); |
| 293 VerifyWorker(50, 0); |
| 294 |
| 295 // Job is paused after building parallel requests and before adding the byte |
| 296 // stream to the file sink. |
| 297 job_->Pause(); |
| 298 EXPECT_TRUE(job_->is_paused()); |
| 299 |
| 300 for (auto* worker : job_->mock_workers()) { |
| 301 worker->MakeResponseReady(); |
| 302 EXPECT_CALL(*worker->mock_request_handle(), PauseRequest()); |
| 303 EXPECT_CALL(*worker->mock_request_handle(), CancelRequest()); |
| 304 } |
| 305 |
| 306 PushAllByteStream(); |
| 307 DestroyParallelJob(); |
| 308 } |
| 309 |
| 167 } // namespace content | 310 } // namespace content |
| OLD | NEW |