1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/files/file.h" 6 #include "base/files/file_util.h" 7 #include "base/message_loop/message_loop.h" 8 #include "base/run_loop.h" 9 #include "base/strings/string_number_conversions.h" 10 #include "base/test/test_file_util.h" 11 #include "content/browser/browser_thread_impl.h" 12 #include "content/browser/byte_stream.h" 13 #include "content/browser/download/download_create_info.h" 14 #include "content/browser/download/download_file_impl.h" 15 #include "content/browser/download/download_request_handle.h" 16 #include "content/public/browser/download_destination_observer.h" 17 #include "content/public/browser/download_interrupt_reasons.h" 18 #include "content/public/browser/download_manager.h" 19 #include "content/public/test/mock_download_manager.h" 20 #include "net/base/file_stream.h" 21 #include "net/base/mock_file_stream.h" 22 #include "net/base/net_errors.h" 23 #include "testing/gmock/include/gmock/gmock.h" 24 #include "testing/gtest/include/gtest/gtest.h" 25 26 using ::testing::_; 27 using ::testing::AnyNumber; 28 using ::testing::DoAll; 29 using ::testing::InSequence; 30 using ::testing::Return; 31 using ::testing::SetArgPointee; 32 using ::testing::StrictMock; 33 34 namespace content { 35 namespace { 36 37 class MockByteStreamReader : public ByteStreamReader { 38 public: 39 MockByteStreamReader() {} 40 ~MockByteStreamReader() {} 41 42 // ByteStream functions 43 MOCK_METHOD2(Read, ByteStreamReader::StreamState( 44 scoped_refptr<net::IOBuffer>*, size_t*)); 45 MOCK_CONST_METHOD0(GetStatus, int()); 46 MOCK_METHOD1(RegisterCallback, void(const base::Closure&)); 47 }; 48 49 class MockDownloadDestinationObserver : public DownloadDestinationObserver { 50 public: 51 MOCK_METHOD3(DestinationUpdate, void(int64, int64, const std::string&)); 52 MOCK_METHOD1(DestinationError, void(DownloadInterruptReason)); 53 MOCK_METHOD1(DestinationCompleted, void(const std::string&)); 54 55 // Doesn't override any methods in the base class. Used to make sure 56 // that the last DestinationUpdate before a Destination{Completed,Error} 57 // had the right values. 58 MOCK_METHOD3(CurrentUpdateStatus, 59 void(int64, int64, const std::string&)); 60 }; 61 62 MATCHER(IsNullCallback, "") { return (arg.is_null()); } 63 64 typedef void (DownloadFile::*DownloadFileRenameMethodType)( 65 const base::FilePath&, 66 const DownloadFile::RenameCompletionCallback&); 67 68 // This is a test DownloadFileImpl that has no retry delay and, on Posix, 69 // retries renames failed due to ACCESS_DENIED. 70 class TestDownloadFileImpl : public DownloadFileImpl { 71 public: 72 TestDownloadFileImpl(scoped_ptr<DownloadSaveInfo> save_info, 73 const base::FilePath& default_downloads_directory, 74 const GURL& url, 75 const GURL& referrer_url, 76 bool calculate_hash, 77 scoped_ptr<ByteStreamReader> stream, 78 const net::BoundNetLog& bound_net_log, 79 base::WeakPtr<DownloadDestinationObserver> observer) 80 : DownloadFileImpl(save_info.Pass(), 81 default_downloads_directory, 82 url, 83 referrer_url, 84 calculate_hash, 85 stream.Pass(), 86 bound_net_log, 87 observer) {} 88 89 protected: 90 virtual base::TimeDelta GetRetryDelayForFailedRename( 91 int attempt_count) OVERRIDE { 92 return base::TimeDelta::FromMilliseconds(0); 93 } 94 95 #if !defined(OS_WIN) 96 // On Posix, we don't encounter transient errors during renames, except 97 // possibly EAGAIN, which is difficult to replicate reliably. So we resort to 98 // simulating a transient error using ACCESS_DENIED instead. 99 virtual bool ShouldRetryFailedRename( 100 DownloadInterruptReason reason) OVERRIDE { 101 return reason == DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED; 102 } 103 #endif 104 }; 105 106 } // namespace 107 108 class DownloadFileTest : public testing::Test { 109 public: 110 111 static const char* kTestData1; 112 static const char* kTestData2; 113 static const char* kTestData3; 114 static const char* kDataHash; 115 static const uint32 kDummyDownloadId; 116 static const int kDummyChildId; 117 static const int kDummyRequestId; 118 119 DownloadFileTest() : 120 observer_(new StrictMock<MockDownloadDestinationObserver>), 121 observer_factory_(observer_.get()), 122 input_stream_(NULL), 123 bytes_(-1), 124 bytes_per_sec_(-1), 125 hash_state_("xyzzy"), 126 ui_thread_(BrowserThread::UI, &loop_), 127 file_thread_(BrowserThread::FILE, &loop_) { 128 } 129 130 virtual ~DownloadFileTest() { 131 } 132 133 void SetUpdateDownloadInfo(int64 bytes, int64 bytes_per_sec, 134 const std::string& hash_state) { 135 bytes_ = bytes; 136 bytes_per_sec_ = bytes_per_sec; 137 hash_state_ = hash_state; 138 } 139 140 void ConfirmUpdateDownloadInfo() { 141 observer_->CurrentUpdateStatus(bytes_, bytes_per_sec_, hash_state_); 142 } 143 144 virtual void SetUp() { 145 EXPECT_CALL(*(observer_.get()), DestinationUpdate(_, _, _)) 146 .Times(AnyNumber()) 147 .WillRepeatedly(Invoke(this, &DownloadFileTest::SetUpdateDownloadInfo)); 148 } 149 150 // Mock calls to this function are forwarded here. 151 void RegisterCallback(const base::Closure& sink_callback) { 152 sink_callback_ = sink_callback; 153 } 154 155 void SetInterruptReasonCallback(const base::Closure& closure, 156 DownloadInterruptReason* reason_p, 157 DownloadInterruptReason reason) { 158 *reason_p = reason; 159 closure.Run(); 160 } 161 162 bool CreateDownloadFile(int offset, bool calculate_hash) { 163 // There can be only one. 164 DCHECK(!download_file_.get()); 165 166 input_stream_ = new StrictMock<MockByteStreamReader>(); 167 168 // TODO: Need to actually create a function that'll set the variables 169 // based on the inputs from the callback. 170 EXPECT_CALL(*input_stream_, RegisterCallback(_)) 171 .WillOnce(Invoke(this, &DownloadFileTest::RegisterCallback)) 172 .RetiresOnSaturation(); 173 174 scoped_ptr<DownloadSaveInfo> save_info(new DownloadSaveInfo()); 175 scoped_ptr<TestDownloadFileImpl> download_file_impl( 176 new TestDownloadFileImpl(save_info.Pass(), 177 base::FilePath(), 178 GURL(), // Source 179 GURL(), // Referrer 180 calculate_hash, 181 scoped_ptr<ByteStreamReader>(input_stream_), 182 net::BoundNetLog(), 183 observer_factory_.GetWeakPtr())); 184 download_file_impl->SetClientGuid("12345678-ABCD-1234-DCBA-123456789ABC"); 185 download_file_ = download_file_impl.PassAs<DownloadFile>(); 186 187 EXPECT_CALL(*input_stream_, Read(_, _)) 188 .WillOnce(Return(ByteStreamReader::STREAM_EMPTY)) 189 .RetiresOnSaturation(); 190 191 base::WeakPtrFactory<DownloadFileTest> weak_ptr_factory(this); 192 DownloadInterruptReason result = DOWNLOAD_INTERRUPT_REASON_NONE; 193 base::RunLoop loop_runner; 194 download_file_->Initialize(base::Bind( 195 &DownloadFileTest::SetInterruptReasonCallback, 196 weak_ptr_factory.GetWeakPtr(), loop_runner.QuitClosure(), &result)); 197 loop_runner.Run(); 198 199 ::testing::Mock::VerifyAndClearExpectations(input_stream_); 200 return result == DOWNLOAD_INTERRUPT_REASON_NONE; 201 } 202 203 void DestroyDownloadFile(int offset) { 204 EXPECT_FALSE(download_file_->InProgress()); 205 206 // Make sure the data has been properly written to disk. 207 std::string disk_data; 208 EXPECT_TRUE(base::ReadFileToString(download_file_->FullPath(), &disk_data)); 209 EXPECT_EQ(expected_data_, disk_data); 210 211 // Make sure the Browser and File threads outlive the DownloadFile 212 // to satisfy thread checks inside it. 213 download_file_.reset(); 214 } 215 216 // Setup the stream to do be a data append; don't actually trigger 217 // the callback or do verifications. 218 void SetupDataAppend(const char **data_chunks, size_t num_chunks, 219 ::testing::Sequence s) { 220 DCHECK(input_stream_); 221 for (size_t i = 0; i < num_chunks; i++) { 222 const char *source_data = data_chunks[i]; 223 size_t length = strlen(source_data); 224 scoped_refptr<net::IOBuffer> data = new net::IOBuffer(length); 225 memcpy(data->data(), source_data, length); 226 EXPECT_CALL(*input_stream_, Read(_, _)) 227 .InSequence(s) 228 .WillOnce(DoAll(SetArgPointee<0>(data), 229 SetArgPointee<1>(length), 230 Return(ByteStreamReader::STREAM_HAS_DATA))) 231 .RetiresOnSaturation(); 232 expected_data_ += source_data; 233 } 234 } 235 236 void VerifyStreamAndSize() { 237 ::testing::Mock::VerifyAndClearExpectations(input_stream_); 238 int64 size; 239 EXPECT_TRUE(base::GetFileSize(download_file_->FullPath(), &size)); 240 EXPECT_EQ(expected_data_.size(), static_cast<size_t>(size)); 241 } 242 243 // TODO(rdsmith): Manage full percentage issues properly. 244 void AppendDataToFile(const char **data_chunks, size_t num_chunks) { 245 ::testing::Sequence s1; 246 SetupDataAppend(data_chunks, num_chunks, s1); 247 EXPECT_CALL(*input_stream_, Read(_, _)) 248 .InSequence(s1) 249 .WillOnce(Return(ByteStreamReader::STREAM_EMPTY)) 250 .RetiresOnSaturation(); 251 sink_callback_.Run(); 252 VerifyStreamAndSize(); 253 } 254 255 void SetupFinishStream(DownloadInterruptReason interrupt_reason, 256 ::testing::Sequence s) { 257 EXPECT_CALL(*input_stream_, Read(_, _)) 258 .InSequence(s) 259 .WillOnce(Return(ByteStreamReader::STREAM_COMPLETE)) 260 .RetiresOnSaturation(); 261 EXPECT_CALL(*input_stream_, GetStatus()) 262 .InSequence(s) 263 .WillOnce(Return(interrupt_reason)) 264 .RetiresOnSaturation(); 265 EXPECT_CALL(*input_stream_, RegisterCallback(_)) 266 .RetiresOnSaturation(); 267 } 268 269 void FinishStream(DownloadInterruptReason interrupt_reason, 270 bool check_observer) { 271 ::testing::Sequence s1; 272 SetupFinishStream(interrupt_reason, s1); 273 sink_callback_.Run(); 274 VerifyStreamAndSize(); 275 if (check_observer) { 276 EXPECT_CALL(*(observer_.get()), DestinationCompleted(_)); 277 loop_.RunUntilIdle(); 278 ::testing::Mock::VerifyAndClearExpectations(observer_.get()); 279 EXPECT_CALL(*(observer_.get()), DestinationUpdate(_, _, _)) 280 .Times(AnyNumber()) 281 .WillRepeatedly(Invoke(this, 282 &DownloadFileTest::SetUpdateDownloadInfo)); 283 } 284 } 285 286 DownloadInterruptReason RenameAndUniquify( 287 const base::FilePath& full_path, 288 base::FilePath* result_path_p) { 289 return InvokeRenameMethodAndWaitForCallback( 290 &DownloadFile::RenameAndUniquify, full_path, result_path_p); 291 } 292 293 DownloadInterruptReason RenameAndAnnotate( 294 const base::FilePath& full_path, 295 base::FilePath* result_path_p) { 296 return InvokeRenameMethodAndWaitForCallback( 297 &DownloadFile::RenameAndAnnotate, full_path, result_path_p); 298 } 299 300 void ExpectPermissionError(DownloadInterruptReason err) { 301 EXPECT_TRUE(err == DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR || 302 err == DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED) 303 << "Interrupt reason = " << err; 304 } 305 306 protected: 307 DownloadInterruptReason InvokeRenameMethodAndWaitForCallback( 308 DownloadFileRenameMethodType method, 309 const base::FilePath& full_path, 310 base::FilePath* result_path_p) { 311 DownloadInterruptReason result_reason(DOWNLOAD_INTERRUPT_REASON_NONE); 312 base::FilePath result_path; 313 314 base::RunLoop loop_runner; 315 ((*download_file_).*method)(full_path, 316 base::Bind(&DownloadFileTest::SetRenameResult, 317 base::Unretained(this), 318 loop_runner.QuitClosure(), 319 &result_reason, 320 result_path_p)); 321 loop_runner.Run(); 322 return result_reason; 323 } 324 325 scoped_ptr<StrictMock<MockDownloadDestinationObserver> > observer_; 326 base::WeakPtrFactory<DownloadDestinationObserver> observer_factory_; 327 328 // DownloadFile instance we are testing. 329 scoped_ptr<DownloadFile> download_file_; 330 331 // Stream for sending data into the download file. 332 // Owned by download_file_; will be alive for lifetime of download_file_. 333 StrictMock<MockByteStreamReader>* input_stream_; 334 335 // Sink callback data for stream. 336 base::Closure sink_callback_; 337 338 // Latest update sent to the observer. 339 int64 bytes_; 340 int64 bytes_per_sec_; 341 std::string hash_state_; 342 343 base::MessageLoop loop_; 344 345 private: 346 void SetRenameResult(const base::Closure& closure, 347 DownloadInterruptReason* reason_p, 348 base::FilePath* result_path_p, 349 DownloadInterruptReason reason, 350 const base::FilePath& result_path) { 351 if (reason_p) 352 *reason_p = reason; 353 if (result_path_p) 354 *result_path_p = result_path; 355 closure.Run(); 356 } 357 358 // UI thread. 359 BrowserThreadImpl ui_thread_; 360 // File thread to satisfy debug checks in DownloadFile. 361 BrowserThreadImpl file_thread_; 362 363 // Keep track of what data should be saved to the disk file. 364 std::string expected_data_; 365 }; 366 367 // DownloadFile::RenameAndAnnotate and DownloadFile::RenameAndUniquify have a 368 // considerable amount of functional overlap. In order to re-use test logic, we 369 // are going to introduce this value parameterized test fixture. It will take a 370 // DownloadFileRenameMethodType value which can be either of the two rename 371 // methods. 372 class DownloadFileTestWithRename 373 : public DownloadFileTest, 374 public ::testing::WithParamInterface<DownloadFileRenameMethodType> { 375 protected: 376 DownloadInterruptReason InvokeSelectedRenameMethod( 377 const base::FilePath& full_path, 378 base::FilePath* result_path_p) { 379 return InvokeRenameMethodAndWaitForCallback( 380 GetParam(), full_path, result_path_p); 381 } 382 }; 383 384 // And now instantiate all DownloadFileTestWithRename tests using both 385 // DownloadFile rename methods. Each test of the form 386 // DownloadFileTestWithRename.<FooTest> will be instantiated once with 387 // RenameAndAnnotate as the value parameter and once with RenameAndUniquify as 388 // the value parameter. 389 INSTANTIATE_TEST_CASE_P(DownloadFile, 390 DownloadFileTestWithRename, 391 ::testing::Values(&DownloadFile::RenameAndAnnotate, 392 &DownloadFile::RenameAndUniquify)); 393 394 const char* DownloadFileTest::kTestData1 = 395 "Let's write some data to the file!\n"; 396 const char* DownloadFileTest::kTestData2 = "Writing more data.\n"; 397 const char* DownloadFileTest::kTestData3 = "Final line."; 398 const char* DownloadFileTest::kDataHash = 399 "CBF68BF10F8003DB86B31343AFAC8C7175BD03FB5FC905650F8C80AF087443A8"; 400 401 const uint32 DownloadFileTest::kDummyDownloadId = 23; 402 const int DownloadFileTest::kDummyChildId = 3; 403 const int DownloadFileTest::kDummyRequestId = 67; 404 405 // Rename the file before any data is downloaded, after some has, after it all 406 // has, and after it's closed. 407 TEST_P(DownloadFileTestWithRename, RenameFileFinal) { 408 ASSERT_TRUE(CreateDownloadFile(0, true)); 409 base::FilePath initial_path(download_file_->FullPath()); 410 EXPECT_TRUE(base::PathExists(initial_path)); 411 base::FilePath path_1(initial_path.InsertBeforeExtensionASCII("_1")); 412 base::FilePath path_2(initial_path.InsertBeforeExtensionASCII("_2")); 413 base::FilePath path_3(initial_path.InsertBeforeExtensionASCII("_3")); 414 base::FilePath path_4(initial_path.InsertBeforeExtensionASCII("_4")); 415 base::FilePath output_path; 416 417 // Rename the file before downloading any data. 418 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, 419 InvokeSelectedRenameMethod(path_1, &output_path)); 420 base::FilePath renamed_path = download_file_->FullPath(); 421 EXPECT_EQ(path_1, renamed_path); 422 EXPECT_EQ(path_1, output_path); 423 424 // Check the files. 425 EXPECT_FALSE(base::PathExists(initial_path)); 426 EXPECT_TRUE(base::PathExists(path_1)); 427 428 // Download the data. 429 const char* chunks1[] = { kTestData1, kTestData2 }; 430 AppendDataToFile(chunks1, 2); 431 432 // Rename the file after downloading some data. 433 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, 434 InvokeSelectedRenameMethod(path_2, &output_path)); 435 renamed_path = download_file_->FullPath(); 436 EXPECT_EQ(path_2, renamed_path); 437 EXPECT_EQ(path_2, output_path); 438 439 // Check the files. 440 EXPECT_FALSE(base::PathExists(path_1)); 441 EXPECT_TRUE(base::PathExists(path_2)); 442 443 const char* chunks2[] = { kTestData3 }; 444 AppendDataToFile(chunks2, 1); 445 446 // Rename the file after downloading all the data. 447 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, 448 InvokeSelectedRenameMethod(path_3, &output_path)); 449 renamed_path = download_file_->FullPath(); 450 EXPECT_EQ(path_3, renamed_path); 451 EXPECT_EQ(path_3, output_path); 452 453 // Check the files. 454 EXPECT_FALSE(base::PathExists(path_2)); 455 EXPECT_TRUE(base::PathExists(path_3)); 456 457 // Should not be able to get the hash until the file is closed. 458 std::string hash; 459 EXPECT_FALSE(download_file_->GetHash(&hash)); 460 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true); 461 loop_.RunUntilIdle(); 462 463 // Rename the file after downloading all the data and closing the file. 464 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, 465 InvokeSelectedRenameMethod(path_4, &output_path)); 466 renamed_path = download_file_->FullPath(); 467 EXPECT_EQ(path_4, renamed_path); 468 EXPECT_EQ(path_4, output_path); 469 470 // Check the files. 471 EXPECT_FALSE(base::PathExists(path_3)); 472 EXPECT_TRUE(base::PathExists(path_4)); 473 474 // Check the hash. 475 EXPECT_TRUE(download_file_->GetHash(&hash)); 476 EXPECT_EQ(kDataHash, base::HexEncode(hash.data(), hash.size())); 477 478 DestroyDownloadFile(0); 479 } 480 481 // Test to make sure the rename overwrites when requested. This is separate from 482 // the above test because it only applies to RenameAndAnnotate(). 483 // RenameAndUniquify() doesn't overwrite by design. 484 TEST_F(DownloadFileTest, RenameOverwrites) { 485 ASSERT_TRUE(CreateDownloadFile(0, true)); 486 base::FilePath initial_path(download_file_->FullPath()); 487 EXPECT_TRUE(base::PathExists(initial_path)); 488 base::FilePath path_1(initial_path.InsertBeforeExtensionASCII("_1")); 489 490 ASSERT_FALSE(base::PathExists(path_1)); 491 static const char file_data[] = "xyzzy"; 492 ASSERT_EQ(static_cast<int>(sizeof(file_data)), 493 base::WriteFile(path_1, file_data, sizeof(file_data))); 494 ASSERT_TRUE(base::PathExists(path_1)); 495 496 base::FilePath new_path; 497 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, 498 RenameAndAnnotate(path_1, &new_path)); 499 EXPECT_EQ(path_1.value(), new_path.value()); 500 501 std::string file_contents; 502 ASSERT_TRUE(base::ReadFileToString(new_path, &file_contents)); 503 EXPECT_NE(std::string(file_data), file_contents); 504 505 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true); 506 loop_.RunUntilIdle(); 507 DestroyDownloadFile(0); 508 } 509 510 // Test to make sure the rename uniquifies if we aren't overwriting 511 // and there's a file where we're aiming. As above, not a 512 // DownloadFileTestWithRename test because this only applies to 513 // RenameAndUniquify(). 514 TEST_F(DownloadFileTest, RenameUniquifies) { 515 ASSERT_TRUE(CreateDownloadFile(0, true)); 516 base::FilePath initial_path(download_file_->FullPath()); 517 EXPECT_TRUE(base::PathExists(initial_path)); 518 base::FilePath path_1(initial_path.InsertBeforeExtensionASCII("_1")); 519 base::FilePath path_1_suffixed(path_1.InsertBeforeExtensionASCII(" (1)")); 520 521 ASSERT_FALSE(base::PathExists(path_1)); 522 static const char file_data[] = "xyzzy"; 523 ASSERT_EQ(static_cast<int>(sizeof(file_data)), 524 base::WriteFile(path_1, file_data, sizeof(file_data))); 525 ASSERT_TRUE(base::PathExists(path_1)); 526 527 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, RenameAndUniquify(path_1, NULL)); 528 EXPECT_TRUE(base::PathExists(path_1_suffixed)); 529 530 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true); 531 loop_.RunUntilIdle(); 532 DestroyDownloadFile(0); 533 } 534 535 // Test that RenameAndUniquify doesn't try to uniquify in the case where the 536 // target filename is the same as the current filename. 537 TEST_F(DownloadFileTest, RenameRecognizesSelfConflict) { 538 ASSERT_TRUE(CreateDownloadFile(0, true)); 539 base::FilePath initial_path(download_file_->FullPath()); 540 EXPECT_TRUE(base::PathExists(initial_path)); 541 542 base::FilePath new_path; 543 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, 544 RenameAndUniquify(initial_path, &new_path)); 545 EXPECT_TRUE(base::PathExists(initial_path)); 546 547 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true); 548 loop_.RunUntilIdle(); 549 DestroyDownloadFile(0); 550 EXPECT_EQ(initial_path.value(), new_path.value()); 551 } 552 553 // Test to make sure we get the proper error on failure. 554 TEST_P(DownloadFileTestWithRename, RenameError) { 555 ASSERT_TRUE(CreateDownloadFile(0, true)); 556 base::FilePath initial_path(download_file_->FullPath()); 557 558 // Create a subdirectory. 559 base::FilePath target_dir( 560 initial_path.DirName().Append(FILE_PATH_LITERAL("TargetDir"))); 561 ASSERT_FALSE(base::DirectoryExists(target_dir)); 562 ASSERT_TRUE(base::CreateDirectory(target_dir)); 563 base::FilePath target_path(target_dir.Append(initial_path.BaseName())); 564 565 // Targets 566 base::FilePath target_path_suffixed( 567 target_path.InsertBeforeExtensionASCII(" (1)")); 568 ASSERT_FALSE(base::PathExists(target_path)); 569 ASSERT_FALSE(base::PathExists(target_path_suffixed)); 570 571 // Make the directory unwritable and try to rename within it. 572 { 573 base::FilePermissionRestorer restorer(target_dir); 574 ASSERT_TRUE(base::MakeFileUnwritable(target_dir)); 575 576 // Expect nulling out of further processing. 577 EXPECT_CALL(*input_stream_, RegisterCallback(IsNullCallback())); 578 ExpectPermissionError(InvokeSelectedRenameMethod(target_path, NULL)); 579 EXPECT_FALSE(base::PathExists(target_path_suffixed)); 580 } 581 582 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true); 583 loop_.RunUntilIdle(); 584 DestroyDownloadFile(0); 585 } 586 587 namespace { 588 589 void TestRenameCompletionCallback(const base::Closure& closure, 590 bool* did_run_callback, 591 DownloadInterruptReason interrupt_reason, 592 const base::FilePath& new_path) { 593 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason); 594 *did_run_callback = true; 595 closure.Run(); 596 } 597 598 } // namespace 599 600 // Test that the retry logic works. This test assumes that DownloadFileImpl will 601 // post tasks to the current message loop (acting as the FILE thread) 602 // asynchronously to retry the renames. We will stuff RunLoop::QuitClosures() 603 // in between the retry tasks to stagger them and then allow the rename to 604 // succeed. 605 // 606 // Note that there is only one queue of tasks to run, and that is in the tests' 607 // base::MessageLoop::current(). Each RunLoop processes that queue until it sees 608 // a QuitClosure() targeted at itself, at which point it stops processing. 609 TEST_P(DownloadFileTestWithRename, RenameWithErrorRetry) { 610 ASSERT_TRUE(CreateDownloadFile(0, true)); 611 base::FilePath initial_path(download_file_->FullPath()); 612 613 // Create a subdirectory. 614 base::FilePath target_dir( 615 initial_path.DirName().Append(FILE_PATH_LITERAL("TargetDir"))); 616 ASSERT_FALSE(base::DirectoryExists(target_dir)); 617 ASSERT_TRUE(base::CreateDirectory(target_dir)); 618 base::FilePath target_path(target_dir.Append(initial_path.BaseName())); 619 620 bool did_run_callback = false; 621 622 // Each RunLoop can be used the run the MessageLoop until the corresponding 623 // QuitClosure() is run. This one is used to produce the QuitClosure() that 624 // will be run when the entire rename operation is complete. 625 base::RunLoop succeeding_run; 626 { 627 // (Scope for the base::File or base::FilePermissionRestorer below.) 628 #if defined(OS_WIN) 629 // On Windows we test with an actual transient error, a sharing violation. 630 // The rename will fail because we are holding the file open for READ. On 631 // Posix this doesn't cause a failure. 632 base::File locked_file(initial_path, 633 base::File::FLAG_OPEN | base::File::FLAG_READ); 634 ASSERT_TRUE(locked_file.IsValid()); 635 #else 636 // Simulate a transient failure by revoking write permission for target_dir. 637 // The TestDownloadFileImpl class treats this error as transient even though 638 // DownloadFileImpl itself doesn't. 639 base::FilePermissionRestorer restore_permissions_for(target_dir); 640 ASSERT_TRUE(base::MakeFileUnwritable(target_dir)); 641 #endif 642 643 // The Rename() should fail here and enqueue a retry task without invoking 644 // the completion callback. 645 ((*download_file_).*GetParam())(target_path, 646 base::Bind(&TestRenameCompletionCallback, 647 succeeding_run.QuitClosure(), 648 &did_run_callback)); 649 EXPECT_FALSE(did_run_callback); 650 651 base::RunLoop first_failing_run; 652 // Queue the QuitClosure() on the MessageLoop now. Any tasks queued by the 653 // Rename() will be in front of the QuitClosure(). Running the message loop 654 // now causes the just the first retry task to be run. The rename still 655 // fails, so another retry task would get queued behind the QuitClosure(). 656 base::MessageLoop::current()->PostTask(FROM_HERE, 657 first_failing_run.QuitClosure()); 658 first_failing_run.Run(); 659 EXPECT_FALSE(did_run_callback); 660 661 // Running another loop should have the same effect as the above as long as 662 // kMaxRenameRetries is greater than 2. 663 base::RunLoop second_failing_run; 664 base::MessageLoop::current()->PostTask(FROM_HERE, 665 second_failing_run.QuitClosure()); 666 second_failing_run.Run(); 667 EXPECT_FALSE(did_run_callback); 668 } 669 670 // This time the QuitClosure from succeeding_run should get executed. 671 succeeding_run.Run(); 672 EXPECT_TRUE(did_run_callback); 673 674 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true); 675 loop_.RunUntilIdle(); 676 DestroyDownloadFile(0); 677 } 678 679 // Various tests of the StreamActive method. 680 TEST_F(DownloadFileTest, StreamEmptySuccess) { 681 ASSERT_TRUE(CreateDownloadFile(0, true)); 682 base::FilePath initial_path(download_file_->FullPath()); 683 EXPECT_TRUE(base::PathExists(initial_path)); 684 685 // Test that calling the sink_callback_ on an empty stream shouldn't 686 // do anything. 687 AppendDataToFile(NULL, 0); 688 689 // Finish the download this way and make sure we see it on the 690 // observer. 691 EXPECT_CALL(*(observer_.get()), DestinationCompleted(_)); 692 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, false); 693 loop_.RunUntilIdle(); 694 695 DestroyDownloadFile(0); 696 } 697 698 TEST_F(DownloadFileTest, StreamEmptyError) { 699 ASSERT_TRUE(CreateDownloadFile(0, true)); 700 base::FilePath initial_path(download_file_->FullPath()); 701 EXPECT_TRUE(base::PathExists(initial_path)); 702 703 // Finish the download in error and make sure we see it on the 704 // observer. 705 EXPECT_CALL(*(observer_.get()), 706 DestinationError( 707 DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED)) 708 .WillOnce(InvokeWithoutArgs( 709 this, &DownloadFileTest::ConfirmUpdateDownloadInfo)); 710 711 // If this next EXPECT_CALL fails flakily, it's probably a real failure. 712 // We'll be getting a stream of UpdateDownload calls from the timer, and 713 // the last one may have the correct information even if the failure 714 // doesn't produce an update, as the timer update may have triggered at the 715 // same time. 716 EXPECT_CALL(*(observer_.get()), CurrentUpdateStatus(0, _, _)); 717 718 FinishStream(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, false); 719 720 loop_.RunUntilIdle(); 721 722 DestroyDownloadFile(0); 723 } 724 725 TEST_F(DownloadFileTest, StreamNonEmptySuccess) { 726 ASSERT_TRUE(CreateDownloadFile(0, true)); 727 base::FilePath initial_path(download_file_->FullPath()); 728 EXPECT_TRUE(base::PathExists(initial_path)); 729 730 const char* chunks1[] = { kTestData1, kTestData2 }; 731 ::testing::Sequence s1; 732 SetupDataAppend(chunks1, 2, s1); 733 SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, s1); 734 EXPECT_CALL(*(observer_.get()), DestinationCompleted(_)); 735 sink_callback_.Run(); 736 VerifyStreamAndSize(); 737 loop_.RunUntilIdle(); 738 DestroyDownloadFile(0); 739 } 740 741 TEST_F(DownloadFileTest, StreamNonEmptyError) { 742 ASSERT_TRUE(CreateDownloadFile(0, true)); 743 base::FilePath initial_path(download_file_->FullPath()); 744 EXPECT_TRUE(base::PathExists(initial_path)); 745 746 const char* chunks1[] = { kTestData1, kTestData2 }; 747 ::testing::Sequence s1; 748 SetupDataAppend(chunks1, 2, s1); 749 SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, s1); 750 751 EXPECT_CALL(*(observer_.get()), 752 DestinationError( 753 DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED)) 754 .WillOnce(InvokeWithoutArgs( 755 this, &DownloadFileTest::ConfirmUpdateDownloadInfo)); 756 757 // If this next EXPECT_CALL fails flakily, it's probably a real failure. 758 // We'll be getting a stream of UpdateDownload calls from the timer, and 759 // the last one may have the correct information even if the failure 760 // doesn't produce an update, as the timer update may have triggered at the 761 // same time. 762 EXPECT_CALL(*(observer_.get()), 763 CurrentUpdateStatus(strlen(kTestData1) + strlen(kTestData2), 764 _, _)); 765 766 sink_callback_.Run(); 767 loop_.RunUntilIdle(); 768 VerifyStreamAndSize(); 769 DestroyDownloadFile(0); 770 } 771 772 // Send some data, wait 3/4s of a second, run the message loop, and 773 // confirm the values the observer received are correct. 774 TEST_F(DownloadFileTest, ConfirmUpdate) { 775 CreateDownloadFile(0, true); 776 777 const char* chunks1[] = { kTestData1, kTestData2 }; 778 AppendDataToFile(chunks1, 2); 779 780 // Run the message loops for 750ms and check for results. 781 loop_.PostDelayedTask(FROM_HERE, 782 base::MessageLoop::QuitClosure(), 783 base::TimeDelta::FromMilliseconds(750)); 784 loop_.Run(); 785 786 EXPECT_EQ(static_cast<int64>(strlen(kTestData1) + strlen(kTestData2)), 787 bytes_); 788 EXPECT_EQ(download_file_->GetHashState(), hash_state_); 789 790 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true); 791 DestroyDownloadFile(0); 792 } 793 794 } // namespace content 795