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.
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"
27 using ::testing::AnyNumber;
28 using ::testing::DoAll;
29 using ::testing::InSequence;
30 using ::testing::Return;
31 using ::testing::SetArgPointee;
32 using ::testing::StrictMock;
37 class MockByteStreamReader : public ByteStreamReader {
39 MockByteStreamReader() {}
40 ~MockByteStreamReader() {}
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&));
49 class MockDownloadDestinationObserver : public DownloadDestinationObserver {
51 MOCK_METHOD3(DestinationUpdate, void(int64, int64, const std::string&));
52 MOCK_METHOD1(DestinationError, void(DownloadInterruptReason));
53 MOCK_METHOD1(DestinationCompleted, void(const std::string&));
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&));
62 MATCHER(IsNullCallback, "") { return (arg.is_null()); }
64 typedef void (DownloadFile::*DownloadFileRenameMethodType)(
65 const base::FilePath&,
66 const DownloadFile::RenameCompletionCallback&);
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 {
72 TestDownloadFileImpl(scoped_ptr<DownloadSaveInfo> save_info,
73 const base::FilePath& default_downloads_directory,
75 const GURL& referrer_url,
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,
90 virtual base::TimeDelta GetRetryDelayForFailedRename(
91 int attempt_count) OVERRIDE {
92 return base::TimeDelta::FromMilliseconds(0);
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;
108 class DownloadFileTest : public testing::Test {
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;
120 observer_(new StrictMock<MockDownloadDestinationObserver>),
121 observer_factory_(observer_.get()),
125 hash_state_("xyzzy"),
126 ui_thread_(BrowserThread::UI, &loop_),
127 file_thread_(BrowserThread::FILE, &loop_) {
130 virtual ~DownloadFileTest() {
133 void SetUpdateDownloadInfo(int64 bytes, int64 bytes_per_sec,
134 const std::string& hash_state) {
136 bytes_per_sec_ = bytes_per_sec;
137 hash_state_ = hash_state;
140 void ConfirmUpdateDownloadInfo() {
141 observer_->CurrentUpdateStatus(bytes_, bytes_per_sec_, hash_state_);
144 virtual void SetUp() {
145 EXPECT_CALL(*(observer_.get()), DestinationUpdate(_, _, _))
147 .WillRepeatedly(Invoke(this, &DownloadFileTest::SetUpdateDownloadInfo));
150 // Mock calls to this function are forwarded here.
151 void RegisterCallback(const base::Closure& sink_callback) {
152 sink_callback_ = sink_callback;
155 void SetInterruptReasonCallback(const base::Closure& closure,
156 DownloadInterruptReason* reason_p,
157 DownloadInterruptReason reason) {
162 bool CreateDownloadFile(int offset, bool calculate_hash) {
163 // There can be only one.
164 DCHECK(!download_file_.get());
166 input_stream_ = new StrictMock<MockByteStreamReader>();
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();
174 scoped_ptr<DownloadSaveInfo> save_info(new DownloadSaveInfo());
175 scoped_ptr<TestDownloadFileImpl> download_file_impl(
176 new TestDownloadFileImpl(save_info.Pass(),
181 scoped_ptr<ByteStreamReader>(input_stream_),
183 observer_factory_.GetWeakPtr()));
184 download_file_impl->SetClientGuid("12345678-ABCD-1234-DCBA-123456789ABC");
185 download_file_ = download_file_impl.PassAs<DownloadFile>();
187 EXPECT_CALL(*input_stream_, Read(_, _))
188 .WillOnce(Return(ByteStreamReader::STREAM_EMPTY))
189 .RetiresOnSaturation();
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));
199 ::testing::Mock::VerifyAndClearExpectations(input_stream_);
200 return result == DOWNLOAD_INTERRUPT_REASON_NONE;
203 void DestroyDownloadFile(int offset) {
204 EXPECT_FALSE(download_file_->InProgress());
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);
211 // Make sure the Browser and File threads outlive the DownloadFile
212 // to satisfy thread checks inside it.
213 download_file_.reset();
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(_, _))
228 .WillOnce(DoAll(SetArgPointee<0>(data),
229 SetArgPointee<1>(length),
230 Return(ByteStreamReader::STREAM_HAS_DATA)))
231 .RetiresOnSaturation();
232 expected_data_ += source_data;
236 void VerifyStreamAndSize() {
237 ::testing::Mock::VerifyAndClearExpectations(input_stream_);
239 EXPECT_TRUE(base::GetFileSize(download_file_->FullPath(), &size));
240 EXPECT_EQ(expected_data_.size(), static_cast<size_t>(size));
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(_, _))
249 .WillOnce(Return(ByteStreamReader::STREAM_EMPTY))
250 .RetiresOnSaturation();
251 sink_callback_.Run();
252 VerifyStreamAndSize();
255 void SetupFinishStream(DownloadInterruptReason interrupt_reason,
256 ::testing::Sequence s) {
257 EXPECT_CALL(*input_stream_, Read(_, _))
259 .WillOnce(Return(ByteStreamReader::STREAM_COMPLETE))
260 .RetiresOnSaturation();
261 EXPECT_CALL(*input_stream_, GetStatus())
263 .WillOnce(Return(interrupt_reason))
264 .RetiresOnSaturation();
265 EXPECT_CALL(*input_stream_, RegisterCallback(_))
266 .RetiresOnSaturation();
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(_, _, _))
281 .WillRepeatedly(Invoke(this,
282 &DownloadFileTest::SetUpdateDownloadInfo));
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);
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);
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;
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;
314 base::RunLoop loop_runner;
315 ((*download_file_).*method)(full_path,
316 base::Bind(&DownloadFileTest::SetRenameResult,
317 base::Unretained(this),
318 loop_runner.QuitClosure(),
322 return result_reason;
325 scoped_ptr<StrictMock<MockDownloadDestinationObserver> > observer_;
326 base::WeakPtrFactory<DownloadDestinationObserver> observer_factory_;
328 // DownloadFile instance we are testing.
329 scoped_ptr<DownloadFile> download_file_;
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_;
335 // Sink callback data for stream.
336 base::Closure sink_callback_;
338 // Latest update sent to the observer.
340 int64 bytes_per_sec_;
341 std::string hash_state_;
343 base::MessageLoop loop_;
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) {
354 *result_path_p = result_path;
359 BrowserThreadImpl ui_thread_;
360 // File thread to satisfy debug checks in DownloadFile.
361 BrowserThreadImpl file_thread_;
363 // Keep track of what data should be saved to the disk file.
364 std::string expected_data_;
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
372 class DownloadFileTestWithRename
373 : public DownloadFileTest,
374 public ::testing::WithParamInterface<DownloadFileRenameMethodType> {
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);
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));
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";
401 const uint32 DownloadFileTest::kDummyDownloadId = 23;
402 const int DownloadFileTest::kDummyChildId = 3;
403 const int DownloadFileTest::kDummyRequestId = 67;
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;
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);
425 EXPECT_FALSE(base::PathExists(initial_path));
426 EXPECT_TRUE(base::PathExists(path_1));
428 // Download the data.
429 const char* chunks1[] = { kTestData1, kTestData2 };
430 AppendDataToFile(chunks1, 2);
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);
440 EXPECT_FALSE(base::PathExists(path_1));
441 EXPECT_TRUE(base::PathExists(path_2));
443 const char* chunks2[] = { kTestData3 };
444 AppendDataToFile(chunks2, 1);
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);
454 EXPECT_FALSE(base::PathExists(path_2));
455 EXPECT_TRUE(base::PathExists(path_3));
457 // Should not be able to get the hash until the file is closed.
459 EXPECT_FALSE(download_file_->GetHash(&hash));
460 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
461 loop_.RunUntilIdle();
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);
471 EXPECT_FALSE(base::PathExists(path_3));
472 EXPECT_TRUE(base::PathExists(path_4));
475 EXPECT_TRUE(download_file_->GetHash(&hash));
476 EXPECT_EQ(kDataHash, base::HexEncode(hash.data(), hash.size()));
478 DestroyDownloadFile(0);
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"));
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));
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());
501 std::string file_contents;
502 ASSERT_TRUE(base::ReadFileToString(new_path, &file_contents));
503 EXPECT_NE(std::string(file_data), file_contents);
505 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
506 loop_.RunUntilIdle();
507 DestroyDownloadFile(0);
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)"));
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));
527 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, RenameAndUniquify(path_1, NULL));
528 EXPECT_TRUE(base::PathExists(path_1_suffixed));
530 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
531 loop_.RunUntilIdle();
532 DestroyDownloadFile(0);
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));
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));
547 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
548 loop_.RunUntilIdle();
549 DestroyDownloadFile(0);
550 EXPECT_EQ(initial_path.value(), new_path.value());
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());
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()));
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));
571 // Make the directory unwritable and try to rename within it.
573 base::FilePermissionRestorer restorer(target_dir);
574 ASSERT_TRUE(base::MakeFileUnwritable(target_dir));
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));
582 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
583 loop_.RunUntilIdle();
584 DestroyDownloadFile(0);
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;
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
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());
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()));
620 bool did_run_callback = false;
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;
627 // (Scope for the base::File or base::FilePermissionRestorer below.)
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());
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));
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(),
649 EXPECT_FALSE(did_run_callback);
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);
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);
670 // This time the QuitClosure from succeeding_run should get executed.
671 succeeding_run.Run();
672 EXPECT_TRUE(did_run_callback);
674 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
675 loop_.RunUntilIdle();
676 DestroyDownloadFile(0);
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));
685 // Test that calling the sink_callback_ on an empty stream shouldn't
687 AppendDataToFile(NULL, 0);
689 // Finish the download this way and make sure we see it on the
691 EXPECT_CALL(*(observer_.get()), DestinationCompleted(_));
692 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, false);
693 loop_.RunUntilIdle();
695 DestroyDownloadFile(0);
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));
703 // Finish the download in error and make sure we see it on the
705 EXPECT_CALL(*(observer_.get()),
707 DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED))
708 .WillOnce(InvokeWithoutArgs(
709 this, &DownloadFileTest::ConfirmUpdateDownloadInfo));
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
716 EXPECT_CALL(*(observer_.get()), CurrentUpdateStatus(0, _, _));
718 FinishStream(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, false);
720 loop_.RunUntilIdle();
722 DestroyDownloadFile(0);
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));
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);
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));
746 const char* chunks1[] = { kTestData1, kTestData2 };
747 ::testing::Sequence s1;
748 SetupDataAppend(chunks1, 2, s1);
749 SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, s1);
751 EXPECT_CALL(*(observer_.get()),
753 DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED))
754 .WillOnce(InvokeWithoutArgs(
755 this, &DownloadFileTest::ConfirmUpdateDownloadInfo));
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
762 EXPECT_CALL(*(observer_.get()),
763 CurrentUpdateStatus(strlen(kTestData1) + strlen(kTestData2),
766 sink_callback_.Run();
767 loop_.RunUntilIdle();
768 VerifyStreamAndSize();
769 DestroyDownloadFile(0);
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);
777 const char* chunks1[] = { kTestData1, kTestData2 };
778 AppendDataToFile(chunks1, 2);
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));
786 EXPECT_EQ(static_cast<int64>(strlen(kTestData1) + strlen(kTestData2)),
788 EXPECT_EQ(download_file_->GetHashState(), hash_state_);
790 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
791 DestroyDownloadFile(0);
794 } // namespace content