cc: Make picture pile base thread safe.
[chromium-blink-merge.git] / content / browser / download / download_file_unittest.cc
blobdbf0fe38e8b114addc038bd99b9626b8ac3320a7
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"
26 using ::testing::_;
27 using ::testing::AnyNumber;
28 using ::testing::DoAll;
29 using ::testing::InSequence;
30 using ::testing::Return;
31 using ::testing::SetArgPointee;
32 using ::testing::StrictMock;
34 namespace content {
35 namespace {
37 class MockByteStreamReader : public ByteStreamReader {
38 public:
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 {
50 public:
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 {
71 public:
72 TestDownloadFileImpl(scoped_ptr<DownloadSaveInfo> save_info,
73 const base::FilePath& default_downloads_directory,
74 const GURL& url,
75 const GURL& referrer_url,
76 bool calculate_hash,
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,
82 url,
83 referrer_url,
84 calculate_hash,
85 stream.Pass(),
86 bound_net_log,
87 observer) {}
89 protected:
90 base::TimeDelta GetRetryDelayForFailedRename(int attempt_count) override {
91 return base::TimeDelta::FromMilliseconds(0);
94 #if !defined(OS_WIN)
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;
101 #endif
104 } // namespace
106 class DownloadFileTest : public testing::Test {
107 public:
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;
117 DownloadFileTest() :
118 observer_(new StrictMock<MockDownloadDestinationObserver>),
119 observer_factory_(observer_.get()),
120 input_stream_(NULL),
121 bytes_(-1),
122 bytes_per_sec_(-1),
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) {
133 bytes_ = bytes;
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(_, _, _))
144 .Times(AnyNumber())
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) {
156 *reason_p = reason;
157 closure.Run();
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(),
175 base::FilePath(),
176 GURL(), // Source
177 GURL(), // Referrer
178 calculate_hash,
179 scoped_ptr<ByteStreamReader>(input_stream_),
180 net::BoundNetLog(),
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));
195 loop_runner.Run();
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(_, _))
225 .InSequence(s)
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_);
236 int64 size;
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(_, _))
246 .InSequence(s1)
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(_, _))
256 .InSequence(s)
257 .WillOnce(Return(ByteStreamReader::STREAM_COMPLETE))
258 .RetiresOnSaturation();
259 EXPECT_CALL(*input_stream_, GetStatus())
260 .InSequence(s)
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(_, _, _))
278 .Times(AnyNumber())
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;
304 protected:
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(),
317 &result_reason,
318 result_path_p));
319 loop_runner.Run();
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.
337 int64 bytes_;
338 int64 bytes_per_sec_;
339 std::string hash_state_;
341 base::MessageLoop loop_;
343 private:
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) {
349 if (reason_p)
350 *reason_p = reason;
351 if (result_path_p)
352 *result_path_p = result_path;
353 closure.Run();
356 // UI thread.
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
369 // methods.
370 class DownloadFileTestWithRename
371 : public DownloadFileTest,
372 public ::testing::WithParamInterface<DownloadFileRenameMethodType> {
373 protected:
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);
422 // Check the files.
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);
437 // Check the files.
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);
451 // Check the files.
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.
456 std::string hash;
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);
468 // Check the files.
469 EXPECT_FALSE(base::PathExists(path_3));
470 EXPECT_TRUE(base::PathExists(path_4));
472 // Check the hash.
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()));
563 // Targets
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);
585 namespace {
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;
593 closure.Run();
596 } // namespace
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
602 // succeed.
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.)
626 #if defined(OS_WIN)
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());
633 #else
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));
639 #endif
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(),
646 &did_run_callback));
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
684 // do anything.
685 AppendDataToFile(NULL, 0);
687 // Finish the download this way and make sure we see it on the
688 // observer.
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
702 // observer.
703 EXPECT_CALL(*(observer_.get()),
704 DestinationError(
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
713 // same time.
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()),
750 DestinationError(
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
759 // same time.
760 EXPECT_CALL(*(observer_.get()),
761 CurrentUpdateStatus(strlen(kTestData1) + strlen(kTestData2),
762 _, _));
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));
782 loop_.Run();
784 EXPECT_EQ(static_cast<int64>(strlen(kTestData1) + strlen(kTestData2)),
785 bytes_);
786 EXPECT_EQ(download_file_->GetHashState(), hash_state_);
788 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
789 DestroyDownloadFile(0);
792 } // namespace content