| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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 <stddef.h> | 5 #include <stddef.h> |
| 6 #include <stdint.h> | 6 #include <stdint.h> |
| 7 | 7 |
| 8 #include <utility> | 8 #include <utility> |
| 9 #include <vector> | 9 #include <vector> |
| 10 | 10 |
| 11 #include "base/files/file.h" | 11 #include "base/files/file.h" |
| 12 #include "base/files/file_util.h" | 12 #include "base/files/file_util.h" |
| 13 #include "base/location.h" | 13 #include "base/location.h" |
| 14 #include "base/memory/ptr_util.h" |
| 14 #include "base/run_loop.h" | 15 #include "base/run_loop.h" |
| 15 #include "base/single_thread_task_runner.h" | 16 #include "base/single_thread_task_runner.h" |
| 16 #include "base/strings/string_number_conversions.h" | 17 #include "base/strings/string_number_conversions.h" |
| 17 #include "base/test/test_file_util.h" | 18 #include "base/test/test_file_util.h" |
| 18 #include "base/threading/thread_task_runner_handle.h" | 19 #include "base/threading/thread_task_runner_handle.h" |
| 19 #include "build/build_config.h" | 20 #include "build/build_config.h" |
| 20 #include "content/browser/byte_stream.h" | 21 #include "content/browser/byte_stream.h" |
| 21 #include "content/browser/download/download_create_info.h" | 22 #include "content/browser/download/download_create_info.h" |
| 22 #include "content/browser/download/download_destination_observer.h" | 23 #include "content/browser/download/download_destination_observer.h" |
| 23 #include "content/browser/download/download_file_impl.h" | 24 #include "content/browser/download/download_file_impl.h" |
| (...skipping 13 matching lines...) Expand all Loading... |
| 37 using ::testing::AnyNumber; | 38 using ::testing::AnyNumber; |
| 38 using ::testing::DoAll; | 39 using ::testing::DoAll; |
| 39 using ::testing::InSequence; | 40 using ::testing::InSequence; |
| 40 using ::testing::Return; | 41 using ::testing::Return; |
| 41 using ::testing::SetArgPointee; | 42 using ::testing::SetArgPointee; |
| 42 using ::testing::StrictMock; | 43 using ::testing::StrictMock; |
| 43 | 44 |
| 44 namespace content { | 45 namespace content { |
| 45 namespace { | 46 namespace { |
| 46 | 47 |
| 48 // Struct for SourceStream states verification. |
| 49 struct SourceStreamTestData { |
| 50 SourceStreamTestData(int64_t offset, int64_t bytes_written, bool finished) |
| 51 : offset(offset), bytes_written(bytes_written), finished(finished) {} |
| 52 int64_t offset; |
| 53 int64_t bytes_written; |
| 54 bool finished; |
| 55 }; |
| 56 |
| 47 std::string GetHexEncodedHashValue(crypto::SecureHash* hash_state) { | 57 std::string GetHexEncodedHashValue(crypto::SecureHash* hash_state) { |
| 48 if (!hash_state) | 58 if (!hash_state) |
| 49 return std::string(); | 59 return std::string(); |
| 50 std::vector<char> hash_value(hash_state->GetHashLength()); | 60 std::vector<char> hash_value(hash_state->GetHashLength()); |
| 51 hash_state->Finish(&hash_value.front(), hash_value.size()); | 61 hash_state->Finish(&hash_value.front(), hash_value.size()); |
| 52 return base::HexEncode(&hash_value.front(), hash_value.size()); | 62 return base::HexEncode(&hash_value.front(), hash_value.size()); |
| 53 } | 63 } |
| 54 | 64 |
| 55 class MockByteStreamReader : public ByteStreamReader { | 65 class MockByteStreamReader : public ByteStreamReader { |
| 56 public: | 66 public: |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 97 enum DownloadFileRenameMethodType { RENAME_AND_UNIQUIFY, RENAME_AND_ANNOTATE }; | 107 enum DownloadFileRenameMethodType { RENAME_AND_UNIQUIFY, RENAME_AND_ANNOTATE }; |
| 98 | 108 |
| 99 // This is a test DownloadFileImpl that has no retry delay and, on Posix, | 109 // This is a test DownloadFileImpl that has no retry delay and, on Posix, |
| 100 // retries renames failed due to ACCESS_DENIED. | 110 // retries renames failed due to ACCESS_DENIED. |
| 101 class TestDownloadFileImpl : public DownloadFileImpl { | 111 class TestDownloadFileImpl : public DownloadFileImpl { |
| 102 public: | 112 public: |
| 103 TestDownloadFileImpl(std::unique_ptr<DownloadSaveInfo> save_info, | 113 TestDownloadFileImpl(std::unique_ptr<DownloadSaveInfo> save_info, |
| 104 const base::FilePath& default_downloads_directory, | 114 const base::FilePath& default_downloads_directory, |
| 105 std::unique_ptr<ByteStreamReader> stream, | 115 std::unique_ptr<ByteStreamReader> stream, |
| 106 const net::NetLogWithSource& net_log, | 116 const net::NetLogWithSource& net_log, |
| 117 bool is_sparse_file, |
| 107 base::WeakPtr<DownloadDestinationObserver> observer) | 118 base::WeakPtr<DownloadDestinationObserver> observer) |
| 108 : DownloadFileImpl(std::move(save_info), | 119 : DownloadFileImpl(std::move(save_info), |
| 109 default_downloads_directory, | 120 default_downloads_directory, |
| 110 std::move(stream), | 121 std::move(stream), |
| 111 net_log, | 122 net_log, |
| 123 is_sparse_file, |
| 112 observer) {} | 124 observer) {} |
| 113 | 125 |
| 114 protected: | 126 protected: |
| 115 base::TimeDelta GetRetryDelayForFailedRename(int attempt_count) override { | 127 base::TimeDelta GetRetryDelayForFailedRename(int attempt_count) override { |
| 116 return base::TimeDelta::FromMilliseconds(0); | 128 return base::TimeDelta::FromMilliseconds(0); |
| 117 } | 129 } |
| 118 | 130 |
| 119 #if !defined(OS_WIN) | 131 #if !defined(OS_WIN) |
| 120 // On Posix, we don't encounter transient errors during renames, except | 132 // On Posix, we don't encounter transient errors during renames, except |
| 121 // possibly EAGAIN, which is difficult to replicate reliably. So we resort to | 133 // possibly EAGAIN, which is difficult to replicate reliably. So we resort to |
| 122 // simulating a transient error using ACCESS_DENIED instead. | 134 // simulating a transient error using ACCESS_DENIED instead. |
| 123 bool ShouldRetryFailedRename(DownloadInterruptReason reason) override { | 135 bool ShouldRetryFailedRename(DownloadInterruptReason reason) override { |
| 124 return reason == DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED; | 136 return reason == DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED; |
| 125 } | 137 } |
| 126 #endif | 138 #endif |
| 127 }; | 139 }; |
| 128 | 140 |
| 129 } // namespace | 141 } // namespace |
| 130 | 142 |
| 131 class DownloadFileTest : public testing::Test { | 143 class DownloadFileTest : public testing::Test { |
| 132 public: | 144 public: |
| 133 static const char kTestData1[]; | 145 static const char kTestData1[]; |
| 134 static const char kTestData2[]; | 146 static const char kTestData2[]; |
| 135 static const char kTestData3[]; | 147 static const char kTestData3[]; |
| 148 static const char kTestData4[]; |
| 149 static const char kTestData5[]; |
| 136 static const char kDataHash[]; | 150 static const char kDataHash[]; |
| 137 static const char kEmptyHash[]; | 151 static const char kEmptyHash[]; |
| 138 static const uint32_t kDummyDownloadId; | 152 static const uint32_t kDummyDownloadId; |
| 139 static const int kDummyChildId; | 153 static const int kDummyChildId; |
| 140 static const int kDummyRequestId; | 154 static const int kDummyRequestId; |
| 141 | 155 |
| 142 DownloadFileTest() | 156 DownloadFileTest() |
| 143 : observer_(new StrictMock<MockDownloadDestinationObserver>), | 157 : observer_(new StrictMock<MockDownloadDestinationObserver>), |
| 144 observer_factory_(observer_.get()), | 158 observer_factory_(observer_.get()), |
| 145 input_stream_(NULL), | 159 input_stream_(NULL), |
| 160 input_stream_1_(NULL), |
| 146 bytes_(-1), | 161 bytes_(-1), |
| 147 bytes_per_sec_(-1) {} | 162 bytes_per_sec_(-1) {} |
| 148 | 163 |
| 149 ~DownloadFileTest() override {} | 164 ~DownloadFileTest() override {} |
| 150 | 165 |
| 151 void SetUpdateDownloadInfo( | 166 void SetUpdateDownloadInfo( |
| 152 int64_t bytes, int64_t bytes_per_sec, | 167 int64_t bytes, int64_t bytes_per_sec, |
| 153 const std::vector<DownloadItem::ReceivedSlice>& received_slices) { | 168 const std::vector<DownloadItem::ReceivedSlice>& received_slices) { |
| 154 bytes_ = bytes; | 169 bytes_ = bytes; |
| 155 bytes_per_sec_ = bytes_per_sec; | 170 bytes_per_sec_ = bytes_per_sec; |
| (...skipping 14 matching lines...) Expand all Loading... |
| 170 sink_callback_ = sink_callback; | 185 sink_callback_ = sink_callback; |
| 171 } | 186 } |
| 172 | 187 |
| 173 void SetInterruptReasonCallback(const base::Closure& closure, | 188 void SetInterruptReasonCallback(const base::Closure& closure, |
| 174 DownloadInterruptReason* reason_p, | 189 DownloadInterruptReason* reason_p, |
| 175 DownloadInterruptReason reason) { | 190 DownloadInterruptReason reason) { |
| 176 *reason_p = reason; | 191 *reason_p = reason; |
| 177 closure.Run(); | 192 closure.Run(); |
| 178 } | 193 } |
| 179 | 194 |
| 180 bool CreateDownloadFile(int offset, bool calculate_hash) { | 195 bool CreateDownloadFile(int offset, |
| 196 bool calculate_hash, |
| 197 bool is_sparse_file = false) { |
| 181 // There can be only one. | 198 // There can be only one. |
| 182 DCHECK(!download_file_.get()); | 199 DCHECK(!download_file_.get()); |
| 183 | 200 |
| 184 input_stream_ = new StrictMock<MockByteStreamReader>(); | 201 input_stream_ = new StrictMock<MockByteStreamReader>(); |
| 185 | 202 |
| 186 // TODO: Need to actually create a function that'll set the variables | 203 // TODO: Need to actually create a function that'll set the variables |
| 187 // based on the inputs from the callback. | 204 // based on the inputs from the callback. |
| 188 EXPECT_CALL(*input_stream_, RegisterCallback(_)) | 205 EXPECT_CALL(*input_stream_, RegisterCallback(_)) |
| 189 .WillOnce(Invoke(this, &DownloadFileTest::RegisterCallback)) | 206 .WillOnce(Invoke(this, &DownloadFileTest::RegisterCallback)) |
| 190 .RetiresOnSaturation(); | 207 .RetiresOnSaturation(); |
| 191 | 208 |
| 192 std::unique_ptr<DownloadSaveInfo> save_info(new DownloadSaveInfo()); | 209 std::unique_ptr<DownloadSaveInfo> save_info(new DownloadSaveInfo()); |
| 193 download_file_.reset(new TestDownloadFileImpl( | 210 download_file_.reset(new TestDownloadFileImpl( |
| 194 std::move(save_info), base::FilePath(), | 211 std::move(save_info), base::FilePath(), |
| 195 std::unique_ptr<ByteStreamReader>(input_stream_), | 212 std::unique_ptr<ByteStreamReader>(input_stream_), |
| 196 net::NetLogWithSource(), observer_factory_.GetWeakPtr())); | 213 net::NetLogWithSource(), is_sparse_file, |
| 214 observer_factory_.GetWeakPtr())); |
| 197 | 215 |
| 198 EXPECT_CALL(*input_stream_, Read(_, _)) | 216 EXPECT_CALL(*input_stream_, Read(_, _)) |
| 199 .WillOnce(Return(ByteStreamReader::STREAM_EMPTY)) | 217 .WillOnce(Return(ByteStreamReader::STREAM_EMPTY)) |
| 200 .RetiresOnSaturation(); | 218 .RetiresOnSaturation(); |
| 201 | 219 |
| 202 base::WeakPtrFactory<DownloadFileTest> weak_ptr_factory(this); | 220 base::WeakPtrFactory<DownloadFileTest> weak_ptr_factory(this); |
| 203 DownloadInterruptReason result = DOWNLOAD_INTERRUPT_REASON_NONE; | 221 DownloadInterruptReason result = DOWNLOAD_INTERRUPT_REASON_NONE; |
| 204 base::RunLoop loop_runner; | 222 base::RunLoop loop_runner; |
| 205 download_file_->Initialize(base::Bind( | 223 download_file_->Initialize(base::Bind( |
| 206 &DownloadFileTest::SetInterruptReasonCallback, | 224 &DownloadFileTest::SetInterruptReasonCallback, |
| 207 weak_ptr_factory.GetWeakPtr(), loop_runner.QuitClosure(), &result)); | 225 weak_ptr_factory.GetWeakPtr(), loop_runner.QuitClosure(), &result)); |
| 208 loop_runner.Run(); | 226 loop_runner.Run(); |
| 209 | 227 |
| 210 ::testing::Mock::VerifyAndClearExpectations(input_stream_); | 228 ::testing::Mock::VerifyAndClearExpectations(input_stream_); |
| 211 return result == DOWNLOAD_INTERRUPT_REASON_NONE; | 229 return result == DOWNLOAD_INTERRUPT_REASON_NONE; |
| 212 } | 230 } |
| 213 | 231 |
| 214 void DestroyDownloadFile(int offset) { | 232 void DestroyDownloadFile(int offset, bool compare_disk_data = true) { |
| 215 EXPECT_FALSE(download_file_->InProgress()); | 233 EXPECT_FALSE(download_file_->InProgress()); |
| 216 | 234 |
| 217 // Make sure the data has been properly written to disk. | 235 // Make sure the data has been properly written to disk. |
| 218 std::string disk_data; | 236 if (compare_disk_data) { |
| 219 EXPECT_TRUE(base::ReadFileToString(download_file_->FullPath(), &disk_data)); | 237 std::string disk_data; |
| 220 EXPECT_EQ(expected_data_, disk_data); | 238 EXPECT_TRUE( |
| 239 base::ReadFileToString(download_file_->FullPath(), &disk_data)); |
| 240 EXPECT_EQ(expected_data_, disk_data); |
| 241 } |
| 221 | 242 |
| 222 // Make sure the Browser and File threads outlive the DownloadFile | 243 // Make sure the Browser and File threads outlive the DownloadFile |
| 223 // to satisfy thread checks inside it. | 244 // to satisfy thread checks inside it. |
| 224 download_file_.reset(); | 245 download_file_.reset(); |
| 225 } | 246 } |
| 226 | 247 |
| 227 // Setup the stream to do be a data append; don't actually trigger | 248 // Setup the stream to append data or write from |offset| to the file. |
| 228 // the callback or do verifications. | 249 // Don't actually trigger the callback or do verifications. |
| 229 void SetupDataAppend(const char **data_chunks, size_t num_chunks, | 250 void SetupDataAppend(const char** data_chunks, |
| 230 ::testing::Sequence s) { | 251 size_t num_chunks, |
| 231 DCHECK(input_stream_); | 252 MockByteStreamReader* stream_reader, |
| 253 ::testing::Sequence s, |
| 254 int64_t offset = -1) { |
| 255 DCHECK(stream_reader); |
| 256 size_t current_pos = static_cast<size_t>(offset); |
| 232 for (size_t i = 0; i < num_chunks; i++) { | 257 for (size_t i = 0; i < num_chunks; i++) { |
| 233 const char *source_data = data_chunks[i]; | 258 const char *source_data = data_chunks[i]; |
| 234 size_t length = strlen(source_data); | 259 size_t length = strlen(source_data); |
| 235 scoped_refptr<net::IOBuffer> data = new net::IOBuffer(length); | 260 scoped_refptr<net::IOBuffer> data = new net::IOBuffer(length); |
| 236 memcpy(data->data(), source_data, length); | 261 memcpy(data->data(), source_data, length); |
| 237 EXPECT_CALL(*input_stream_, Read(_, _)) | 262 EXPECT_CALL(*stream_reader, Read(_, _)) |
| 238 .InSequence(s) | 263 .InSequence(s) |
| 239 .WillOnce(DoAll(SetArgPointee<0>(data), | 264 .WillOnce(DoAll(SetArgPointee<0>(data), SetArgPointee<1>(length), |
| 240 SetArgPointee<1>(length), | |
| 241 Return(ByteStreamReader::STREAM_HAS_DATA))) | 265 Return(ByteStreamReader::STREAM_HAS_DATA))) |
| 242 .RetiresOnSaturation(); | 266 .RetiresOnSaturation(); |
| 243 expected_data_ += source_data; | 267 |
| 268 if (offset < 0) { |
| 269 // Append data. |
| 270 expected_data_ += source_data; |
| 271 continue; |
| 272 } |
| 273 |
| 274 // Write from offset. May fill holes with '\0'. |
| 275 size_t new_len = current_pos + length; |
| 276 if (new_len > expected_data_.size()) |
| 277 expected_data_.append(new_len - expected_data_.size(), '\0'); |
| 278 expected_data_.replace(current_pos, length, source_data); |
| 279 current_pos += length; |
| 244 } | 280 } |
| 245 } | 281 } |
| 246 | 282 |
| 247 void VerifyStreamAndSize() { | 283 void VerifyStreamAndSize() { |
| 248 ::testing::Mock::VerifyAndClearExpectations(input_stream_); | 284 ::testing::Mock::VerifyAndClearExpectations(input_stream_); |
| 249 int64_t size; | 285 int64_t size; |
| 250 EXPECT_TRUE(base::GetFileSize(download_file_->FullPath(), &size)); | 286 EXPECT_TRUE(base::GetFileSize(download_file_->FullPath(), &size)); |
| 251 EXPECT_EQ(expected_data_.size(), static_cast<size_t>(size)); | 287 EXPECT_EQ(expected_data_.size(), static_cast<size_t>(size)); |
| 252 } | 288 } |
| 253 | 289 |
| 254 // TODO(rdsmith): Manage full percentage issues properly. | 290 // TODO(rdsmith): Manage full percentage issues properly. |
| 255 void AppendDataToFile(const char **data_chunks, size_t num_chunks) { | 291 void AppendDataToFile(const char **data_chunks, size_t num_chunks) { |
| 256 ::testing::Sequence s1; | 292 ::testing::Sequence s1; |
| 257 SetupDataAppend(data_chunks, num_chunks, s1); | 293 SetupDataAppend(data_chunks, num_chunks, input_stream_, s1); |
| 258 EXPECT_CALL(*input_stream_, Read(_, _)) | 294 EXPECT_CALL(*input_stream_, Read(_, _)) |
| 259 .InSequence(s1) | 295 .InSequence(s1) |
| 260 .WillOnce(Return(ByteStreamReader::STREAM_EMPTY)) | 296 .WillOnce(Return(ByteStreamReader::STREAM_EMPTY)) |
| 261 .RetiresOnSaturation(); | 297 .RetiresOnSaturation(); |
| 262 sink_callback_.Run(); | 298 sink_callback_.Run(); |
| 263 VerifyStreamAndSize(); | 299 VerifyStreamAndSize(); |
| 264 } | 300 } |
| 265 | 301 |
| 266 void SetupFinishStream(DownloadInterruptReason interrupt_reason, | 302 void SetupFinishStream(DownloadInterruptReason interrupt_reason, |
| 267 ::testing::Sequence s) { | 303 MockByteStreamReader* stream_reader, |
| 268 EXPECT_CALL(*input_stream_, Read(_, _)) | 304 ::testing::Sequence s) { |
| 305 EXPECT_CALL(*stream_reader, Read(_, _)) |
| 269 .InSequence(s) | 306 .InSequence(s) |
| 270 .WillOnce(Return(ByteStreamReader::STREAM_COMPLETE)) | 307 .WillOnce(Return(ByteStreamReader::STREAM_COMPLETE)) |
| 271 .RetiresOnSaturation(); | 308 .RetiresOnSaturation(); |
| 272 EXPECT_CALL(*input_stream_, GetStatus()) | 309 EXPECT_CALL(*stream_reader, GetStatus()) |
| 273 .InSequence(s) | 310 .InSequence(s) |
| 274 .WillOnce(Return(interrupt_reason)) | 311 .WillOnce(Return(interrupt_reason)) |
| 275 .RetiresOnSaturation(); | 312 .RetiresOnSaturation(); |
| 276 EXPECT_CALL(*input_stream_, RegisterCallback(_)) | 313 EXPECT_CALL(*stream_reader, RegisterCallback(_)).RetiresOnSaturation(); |
| 277 .RetiresOnSaturation(); | |
| 278 } | 314 } |
| 279 | 315 |
| 280 void FinishStream(DownloadInterruptReason interrupt_reason, | 316 void FinishStream(DownloadInterruptReason interrupt_reason, |
| 281 bool check_observer, | 317 bool check_observer, |
| 282 const std::string& expected_hash) { | 318 const std::string& expected_hash) { |
| 283 ::testing::Sequence s1; | 319 ::testing::Sequence s1; |
| 284 SetupFinishStream(interrupt_reason, s1); | 320 SetupFinishStream(interrupt_reason, input_stream_, s1); |
| 285 sink_callback_.Run(); | 321 sink_callback_.Run(); |
| 286 VerifyStreamAndSize(); | 322 VerifyStreamAndSize(); |
| 287 if (check_observer) { | 323 if (check_observer) { |
| 288 EXPECT_CALL(*(observer_.get()), | 324 EXPECT_CALL(*(observer_.get()), |
| 289 MockDestinationCompleted(_, expected_hash)); | 325 MockDestinationCompleted(_, expected_hash)); |
| 290 base::RunLoop().RunUntilIdle(); | 326 base::RunLoop().RunUntilIdle(); |
| 291 ::testing::Mock::VerifyAndClearExpectations(observer_.get()); | 327 ::testing::Mock::VerifyAndClearExpectations(observer_.get()); |
| 292 EXPECT_CALL(*(observer_.get()), DestinationUpdate(_, _, _)) | 328 EXPECT_CALL(*(observer_.get()), DestinationUpdate(_, _, _)) |
| 293 .Times(AnyNumber()) | 329 .Times(AnyNumber()) |
| 294 .WillRepeatedly( | 330 .WillRepeatedly( |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 348 base::Bind(&DownloadFileTest::SetRenameResult, | 384 base::Bind(&DownloadFileTest::SetRenameResult, |
| 349 base::Unretained(this), | 385 base::Unretained(this), |
| 350 loop_runner.QuitClosure(), | 386 loop_runner.QuitClosure(), |
| 351 &result_reason, | 387 &result_reason, |
| 352 result_path_p); | 388 result_path_p); |
| 353 InvokeRenameMethod(method, full_path, completion_callback); | 389 InvokeRenameMethod(method, full_path, completion_callback); |
| 354 loop_runner.Run(); | 390 loop_runner.Run(); |
| 355 return result_reason; | 391 return result_reason; |
| 356 } | 392 } |
| 357 | 393 |
| 394 // Prepare two byte streams to write to the same file sink. |
| 395 void PrepareMultipleStreams(int64_t second_stream_length) { |
| 396 // Create a sparse file. |
| 397 ASSERT_TRUE(CreateDownloadFile(0, true, true)); |
| 398 base::FilePath initial_path(download_file_->FullPath()); |
| 399 EXPECT_TRUE(base::PathExists(initial_path)); |
| 400 DCHECK(download_file_); |
| 401 |
| 402 const char* stream_0_data[] = {kTestData1, kTestData2}; |
| 403 const char* stream_1_data[] = {kTestData4, kTestData5}; |
| 404 size_t stream_1_offset = strlen(kTestData1) + strlen(kTestData2); |
| 405 |
| 406 // Register second SourceStream entry for the second stream. |
| 407 // The first stream should be registered in ctor of DownloadFile. |
| 408 DownloadFileImpl::SourceStreams& source_streams = |
| 409 download_file_->source_streams_; |
| 410 EXPECT_EQ(static_cast<size_t>(1), source_streams.size()); |
| 411 source_streams[stream_1_offset] = |
| 412 base::MakeUnique<DownloadFileImpl::SourceStream>(stream_1_offset, |
| 413 second_stream_length); |
| 414 |
| 415 // Create the second byte stream. Will be moved to DownloadFile. |
| 416 input_stream_1_ = new MockByteStreamReader(); |
| 417 |
| 418 ::testing::Sequence s0; |
| 419 ::testing::Sequence s1; |
| 420 SetupDataAppend(stream_1_data, 2, input_stream_1_, s1, stream_1_offset); |
| 421 SetupDataAppend(stream_0_data, 2, input_stream_, s0, 0); |
| 422 SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, input_stream_, s0); |
| 423 |
| 424 // Expectation on MockByteStreamReader for MultipleStreams tests: |
| 425 // 1. RegisterCallback: Must called twice. One to set the callback, the |
| 426 // other to release the stream. |
| 427 // 2. Read: If filled with N buffer, called (N+1) times, where the last Read |
| 428 // call doesn't read any data but returns STRAM_COMPLETE. |
| 429 // The stream may terminate in the middle and less Read calls are expected. |
| 430 // 3. GetStatus: Only called if the stream is completed and last Read call |
| 431 // returns STREAM_COMPLETE. |
| 432 if (second_stream_length == 0) |
| 433 SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, input_stream_1_, s1); |
| 434 else |
| 435 EXPECT_CALL(*input_stream_1_, RegisterCallback(_)).RetiresOnSaturation(); |
| 436 |
| 437 EXPECT_CALL(*input_stream_1_, RegisterCallback(_)).RetiresOnSaturation(); |
| 438 } |
| 439 |
| 440 void VerifySourceStreamsStates(const SourceStreamTestData& data) { |
| 441 DCHECK(download_file_->source_streams_.find(data.offset) != |
| 442 download_file_->source_streams_.end()); |
| 443 DownloadFileImpl::SourceStream* stream = |
| 444 download_file_->source_streams_[data.offset].get(); |
| 445 DCHECK(stream); |
| 446 EXPECT_EQ(data.offset, stream->offset()); |
| 447 EXPECT_EQ(data.bytes_written, stream->bytes_written()); |
| 448 EXPECT_EQ(data.finished, stream->is_finished()); |
| 449 } |
| 450 |
| 451 int64_t TotalBytesReceived() const { |
| 452 DCHECK(download_file_); |
| 453 return download_file_->TotalBytesReceived(); |
| 454 } |
| 455 |
| 358 std::unique_ptr<StrictMock<MockDownloadDestinationObserver>> observer_; | 456 std::unique_ptr<StrictMock<MockDownloadDestinationObserver>> observer_; |
| 359 base::WeakPtrFactory<DownloadDestinationObserver> observer_factory_; | 457 base::WeakPtrFactory<DownloadDestinationObserver> observer_factory_; |
| 360 | 458 |
| 361 // DownloadFile instance we are testing. | 459 // DownloadFile instance we are testing. |
| 362 std::unique_ptr<DownloadFile> download_file_; | 460 std::unique_ptr<DownloadFileImpl> download_file_; |
| 363 | 461 |
| 364 // Stream for sending data into the download file. | 462 // Stream for sending data into the download file. |
| 365 // Owned by download_file_; will be alive for lifetime of download_file_. | 463 // Owned by download_file_; will be alive for lifetime of download_file_. |
| 366 StrictMock<MockByteStreamReader>* input_stream_; | 464 StrictMock<MockByteStreamReader>* input_stream_; |
| 367 | 465 |
| 466 // A second byte stream to test multiple stream write. |
| 467 MockByteStreamReader* input_stream_1_; |
| 468 |
| 368 // Sink callback data for stream. | 469 // Sink callback data for stream. |
| 369 base::Closure sink_callback_; | 470 base::Closure sink_callback_; |
| 370 | 471 |
| 371 // Latest update sent to the observer. | 472 // Latest update sent to the observer. |
| 372 int64_t bytes_; | 473 int64_t bytes_; |
| 373 int64_t bytes_per_sec_; | 474 int64_t bytes_per_sec_; |
| 374 | 475 |
| 375 private: | 476 private: |
| 376 void SetRenameResult(const base::Closure& closure, | 477 void SetRenameResult(const base::Closure& closure, |
| 377 DownloadInterruptReason* reason_p, | 478 DownloadInterruptReason* reason_p, |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 415 // the value parameter. | 516 // the value parameter. |
| 416 INSTANTIATE_TEST_CASE_P(DownloadFile, | 517 INSTANTIATE_TEST_CASE_P(DownloadFile, |
| 417 DownloadFileTestWithRename, | 518 DownloadFileTestWithRename, |
| 418 ::testing::Values(RENAME_AND_ANNOTATE, | 519 ::testing::Values(RENAME_AND_ANNOTATE, |
| 419 RENAME_AND_UNIQUIFY)); | 520 RENAME_AND_UNIQUIFY)); |
| 420 | 521 |
| 421 const char DownloadFileTest::kTestData1[] = | 522 const char DownloadFileTest::kTestData1[] = |
| 422 "Let's write some data to the file!\n"; | 523 "Let's write some data to the file!\n"; |
| 423 const char DownloadFileTest::kTestData2[] = "Writing more data.\n"; | 524 const char DownloadFileTest::kTestData2[] = "Writing more data.\n"; |
| 424 const char DownloadFileTest::kTestData3[] = "Final line."; | 525 const char DownloadFileTest::kTestData3[] = "Final line."; |
| 526 const char DownloadFileTest::kTestData4[] = "abcdefg"; |
| 527 const char DownloadFileTest::kTestData5[] = "01234"; |
| 425 const char DownloadFileTest::kDataHash[] = | 528 const char DownloadFileTest::kDataHash[] = |
| 426 "CBF68BF10F8003DB86B31343AFAC8C7175BD03FB5FC905650F8C80AF087443A8"; | 529 "CBF68BF10F8003DB86B31343AFAC8C7175BD03FB5FC905650F8C80AF087443A8"; |
| 427 const char DownloadFileTest::kEmptyHash[] = | 530 const char DownloadFileTest::kEmptyHash[] = |
| 428 "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855"; | 531 "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855"; |
| 429 | 532 |
| 430 const uint32_t DownloadFileTest::kDummyDownloadId = 23; | 533 const uint32_t DownloadFileTest::kDummyDownloadId = 23; |
| 431 const int DownloadFileTest::kDummyChildId = 3; | 534 const int DownloadFileTest::kDummyChildId = 3; |
| 432 const int DownloadFileTest::kDummyRequestId = 67; | 535 const int DownloadFileTest::kDummyRequestId = 67; |
| 433 | 536 |
| 434 // Rename the file before any data is downloaded, after some has, after it all | 537 // Rename the file before any data is downloaded, after some has, after it all |
| (...skipping 310 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 745 DestroyDownloadFile(0); | 848 DestroyDownloadFile(0); |
| 746 } | 849 } |
| 747 | 850 |
| 748 TEST_F(DownloadFileTest, StreamNonEmptySuccess) { | 851 TEST_F(DownloadFileTest, StreamNonEmptySuccess) { |
| 749 ASSERT_TRUE(CreateDownloadFile(0, true)); | 852 ASSERT_TRUE(CreateDownloadFile(0, true)); |
| 750 base::FilePath initial_path(download_file_->FullPath()); | 853 base::FilePath initial_path(download_file_->FullPath()); |
| 751 EXPECT_TRUE(base::PathExists(initial_path)); | 854 EXPECT_TRUE(base::PathExists(initial_path)); |
| 752 | 855 |
| 753 const char* chunks1[] = { kTestData1, kTestData2 }; | 856 const char* chunks1[] = { kTestData1, kTestData2 }; |
| 754 ::testing::Sequence s1; | 857 ::testing::Sequence s1; |
| 755 SetupDataAppend(chunks1, 2, s1); | 858 SetupDataAppend(chunks1, 2, input_stream_, s1); |
| 756 SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, s1); | 859 SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, input_stream_, s1); |
| 757 EXPECT_CALL(*(observer_.get()), MockDestinationCompleted(_, _)); | 860 EXPECT_CALL(*(observer_.get()), MockDestinationCompleted(_, _)); |
| 758 sink_callback_.Run(); | 861 sink_callback_.Run(); |
| 759 VerifyStreamAndSize(); | 862 VerifyStreamAndSize(); |
| 760 base::RunLoop().RunUntilIdle(); | 863 base::RunLoop().RunUntilIdle(); |
| 761 DestroyDownloadFile(0); | 864 DestroyDownloadFile(0); |
| 762 } | 865 } |
| 763 | 866 |
| 764 TEST_F(DownloadFileTest, StreamNonEmptyError) { | 867 TEST_F(DownloadFileTest, StreamNonEmptyError) { |
| 765 ASSERT_TRUE(CreateDownloadFile(0, true)); | 868 ASSERT_TRUE(CreateDownloadFile(0, true)); |
| 766 base::FilePath initial_path(download_file_->FullPath()); | 869 base::FilePath initial_path(download_file_->FullPath()); |
| 767 EXPECT_TRUE(base::PathExists(initial_path)); | 870 EXPECT_TRUE(base::PathExists(initial_path)); |
| 768 | 871 |
| 769 const char* chunks1[] = { kTestData1, kTestData2 }; | 872 const char* chunks1[] = { kTestData1, kTestData2 }; |
| 770 ::testing::Sequence s1; | 873 ::testing::Sequence s1; |
| 771 SetupDataAppend(chunks1, 2, s1); | 874 SetupDataAppend(chunks1, 2, input_stream_, s1); |
| 772 SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, s1); | 875 SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, |
| 876 input_stream_, s1); |
| 773 | 877 |
| 774 EXPECT_CALL(*(observer_.get()), | 878 EXPECT_CALL(*(observer_.get()), |
| 775 MockDestinationError( | 879 MockDestinationError( |
| 776 DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, _, _)) | 880 DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, _, _)) |
| 777 .WillOnce(InvokeWithoutArgs( | 881 .WillOnce(InvokeWithoutArgs( |
| 778 this, &DownloadFileTest::ConfirmUpdateDownloadInfo)); | 882 this, &DownloadFileTest::ConfirmUpdateDownloadInfo)); |
| 779 | 883 |
| 780 // If this next EXPECT_CALL fails flakily, it's probably a real failure. | 884 // If this next EXPECT_CALL fails flakily, it's probably a real failure. |
| 781 // We'll be getting a stream of UpdateDownload calls from the timer, and | 885 // We'll be getting a stream of UpdateDownload calls from the timer, and |
| 782 // the last one may have the correct information even if the failure | 886 // the last one may have the correct information even if the failure |
| 783 // doesn't produce an update, as the timer update may have triggered at the | 887 // doesn't produce an update, as the timer update may have triggered at the |
| 784 // same time. | 888 // same time. |
| 785 EXPECT_CALL(*(observer_.get()), | 889 EXPECT_CALL(*(observer_.get()), |
| 786 CurrentUpdateStatus(strlen(kTestData1) + strlen(kTestData2), _)); | 890 CurrentUpdateStatus(strlen(kTestData1) + strlen(kTestData2), _)); |
| 787 | 891 |
| 788 sink_callback_.Run(); | 892 sink_callback_.Run(); |
| 789 base::RunLoop().RunUntilIdle(); | 893 base::RunLoop().RunUntilIdle(); |
| 790 VerifyStreamAndSize(); | 894 VerifyStreamAndSize(); |
| 791 DestroyDownloadFile(0); | 895 DestroyDownloadFile(0); |
| 792 } | 896 } |
| 793 | 897 |
| 898 // Tests for concurrent streams handling, used for parallel download. |
| 899 // |
| 900 // Activate both streams at the same time. |
| 901 TEST_F(DownloadFileTest, MutipleStreamsWrite) { |
| 902 PrepareMultipleStreams(0); |
| 903 EXPECT_CALL(*(observer_.get()), MockDestinationCompleted(_, _)); |
| 904 |
| 905 int64_t stream_0_length = |
| 906 static_cast<int64_t>(strlen(kTestData1) + strlen(kTestData2)); |
| 907 int64_t stream_1_length = |
| 908 static_cast<int64_t>(strlen(kTestData4) + strlen(kTestData5)); |
| 909 |
| 910 download_file_->AddByteStream( |
| 911 std::unique_ptr<MockByteStreamReader>(input_stream_1_), stream_0_length); |
| 912 sink_callback_.Run(); |
| 913 base::RunLoop().RunUntilIdle(); |
| 914 |
| 915 SourceStreamTestData stream_data_0(0, stream_0_length, true); |
| 916 SourceStreamTestData stream_data_1(stream_0_length, stream_1_length, true); |
| 917 VerifySourceStreamsStates(stream_data_0); |
| 918 VerifySourceStreamsStates(stream_data_1); |
| 919 EXPECT_EQ(stream_0_length + stream_1_length, TotalBytesReceived()); |
| 920 |
| 921 DestroyDownloadFile(0); |
| 922 } |
| 923 |
| 924 // Activate and deplete one stream, later add the second stream. |
| 925 TEST_F(DownloadFileTest, MutipleStreamsOneStreamFirst) { |
| 926 PrepareMultipleStreams(0); |
| 927 |
| 928 int64_t stream_0_length = |
| 929 static_cast<int64_t>(strlen(kTestData1) + strlen(kTestData2)); |
| 930 int64_t stream_1_length = |
| 931 static_cast<int64_t>(strlen(kTestData4) + strlen(kTestData5)); |
| 932 |
| 933 // Deplete the first stream. |
| 934 sink_callback_.Run(); |
| 935 base::RunLoop().RunUntilIdle(); |
| 936 |
| 937 SourceStreamTestData stream_data_0(0, stream_0_length, true); |
| 938 SourceStreamTestData stream_data_1(stream_0_length, 0, false); |
| 939 VerifySourceStreamsStates(stream_data_0); |
| 940 VerifySourceStreamsStates(stream_data_1); |
| 941 EXPECT_EQ(stream_0_length, TotalBytesReceived()); |
| 942 |
| 943 // Won't inform the observer until the second stream is depleted. |
| 944 EXPECT_CALL(*(observer_.get()), MockDestinationCompleted(_, _)); |
| 945 |
| 946 // Drain the second stream after the first stream is depleted. |
| 947 download_file_->AddByteStream( |
| 948 std::unique_ptr<MockByteStreamReader>(input_stream_1_), stream_0_length); |
| 949 base::RunLoop().RunUntilIdle(); |
| 950 |
| 951 stream_data_1.bytes_written = stream_1_length; |
| 952 stream_data_1.finished = true; |
| 953 VerifySourceStreamsStates(stream_data_0); |
| 954 VerifySourceStreamsStates(stream_data_1); |
| 955 EXPECT_EQ(stream_0_length + stream_1_length, TotalBytesReceived()); |
| 956 |
| 957 DestroyDownloadFile(0); |
| 958 } |
| 959 |
| 960 // Two streams write to one sink, the second stream has a limited length. |
| 961 TEST_F(DownloadFileTest, MutipleStreamsLimitedLength) { |
| 962 // The second stream has two buffers, kTestData4 and kTestData5. |
| 963 // The length limit is set to less than the length of kTestData4. |
| 964 // kTestData4 should be partially written to disk, where kTestData5 should be |
| 965 // ignored. |
| 966 int64_t stream_0_length = |
| 967 static_cast<int64_t>(strlen(kTestData1) + strlen(kTestData2)); |
| 968 int64_t stream_1_length = static_cast<int64_t>(strlen(kTestData4)) - 1; |
| 969 PrepareMultipleStreams(stream_1_length); |
| 970 |
| 971 EXPECT_CALL(*(observer_.get()), MockDestinationCompleted(_, _)); |
| 972 |
| 973 download_file_->AddByteStream( |
| 974 std::unique_ptr<MockByteStreamReader>(input_stream_1_), stream_0_length); |
| 975 sink_callback_.Run(); |
| 976 base::RunLoop().RunUntilIdle(); |
| 977 |
| 978 SourceStreamTestData stream_data_0(0, stream_0_length, true); |
| 979 SourceStreamTestData stream_data_1(stream_0_length, stream_1_length, true); |
| 980 VerifySourceStreamsStates(stream_data_0); |
| 981 VerifySourceStreamsStates(stream_data_1); |
| 982 EXPECT_EQ(stream_0_length + stream_1_length, TotalBytesReceived()); |
| 983 |
| 984 std::string disk_data, expected_data; |
| 985 EXPECT_TRUE(base::ReadFileToString(download_file_->FullPath(), &disk_data)); |
| 986 expected_data.append(kTestData1).append(kTestData2).append(kTestData4); |
| 987 expected_data = expected_data.substr(0, stream_0_length + stream_1_length); |
| 988 EXPECT_EQ(expected_data, disk_data); |
| 989 |
| 990 // Finish the second stream. |
| 991 // TODO(xingliu): Refactor test code to deal with unfinished streams. |
| 992 scoped_refptr<net::IOBuffer> data = new net::IOBuffer(strlen(kTestData5)); |
| 993 size_t size; |
| 994 input_stream_1_->Read(&data, &size); |
| 995 |
| 996 DestroyDownloadFile(0, false); |
| 997 } |
| 998 |
| 794 } // namespace content | 999 } // namespace content |
| OLD | NEW |