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