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