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 virtual ~DownloadFileTest() {
131 void SetUpdateDownloadInfo(int64 bytes
, int64 bytes_per_sec
,
132 const std::string
& hash_state
) {
134 bytes_per_sec_
= bytes_per_sec
;
135 hash_state_
= hash_state
;
138 void ConfirmUpdateDownloadInfo() {
139 observer_
->CurrentUpdateStatus(bytes_
, bytes_per_sec_
, hash_state_
);
142 virtual void SetUp() {
143 EXPECT_CALL(*(observer_
.get()), DestinationUpdate(_
, _
, _
))
145 .WillRepeatedly(Invoke(this, &DownloadFileTest::SetUpdateDownloadInfo
));
148 // Mock calls to this function are forwarded here.
149 void RegisterCallback(const base::Closure
& sink_callback
) {
150 sink_callback_
= sink_callback
;
153 void SetInterruptReasonCallback(const base::Closure
& closure
,
154 DownloadInterruptReason
* reason_p
,
155 DownloadInterruptReason reason
) {
160 bool CreateDownloadFile(int offset
, bool calculate_hash
) {
161 // There can be only one.
162 DCHECK(!download_file_
.get());
164 input_stream_
= new StrictMock
<MockByteStreamReader
>();
166 // TODO: Need to actually create a function that'll set the variables
167 // based on the inputs from the callback.
168 EXPECT_CALL(*input_stream_
, RegisterCallback(_
))
169 .WillOnce(Invoke(this, &DownloadFileTest::RegisterCallback
))
170 .RetiresOnSaturation();
172 scoped_ptr
<DownloadSaveInfo
> save_info(new DownloadSaveInfo());
173 scoped_ptr
<TestDownloadFileImpl
> download_file_impl(
174 new TestDownloadFileImpl(save_info
.Pass(),
179 scoped_ptr
<ByteStreamReader
>(input_stream_
),
181 observer_factory_
.GetWeakPtr()));
182 download_file_impl
->SetClientGuid("12345678-ABCD-1234-DCBA-123456789ABC");
183 download_file_
= download_file_impl
.Pass();
185 EXPECT_CALL(*input_stream_
, Read(_
, _
))
186 .WillOnce(Return(ByteStreamReader::STREAM_EMPTY
))
187 .RetiresOnSaturation();
189 base::WeakPtrFactory
<DownloadFileTest
> weak_ptr_factory(this);
190 DownloadInterruptReason result
= DOWNLOAD_INTERRUPT_REASON_NONE
;
191 base::RunLoop loop_runner
;
192 download_file_
->Initialize(base::Bind(
193 &DownloadFileTest::SetInterruptReasonCallback
,
194 weak_ptr_factory
.GetWeakPtr(), loop_runner
.QuitClosure(), &result
));
197 ::testing::Mock::VerifyAndClearExpectations(input_stream_
);
198 return result
== DOWNLOAD_INTERRUPT_REASON_NONE
;
201 void DestroyDownloadFile(int offset
) {
202 EXPECT_FALSE(download_file_
->InProgress());
204 // Make sure the data has been properly written to disk.
205 std::string disk_data
;
206 EXPECT_TRUE(base::ReadFileToString(download_file_
->FullPath(), &disk_data
));
207 EXPECT_EQ(expected_data_
, disk_data
);
209 // Make sure the Browser and File threads outlive the DownloadFile
210 // to satisfy thread checks inside it.
211 download_file_
.reset();
214 // Setup the stream to do be a data append; don't actually trigger
215 // the callback or do verifications.
216 void SetupDataAppend(const char **data_chunks
, size_t num_chunks
,
217 ::testing::Sequence s
) {
218 DCHECK(input_stream_
);
219 for (size_t i
= 0; i
< num_chunks
; i
++) {
220 const char *source_data
= data_chunks
[i
];
221 size_t length
= strlen(source_data
);
222 scoped_refptr
<net::IOBuffer
> data
= new net::IOBuffer(length
);
223 memcpy(data
->data(), source_data
, length
);
224 EXPECT_CALL(*input_stream_
, Read(_
, _
))
226 .WillOnce(DoAll(SetArgPointee
<0>(data
),
227 SetArgPointee
<1>(length
),
228 Return(ByteStreamReader::STREAM_HAS_DATA
)))
229 .RetiresOnSaturation();
230 expected_data_
+= source_data
;
234 void VerifyStreamAndSize() {
235 ::testing::Mock::VerifyAndClearExpectations(input_stream_
);
237 EXPECT_TRUE(base::GetFileSize(download_file_
->FullPath(), &size
));
238 EXPECT_EQ(expected_data_
.size(), static_cast<size_t>(size
));
241 // TODO(rdsmith): Manage full percentage issues properly.
242 void AppendDataToFile(const char **data_chunks
, size_t num_chunks
) {
243 ::testing::Sequence s1
;
244 SetupDataAppend(data_chunks
, num_chunks
, s1
);
245 EXPECT_CALL(*input_stream_
, Read(_
, _
))
247 .WillOnce(Return(ByteStreamReader::STREAM_EMPTY
))
248 .RetiresOnSaturation();
249 sink_callback_
.Run();
250 VerifyStreamAndSize();
253 void SetupFinishStream(DownloadInterruptReason interrupt_reason
,
254 ::testing::Sequence s
) {
255 EXPECT_CALL(*input_stream_
, Read(_
, _
))
257 .WillOnce(Return(ByteStreamReader::STREAM_COMPLETE
))
258 .RetiresOnSaturation();
259 EXPECT_CALL(*input_stream_
, GetStatus())
261 .WillOnce(Return(interrupt_reason
))
262 .RetiresOnSaturation();
263 EXPECT_CALL(*input_stream_
, RegisterCallback(_
))
264 .RetiresOnSaturation();
267 void FinishStream(DownloadInterruptReason interrupt_reason
,
268 bool check_observer
) {
269 ::testing::Sequence s1
;
270 SetupFinishStream(interrupt_reason
, s1
);
271 sink_callback_
.Run();
272 VerifyStreamAndSize();
273 if (check_observer
) {
274 EXPECT_CALL(*(observer_
.get()), DestinationCompleted(_
));
275 loop_
.RunUntilIdle();
276 ::testing::Mock::VerifyAndClearExpectations(observer_
.get());
277 EXPECT_CALL(*(observer_
.get()), DestinationUpdate(_
, _
, _
))
279 .WillRepeatedly(Invoke(this,
280 &DownloadFileTest::SetUpdateDownloadInfo
));
284 DownloadInterruptReason
RenameAndUniquify(
285 const base::FilePath
& full_path
,
286 base::FilePath
* result_path_p
) {
287 return InvokeRenameMethodAndWaitForCallback(
288 &DownloadFile::RenameAndUniquify
, full_path
, result_path_p
);
291 DownloadInterruptReason
RenameAndAnnotate(
292 const base::FilePath
& full_path
,
293 base::FilePath
* result_path_p
) {
294 return InvokeRenameMethodAndWaitForCallback(
295 &DownloadFile::RenameAndAnnotate
, full_path
, result_path_p
);
298 void ExpectPermissionError(DownloadInterruptReason err
) {
299 EXPECT_TRUE(err
== DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR
||
300 err
== DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED
)
301 << "Interrupt reason = " << err
;
305 DownloadInterruptReason
InvokeRenameMethodAndWaitForCallback(
306 DownloadFileRenameMethodType method
,
307 const base::FilePath
& full_path
,
308 base::FilePath
* result_path_p
) {
309 DownloadInterruptReason
result_reason(DOWNLOAD_INTERRUPT_REASON_NONE
);
310 base::FilePath result_path
;
312 base::RunLoop loop_runner
;
313 ((*download_file_
).*method
)(full_path
,
314 base::Bind(&DownloadFileTest::SetRenameResult
,
315 base::Unretained(this),
316 loop_runner
.QuitClosure(),
320 return result_reason
;
323 scoped_ptr
<StrictMock
<MockDownloadDestinationObserver
> > observer_
;
324 base::WeakPtrFactory
<DownloadDestinationObserver
> observer_factory_
;
326 // DownloadFile instance we are testing.
327 scoped_ptr
<DownloadFile
> download_file_
;
329 // Stream for sending data into the download file.
330 // Owned by download_file_; will be alive for lifetime of download_file_.
331 StrictMock
<MockByteStreamReader
>* input_stream_
;
333 // Sink callback data for stream.
334 base::Closure sink_callback_
;
336 // Latest update sent to the observer.
338 int64 bytes_per_sec_
;
339 std::string hash_state_
;
341 base::MessageLoop loop_
;
344 void SetRenameResult(const base::Closure
& closure
,
345 DownloadInterruptReason
* reason_p
,
346 base::FilePath
* result_path_p
,
347 DownloadInterruptReason reason
,
348 const base::FilePath
& result_path
) {
352 *result_path_p
= result_path
;
357 BrowserThreadImpl ui_thread_
;
358 // File thread to satisfy debug checks in DownloadFile.
359 BrowserThreadImpl file_thread_
;
361 // Keep track of what data should be saved to the disk file.
362 std::string expected_data_
;
365 // DownloadFile::RenameAndAnnotate and DownloadFile::RenameAndUniquify have a
366 // considerable amount of functional overlap. In order to re-use test logic, we
367 // are going to introduce this value parameterized test fixture. It will take a
368 // DownloadFileRenameMethodType value which can be either of the two rename
370 class DownloadFileTestWithRename
371 : public DownloadFileTest
,
372 public ::testing::WithParamInterface
<DownloadFileRenameMethodType
> {
374 DownloadInterruptReason
InvokeSelectedRenameMethod(
375 const base::FilePath
& full_path
,
376 base::FilePath
* result_path_p
) {
377 return InvokeRenameMethodAndWaitForCallback(
378 GetParam(), full_path
, result_path_p
);
382 // And now instantiate all DownloadFileTestWithRename tests using both
383 // DownloadFile rename methods. Each test of the form
384 // DownloadFileTestWithRename.<FooTest> will be instantiated once with
385 // RenameAndAnnotate as the value parameter and once with RenameAndUniquify as
386 // the value parameter.
387 INSTANTIATE_TEST_CASE_P(DownloadFile
,
388 DownloadFileTestWithRename
,
389 ::testing::Values(&DownloadFile::RenameAndAnnotate
,
390 &DownloadFile::RenameAndUniquify
));
392 const char* DownloadFileTest::kTestData1
=
393 "Let's write some data to the file!\n";
394 const char* DownloadFileTest::kTestData2
= "Writing more data.\n";
395 const char* DownloadFileTest::kTestData3
= "Final line.";
396 const char* DownloadFileTest::kDataHash
=
397 "CBF68BF10F8003DB86B31343AFAC8C7175BD03FB5FC905650F8C80AF087443A8";
399 const uint32
DownloadFileTest::kDummyDownloadId
= 23;
400 const int DownloadFileTest::kDummyChildId
= 3;
401 const int DownloadFileTest::kDummyRequestId
= 67;
403 // Rename the file before any data is downloaded, after some has, after it all
404 // has, and after it's closed.
405 TEST_P(DownloadFileTestWithRename
, RenameFileFinal
) {
406 ASSERT_TRUE(CreateDownloadFile(0, true));
407 base::FilePath
initial_path(download_file_
->FullPath());
408 EXPECT_TRUE(base::PathExists(initial_path
));
409 base::FilePath
path_1(initial_path
.InsertBeforeExtensionASCII("_1"));
410 base::FilePath
path_2(initial_path
.InsertBeforeExtensionASCII("_2"));
411 base::FilePath
path_3(initial_path
.InsertBeforeExtensionASCII("_3"));
412 base::FilePath
path_4(initial_path
.InsertBeforeExtensionASCII("_4"));
413 base::FilePath output_path
;
415 // Rename the file before downloading any data.
416 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
417 InvokeSelectedRenameMethod(path_1
, &output_path
));
418 base::FilePath renamed_path
= download_file_
->FullPath();
419 EXPECT_EQ(path_1
, renamed_path
);
420 EXPECT_EQ(path_1
, output_path
);
423 EXPECT_FALSE(base::PathExists(initial_path
));
424 EXPECT_TRUE(base::PathExists(path_1
));
426 // Download the data.
427 const char* chunks1
[] = { kTestData1
, kTestData2
};
428 AppendDataToFile(chunks1
, 2);
430 // Rename the file after downloading some data.
431 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
432 InvokeSelectedRenameMethod(path_2
, &output_path
));
433 renamed_path
= download_file_
->FullPath();
434 EXPECT_EQ(path_2
, renamed_path
);
435 EXPECT_EQ(path_2
, output_path
);
438 EXPECT_FALSE(base::PathExists(path_1
));
439 EXPECT_TRUE(base::PathExists(path_2
));
441 const char* chunks2
[] = { kTestData3
};
442 AppendDataToFile(chunks2
, 1);
444 // Rename the file after downloading all the data.
445 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
446 InvokeSelectedRenameMethod(path_3
, &output_path
));
447 renamed_path
= download_file_
->FullPath();
448 EXPECT_EQ(path_3
, renamed_path
);
449 EXPECT_EQ(path_3
, output_path
);
452 EXPECT_FALSE(base::PathExists(path_2
));
453 EXPECT_TRUE(base::PathExists(path_3
));
455 // Should not be able to get the hash until the file is closed.
457 EXPECT_FALSE(download_file_
->GetHash(&hash
));
458 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, true);
459 loop_
.RunUntilIdle();
461 // Rename the file after downloading all the data and closing the file.
462 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
463 InvokeSelectedRenameMethod(path_4
, &output_path
));
464 renamed_path
= download_file_
->FullPath();
465 EXPECT_EQ(path_4
, renamed_path
);
466 EXPECT_EQ(path_4
, output_path
);
469 EXPECT_FALSE(base::PathExists(path_3
));
470 EXPECT_TRUE(base::PathExists(path_4
));
473 EXPECT_TRUE(download_file_
->GetHash(&hash
));
474 EXPECT_EQ(kDataHash
, base::HexEncode(hash
.data(), hash
.size()));
476 DestroyDownloadFile(0);
479 // Test to make sure the rename overwrites when requested. This is separate from
480 // the above test because it only applies to RenameAndAnnotate().
481 // RenameAndUniquify() doesn't overwrite by design.
482 TEST_F(DownloadFileTest
, RenameOverwrites
) {
483 ASSERT_TRUE(CreateDownloadFile(0, true));
484 base::FilePath
initial_path(download_file_
->FullPath());
485 EXPECT_TRUE(base::PathExists(initial_path
));
486 base::FilePath
path_1(initial_path
.InsertBeforeExtensionASCII("_1"));
488 ASSERT_FALSE(base::PathExists(path_1
));
489 static const char file_data
[] = "xyzzy";
490 ASSERT_EQ(static_cast<int>(sizeof(file_data
)),
491 base::WriteFile(path_1
, file_data
, sizeof(file_data
)));
492 ASSERT_TRUE(base::PathExists(path_1
));
494 base::FilePath new_path
;
495 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
496 RenameAndAnnotate(path_1
, &new_path
));
497 EXPECT_EQ(path_1
.value(), new_path
.value());
499 std::string file_contents
;
500 ASSERT_TRUE(base::ReadFileToString(new_path
, &file_contents
));
501 EXPECT_NE(std::string(file_data
), file_contents
);
503 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, true);
504 loop_
.RunUntilIdle();
505 DestroyDownloadFile(0);
508 // Test to make sure the rename uniquifies if we aren't overwriting
509 // and there's a file where we're aiming. As above, not a
510 // DownloadFileTestWithRename test because this only applies to
511 // RenameAndUniquify().
512 TEST_F(DownloadFileTest
, RenameUniquifies
) {
513 ASSERT_TRUE(CreateDownloadFile(0, true));
514 base::FilePath
initial_path(download_file_
->FullPath());
515 EXPECT_TRUE(base::PathExists(initial_path
));
516 base::FilePath
path_1(initial_path
.InsertBeforeExtensionASCII("_1"));
517 base::FilePath
path_1_suffixed(path_1
.InsertBeforeExtensionASCII(" (1)"));
519 ASSERT_FALSE(base::PathExists(path_1
));
520 static const char file_data
[] = "xyzzy";
521 ASSERT_EQ(static_cast<int>(sizeof(file_data
)),
522 base::WriteFile(path_1
, file_data
, sizeof(file_data
)));
523 ASSERT_TRUE(base::PathExists(path_1
));
525 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
, RenameAndUniquify(path_1
, NULL
));
526 EXPECT_TRUE(base::PathExists(path_1_suffixed
));
528 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, true);
529 loop_
.RunUntilIdle();
530 DestroyDownloadFile(0);
533 // Test that RenameAndUniquify doesn't try to uniquify in the case where the
534 // target filename is the same as the current filename.
535 TEST_F(DownloadFileTest
, RenameRecognizesSelfConflict
) {
536 ASSERT_TRUE(CreateDownloadFile(0, true));
537 base::FilePath
initial_path(download_file_
->FullPath());
538 EXPECT_TRUE(base::PathExists(initial_path
));
540 base::FilePath new_path
;
541 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
,
542 RenameAndUniquify(initial_path
, &new_path
));
543 EXPECT_TRUE(base::PathExists(initial_path
));
545 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, true);
546 loop_
.RunUntilIdle();
547 DestroyDownloadFile(0);
548 EXPECT_EQ(initial_path
.value(), new_path
.value());
551 // Test to make sure we get the proper error on failure.
552 TEST_P(DownloadFileTestWithRename
, RenameError
) {
553 ASSERT_TRUE(CreateDownloadFile(0, true));
554 base::FilePath
initial_path(download_file_
->FullPath());
556 // Create a subdirectory.
557 base::FilePath
target_dir(
558 initial_path
.DirName().Append(FILE_PATH_LITERAL("TargetDir")));
559 ASSERT_FALSE(base::DirectoryExists(target_dir
));
560 ASSERT_TRUE(base::CreateDirectory(target_dir
));
561 base::FilePath
target_path(target_dir
.Append(initial_path
.BaseName()));
564 base::FilePath
target_path_suffixed(
565 target_path
.InsertBeforeExtensionASCII(" (1)"));
566 ASSERT_FALSE(base::PathExists(target_path
));
567 ASSERT_FALSE(base::PathExists(target_path_suffixed
));
569 // Make the directory unwritable and try to rename within it.
571 base::FilePermissionRestorer
restorer(target_dir
);
572 ASSERT_TRUE(base::MakeFileUnwritable(target_dir
));
574 // Expect nulling out of further processing.
575 EXPECT_CALL(*input_stream_
, RegisterCallback(IsNullCallback()));
576 ExpectPermissionError(InvokeSelectedRenameMethod(target_path
, NULL
));
577 EXPECT_FALSE(base::PathExists(target_path_suffixed
));
580 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, true);
581 loop_
.RunUntilIdle();
582 DestroyDownloadFile(0);
587 void TestRenameCompletionCallback(const base::Closure
& closure
,
588 bool* did_run_callback
,
589 DownloadInterruptReason interrupt_reason
,
590 const base::FilePath
& new_path
) {
591 EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE
, interrupt_reason
);
592 *did_run_callback
= true;
598 // Test that the retry logic works. This test assumes that DownloadFileImpl will
599 // post tasks to the current message loop (acting as the FILE thread)
600 // asynchronously to retry the renames. We will stuff RunLoop::QuitClosures()
601 // in between the retry tasks to stagger them and then allow the rename to
604 // Note that there is only one queue of tasks to run, and that is in the tests'
605 // base::MessageLoop::current(). Each RunLoop processes that queue until it sees
606 // a QuitClosure() targeted at itself, at which point it stops processing.
607 TEST_P(DownloadFileTestWithRename
, RenameWithErrorRetry
) {
608 ASSERT_TRUE(CreateDownloadFile(0, true));
609 base::FilePath
initial_path(download_file_
->FullPath());
611 // Create a subdirectory.
612 base::FilePath
target_dir(
613 initial_path
.DirName().Append(FILE_PATH_LITERAL("TargetDir")));
614 ASSERT_FALSE(base::DirectoryExists(target_dir
));
615 ASSERT_TRUE(base::CreateDirectory(target_dir
));
616 base::FilePath
target_path(target_dir
.Append(initial_path
.BaseName()));
618 bool did_run_callback
= false;
620 // Each RunLoop can be used the run the MessageLoop until the corresponding
621 // QuitClosure() is run. This one is used to produce the QuitClosure() that
622 // will be run when the entire rename operation is complete.
623 base::RunLoop succeeding_run
;
625 // (Scope for the base::File or base::FilePermissionRestorer below.)
627 // On Windows we test with an actual transient error, a sharing violation.
628 // The rename will fail because we are holding the file open for READ. On
629 // Posix this doesn't cause a failure.
630 base::File
locked_file(initial_path
,
631 base::File::FLAG_OPEN
| base::File::FLAG_READ
);
632 ASSERT_TRUE(locked_file
.IsValid());
634 // Simulate a transient failure by revoking write permission for target_dir.
635 // The TestDownloadFileImpl class treats this error as transient even though
636 // DownloadFileImpl itself doesn't.
637 base::FilePermissionRestorer
restore_permissions_for(target_dir
);
638 ASSERT_TRUE(base::MakeFileUnwritable(target_dir
));
641 // The Rename() should fail here and enqueue a retry task without invoking
642 // the completion callback.
643 ((*download_file_
).*GetParam())(target_path
,
644 base::Bind(&TestRenameCompletionCallback
,
645 succeeding_run
.QuitClosure(),
647 EXPECT_FALSE(did_run_callback
);
649 base::RunLoop first_failing_run
;
650 // Queue the QuitClosure() on the MessageLoop now. Any tasks queued by the
651 // Rename() will be in front of the QuitClosure(). Running the message loop
652 // now causes the just the first retry task to be run. The rename still
653 // fails, so another retry task would get queued behind the QuitClosure().
654 base::MessageLoop::current()->PostTask(FROM_HERE
,
655 first_failing_run
.QuitClosure());
656 first_failing_run
.Run();
657 EXPECT_FALSE(did_run_callback
);
659 // Running another loop should have the same effect as the above as long as
660 // kMaxRenameRetries is greater than 2.
661 base::RunLoop second_failing_run
;
662 base::MessageLoop::current()->PostTask(FROM_HERE
,
663 second_failing_run
.QuitClosure());
664 second_failing_run
.Run();
665 EXPECT_FALSE(did_run_callback
);
668 // This time the QuitClosure from succeeding_run should get executed.
669 succeeding_run
.Run();
670 EXPECT_TRUE(did_run_callback
);
672 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, true);
673 loop_
.RunUntilIdle();
674 DestroyDownloadFile(0);
677 // Various tests of the StreamActive method.
678 TEST_F(DownloadFileTest
, StreamEmptySuccess
) {
679 ASSERT_TRUE(CreateDownloadFile(0, true));
680 base::FilePath
initial_path(download_file_
->FullPath());
681 EXPECT_TRUE(base::PathExists(initial_path
));
683 // Test that calling the sink_callback_ on an empty stream shouldn't
685 AppendDataToFile(NULL
, 0);
687 // Finish the download this way and make sure we see it on the
689 EXPECT_CALL(*(observer_
.get()), DestinationCompleted(_
));
690 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, false);
691 loop_
.RunUntilIdle();
693 DestroyDownloadFile(0);
696 TEST_F(DownloadFileTest
, StreamEmptyError
) {
697 ASSERT_TRUE(CreateDownloadFile(0, true));
698 base::FilePath
initial_path(download_file_
->FullPath());
699 EXPECT_TRUE(base::PathExists(initial_path
));
701 // Finish the download in error and make sure we see it on the
703 EXPECT_CALL(*(observer_
.get()),
705 DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED
))
706 .WillOnce(InvokeWithoutArgs(
707 this, &DownloadFileTest::ConfirmUpdateDownloadInfo
));
709 // If this next EXPECT_CALL fails flakily, it's probably a real failure.
710 // We'll be getting a stream of UpdateDownload calls from the timer, and
711 // the last one may have the correct information even if the failure
712 // doesn't produce an update, as the timer update may have triggered at the
714 EXPECT_CALL(*(observer_
.get()), CurrentUpdateStatus(0, _
, _
));
716 FinishStream(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED
, false);
718 loop_
.RunUntilIdle();
720 DestroyDownloadFile(0);
723 TEST_F(DownloadFileTest
, StreamNonEmptySuccess
) {
724 ASSERT_TRUE(CreateDownloadFile(0, true));
725 base::FilePath
initial_path(download_file_
->FullPath());
726 EXPECT_TRUE(base::PathExists(initial_path
));
728 const char* chunks1
[] = { kTestData1
, kTestData2
};
729 ::testing::Sequence s1
;
730 SetupDataAppend(chunks1
, 2, s1
);
731 SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, s1
);
732 EXPECT_CALL(*(observer_
.get()), DestinationCompleted(_
));
733 sink_callback_
.Run();
734 VerifyStreamAndSize();
735 loop_
.RunUntilIdle();
736 DestroyDownloadFile(0);
739 TEST_F(DownloadFileTest
, StreamNonEmptyError
) {
740 ASSERT_TRUE(CreateDownloadFile(0, true));
741 base::FilePath
initial_path(download_file_
->FullPath());
742 EXPECT_TRUE(base::PathExists(initial_path
));
744 const char* chunks1
[] = { kTestData1
, kTestData2
};
745 ::testing::Sequence s1
;
746 SetupDataAppend(chunks1
, 2, s1
);
747 SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED
, s1
);
749 EXPECT_CALL(*(observer_
.get()),
751 DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED
))
752 .WillOnce(InvokeWithoutArgs(
753 this, &DownloadFileTest::ConfirmUpdateDownloadInfo
));
755 // If this next EXPECT_CALL fails flakily, it's probably a real failure.
756 // We'll be getting a stream of UpdateDownload calls from the timer, and
757 // the last one may have the correct information even if the failure
758 // doesn't produce an update, as the timer update may have triggered at the
760 EXPECT_CALL(*(observer_
.get()),
761 CurrentUpdateStatus(strlen(kTestData1
) + strlen(kTestData2
),
764 sink_callback_
.Run();
765 loop_
.RunUntilIdle();
766 VerifyStreamAndSize();
767 DestroyDownloadFile(0);
770 // Send some data, wait 3/4s of a second, run the message loop, and
771 // confirm the values the observer received are correct.
772 TEST_F(DownloadFileTest
, ConfirmUpdate
) {
773 CreateDownloadFile(0, true);
775 const char* chunks1
[] = { kTestData1
, kTestData2
};
776 AppendDataToFile(chunks1
, 2);
778 // Run the message loops for 750ms and check for results.
779 loop_
.PostDelayedTask(FROM_HERE
,
780 base::MessageLoop::QuitClosure(),
781 base::TimeDelta::FromMilliseconds(750));
784 EXPECT_EQ(static_cast<int64
>(strlen(kTestData1
) + strlen(kTestData2
)),
786 EXPECT_EQ(download_file_
->GetHashState(), hash_state_
);
788 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE
, true);
789 DestroyDownloadFile(0);
792 } // namespace content