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/location.h"
8 #include "base/run_loop.h"
9 #include "base/single_thread_task_runner.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/test/test_file_util.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "content/browser/browser_thread_impl.h"
14 #include "content/browser/byte_stream.h"
15 #include "content/browser/download/download_create_info.h"
16 #include "content/browser/download/download_file_impl.h"
17 #include "content/browser/download/download_request_handle.h"
18 #include "content/public/browser/download_destination_observer.h"
19 #include "content/public/browser/download_interrupt_reasons.h"
20 #include "content/public/browser/download_manager.h"
21 #include "content/public/test/mock_download_manager.h"
22 #include "net/base/file_stream.h"
23 #include "net/base/mock_file_stream.h"
24 #include "net/base/net_errors.h"
25 #include "testing/gmock/include/gmock/gmock.h"
26 #include "testing/gtest/include/gtest/gtest.h"
29 using ::testing::AnyNumber
;
30 using ::testing::DoAll
;
31 using ::testing::InSequence
;
32 using ::testing::Return
;
33 using ::testing::SetArgPointee
;
34 using ::testing::StrictMock
;
39 class MockByteStreamReader
: public ByteStreamReader
{
41 MockByteStreamReader() {}
42 ~MockByteStreamReader() {}
44 // ByteStream functions
45 MOCK_METHOD2(Read
, ByteStreamReader::StreamState(
46 scoped_refptr
<net::IOBuffer
>*, size_t*));
47 MOCK_CONST_METHOD0(GetStatus
, int());
48 MOCK_METHOD1(RegisterCallback
, void(const base::Closure
&));
51 class MockDownloadDestinationObserver
: public DownloadDestinationObserver
{
53 MOCK_METHOD3(DestinationUpdate
, void(int64
, int64
, const std::string
&));
54 MOCK_METHOD1(DestinationError
, void(DownloadInterruptReason
));
55 MOCK_METHOD1(DestinationCompleted
, void(const std::string
&));
57 // Doesn't override any methods in the base class. Used to make sure
58 // that the last DestinationUpdate before a Destination{Completed,Error}
59 // had the right values.
60 MOCK_METHOD3(CurrentUpdateStatus
,
61 void(int64
, int64
, const std::string
&));
64 MATCHER(IsNullCallback
, "") { return (arg
.is_null()); }
66 typedef void (DownloadFile::*DownloadFileRenameMethodType
)(
67 const base::FilePath
&,
68 const DownloadFile::RenameCompletionCallback
&);
70 // This is a test DownloadFileImpl that has no retry delay and, on Posix,
71 // retries renames failed due to ACCESS_DENIED.
72 class TestDownloadFileImpl
: public DownloadFileImpl
{
74 TestDownloadFileImpl(scoped_ptr
<DownloadSaveInfo
> save_info
,
75 const base::FilePath
& default_downloads_directory
,
77 const GURL
& referrer_url
,
79 scoped_ptr
<ByteStreamReader
> stream
,
80 const net::BoundNetLog
& bound_net_log
,
81 base::WeakPtr
<DownloadDestinationObserver
> observer
)
82 : DownloadFileImpl(save_info
.Pass(),
83 default_downloads_directory
,
92 base::TimeDelta
GetRetryDelayForFailedRename(int attempt_count
) override
{
93 return base::TimeDelta::FromMilliseconds(0);
97 // On Posix, we don't encounter transient errors during renames, except
98 // possibly EAGAIN, which is difficult to replicate reliably. So we resort to
99 // simulating a transient error using ACCESS_DENIED instead.
100 bool ShouldRetryFailedRename(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 ~DownloadFileTest() override
{}
132 void SetUpdateDownloadInfo(int64 bytes
, int64 bytes_per_sec
,
133 const std::string
& hash_state
) {
135 bytes_per_sec_
= bytes_per_sec
;
136 hash_state_
= hash_state
;
139 void ConfirmUpdateDownloadInfo() {
140 observer_
->CurrentUpdateStatus(bytes_
, bytes_per_sec_
, hash_state_
);
143 void SetUp() override
{
144 EXPECT_CALL(*(observer_
.get()), DestinationUpdate(_
, _
, _
))
146 .WillRepeatedly(Invoke(this, &DownloadFileTest::SetUpdateDownloadInfo
));
149 // Mock calls to this function are forwarded here.
150 void RegisterCallback(const base::Closure
& sink_callback
) {
151 sink_callback_
= sink_callback
;
154 void SetInterruptReasonCallback(const base::Closure
& closure
,
155 DownloadInterruptReason
* reason_p
,
156 DownloadInterruptReason reason
) {
161 bool CreateDownloadFile(int offset
, bool calculate_hash
) {
162 // There can be only one.
163 DCHECK(!download_file_
.get());
165 input_stream_
= new StrictMock
<MockByteStreamReader
>();
167 // TODO: Need to actually create a function that'll set the variables
168 // based on the inputs from the callback.
169 EXPECT_CALL(*input_stream_
, RegisterCallback(_
))
170 .WillOnce(Invoke(this, &DownloadFileTest::RegisterCallback
))
171 .RetiresOnSaturation();
173 scoped_ptr
<DownloadSaveInfo
> save_info(new DownloadSaveInfo());
174 scoped_ptr
<TestDownloadFileImpl
> download_file_impl(
175 new TestDownloadFileImpl(save_info
.Pass(),
180 scoped_ptr
<ByteStreamReader
>(input_stream_
),
182 observer_factory_
.GetWeakPtr()));
183 download_file_impl
->SetClientGuid("12345678-ABCD-1234-DCBA-123456789ABC");
184 download_file_
= download_file_impl
.Pass();
186 EXPECT_CALL(*input_stream_
, Read(_
, _
))
187 .WillOnce(Return(ByteStreamReader::STREAM_EMPTY
))
188 .RetiresOnSaturation();
190 base::WeakPtrFactory
<DownloadFileTest
> weak_ptr_factory(this);
191 DownloadInterruptReason result
= DOWNLOAD_INTERRUPT_REASON_NONE
;
192 base::RunLoop loop_runner
;
193 download_file_
->Initialize(base::Bind(
194 &DownloadFileTest::SetInterruptReasonCallback
,
195 weak_ptr_factory
.GetWeakPtr(), loop_runner
.QuitClosure(), &result
));
198 ::testing::Mock::VerifyAndClearExpectations(input_stream_
);
199 return result
== DOWNLOAD_INTERRUPT_REASON_NONE
;
202 void DestroyDownloadFile(int offset
) {
203 EXPECT_FALSE(download_file_
->InProgress());
205 // Make sure the data has been properly written to disk.
206 std::string disk_data
;
207 EXPECT_TRUE(base::ReadFileToString(download_file_
->FullPath(), &disk_data
));
208 EXPECT_EQ(expected_data_
, disk_data
);
210 // Make sure the Browser and File threads outlive the DownloadFile
211 // to satisfy thread checks inside it.
212 download_file_
.reset();
215 // Setup the stream to do be a data append; don't actually trigger
216 // the callback or do verifications.
217 void SetupDataAppend(const char **data_chunks
, size_t num_chunks
,
218 ::testing::Sequence s
) {
219 DCHECK(input_stream_
);
220 for (size_t i
= 0; i
< num_chunks
; i
++) {
221 const char *source_data
= data_chunks
[i
];
222 size_t length
= strlen(source_data
);
223 scoped_refptr
<net::IOBuffer
> data
= new net::IOBuffer(length
);
224 memcpy(data
->data(), source_data
, length
);
225 EXPECT_CALL(*input_stream_
, Read(_
, _
))
227 .WillOnce(DoAll(SetArgPointee
<0>(data
),
228 SetArgPointee
<1>(length
),
229 Return(ByteStreamReader::STREAM_HAS_DATA
)))
230 .RetiresOnSaturation();
231 expected_data_
+= source_data
;
235 void VerifyStreamAndSize() {
236 ::testing::Mock::VerifyAndClearExpectations(input_stream_
);
238 EXPECT_TRUE(base::GetFileSize(download_file_
->FullPath(), &size
));
239 EXPECT_EQ(expected_data_
.size(), static_cast<size_t>(size
));
242 // TODO(rdsmith): Manage full percentage issues properly.
243 void AppendDataToFile(const char **data_chunks
, size_t num_chunks
) {
244 ::testing::Sequence s1
;
245 SetupDataAppend(data_chunks
, num_chunks
, s1
);
246 EXPECT_CALL(*input_stream_
, Read(_
, _
))
248 .WillOnce(Return(ByteStreamReader::STREAM_EMPTY
))
249 .RetiresOnSaturation();
250 sink_callback_
.Run();
251 VerifyStreamAndSize();
254 void SetupFinishStream(DownloadInterruptReason interrupt_reason
,
255 ::testing::Sequence s
) {
256 EXPECT_CALL(*input_stream_
, Read(_
, _
))
258 .WillOnce(Return(ByteStreamReader::STREAM_COMPLETE
))
259 .RetiresOnSaturation();
260 EXPECT_CALL(*input_stream_
, GetStatus())
262 .WillOnce(Return(interrupt_reason
))
263 .RetiresOnSaturation();
264 EXPECT_CALL(*input_stream_
, RegisterCallback(_
))
265 .RetiresOnSaturation();
268 void FinishStream(DownloadInterruptReason interrupt_reason
,
269 bool check_observer
) {
270 ::testing::Sequence s1
;
271 SetupFinishStream(interrupt_reason
, s1
);
272 sink_callback_
.Run();
273 VerifyStreamAndSize();
274 if (check_observer
) {
275 EXPECT_CALL(*(observer_
.get()), DestinationCompleted(_
));
276 loop_
.RunUntilIdle();
277 ::testing::Mock::VerifyAndClearExpectations(observer_
.get());
278 EXPECT_CALL(*(observer_
.get()), DestinationUpdate(_
, _
, _
))
280 .WillRepeatedly(Invoke(this,
281 &DownloadFileTest::SetUpdateDownloadInfo
));
285 DownloadInterruptReason
RenameAndUniquify(
286 const base::FilePath
& full_path
,
287 base::FilePath
* result_path_p
) {
288 return InvokeRenameMethodAndWaitForCallback(
289 &DownloadFile::RenameAndUniquify
, full_path
, result_path_p
);
292 DownloadInterruptReason
RenameAndAnnotate(
293 const base::FilePath
& full_path
,
294 base::FilePath
* result_path_p
) {
295 return InvokeRenameMethodAndWaitForCallback(
296 &DownloadFile::RenameAndAnnotate
, full_path
, result_path_p
);
299 void ExpectPermissionError(DownloadInterruptReason err
) {
300 EXPECT_TRUE(err
== DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR
||
301 err
== DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED
)
302 << "Interrupt reason = " << err
;
306 DownloadInterruptReason
InvokeRenameMethodAndWaitForCallback(
307 DownloadFileRenameMethodType method
,
308 const base::FilePath
& full_path
,
309 base::FilePath
* result_path_p
) {
310 DownloadInterruptReason
result_reason(DOWNLOAD_INTERRUPT_REASON_NONE
);
311 base::FilePath result_path
;
313 base::RunLoop loop_runner
;
314 ((*download_file_
).*method
)(full_path
,
315 base::Bind(&DownloadFileTest::SetRenameResult
,
316 base::Unretained(this),
317 loop_runner
.QuitClosure(),
321 return result_reason
;
324 scoped_ptr
<StrictMock
<MockDownloadDestinationObserver
> > observer_
;
325 base::WeakPtrFactory
<DownloadDestinationObserver
> observer_factory_
;
327 // DownloadFile instance we are testing.
328 scoped_ptr
<DownloadFile
> download_file_
;
330 // Stream for sending data into the download file.
331 // Owned by download_file_; will be alive for lifetime of download_file_.
332 StrictMock
<MockByteStreamReader
>* input_stream_
;
334 // Sink callback data for stream.
335 base::Closure sink_callback_
;
337 // Latest update sent to the observer.
339 int64 bytes_per_sec_
;
340 std::string hash_state_
;
342 base::MessageLoop loop_
;
345 void SetRenameResult(const base::Closure
& closure
,
346 DownloadInterruptReason
* reason_p
,
347 base::FilePath
* result_path_p
,
348 DownloadInterruptReason reason
,
349 const base::FilePath
& result_path
) {
353 *result_path_p
= result_path
;
358 BrowserThreadImpl ui_thread_
;
359 // File thread to satisfy debug checks in DownloadFile.
360 BrowserThreadImpl file_thread_
;
362 // Keep track of what data should be saved to the disk file.
363 std::string expected_data_
;
366 // DownloadFile::RenameAndAnnotate and DownloadFile::RenameAndUniquify have a
367 // considerable amount of functional overlap. In order to re-use test logic, we
368 // are going to introduce this value parameterized test fixture. It will take a
369 // DownloadFileRenameMethodType value which can be either of the two rename
371 class DownloadFileTestWithRename
372 : public DownloadFileTest
,
373 public ::testing::WithParamInterface
<DownloadFileRenameMethodType
> {
375 DownloadInterruptReason
InvokeSelectedRenameMethod(
376 const base::FilePath
& full_path
,
377 base::FilePath
* result_path_p
) {
378 return InvokeRenameMethodAndWaitForCallback(
379 GetParam(), full_path
, result_path_p
);
383 // And now instantiate all DownloadFileTestWithRename tests using both
384 // DownloadFile rename methods. Each test of the form
385 // DownloadFileTestWithRename.<FooTest> will be instantiated once with
386 // RenameAndAnnotate as the value parameter and once with RenameAndUniquify as
387 // the value parameter.
388 INSTANTIATE_TEST_CASE_P(DownloadFile
,
389 DownloadFileTestWithRename
,
390 ::testing::Values(&DownloadFile::RenameAndAnnotate
,
391 &DownloadFile::RenameAndUniquify
));
393 const char* DownloadFileTest::kTestData1
=
394 "Let's write some data to the file!\n";
395 const char* DownloadFileTest::kTestData2
= "Writing more data.\n";
396 const char* DownloadFileTest::kTestData3
= "Final line.";
397 const char* DownloadFileTest::kDataHash
=
398 "CBF68BF10F8003DB86B31343AFAC8C7175BD03FB5FC905650F8C80AF087443A8";
400 const uint32
DownloadFileTest::kDummyDownloadId
= 23;
401 const int DownloadFileTest::kDummyChildId
= 3;
402 const int DownloadFileTest::kDummyRequestId
= 67;
404 // Rename the file before any data is downloaded, after some has, after it all
405 // has, and after it's closed.
406 TEST_P(DownloadFileTestWithRename
, RenameFileFinal
) {
407 ASSERT_TRUE(CreateDownloadFile(0, true));
408 base::FilePath
initial_path(download_file_
->FullPath());
409 EXPECT_TRUE(base::PathExists(initial_path
));
410 base::FilePath
path_1(initial_path
.InsertBeforeExtensionASCII("_1"));
411 base::FilePath
path_2(initial_path
.InsertBeforeExtensionASCII("_2"));
412 base::FilePath
path_3(initial_path
.InsertBeforeExtensionASCII("_3"));
413 base::FilePath
path_4(initial_path
.InsertBeforeExtensionASCII("_4"));
414 base::FilePath output_path
;
416 // Rename the file before downloading any data.
417 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
418 InvokeSelectedRenameMethod(path_1
, &output_path
));
419 base::FilePath renamed_path
= download_file_
->FullPath();
420 EXPECT_EQ(path_1
, renamed_path
);
421 EXPECT_EQ(path_1
, output_path
);
424 EXPECT_FALSE(base::PathExists(initial_path
));
425 EXPECT_TRUE(base::PathExists(path_1
));
427 // Download the data.
428 const char* chunks1
[] = { kTestData1
, kTestData2
};
429 AppendDataToFile(chunks1
, 2);
431 // Rename the file after downloading some data.
432 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
433 InvokeSelectedRenameMethod(path_2
, &output_path
));
434 renamed_path
= download_file_
->FullPath();
435 EXPECT_EQ(path_2
, renamed_path
);
436 EXPECT_EQ(path_2
, output_path
);
439 EXPECT_FALSE(base::PathExists(path_1
));
440 EXPECT_TRUE(base::PathExists(path_2
));
442 const char* chunks2
[] = { kTestData3
};
443 AppendDataToFile(chunks2
, 1);
445 // Rename the file after downloading all the data.
446 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
447 InvokeSelectedRenameMethod(path_3
, &output_path
));
448 renamed_path
= download_file_
->FullPath();
449 EXPECT_EQ(path_3
, renamed_path
);
450 EXPECT_EQ(path_3
, output_path
);
453 EXPECT_FALSE(base::PathExists(path_2
));
454 EXPECT_TRUE(base::PathExists(path_3
));
456 // Should not be able to get the hash until the file is closed.
458 EXPECT_FALSE(download_file_
->GetHash(&hash
));
459 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, true);
460 loop_
.RunUntilIdle();
462 // Rename the file after downloading all the data and closing the file.
463 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
464 InvokeSelectedRenameMethod(path_4
, &output_path
));
465 renamed_path
= download_file_
->FullPath();
466 EXPECT_EQ(path_4
, renamed_path
);
467 EXPECT_EQ(path_4
, output_path
);
470 EXPECT_FALSE(base::PathExists(path_3
));
471 EXPECT_TRUE(base::PathExists(path_4
));
474 EXPECT_TRUE(download_file_
->GetHash(&hash
));
475 EXPECT_EQ(kDataHash
, base::HexEncode(hash
.data(), hash
.size()));
477 DestroyDownloadFile(0);
480 // Test to make sure the rename overwrites when requested. This is separate from
481 // the above test because it only applies to RenameAndAnnotate().
482 // RenameAndUniquify() doesn't overwrite by design.
483 TEST_F(DownloadFileTest
, RenameOverwrites
) {
484 ASSERT_TRUE(CreateDownloadFile(0, true));
485 base::FilePath
initial_path(download_file_
->FullPath());
486 EXPECT_TRUE(base::PathExists(initial_path
));
487 base::FilePath
path_1(initial_path
.InsertBeforeExtensionASCII("_1"));
489 ASSERT_FALSE(base::PathExists(path_1
));
490 static const char file_data
[] = "xyzzy";
491 ASSERT_EQ(static_cast<int>(sizeof(file_data
)),
492 base::WriteFile(path_1
, file_data
, sizeof(file_data
)));
493 ASSERT_TRUE(base::PathExists(path_1
));
495 base::FilePath new_path
;
496 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
497 RenameAndAnnotate(path_1
, &new_path
));
498 EXPECT_EQ(path_1
.value(), new_path
.value());
500 std::string file_contents
;
501 ASSERT_TRUE(base::ReadFileToString(new_path
, &file_contents
));
502 EXPECT_NE(std::string(file_data
), file_contents
);
504 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, true);
505 loop_
.RunUntilIdle();
506 DestroyDownloadFile(0);
509 // Test to make sure the rename uniquifies if we aren't overwriting
510 // and there's a file where we're aiming. As above, not a
511 // DownloadFileTestWithRename test because this only applies to
512 // RenameAndUniquify().
513 TEST_F(DownloadFileTest
, RenameUniquifies
) {
514 ASSERT_TRUE(CreateDownloadFile(0, true));
515 base::FilePath
initial_path(download_file_
->FullPath());
516 EXPECT_TRUE(base::PathExists(initial_path
));
517 base::FilePath
path_1(initial_path
.InsertBeforeExtensionASCII("_1"));
518 base::FilePath
path_1_suffixed(path_1
.InsertBeforeExtensionASCII(" (1)"));
520 ASSERT_FALSE(base::PathExists(path_1
));
521 static const char file_data
[] = "xyzzy";
522 ASSERT_EQ(static_cast<int>(sizeof(file_data
)),
523 base::WriteFile(path_1
, file_data
, sizeof(file_data
)));
524 ASSERT_TRUE(base::PathExists(path_1
));
526 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
, RenameAndUniquify(path_1
, NULL
));
527 EXPECT_TRUE(base::PathExists(path_1_suffixed
));
529 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, true);
530 loop_
.RunUntilIdle();
531 DestroyDownloadFile(0);
534 // Test that RenameAndUniquify doesn't try to uniquify in the case where the
535 // target filename is the same as the current filename.
536 TEST_F(DownloadFileTest
, RenameRecognizesSelfConflict
) {
537 ASSERT_TRUE(CreateDownloadFile(0, true));
538 base::FilePath
initial_path(download_file_
->FullPath());
539 EXPECT_TRUE(base::PathExists(initial_path
));
541 base::FilePath new_path
;
542 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
543 RenameAndUniquify(initial_path
, &new_path
));
544 EXPECT_TRUE(base::PathExists(initial_path
));
546 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, true);
547 loop_
.RunUntilIdle();
548 DestroyDownloadFile(0);
549 EXPECT_EQ(initial_path
.value(), new_path
.value());
552 // Test to make sure we get the proper error on failure.
553 TEST_P(DownloadFileTestWithRename
, RenameError
) {
554 ASSERT_TRUE(CreateDownloadFile(0, true));
555 base::FilePath
initial_path(download_file_
->FullPath());
557 // Create a subdirectory.
558 base::FilePath
target_dir(
559 initial_path
.DirName().Append(FILE_PATH_LITERAL("TargetDir")));
560 ASSERT_FALSE(base::DirectoryExists(target_dir
));
561 ASSERT_TRUE(base::CreateDirectory(target_dir
));
562 base::FilePath
target_path(target_dir
.Append(initial_path
.BaseName()));
565 base::FilePath
target_path_suffixed(
566 target_path
.InsertBeforeExtensionASCII(" (1)"));
567 ASSERT_FALSE(base::PathExists(target_path
));
568 ASSERT_FALSE(base::PathExists(target_path_suffixed
));
570 // Make the directory unwritable and try to rename within it.
572 base::FilePermissionRestorer
restorer(target_dir
);
573 ASSERT_TRUE(base::MakeFileUnwritable(target_dir
));
575 // Expect nulling out of further processing.
576 EXPECT_CALL(*input_stream_
, RegisterCallback(IsNullCallback()));
577 ExpectPermissionError(InvokeSelectedRenameMethod(target_path
, NULL
));
578 EXPECT_FALSE(base::PathExists(target_path_suffixed
));
581 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, true);
582 loop_
.RunUntilIdle();
583 DestroyDownloadFile(0);
588 void TestRenameCompletionCallback(const base::Closure
& closure
,
589 bool* did_run_callback
,
590 DownloadInterruptReason interrupt_reason
,
591 const base::FilePath
& new_path
) {
592 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
, interrupt_reason
);
593 *did_run_callback
= true;
599 // Test that the retry logic works. This test assumes that DownloadFileImpl will
600 // post tasks to the current message loop (acting as the FILE thread)
601 // asynchronously to retry the renames. We will stuff RunLoop::QuitClosures()
602 // in between the retry tasks to stagger them and then allow the rename to
605 // Note that there is only one queue of tasks to run, and that is in the tests'
606 // base::MessageLoop::current(). Each RunLoop processes that queue until it sees
607 // a QuitClosure() targeted at itself, at which point it stops processing.
608 TEST_P(DownloadFileTestWithRename
, RenameWithErrorRetry
) {
609 ASSERT_TRUE(CreateDownloadFile(0, true));
610 base::FilePath
initial_path(download_file_
->FullPath());
612 // Create a subdirectory.
613 base::FilePath
target_dir(
614 initial_path
.DirName().Append(FILE_PATH_LITERAL("TargetDir")));
615 ASSERT_FALSE(base::DirectoryExists(target_dir
));
616 ASSERT_TRUE(base::CreateDirectory(target_dir
));
617 base::FilePath
target_path(target_dir
.Append(initial_path
.BaseName()));
619 bool did_run_callback
= false;
621 // Each RunLoop can be used the run the MessageLoop until the corresponding
622 // QuitClosure() is run. This one is used to produce the QuitClosure() that
623 // will be run when the entire rename operation is complete.
624 base::RunLoop succeeding_run
;
626 // (Scope for the base::File or base::FilePermissionRestorer below.)
628 // On Windows we test with an actual transient error, a sharing violation.
629 // The rename will fail because we are holding the file open for READ. On
630 // Posix this doesn't cause a failure.
631 base::File
locked_file(initial_path
,
632 base::File::FLAG_OPEN
| base::File::FLAG_READ
);
633 ASSERT_TRUE(locked_file
.IsValid());
635 // Simulate a transient failure by revoking write permission for target_dir.
636 // The TestDownloadFileImpl class treats this error as transient even though
637 // DownloadFileImpl itself doesn't.
638 base::FilePermissionRestorer
restore_permissions_for(target_dir
);
639 ASSERT_TRUE(base::MakeFileUnwritable(target_dir
));
642 // The Rename() should fail here and enqueue a retry task without invoking
643 // the completion callback.
644 ((*download_file_
).*GetParam())(target_path
,
645 base::Bind(&TestRenameCompletionCallback
,
646 succeeding_run
.QuitClosure(),
648 EXPECT_FALSE(did_run_callback
);
650 base::RunLoop first_failing_run
;
651 // Queue the QuitClosure() on the MessageLoop now. Any tasks queued by the
652 // Rename() will be in front of the QuitClosure(). Running the message loop
653 // now causes the just the first retry task to be run. The rename still
654 // fails, so another retry task would get queued behind the QuitClosure().
655 base::ThreadTaskRunnerHandle::Get()->PostTask(
656 FROM_HERE
, first_failing_run
.QuitClosure());
657 first_failing_run
.Run();
658 EXPECT_FALSE(did_run_callback
);
660 // Running another loop should have the same effect as the above as long as
661 // kMaxRenameRetries is greater than 2.
662 base::RunLoop second_failing_run
;
663 base::ThreadTaskRunnerHandle::Get()->PostTask(
664 FROM_HERE
, second_failing_run
.QuitClosure());
665 second_failing_run
.Run();
666 EXPECT_FALSE(did_run_callback
);
669 // This time the QuitClosure from succeeding_run should get executed.
670 succeeding_run
.Run();
671 EXPECT_TRUE(did_run_callback
);
673 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, true);
674 loop_
.RunUntilIdle();
675 DestroyDownloadFile(0);
678 // Various tests of the StreamActive method.
679 TEST_F(DownloadFileTest
, StreamEmptySuccess
) {
680 ASSERT_TRUE(CreateDownloadFile(0, true));
681 base::FilePath
initial_path(download_file_
->FullPath());
682 EXPECT_TRUE(base::PathExists(initial_path
));
684 // Test that calling the sink_callback_ on an empty stream shouldn't
686 AppendDataToFile(NULL
, 0);
688 // Finish the download this way and make sure we see it on the
690 EXPECT_CALL(*(observer_
.get()), DestinationCompleted(_
));
691 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, false);
692 loop_
.RunUntilIdle();
694 DestroyDownloadFile(0);
697 TEST_F(DownloadFileTest
, StreamEmptyError
) {
698 ASSERT_TRUE(CreateDownloadFile(0, true));
699 base::FilePath
initial_path(download_file_
->FullPath());
700 EXPECT_TRUE(base::PathExists(initial_path
));
702 // Finish the download in error and make sure we see it on the
704 EXPECT_CALL(*(observer_
.get()),
706 DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED
))
707 .WillOnce(InvokeWithoutArgs(
708 this, &DownloadFileTest::ConfirmUpdateDownloadInfo
));
710 // If this next EXPECT_CALL fails flakily, it's probably a real failure.
711 // We'll be getting a stream of UpdateDownload calls from the timer, and
712 // the last one may have the correct information even if the failure
713 // doesn't produce an update, as the timer update may have triggered at the
715 EXPECT_CALL(*(observer_
.get()), CurrentUpdateStatus(0, _
, _
));
717 FinishStream(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED
, false);
719 loop_
.RunUntilIdle();
721 DestroyDownloadFile(0);
724 TEST_F(DownloadFileTest
, StreamNonEmptySuccess
) {
725 ASSERT_TRUE(CreateDownloadFile(0, true));
726 base::FilePath
initial_path(download_file_
->FullPath());
727 EXPECT_TRUE(base::PathExists(initial_path
));
729 const char* chunks1
[] = { kTestData1
, kTestData2
};
730 ::testing::Sequence s1
;
731 SetupDataAppend(chunks1
, 2, s1
);
732 SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, s1
);
733 EXPECT_CALL(*(observer_
.get()), DestinationCompleted(_
));
734 sink_callback_
.Run();
735 VerifyStreamAndSize();
736 loop_
.RunUntilIdle();
737 DestroyDownloadFile(0);
740 TEST_F(DownloadFileTest
, StreamNonEmptyError
) {
741 ASSERT_TRUE(CreateDownloadFile(0, true));
742 base::FilePath
initial_path(download_file_
->FullPath());
743 EXPECT_TRUE(base::PathExists(initial_path
));
745 const char* chunks1
[] = { kTestData1
, kTestData2
};
746 ::testing::Sequence s1
;
747 SetupDataAppend(chunks1
, 2, s1
);
748 SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED
, s1
);
750 EXPECT_CALL(*(observer_
.get()),
752 DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED
))
753 .WillOnce(InvokeWithoutArgs(
754 this, &DownloadFileTest::ConfirmUpdateDownloadInfo
));
756 // If this next EXPECT_CALL fails flakily, it's probably a real failure.
757 // We'll be getting a stream of UpdateDownload calls from the timer, and
758 // the last one may have the correct information even if the failure
759 // doesn't produce an update, as the timer update may have triggered at the
761 EXPECT_CALL(*(observer_
.get()),
762 CurrentUpdateStatus(strlen(kTestData1
) + strlen(kTestData2
),
765 sink_callback_
.Run();
766 loop_
.RunUntilIdle();
767 VerifyStreamAndSize();
768 DestroyDownloadFile(0);
771 // Send some data, wait 3/4s of a second, run the message loop, and
772 // confirm the values the observer received are correct.
773 TEST_F(DownloadFileTest
, ConfirmUpdate
) {
774 CreateDownloadFile(0, true);
776 const char* chunks1
[] = { kTestData1
, kTestData2
};
777 AppendDataToFile(chunks1
, 2);
779 // Run the message loops for 750ms and check for results.
780 loop_
.task_runner()->PostDelayedTask(FROM_HERE
,
781 base::MessageLoop::QuitClosure(),
782 base::TimeDelta::FromMilliseconds(750));
785 EXPECT_EQ(static_cast<int64
>(strlen(kTestData1
) + strlen(kTestData2
)),
787 EXPECT_EQ(download_file_
->GetHashState(), hash_state_
);
789 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, true);
790 DestroyDownloadFile(0);
793 } // namespace content