Add ICU message format support
[chromium-blink-merge.git] / content / browser / download / download_file_unittest.cc
blob59cc4a766de9e7d386c6ae36908a4bf55689ba89
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"
28 using ::testing::_;
29 using ::testing::AnyNumber;
30 using ::testing::DoAll;
31 using ::testing::InSequence;
32 using ::testing::Return;
33 using ::testing::SetArgPointee;
34 using ::testing::StrictMock;
36 namespace content {
37 namespace {
39 class MockByteStreamReader : public ByteStreamReader {
40 public:
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 {
52 public:
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 {
73 public:
74 TestDownloadFileImpl(scoped_ptr<DownloadSaveInfo> save_info,
75 const base::FilePath& default_downloads_directory,
76 const GURL& url,
77 const GURL& referrer_url,
78 bool calculate_hash,
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,
84 url,
85 referrer_url,
86 calculate_hash,
87 stream.Pass(),
88 bound_net_log,
89 observer) {}
91 protected:
92 base::TimeDelta GetRetryDelayForFailedRename(int attempt_count) override {
93 return base::TimeDelta::FromMilliseconds(0);
96 #if !defined(OS_WIN)
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;
103 #endif
106 } // namespace
108 class DownloadFileTest : public testing::Test {
109 public:
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;
119 DownloadFileTest() :
120 observer_(new StrictMock<MockDownloadDestinationObserver>),
121 observer_factory_(observer_.get()),
122 input_stream_(NULL),
123 bytes_(-1),
124 bytes_per_sec_(-1),
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) {
134 bytes_ = bytes;
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(_, _, _))
145 .Times(AnyNumber())
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) {
157 *reason_p = reason;
158 closure.Run();
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(),
176 base::FilePath(),
177 GURL(), // Source
178 GURL(), // Referrer
179 calculate_hash,
180 scoped_ptr<ByteStreamReader>(input_stream_),
181 net::BoundNetLog(),
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));
196 loop_runner.Run();
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(_, _))
226 .InSequence(s)
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_);
237 int64 size;
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(_, _))
247 .InSequence(s1)
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(_, _))
257 .InSequence(s)
258 .WillOnce(Return(ByteStreamReader::STREAM_COMPLETE))
259 .RetiresOnSaturation();
260 EXPECT_CALL(*input_stream_, GetStatus())
261 .InSequence(s)
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(_, _, _))
279 .Times(AnyNumber())
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;
305 protected:
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(),
318 &result_reason,
319 result_path_p));
320 loop_runner.Run();
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.
338 int64 bytes_;
339 int64 bytes_per_sec_;
340 std::string hash_state_;
342 base::MessageLoop loop_;
344 private:
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) {
350 if (reason_p)
351 *reason_p = reason;
352 if (result_path_p)
353 *result_path_p = result_path;
354 closure.Run();
357 // UI thread.
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
370 // methods.
371 class DownloadFileTestWithRename
372 : public DownloadFileTest,
373 public ::testing::WithParamInterface<DownloadFileRenameMethodType> {
374 protected:
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);
423 // Check the files.
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);
438 // Check the files.
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);
452 // Check the files.
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.
457 std::string hash;
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);
469 // Check the files.
470 EXPECT_FALSE(base::PathExists(path_3));
471 EXPECT_TRUE(base::PathExists(path_4));
473 // Check the hash.
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()));
564 // Targets
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);
586 namespace {
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;
594 closure.Run();
597 } // namespace
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
603 // succeed.
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.)
627 #if defined(OS_WIN)
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());
634 #else
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));
640 #endif
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(),
647 &did_run_callback));
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
685 // do anything.
686 AppendDataToFile(NULL, 0);
688 // Finish the download this way and make sure we see it on the
689 // observer.
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
703 // observer.
704 EXPECT_CALL(*(observer_.get()),
705 DestinationError(
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
714 // same time.
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()),
751 DestinationError(
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
760 // same time.
761 EXPECT_CALL(*(observer_.get()),
762 CurrentUpdateStatus(strlen(kTestData1) + strlen(kTestData2),
763 _, _));
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));
783 loop_.Run();
785 EXPECT_EQ(static_cast<int64>(strlen(kTestData1) + strlen(kTestData2)),
786 bytes_);
787 EXPECT_EQ(download_file_->GetHashState(), hash_state_);
789 FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
790 DestroyDownloadFile(0);
793 } // namespace content