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/file_util.h" 6 #include "base/message_loop/message_loop.h" 7 #include "base/strings/string_number_conversions.h" 8 #include "base/test/test_file_util.h" 9 #include "content/browser/browser_thread_impl.h" 10 #include "content/browser/byte_stream.h" 11 #include "content/browser/download/download_create_info.h" 12 #include "content/browser/download/download_file_impl.h" 13 #include "content/browser/download/download_request_handle.h" 14 #include "content/public/browser/download_destination_observer.h" 15 #include "content/public/browser/download_interrupt_reasons.h" 16 #include "content/public/browser/download_manager.h" 17 #include "content/public/browser/power_save_blocker.h" 18 #include "content/public/test/mock_download_manager.h" 19 #include "net/base/file_stream.h" 20 #include "net/base/mock_file_stream.h" 21 #include "net/base/net_errors.h" 22 #include "testing/gmock/include/gmock/gmock.h" 23 #include "testing/gtest/include/gtest/gtest.h" 24 25 using ::testing::_; 26 using ::testing::AnyNumber; 27 using ::testing::DoAll; 28 using ::testing::InSequence; 29 using ::testing::Return; 30 using ::testing::SetArgPointee; 31 using ::testing::StrictMock; 32 33 namespace content { 34 namespace { 35 36 class MockByteStreamReader : public ByteStreamReader { 37 public: 38 MockByteStreamReader() {} 39 ~MockByteStreamReader() {} 40 41 // ByteStream functions 42 MOCK_METHOD2(Read, ByteStreamReader::StreamState( 43 scoped_refptr<net::IOBuffer>*, size_t*)); 44 MOCK_CONST_METHOD0(GetStatus, int()); 45 MOCK_METHOD1(RegisterCallback, void(const base::Closure&)); 46 }; 47 48 class MockDownloadDestinationObserver : public DownloadDestinationObserver { 49 public: 50 MOCK_METHOD3(DestinationUpdate, void(int64, int64, const std::string&)); 51 MOCK_METHOD1(DestinationError, void(DownloadInterruptReason)); 52 MOCK_METHOD1(DestinationCompleted, void(const std::string&)); 53 54 // Doesn't override any methods in the base class. Used to make sure 55 // that the last DestinationUpdate before a Destination{Completed,Error} 56 // had the right values. 57 MOCK_METHOD3(CurrentUpdateStatus, 58 void(int64, int64, const std::string&)); 59 }; 60 61 MATCHER(IsNullCallback, "") { return (arg.is_null()); } 62 63 } // namespace 64 65 class DownloadFileTest : public testing::Test { 66 public: 67 68 static const char* kTestData1; 69 static const char* kTestData2; 70 static const char* kTestData3; 71 static const char* kDataHash; 72 static const uint32 kDummyDownloadId; 73 static const int kDummyChildId; 74 static const int kDummyRequestId; 75 76 DownloadFileTest() : 77 observer_(new StrictMock<MockDownloadDestinationObserver>), 78 observer_factory_(observer_.get()), 79 input_stream_(NULL), 80 bytes_(-1), 81 bytes_per_sec_(-1), 82 hash_state_("xyzzy"), 83 ui_thread_(BrowserThread::UI, &loop_), 84 file_thread_(BrowserThread::FILE, &loop_) { 85 } 86 87 virtual ~DownloadFileTest() { 88 } 89 90 void SetUpdateDownloadInfo(int64 bytes, int64 bytes_per_sec, 91 const std::string& hash_state) { 92 bytes_ = bytes; 93 bytes_per_sec_ = bytes_per_sec; 94 hash_state_ = hash_state; 95 } 96 97 void ConfirmUpdateDownloadInfo() { 98 observer_->CurrentUpdateStatus(bytes_, bytes_per_sec_, hash_state_); 99 } 100 101 virtual void SetUp() { 102 EXPECT_CALL(*(observer_.get()), DestinationUpdate(_, _, _)) 103 .Times(AnyNumber()) 104 .WillRepeatedly(Invoke(this, &DownloadFileTest::SetUpdateDownloadInfo)); 105 } 106 107 // Mock calls to this function are forwarded here. 108 void RegisterCallback(base::Closure sink_callback) { 109 sink_callback_ = sink_callback; 110 } 111 112 void SetInterruptReasonCallback(bool* was_called, 113 DownloadInterruptReason* reason_p, 114 DownloadInterruptReason reason) { 115 *was_called = true; 116 *reason_p = reason; 117 } 118 119 virtual bool CreateDownloadFile(int offset, bool calculate_hash) { 120 // There can be only one. 121 DCHECK(!download_file_.get()); 122 123 input_stream_ = new StrictMock<MockByteStreamReader>(); 124 125 // TODO: Need to actually create a function that'll set the variables 126 // based on the inputs from the callback. 127 EXPECT_CALL(*input_stream_, RegisterCallback(_)) 128 .WillOnce(Invoke(this, &DownloadFileTest::RegisterCallback)) 129 .RetiresOnSaturation(); 130 131 scoped_ptr<DownloadSaveInfo> save_info(new DownloadSaveInfo()); 132 download_file_.reset( 133 new DownloadFileImpl(save_info.Pass(), 134 base::FilePath(), 135 GURL(), // Source 136 GURL(), // Referrer 137 calculate_hash, 138 scoped_ptr<ByteStreamReader>(input_stream_), 139 net::BoundNetLog(), 140 scoped_ptr<PowerSaveBlocker>().Pass(), 141 observer_factory_.GetWeakPtr())); 142 download_file_->SetClientGuid( 143 "12345678-ABCD-1234-DCBA-123456789ABC"); 144 145 EXPECT_CALL(*input_stream_, Read(_, _)) 146 .WillOnce(Return(ByteStreamReader::STREAM_EMPTY)) 147 .RetiresOnSaturation(); 148 149 base::WeakPtrFactory<DownloadFileTest> weak_ptr_factory(this); 150 bool called = false; 151 DownloadInterruptReason result; 152 download_file_->Initialize(base::Bind( 153 &DownloadFileTest::SetInterruptReasonCallback, 154 weak_ptr_factory.GetWeakPtr(), &called, &result)); 155 loop_.RunUntilIdle(); 156 EXPECT_TRUE(called); 157 158 ::testing::Mock::VerifyAndClearExpectations(input_stream_); 159 return result == DOWNLOAD_INTERRUPT_REASON_NONE; 160 } 161 162 virtual void DestroyDownloadFile(int offset) { 163 EXPECT_FALSE(download_file_->InProgress()); 164 165 // Make sure the data has been properly written to disk. 166 std::string disk_data; 167 EXPECT_TRUE(base::ReadFileToString(download_file_->FullPath(), &disk_data)); 168 EXPECT_EQ(expected_data_, disk_data); 169 170 // Make sure the Browser and File threads outlive the DownloadFile 171 // to satisfy thread checks inside it. 172 download_file_.reset(); 173 } 174 175 // Setup the stream to do be a data append; don't actually trigger 176 // the callback or do verifications. 177 void SetupDataAppend(const char **data_chunks, size_t num_chunks, 178 ::testing::Sequence s) { 179 DCHECK(input_stream_); 180 for (size_t i = 0; i < num_chunks; i++) { 181 const char *source_data = data_chunks[i]; 182 size_t length = strlen(source_data); 183 scoped_refptr<net::IOBuffer> data = new net::IOBuffer(length); 184 memcpy(data->data(), source_data, length); 185 EXPECT_CALL(*input_stream_, Read(_, _)) 186 .InSequence(s) 187 .WillOnce(DoAll(SetArgPointee<0>(data), 188 SetArgPointee<1>(length), 189 Return(ByteStreamReader::STREAM_HAS_DATA))) 190 .RetiresOnSaturation(); 191 expected_data_ += source_data; 192 } 193 } 194 195 void VerifyStreamAndSize() { 196 ::testing::Mock::VerifyAndClearExpectations(input_stream_); 197 int64 size; 198 EXPECT_TRUE(base::GetFileSize(download_file_->FullPath(), &size)); 199 EXPECT_EQ(expected_data_.size(), static_cast<size_t>(size)); 200 } 201 202 // TODO(rdsmith): Manage full percentage issues properly. 203 void AppendDataToFile(const char **data_chunks, size_t num_chunks) { 204 ::testing::Sequence s1; 205 SetupDataAppend(data_chunks, num_chunks, s1); 206 EXPECT_CALL(*input_stream_, Read(_, _)) 207 .InSequence(s1) 208 .WillOnce(Return(ByteStreamReader::STREAM_EMPTY)) 209 .RetiresOnSaturation(); 210 sink_callback_.Run(); 211 VerifyStreamAndSize(); 212 } 213 214 void SetupFinishStream(DownloadInterruptReason interrupt_reason, 215 ::testing::Sequence s) { 216 EXPECT_CALL(*input_stream_, Read(_, _)) 217 .InSequence(s) 218 .WillOnce(Return(ByteStreamReader::STREAM_COMPLETE)) 219 .RetiresOnSaturation(); 220 EXPECT_CALL(*input_stream_, GetStatus()) 221 .InSequence(s) 222 .WillOnce(Return(interrupt_reason)) 223 .RetiresOnSaturation(); 224 EXPECT_CALL(*input_stream_, RegisterCallback(_)) 225 .RetiresOnSaturation(); 226 } 227 228 void FinishStream(DownloadInterruptReason interrupt_reason, 229 bool check_observer) { 230 ::testing::Sequence s1; 231 SetupFinishStream(interrupt_reason, s1); 232 sink_callback_.Run(); 233 VerifyStreamAndSize(); 234 if (check_observer) { 235 EXPECT_CALL(*(observer_.get()), DestinationCompleted(_)); 236 loop_.RunUntilIdle(); 237 ::testing::Mock::VerifyAndClearExpectations(observer_.get()); 238 EXPECT_CALL(*(observer_.get()), DestinationUpdate(_, _, _)) 239 .Times(AnyNumber()) 240 .WillRepeatedly(Invoke(this, 241 &DownloadFileTest::SetUpdateDownloadInfo)); 242 } 243 } 244 245 DownloadInterruptReason RenameAndUniquify( 246 const base::FilePath& full_path, 247 base::FilePath* result_path_p) { 248 base::WeakPtrFactory<DownloadFileTest> weak_ptr_factory(this); 249 DownloadInterruptReason result_reason(DOWNLOAD_INTERRUPT_REASON_NONE); 250 bool callback_was_called(false); 251 base::FilePath result_path; 252 253 download_file_->RenameAndUniquify( 254 full_path, base::Bind(&DownloadFileTest::SetRenameResult, 255 weak_ptr_factory.GetWeakPtr(), 256 &callback_was_called, 257 &result_reason, result_path_p)); 258 loop_.RunUntilIdle(); 259 260 EXPECT_TRUE(callback_was_called); 261 return result_reason; 262 } 263 264 DownloadInterruptReason RenameAndAnnotate( 265 const base::FilePath& full_path, 266 base::FilePath* result_path_p) { 267 base::WeakPtrFactory<DownloadFileTest> weak_ptr_factory(this); 268 DownloadInterruptReason result_reason(DOWNLOAD_INTERRUPT_REASON_NONE); 269 bool callback_was_called(false); 270 base::FilePath result_path; 271 272 download_file_->RenameAndAnnotate( 273 full_path, base::Bind(&DownloadFileTest::SetRenameResult, 274 weak_ptr_factory.GetWeakPtr(), 275 &callback_was_called, 276 &result_reason, result_path_p)); 277 loop_.RunUntilIdle(); 278 279 EXPECT_TRUE(callback_was_called); 280 return result_reason; 281 } 282 283 protected: 284 scoped_ptr<StrictMock<MockDownloadDestinationObserver> > observer_; 285 base::WeakPtrFactory<DownloadDestinationObserver> observer_factory_; 286 287 // DownloadFile instance we are testing. 288 scoped_ptr<DownloadFile> download_file_; 289 290 // Stream for sending data into the download file. 291 // Owned by download_file_; will be alive for lifetime of download_file_. 292 StrictMock<MockByteStreamReader>* input_stream_; 293 294 // Sink callback data for stream. 295 base::Closure sink_callback_; 296 297 // Latest update sent to the observer. 298 int64 bytes_; 299 int64 bytes_per_sec_; 300 std::string hash_state_; 301 302 base::MessageLoop loop_; 303 304 private: 305 void SetRenameResult(bool* called_p, 306 DownloadInterruptReason* reason_p, 307 base::FilePath* result_path_p, 308 DownloadInterruptReason reason, 309 const base::FilePath& result_path) { 310 if (called_p) 311 *called_p = true; 312 if (reason_p) 313 *reason_p = reason; 314 if (result_path_p) 315 *result_path_p = result_path; 316 } 317 318 // UI thread. 319 BrowserThreadImpl ui_thread_; 320 // File thread to satisfy debug checks in DownloadFile. 321 BrowserThreadImpl file_thread_; 322 323 // Keep track of what data should be saved to the disk file. 324 std::string expected_data_; 325 }; 326 327 const char* DownloadFileTest::kTestData1 = 328 "Let's write some data to the file!\n"; 329 const char* DownloadFileTest::kTestData2 = "Writing more data.\n"; 330 const char* DownloadFileTest::kTestData3 = "Final line."; 331 const char* DownloadFileTest::kDataHash = 332 "CBF68BF10F8003DB86B31343AFAC8C7175BD03FB5FC905650F8C80AF087443A8"; 333 334 const uint32 DownloadFileTest::kDummyDownloadId = 23; 335 const int DownloadFileTest::kDummyChildId = 3; 336 const int DownloadFileTest::kDummyRequestId = 67; 337 338 // Rename the file before any data is downloaded, after some has, after it all 339 // has, and after it's closed. 340 TEST_F(DownloadFileTest, RenameFileFinal) { 341 ASSERT_TRUE(CreateDownloadFile(0, true)); 342 base::FilePath initial_path(download_file_->FullPath()); 343 EXPECT_TRUE(base::PathExists(initial_path)); 344 base::FilePath path_1(initial_path.InsertBeforeExtensionASCII("_1")); 345 base::FilePath path_2(initial_path.InsertBeforeExtensionASCII("_2")); 346 base::FilePath path_3(initial_path.InsertBeforeExtensionASCII("_3")); 347 base::FilePath path_4(initial_path.InsertBeforeExtensionASCII("_4")); 348 base::FilePath path_5(initial_path.InsertBeforeExtensionASCII("_5")); 349 base::FilePath output_path; 350 351 // Rename the file before downloading any data. 352 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, 353 RenameAndUniquify(path_1, &output_path)); 354 base::FilePath renamed_path = download_file_->FullPath(); 355 EXPECT_EQ(path_1, renamed_path); 356 EXPECT_EQ(path_1, output_path); 357 358 // Check the files. 359 EXPECT_FALSE(base::PathExists(initial_path)); 360 EXPECT_TRUE(base::PathExists(path_1)); 361 362 // Download the data. 363 const char* chunks1[] = { kTestData1, kTestData2 }; 364 AppendDataToFile(chunks1, 2); 365 366 // Rename the file after downloading some data. 367 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, 368 RenameAndUniquify(path_2, &output_path)); 369 renamed_path = download_file_->FullPath(); 370 EXPECT_EQ(path_2, renamed_path); 371 EXPECT_EQ(path_2, output_path); 372 373 // Check the files. 374 EXPECT_FALSE(base::PathExists(path_1)); 375 EXPECT_TRUE(base::PathExists(path_2)); 376 377 const char* chunks2[] = { kTestData3 }; 378 AppendDataToFile(chunks2, 1); 379 380 // Rename the file after downloading all the data. 381 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, 382 RenameAndUniquify(path_3, &output_path)); 383 renamed_path = download_file_->FullPath(); 384 EXPECT_EQ(path_3, renamed_path); 385 EXPECT_EQ(path_3, output_path); 386 387 // Check the files. 388 EXPECT_FALSE(base::PathExists(path_2)); 389 EXPECT_TRUE(base::PathExists(path_3)); 390 391 // Should not be able to get the hash until the file is closed. 392 std::string hash; 393 EXPECT_FALSE(download_file_->GetHash(&hash)); 394 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true); 395 loop_.RunUntilIdle(); 396 397 // Rename the file after downloading all the data and closing the file. 398 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, 399 RenameAndUniquify(path_4, &output_path)); 400 renamed_path = download_file_->FullPath(); 401 EXPECT_EQ(path_4, renamed_path); 402 EXPECT_EQ(path_4, output_path); 403 404 // Check the files. 405 EXPECT_FALSE(base::PathExists(path_3)); 406 EXPECT_TRUE(base::PathExists(path_4)); 407 408 // Check the hash. 409 EXPECT_TRUE(download_file_->GetHash(&hash)); 410 EXPECT_EQ(kDataHash, base::HexEncode(hash.data(), hash.size())); 411 412 // Check that a rename with overwrite to an existing file succeeds. 413 std::string file_contents; 414 ASSERT_FALSE(base::PathExists(path_5)); 415 static const char file_data[] = "xyzzy"; 416 ASSERT_EQ(static_cast<int>(sizeof(file_data) - 1), 417 file_util::WriteFile(path_5, file_data, sizeof(file_data) - 1)); 418 ASSERT_TRUE(base::PathExists(path_5)); 419 EXPECT_TRUE(base::ReadFileToString(path_5, &file_contents)); 420 EXPECT_EQ(std::string(file_data), file_contents); 421 422 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, 423 RenameAndAnnotate(path_5, &output_path)); 424 EXPECT_EQ(path_5, output_path); 425 426 file_contents = ""; 427 EXPECT_TRUE(base::ReadFileToString(path_5, &file_contents)); 428 EXPECT_NE(std::string(file_data), file_contents); 429 430 DestroyDownloadFile(0); 431 } 432 433 // Test to make sure the rename uniquifies if we aren't overwriting 434 // and there's a file where we're aiming. 435 TEST_F(DownloadFileTest, RenameUniquifies) { 436 ASSERT_TRUE(CreateDownloadFile(0, true)); 437 base::FilePath initial_path(download_file_->FullPath()); 438 EXPECT_TRUE(base::PathExists(initial_path)); 439 base::FilePath path_1(initial_path.InsertBeforeExtensionASCII("_1")); 440 base::FilePath path_1_suffixed(path_1.InsertBeforeExtensionASCII(" (1)")); 441 442 ASSERT_FALSE(base::PathExists(path_1)); 443 static const char file_data[] = "xyzzy"; 444 ASSERT_EQ(static_cast<int>(sizeof(file_data)), 445 file_util::WriteFile(path_1, file_data, sizeof(file_data))); 446 ASSERT_TRUE(base::PathExists(path_1)); 447 448 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, RenameAndUniquify(path_1, NULL)); 449 EXPECT_TRUE(base::PathExists(path_1_suffixed)); 450 451 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true); 452 loop_.RunUntilIdle(); 453 DestroyDownloadFile(0); 454 } 455 456 // Test to make sure we get the proper error on failure. 457 TEST_F(DownloadFileTest, RenameError) { 458 ASSERT_TRUE(CreateDownloadFile(0, true)); 459 base::FilePath initial_path(download_file_->FullPath()); 460 461 // Create a subdirectory. 462 base::FilePath tempdir( 463 initial_path.DirName().Append(FILE_PATH_LITERAL("tempdir"))); 464 ASSERT_TRUE(base::CreateDirectory(tempdir)); 465 base::FilePath target_path(tempdir.Append(initial_path.BaseName())); 466 467 // Targets 468 base::FilePath target_path_suffixed( 469 target_path.InsertBeforeExtensionASCII(" (1)")); 470 ASSERT_FALSE(base::PathExists(target_path)); 471 ASSERT_FALSE(base::PathExists(target_path_suffixed)); 472 473 // Make the directory unwritable and try to rename within it. 474 { 475 file_util::PermissionRestorer restorer(tempdir); 476 ASSERT_TRUE(file_util::MakeFileUnwritable(tempdir)); 477 478 // Expect nulling out of further processing. 479 EXPECT_CALL(*input_stream_, RegisterCallback(IsNullCallback())); 480 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED, 481 RenameAndAnnotate(target_path, NULL)); 482 EXPECT_FALSE(base::PathExists(target_path_suffixed)); 483 } 484 485 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true); 486 loop_.RunUntilIdle(); 487 DestroyDownloadFile(0); 488 } 489 490 // Various tests of the StreamActive method. 491 TEST_F(DownloadFileTest, StreamEmptySuccess) { 492 ASSERT_TRUE(CreateDownloadFile(0, true)); 493 base::FilePath initial_path(download_file_->FullPath()); 494 EXPECT_TRUE(base::PathExists(initial_path)); 495 496 // Test that calling the sink_callback_ on an empty stream shouldn't 497 // do anything. 498 AppendDataToFile(NULL, 0); 499 500 // Finish the download this way and make sure we see it on the 501 // observer. 502 EXPECT_CALL(*(observer_.get()), DestinationCompleted(_)); 503 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, false); 504 loop_.RunUntilIdle(); 505 506 DestroyDownloadFile(0); 507 } 508 509 TEST_F(DownloadFileTest, StreamEmptyError) { 510 ASSERT_TRUE(CreateDownloadFile(0, true)); 511 base::FilePath initial_path(download_file_->FullPath()); 512 EXPECT_TRUE(base::PathExists(initial_path)); 513 514 // Finish the download in error and make sure we see it on the 515 // observer. 516 EXPECT_CALL(*(observer_.get()), 517 DestinationError( 518 DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED)) 519 .WillOnce(InvokeWithoutArgs( 520 this, &DownloadFileTest::ConfirmUpdateDownloadInfo)); 521 522 // If this next EXPECT_CALL fails flakily, it's probably a real failure. 523 // We'll be getting a stream of UpdateDownload calls from the timer, and 524 // the last one may have the correct information even if the failure 525 // doesn't produce an update, as the timer update may have triggered at the 526 // same time. 527 EXPECT_CALL(*(observer_.get()), CurrentUpdateStatus(0, _, _)); 528 529 FinishStream(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, false); 530 531 loop_.RunUntilIdle(); 532 533 DestroyDownloadFile(0); 534 } 535 536 TEST_F(DownloadFileTest, StreamNonEmptySuccess) { 537 ASSERT_TRUE(CreateDownloadFile(0, true)); 538 base::FilePath initial_path(download_file_->FullPath()); 539 EXPECT_TRUE(base::PathExists(initial_path)); 540 541 const char* chunks1[] = { kTestData1, kTestData2 }; 542 ::testing::Sequence s1; 543 SetupDataAppend(chunks1, 2, s1); 544 SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, s1); 545 EXPECT_CALL(*(observer_.get()), DestinationCompleted(_)); 546 sink_callback_.Run(); 547 VerifyStreamAndSize(); 548 loop_.RunUntilIdle(); 549 DestroyDownloadFile(0); 550 } 551 552 TEST_F(DownloadFileTest, StreamNonEmptyError) { 553 ASSERT_TRUE(CreateDownloadFile(0, true)); 554 base::FilePath initial_path(download_file_->FullPath()); 555 EXPECT_TRUE(base::PathExists(initial_path)); 556 557 const char* chunks1[] = { kTestData1, kTestData2 }; 558 ::testing::Sequence s1; 559 SetupDataAppend(chunks1, 2, s1); 560 SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, s1); 561 562 EXPECT_CALL(*(observer_.get()), 563 DestinationError( 564 DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED)) 565 .WillOnce(InvokeWithoutArgs( 566 this, &DownloadFileTest::ConfirmUpdateDownloadInfo)); 567 568 // If this next EXPECT_CALL fails flakily, it's probably a real failure. 569 // We'll be getting a stream of UpdateDownload calls from the timer, and 570 // the last one may have the correct information even if the failure 571 // doesn't produce an update, as the timer update may have triggered at the 572 // same time. 573 EXPECT_CALL(*(observer_.get()), 574 CurrentUpdateStatus(strlen(kTestData1) + strlen(kTestData2), 575 _, _)); 576 577 sink_callback_.Run(); 578 loop_.RunUntilIdle(); 579 VerifyStreamAndSize(); 580 DestroyDownloadFile(0); 581 } 582 583 // Send some data, wait 3/4s of a second, run the message loop, and 584 // confirm the values the observer received are correct. 585 TEST_F(DownloadFileTest, ConfirmUpdate) { 586 CreateDownloadFile(0, true); 587 588 const char* chunks1[] = { kTestData1, kTestData2 }; 589 AppendDataToFile(chunks1, 2); 590 591 // Run the message loops for 750ms and check for results. 592 loop_.PostDelayedTask(FROM_HERE, 593 base::MessageLoop::QuitClosure(), 594 base::TimeDelta::FromMilliseconds(750)); 595 loop_.Run(); 596 597 EXPECT_EQ(static_cast<int64>(strlen(kTestData1) + strlen(kTestData2)), 598 bytes_); 599 EXPECT_EQ(download_file_->GetHashState(), hash_state_); 600 601 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true); 602 DestroyDownloadFile(0); 603 } 604 605 } // namespace content 606