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 base::TimeDelta
GetRetryDelayForFailedRename(int attempt_count
) override
{
91 return base::TimeDelta::FromMilliseconds(0);
95 // On Posix, we don't encounter transient errors during renames, except
96 // possibly EAGAIN, which is difficult to replicate reliably. So we resort to
97 // simulating a transient error using ACCESS_DENIED instead.
98 bool ShouldRetryFailedRename(DownloadInterruptReason reason
) override
{
99 return reason
== DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED
;
106 class DownloadFileTest
: public testing::Test
{
109 static const char* kTestData1
;
110 static const char* kTestData2
;
111 static const char* kTestData3
;
112 static const char* kDataHash
;
113 static const uint32 kDummyDownloadId
;
114 static const int kDummyChildId
;
115 static const int kDummyRequestId
;
118 observer_(new StrictMock
<MockDownloadDestinationObserver
>),
119 observer_factory_(observer_
.get()),
123 hash_state_("xyzzy"),
124 ui_thread_(BrowserThread::UI
, &loop_
),
125 file_thread_(BrowserThread::FILE, &loop_
) {
128 ~DownloadFileTest() override
{}
130 void SetUpdateDownloadInfo(int64 bytes
, int64 bytes_per_sec
,
131 const std::string
& hash_state
) {
133 bytes_per_sec_
= bytes_per_sec
;
134 hash_state_
= hash_state
;
137 void ConfirmUpdateDownloadInfo() {
138 observer_
->CurrentUpdateStatus(bytes_
, bytes_per_sec_
, hash_state_
);
141 void SetUp() override
{
142 EXPECT_CALL(*(observer_
.get()), DestinationUpdate(_
, _
, _
))
144 .WillRepeatedly(Invoke(this, &DownloadFileTest::SetUpdateDownloadInfo
));
147 // Mock calls to this function are forwarded here.
148 void RegisterCallback(const base::Closure
& sink_callback
) {
149 sink_callback_
= sink_callback
;
152 void SetInterruptReasonCallback(const base::Closure
& closure
,
153 DownloadInterruptReason
* reason_p
,
154 DownloadInterruptReason reason
) {
159 bool CreateDownloadFile(int offset
, bool calculate_hash
) {
160 // There can be only one.
161 DCHECK(!download_file_
.get());
163 input_stream_
= new StrictMock
<MockByteStreamReader
>();
165 // TODO: Need to actually create a function that'll set the variables
166 // based on the inputs from the callback.
167 EXPECT_CALL(*input_stream_
, RegisterCallback(_
))
168 .WillOnce(Invoke(this, &DownloadFileTest::RegisterCallback
))
169 .RetiresOnSaturation();
171 scoped_ptr
<DownloadSaveInfo
> save_info(new DownloadSaveInfo());
172 scoped_ptr
<TestDownloadFileImpl
> download_file_impl(
173 new TestDownloadFileImpl(save_info
.Pass(),
178 scoped_ptr
<ByteStreamReader
>(input_stream_
),
180 observer_factory_
.GetWeakPtr()));
181 download_file_impl
->SetClientGuid("12345678-ABCD-1234-DCBA-123456789ABC");
182 download_file_
= download_file_impl
.Pass();
184 EXPECT_CALL(*input_stream_
, Read(_
, _
))
185 .WillOnce(Return(ByteStreamReader::STREAM_EMPTY
))
186 .RetiresOnSaturation();
188 base::WeakPtrFactory
<DownloadFileTest
> weak_ptr_factory(this);
189 DownloadInterruptReason result
= DOWNLOAD_INTERRUPT_REASON_NONE
;
190 base::RunLoop loop_runner
;
191 download_file_
->Initialize(base::Bind(
192 &DownloadFileTest::SetInterruptReasonCallback
,
193 weak_ptr_factory
.GetWeakPtr(), loop_runner
.QuitClosure(), &result
));
196 ::testing::Mock::VerifyAndClearExpectations(input_stream_
);
197 return result
== DOWNLOAD_INTERRUPT_REASON_NONE
;
200 void DestroyDownloadFile(int offset
) {
201 EXPECT_FALSE(download_file_
->InProgress());
203 // Make sure the data has been properly written to disk.
204 std::string disk_data
;
205 EXPECT_TRUE(base::ReadFileToString(download_file_
->FullPath(), &disk_data
));
206 EXPECT_EQ(expected_data_
, disk_data
);
208 // Make sure the Browser and File threads outlive the DownloadFile
209 // to satisfy thread checks inside it.
210 download_file_
.reset();
213 // Setup the stream to do be a data append; don't actually trigger
214 // the callback or do verifications.
215 void SetupDataAppend(const char **data_chunks
, size_t num_chunks
,
216 ::testing::Sequence s
) {
217 DCHECK(input_stream_
);
218 for (size_t i
= 0; i
< num_chunks
; i
++) {
219 const char *source_data
= data_chunks
[i
];
220 size_t length
= strlen(source_data
);
221 scoped_refptr
<net::IOBuffer
> data
= new net::IOBuffer(length
);
222 memcpy(data
->data(), source_data
, length
);
223 EXPECT_CALL(*input_stream_
, Read(_
, _
))
225 .WillOnce(DoAll(SetArgPointee
<0>(data
),
226 SetArgPointee
<1>(length
),
227 Return(ByteStreamReader::STREAM_HAS_DATA
)))
228 .RetiresOnSaturation();
229 expected_data_
+= source_data
;
233 void VerifyStreamAndSize() {
234 ::testing::Mock::VerifyAndClearExpectations(input_stream_
);
236 EXPECT_TRUE(base::GetFileSize(download_file_
->FullPath(), &size
));
237 EXPECT_EQ(expected_data_
.size(), static_cast<size_t>(size
));
240 // TODO(rdsmith): Manage full percentage issues properly.
241 void AppendDataToFile(const char **data_chunks
, size_t num_chunks
) {
242 ::testing::Sequence s1
;
243 SetupDataAppend(data_chunks
, num_chunks
, s1
);
244 EXPECT_CALL(*input_stream_
, Read(_
, _
))
246 .WillOnce(Return(ByteStreamReader::STREAM_EMPTY
))
247 .RetiresOnSaturation();
248 sink_callback_
.Run();
249 VerifyStreamAndSize();
252 void SetupFinishStream(DownloadInterruptReason interrupt_reason
,
253 ::testing::Sequence s
) {
254 EXPECT_CALL(*input_stream_
, Read(_
, _
))
256 .WillOnce(Return(ByteStreamReader::STREAM_COMPLETE
))
257 .RetiresOnSaturation();
258 EXPECT_CALL(*input_stream_
, GetStatus())
260 .WillOnce(Return(interrupt_reason
))
261 .RetiresOnSaturation();
262 EXPECT_CALL(*input_stream_
, RegisterCallback(_
))
263 .RetiresOnSaturation();
266 void FinishStream(DownloadInterruptReason interrupt_reason
,
267 bool check_observer
) {
268 ::testing::Sequence s1
;
269 SetupFinishStream(interrupt_reason
, s1
);
270 sink_callback_
.Run();
271 VerifyStreamAndSize();
272 if (check_observer
) {
273 EXPECT_CALL(*(observer_
.get()), DestinationCompleted(_
));
274 loop_
.RunUntilIdle();
275 ::testing::Mock::VerifyAndClearExpectations(observer_
.get());
276 EXPECT_CALL(*(observer_
.get()), DestinationUpdate(_
, _
, _
))
278 .WillRepeatedly(Invoke(this,
279 &DownloadFileTest::SetUpdateDownloadInfo
));
283 DownloadInterruptReason
RenameAndUniquify(
284 const base::FilePath
& full_path
,
285 base::FilePath
* result_path_p
) {
286 return InvokeRenameMethodAndWaitForCallback(
287 &DownloadFile::RenameAndUniquify
, full_path
, result_path_p
);
290 DownloadInterruptReason
RenameAndAnnotate(
291 const base::FilePath
& full_path
,
292 base::FilePath
* result_path_p
) {
293 return InvokeRenameMethodAndWaitForCallback(
294 &DownloadFile::RenameAndAnnotate
, full_path
, result_path_p
);
297 void ExpectPermissionError(DownloadInterruptReason err
) {
298 EXPECT_TRUE(err
== DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR
||
299 err
== DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED
)
300 << "Interrupt reason = " << err
;
304 DownloadInterruptReason
InvokeRenameMethodAndWaitForCallback(
305 DownloadFileRenameMethodType method
,
306 const base::FilePath
& full_path
,
307 base::FilePath
* result_path_p
) {
308 DownloadInterruptReason
result_reason(DOWNLOAD_INTERRUPT_REASON_NONE
);
309 base::FilePath result_path
;
311 base::RunLoop loop_runner
;
312 ((*download_file_
).*method
)(full_path
,
313 base::Bind(&DownloadFileTest::SetRenameResult
,
314 base::Unretained(this),
315 loop_runner
.QuitClosure(),
319 return result_reason
;
322 scoped_ptr
<StrictMock
<MockDownloadDestinationObserver
> > observer_
;
323 base::WeakPtrFactory
<DownloadDestinationObserver
> observer_factory_
;
325 // DownloadFile instance we are testing.
326 scoped_ptr
<DownloadFile
> download_file_
;
328 // Stream for sending data into the download file.
329 // Owned by download_file_; will be alive for lifetime of download_file_.
330 StrictMock
<MockByteStreamReader
>* input_stream_
;
332 // Sink callback data for stream.
333 base::Closure sink_callback_
;
335 // Latest update sent to the observer.
337 int64 bytes_per_sec_
;
338 std::string hash_state_
;
340 base::MessageLoop loop_
;
343 void SetRenameResult(const base::Closure
& closure
,
344 DownloadInterruptReason
* reason_p
,
345 base::FilePath
* result_path_p
,
346 DownloadInterruptReason reason
,
347 const base::FilePath
& result_path
) {
351 *result_path_p
= result_path
;
356 BrowserThreadImpl ui_thread_
;
357 // File thread to satisfy debug checks in DownloadFile.
358 BrowserThreadImpl file_thread_
;
360 // Keep track of what data should be saved to the disk file.
361 std::string expected_data_
;
364 // DownloadFile::RenameAndAnnotate and DownloadFile::RenameAndUniquify have a
365 // considerable amount of functional overlap. In order to re-use test logic, we
366 // are going to introduce this value parameterized test fixture. It will take a
367 // DownloadFileRenameMethodType value which can be either of the two rename
369 class DownloadFileTestWithRename
370 : public DownloadFileTest
,
371 public ::testing::WithParamInterface
<DownloadFileRenameMethodType
> {
373 DownloadInterruptReason
InvokeSelectedRenameMethod(
374 const base::FilePath
& full_path
,
375 base::FilePath
* result_path_p
) {
376 return InvokeRenameMethodAndWaitForCallback(
377 GetParam(), full_path
, result_path_p
);
381 // And now instantiate all DownloadFileTestWithRename tests using both
382 // DownloadFile rename methods. Each test of the form
383 // DownloadFileTestWithRename.<FooTest> will be instantiated once with
384 // RenameAndAnnotate as the value parameter and once with RenameAndUniquify as
385 // the value parameter.
386 INSTANTIATE_TEST_CASE_P(DownloadFile
,
387 DownloadFileTestWithRename
,
388 ::testing::Values(&DownloadFile::RenameAndAnnotate
,
389 &DownloadFile::RenameAndUniquify
));
391 const char* DownloadFileTest::kTestData1
=
392 "Let's write some data to the file!\n";
393 const char* DownloadFileTest::kTestData2
= "Writing more data.\n";
394 const char* DownloadFileTest::kTestData3
= "Final line.";
395 const char* DownloadFileTest::kDataHash
=
396 "CBF68BF10F8003DB86B31343AFAC8C7175BD03FB5FC905650F8C80AF087443A8";
398 const uint32
DownloadFileTest::kDummyDownloadId
= 23;
399 const int DownloadFileTest::kDummyChildId
= 3;
400 const int DownloadFileTest::kDummyRequestId
= 67;
402 // Rename the file before any data is downloaded, after some has, after it all
403 // has, and after it's closed.
404 TEST_P(DownloadFileTestWithRename
, RenameFileFinal
) {
405 ASSERT_TRUE(CreateDownloadFile(0, true));
406 base::FilePath
initial_path(download_file_
->FullPath());
407 EXPECT_TRUE(base::PathExists(initial_path
));
408 base::FilePath
path_1(initial_path
.InsertBeforeExtensionASCII("_1"));
409 base::FilePath
path_2(initial_path
.InsertBeforeExtensionASCII("_2"));
410 base::FilePath
path_3(initial_path
.InsertBeforeExtensionASCII("_3"));
411 base::FilePath
path_4(initial_path
.InsertBeforeExtensionASCII("_4"));
412 base::FilePath output_path
;
414 // Rename the file before downloading any data.
415 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
416 InvokeSelectedRenameMethod(path_1
, &output_path
));
417 base::FilePath renamed_path
= download_file_
->FullPath();
418 EXPECT_EQ(path_1
, renamed_path
);
419 EXPECT_EQ(path_1
, output_path
);
422 EXPECT_FALSE(base::PathExists(initial_path
));
423 EXPECT_TRUE(base::PathExists(path_1
));
425 // Download the data.
426 const char* chunks1
[] = { kTestData1
, kTestData2
};
427 AppendDataToFile(chunks1
, 2);
429 // Rename the file after downloading some data.
430 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
431 InvokeSelectedRenameMethod(path_2
, &output_path
));
432 renamed_path
= download_file_
->FullPath();
433 EXPECT_EQ(path_2
, renamed_path
);
434 EXPECT_EQ(path_2
, output_path
);
437 EXPECT_FALSE(base::PathExists(path_1
));
438 EXPECT_TRUE(base::PathExists(path_2
));
440 const char* chunks2
[] = { kTestData3
};
441 AppendDataToFile(chunks2
, 1);
443 // Rename the file after downloading all the data.
444 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
445 InvokeSelectedRenameMethod(path_3
, &output_path
));
446 renamed_path
= download_file_
->FullPath();
447 EXPECT_EQ(path_3
, renamed_path
);
448 EXPECT_EQ(path_3
, output_path
);
451 EXPECT_FALSE(base::PathExists(path_2
));
452 EXPECT_TRUE(base::PathExists(path_3
));
454 // Should not be able to get the hash until the file is closed.
456 EXPECT_FALSE(download_file_
->GetHash(&hash
));
457 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, true);
458 loop_
.RunUntilIdle();
460 // Rename the file after downloading all the data and closing the file.
461 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
462 InvokeSelectedRenameMethod(path_4
, &output_path
));
463 renamed_path
= download_file_
->FullPath();
464 EXPECT_EQ(path_4
, renamed_path
);
465 EXPECT_EQ(path_4
, output_path
);
468 EXPECT_FALSE(base::PathExists(path_3
));
469 EXPECT_TRUE(base::PathExists(path_4
));
472 EXPECT_TRUE(download_file_
->GetHash(&hash
));
473 EXPECT_EQ(kDataHash
, base::HexEncode(hash
.data(), hash
.size()));
475 DestroyDownloadFile(0);
478 // Test to make sure the rename overwrites when requested. This is separate from
479 // the above test because it only applies to RenameAndAnnotate().
480 // RenameAndUniquify() doesn't overwrite by design.
481 TEST_F(DownloadFileTest
, RenameOverwrites
) {
482 ASSERT_TRUE(CreateDownloadFile(0, true));
483 base::FilePath
initial_path(download_file_
->FullPath());
484 EXPECT_TRUE(base::PathExists(initial_path
));
485 base::FilePath
path_1(initial_path
.InsertBeforeExtensionASCII("_1"));
487 ASSERT_FALSE(base::PathExists(path_1
));
488 static const char file_data
[] = "xyzzy";
489 ASSERT_EQ(static_cast<int>(sizeof(file_data
)),
490 base::WriteFile(path_1
, file_data
, sizeof(file_data
)));
491 ASSERT_TRUE(base::PathExists(path_1
));
493 base::FilePath new_path
;
494 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
495 RenameAndAnnotate(path_1
, &new_path
));
496 EXPECT_EQ(path_1
.value(), new_path
.value());
498 std::string file_contents
;
499 ASSERT_TRUE(base::ReadFileToString(new_path
, &file_contents
));
500 EXPECT_NE(std::string(file_data
), file_contents
);
502 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, true);
503 loop_
.RunUntilIdle();
504 DestroyDownloadFile(0);
507 // Test to make sure the rename uniquifies if we aren't overwriting
508 // and there's a file where we're aiming. As above, not a
509 // DownloadFileTestWithRename test because this only applies to
510 // RenameAndUniquify().
511 TEST_F(DownloadFileTest
, RenameUniquifies
) {
512 ASSERT_TRUE(CreateDownloadFile(0, true));
513 base::FilePath
initial_path(download_file_
->FullPath());
514 EXPECT_TRUE(base::PathExists(initial_path
));
515 base::FilePath
path_1(initial_path
.InsertBeforeExtensionASCII("_1"));
516 base::FilePath
path_1_suffixed(path_1
.InsertBeforeExtensionASCII(" (1)"));
518 ASSERT_FALSE(base::PathExists(path_1
));
519 static const char file_data
[] = "xyzzy";
520 ASSERT_EQ(static_cast<int>(sizeof(file_data
)),
521 base::WriteFile(path_1
, file_data
, sizeof(file_data
)));
522 ASSERT_TRUE(base::PathExists(path_1
));
524 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
, RenameAndUniquify(path_1
, NULL
));
525 EXPECT_TRUE(base::PathExists(path_1_suffixed
));
527 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, true);
528 loop_
.RunUntilIdle();
529 DestroyDownloadFile(0);
532 // Test that RenameAndUniquify doesn't try to uniquify in the case where the
533 // target filename is the same as the current filename.
534 TEST_F(DownloadFileTest
, RenameRecognizesSelfConflict
) {
535 ASSERT_TRUE(CreateDownloadFile(0, true));
536 base::FilePath
initial_path(download_file_
->FullPath());
537 EXPECT_TRUE(base::PathExists(initial_path
));
539 base::FilePath new_path
;
540 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
541 RenameAndUniquify(initial_path
, &new_path
));
542 EXPECT_TRUE(base::PathExists(initial_path
));
544 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, true);
545 loop_
.RunUntilIdle();
546 DestroyDownloadFile(0);
547 EXPECT_EQ(initial_path
.value(), new_path
.value());
550 // Test to make sure we get the proper error on failure.
551 TEST_P(DownloadFileTestWithRename
, RenameError
) {
552 ASSERT_TRUE(CreateDownloadFile(0, true));
553 base::FilePath
initial_path(download_file_
->FullPath());
555 // Create a subdirectory.
556 base::FilePath
target_dir(
557 initial_path
.DirName().Append(FILE_PATH_LITERAL("TargetDir")));
558 ASSERT_FALSE(base::DirectoryExists(target_dir
));
559 ASSERT_TRUE(base::CreateDirectory(target_dir
));
560 base::FilePath
target_path(target_dir
.Append(initial_path
.BaseName()));
563 base::FilePath
target_path_suffixed(
564 target_path
.InsertBeforeExtensionASCII(" (1)"));
565 ASSERT_FALSE(base::PathExists(target_path
));
566 ASSERT_FALSE(base::PathExists(target_path_suffixed
));
568 // Make the directory unwritable and try to rename within it.
570 base::FilePermissionRestorer
restorer(target_dir
);
571 ASSERT_TRUE(base::MakeFileUnwritable(target_dir
));
573 // Expect nulling out of further processing.
574 EXPECT_CALL(*input_stream_
, RegisterCallback(IsNullCallback()));
575 ExpectPermissionError(InvokeSelectedRenameMethod(target_path
, NULL
));
576 EXPECT_FALSE(base::PathExists(target_path_suffixed
));
579 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, true);
580 loop_
.RunUntilIdle();
581 DestroyDownloadFile(0);
586 void TestRenameCompletionCallback(const base::Closure
& closure
,
587 bool* did_run_callback
,
588 DownloadInterruptReason interrupt_reason
,
589 const base::FilePath
& new_path
) {
590 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
, interrupt_reason
);
591 *did_run_callback
= true;
597 // Test that the retry logic works. This test assumes that DownloadFileImpl will
598 // post tasks to the current message loop (acting as the FILE thread)
599 // asynchronously to retry the renames. We will stuff RunLoop::QuitClosures()
600 // in between the retry tasks to stagger them and then allow the rename to
603 // Note that there is only one queue of tasks to run, and that is in the tests'
604 // base::MessageLoop::current(). Each RunLoop processes that queue until it sees
605 // a QuitClosure() targeted at itself, at which point it stops processing.
606 TEST_P(DownloadFileTestWithRename
, RenameWithErrorRetry
) {
607 ASSERT_TRUE(CreateDownloadFile(0, true));
608 base::FilePath
initial_path(download_file_
->FullPath());
610 // Create a subdirectory.
611 base::FilePath
target_dir(
612 initial_path
.DirName().Append(FILE_PATH_LITERAL("TargetDir")));
613 ASSERT_FALSE(base::DirectoryExists(target_dir
));
614 ASSERT_TRUE(base::CreateDirectory(target_dir
));
615 base::FilePath
target_path(target_dir
.Append(initial_path
.BaseName()));
617 bool did_run_callback
= false;
619 // Each RunLoop can be used the run the MessageLoop until the corresponding
620 // QuitClosure() is run. This one is used to produce the QuitClosure() that
621 // will be run when the entire rename operation is complete.
622 base::RunLoop succeeding_run
;
624 // (Scope for the base::File or base::FilePermissionRestorer below.)
626 // On Windows we test with an actual transient error, a sharing violation.
627 // The rename will fail because we are holding the file open for READ. On
628 // Posix this doesn't cause a failure.
629 base::File
locked_file(initial_path
,
630 base::File::FLAG_OPEN
| base::File::FLAG_READ
);
631 ASSERT_TRUE(locked_file
.IsValid());
633 // Simulate a transient failure by revoking write permission for target_dir.
634 // The TestDownloadFileImpl class treats this error as transient even though
635 // DownloadFileImpl itself doesn't.
636 base::FilePermissionRestorer
restore_permissions_for(target_dir
);
637 ASSERT_TRUE(base::MakeFileUnwritable(target_dir
));
640 // The Rename() should fail here and enqueue a retry task without invoking
641 // the completion callback.
642 ((*download_file_
).*GetParam())(target_path
,
643 base::Bind(&TestRenameCompletionCallback
,
644 succeeding_run
.QuitClosure(),
646 EXPECT_FALSE(did_run_callback
);
648 base::RunLoop first_failing_run
;
649 // Queue the QuitClosure() on the MessageLoop now. Any tasks queued by the
650 // Rename() will be in front of the QuitClosure(). Running the message loop
651 // now causes the just the first retry task to be run. The rename still
652 // fails, so another retry task would get queued behind the QuitClosure().
653 base::MessageLoop::current()->PostTask(FROM_HERE
,
654 first_failing_run
.QuitClosure());
655 first_failing_run
.Run();
656 EXPECT_FALSE(did_run_callback
);
658 // Running another loop should have the same effect as the above as long as
659 // kMaxRenameRetries is greater than 2.
660 base::RunLoop second_failing_run
;
661 base::MessageLoop::current()->PostTask(FROM_HERE
,
662 second_failing_run
.QuitClosure());
663 second_failing_run
.Run();
664 EXPECT_FALSE(did_run_callback
);
667 // This time the QuitClosure from succeeding_run should get executed.
668 succeeding_run
.Run();
669 EXPECT_TRUE(did_run_callback
);
671 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, true);
672 loop_
.RunUntilIdle();
673 DestroyDownloadFile(0);
676 // Various tests of the StreamActive method.
677 TEST_F(DownloadFileTest
, StreamEmptySuccess
) {
678 ASSERT_TRUE(CreateDownloadFile(0, true));
679 base::FilePath
initial_path(download_file_
->FullPath());
680 EXPECT_TRUE(base::PathExists(initial_path
));
682 // Test that calling the sink_callback_ on an empty stream shouldn't
684 AppendDataToFile(NULL
, 0);
686 // Finish the download this way and make sure we see it on the
688 EXPECT_CALL(*(observer_
.get()), DestinationCompleted(_
));
689 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, false);
690 loop_
.RunUntilIdle();
692 DestroyDownloadFile(0);
695 TEST_F(DownloadFileTest
, StreamEmptyError
) {
696 ASSERT_TRUE(CreateDownloadFile(0, true));
697 base::FilePath
initial_path(download_file_
->FullPath());
698 EXPECT_TRUE(base::PathExists(initial_path
));
700 // Finish the download in error and make sure we see it on the
702 EXPECT_CALL(*(observer_
.get()),
704 DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED
))
705 .WillOnce(InvokeWithoutArgs(
706 this, &DownloadFileTest::ConfirmUpdateDownloadInfo
));
708 // If this next EXPECT_CALL fails flakily, it's probably a real failure.
709 // We'll be getting a stream of UpdateDownload calls from the timer, and
710 // the last one may have the correct information even if the failure
711 // doesn't produce an update, as the timer update may have triggered at the
713 EXPECT_CALL(*(observer_
.get()), CurrentUpdateStatus(0, _
, _
));
715 FinishStream(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED
, false);
717 loop_
.RunUntilIdle();
719 DestroyDownloadFile(0);
722 TEST_F(DownloadFileTest
, StreamNonEmptySuccess
) {
723 ASSERT_TRUE(CreateDownloadFile(0, true));
724 base::FilePath
initial_path(download_file_
->FullPath());
725 EXPECT_TRUE(base::PathExists(initial_path
));
727 const char* chunks1
[] = { kTestData1
, kTestData2
};
728 ::testing::Sequence s1
;
729 SetupDataAppend(chunks1
, 2, s1
);
730 SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, s1
);
731 EXPECT_CALL(*(observer_
.get()), DestinationCompleted(_
));
732 sink_callback_
.Run();
733 VerifyStreamAndSize();
734 loop_
.RunUntilIdle();
735 DestroyDownloadFile(0);
738 TEST_F(DownloadFileTest
, StreamNonEmptyError
) {
739 ASSERT_TRUE(CreateDownloadFile(0, true));
740 base::FilePath
initial_path(download_file_
->FullPath());
741 EXPECT_TRUE(base::PathExists(initial_path
));
743 const char* chunks1
[] = { kTestData1
, kTestData2
};
744 ::testing::Sequence s1
;
745 SetupDataAppend(chunks1
, 2, s1
);
746 SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED
, s1
);
748 EXPECT_CALL(*(observer_
.get()),
750 DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED
))
751 .WillOnce(InvokeWithoutArgs(
752 this, &DownloadFileTest::ConfirmUpdateDownloadInfo
));
754 // If this next EXPECT_CALL fails flakily, it's probably a real failure.
755 // We'll be getting a stream of UpdateDownload calls from the timer, and
756 // the last one may have the correct information even if the failure
757 // doesn't produce an update, as the timer update may have triggered at the
759 EXPECT_CALL(*(observer_
.get()),
760 CurrentUpdateStatus(strlen(kTestData1
) + strlen(kTestData2
),
763 sink_callback_
.Run();
764 loop_
.RunUntilIdle();
765 VerifyStreamAndSize();
766 DestroyDownloadFile(0);
769 // Send some data, wait 3/4s of a second, run the message loop, and
770 // confirm the values the observer received are correct.
771 TEST_F(DownloadFileTest
, ConfirmUpdate
) {
772 CreateDownloadFile(0, true);
774 const char* chunks1
[] = { kTestData1
, kTestData2
};
775 AppendDataToFile(chunks1
, 2);
777 // Run the message loops for 750ms and check for results.
778 loop_
.PostDelayedTask(FROM_HERE
,
779 base::MessageLoop::QuitClosure(),
780 base::TimeDelta::FromMilliseconds(750));
783 EXPECT_EQ(static_cast<int64
>(strlen(kTestData1
) + strlen(kTestData2
)),
785 EXPECT_EQ(download_file_
->GetHashState(), hash_state_
);
787 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, true);
788 DestroyDownloadFile(0);
791 } // namespace content