• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
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"
25 
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;
33 
34 namespace content {
35 namespace {
36 
37 class MockByteStreamReader : public ByteStreamReader {
38  public:
MockByteStreamReader()39   MockByteStreamReader() {}
~MockByteStreamReader()40   ~MockByteStreamReader() {}
41 
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&));
47 };
48 
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&));
54 
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&));
60 };
61 
62 MATCHER(IsNullCallback, "") { return (arg.is_null()); }
63 
64 typedef void (DownloadFile::*DownloadFileRenameMethodType)(
65     const base::FilePath&,
66     const DownloadFile::RenameCompletionCallback&);
67 
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:
TestDownloadFileImpl(scoped_ptr<DownloadSaveInfo> save_info,const base::FilePath & default_downloads_directory,const GURL & url,const GURL & referrer_url,bool calculate_hash,scoped_ptr<ByteStreamReader> stream,const net::BoundNetLog & bound_net_log,base::WeakPtr<DownloadDestinationObserver> observer)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) {}
88 
89  protected:
GetRetryDelayForFailedRename(int attempt_count)90   virtual base::TimeDelta GetRetryDelayForFailedRename(
91       int attempt_count) OVERRIDE {
92     return base::TimeDelta::FromMilliseconds(0);
93   }
94 
95 #if !defined(OS_WIN)
96   // On Posix, we don't encounter transient errors during renames, except
97   // possibly EAGAIN, which is difficult to replicate reliably. So we resort to
98   // simulating a transient error using ACCESS_DENIED instead.
ShouldRetryFailedRename(DownloadInterruptReason reason)99   virtual bool ShouldRetryFailedRename(
100       DownloadInterruptReason reason) OVERRIDE {
101     return reason == DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED;
102   }
103 #endif
104 };
105 
106 }  // namespace
107 
108 class DownloadFileTest : public testing::Test {
109  public:
110 
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;
118 
DownloadFileTest()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_) {
128   }
129 
~DownloadFileTest()130   virtual ~DownloadFileTest() {
131   }
132 
SetUpdateDownloadInfo(int64 bytes,int64 bytes_per_sec,const std::string & hash_state)133   void SetUpdateDownloadInfo(int64 bytes, int64 bytes_per_sec,
134                              const std::string& hash_state) {
135     bytes_ = bytes;
136     bytes_per_sec_ = bytes_per_sec;
137     hash_state_ = hash_state;
138   }
139 
ConfirmUpdateDownloadInfo()140   void ConfirmUpdateDownloadInfo() {
141     observer_->CurrentUpdateStatus(bytes_, bytes_per_sec_, hash_state_);
142   }
143 
SetUp()144   virtual void SetUp() {
145     EXPECT_CALL(*(observer_.get()), DestinationUpdate(_, _, _))
146         .Times(AnyNumber())
147         .WillRepeatedly(Invoke(this, &DownloadFileTest::SetUpdateDownloadInfo));
148   }
149 
150   // Mock calls to this function are forwarded here.
RegisterCallback(const base::Closure & sink_callback)151   void RegisterCallback(const base::Closure& sink_callback) {
152     sink_callback_ = sink_callback;
153   }
154 
SetInterruptReasonCallback(const base::Closure & closure,DownloadInterruptReason * reason_p,DownloadInterruptReason reason)155   void SetInterruptReasonCallback(const base::Closure& closure,
156                                   DownloadInterruptReason* reason_p,
157                                   DownloadInterruptReason reason) {
158     *reason_p = reason;
159     closure.Run();
160   }
161 
CreateDownloadFile(int offset,bool calculate_hash)162   bool CreateDownloadFile(int offset, bool calculate_hash) {
163     // There can be only one.
164     DCHECK(!download_file_.get());
165 
166     input_stream_ = new StrictMock<MockByteStreamReader>();
167 
168     // TODO: Need to actually create a function that'll set the variables
169     // based on the inputs from the callback.
170     EXPECT_CALL(*input_stream_, RegisterCallback(_))
171         .WillOnce(Invoke(this, &DownloadFileTest::RegisterCallback))
172         .RetiresOnSaturation();
173 
174     scoped_ptr<DownloadSaveInfo> save_info(new DownloadSaveInfo());
175     scoped_ptr<TestDownloadFileImpl> download_file_impl(
176         new TestDownloadFileImpl(save_info.Pass(),
177                                  base::FilePath(),
178                                  GURL(),  // Source
179                                  GURL(),  // Referrer
180                                  calculate_hash,
181                                  scoped_ptr<ByteStreamReader>(input_stream_),
182                                  net::BoundNetLog(),
183                                  observer_factory_.GetWeakPtr()));
184     download_file_impl->SetClientGuid("12345678-ABCD-1234-DCBA-123456789ABC");
185     download_file_ = download_file_impl.PassAs<DownloadFile>();
186 
187     EXPECT_CALL(*input_stream_, Read(_, _))
188         .WillOnce(Return(ByteStreamReader::STREAM_EMPTY))
189         .RetiresOnSaturation();
190 
191     base::WeakPtrFactory<DownloadFileTest> weak_ptr_factory(this);
192     DownloadInterruptReason result = DOWNLOAD_INTERRUPT_REASON_NONE;
193     base::RunLoop loop_runner;
194     download_file_->Initialize(base::Bind(
195         &DownloadFileTest::SetInterruptReasonCallback,
196         weak_ptr_factory.GetWeakPtr(), loop_runner.QuitClosure(), &result));
197     loop_runner.Run();
198 
199     ::testing::Mock::VerifyAndClearExpectations(input_stream_);
200     return result == DOWNLOAD_INTERRUPT_REASON_NONE;
201   }
202 
DestroyDownloadFile(int offset)203   void DestroyDownloadFile(int offset) {
204     EXPECT_FALSE(download_file_->InProgress());
205 
206     // Make sure the data has been properly written to disk.
207     std::string disk_data;
208     EXPECT_TRUE(base::ReadFileToString(download_file_->FullPath(), &disk_data));
209     EXPECT_EQ(expected_data_, disk_data);
210 
211     // Make sure the Browser and File threads outlive the DownloadFile
212     // to satisfy thread checks inside it.
213     download_file_.reset();
214   }
215 
216   // Setup the stream to do be a data append; don't actually trigger
217   // the callback or do verifications.
SetupDataAppend(const char ** data_chunks,size_t num_chunks,::testing::Sequence s)218   void SetupDataAppend(const char **data_chunks, size_t num_chunks,
219                        ::testing::Sequence s) {
220     DCHECK(input_stream_);
221     for (size_t i = 0; i < num_chunks; i++) {
222       const char *source_data = data_chunks[i];
223       size_t length = strlen(source_data);
224       scoped_refptr<net::IOBuffer> data = new net::IOBuffer(length);
225       memcpy(data->data(), source_data, length);
226       EXPECT_CALL(*input_stream_, Read(_, _))
227           .InSequence(s)
228           .WillOnce(DoAll(SetArgPointee<0>(data),
229                           SetArgPointee<1>(length),
230                           Return(ByteStreamReader::STREAM_HAS_DATA)))
231           .RetiresOnSaturation();
232       expected_data_ += source_data;
233     }
234   }
235 
VerifyStreamAndSize()236   void VerifyStreamAndSize() {
237     ::testing::Mock::VerifyAndClearExpectations(input_stream_);
238     int64 size;
239     EXPECT_TRUE(base::GetFileSize(download_file_->FullPath(), &size));
240     EXPECT_EQ(expected_data_.size(), static_cast<size_t>(size));
241   }
242 
243   // TODO(rdsmith): Manage full percentage issues properly.
AppendDataToFile(const char ** data_chunks,size_t num_chunks)244   void AppendDataToFile(const char **data_chunks, size_t num_chunks) {
245     ::testing::Sequence s1;
246     SetupDataAppend(data_chunks, num_chunks, s1);
247     EXPECT_CALL(*input_stream_, Read(_, _))
248         .InSequence(s1)
249         .WillOnce(Return(ByteStreamReader::STREAM_EMPTY))
250         .RetiresOnSaturation();
251     sink_callback_.Run();
252     VerifyStreamAndSize();
253   }
254 
SetupFinishStream(DownloadInterruptReason interrupt_reason,::testing::Sequence s)255   void SetupFinishStream(DownloadInterruptReason interrupt_reason,
256                        ::testing::Sequence s) {
257     EXPECT_CALL(*input_stream_, Read(_, _))
258         .InSequence(s)
259         .WillOnce(Return(ByteStreamReader::STREAM_COMPLETE))
260         .RetiresOnSaturation();
261     EXPECT_CALL(*input_stream_, GetStatus())
262         .InSequence(s)
263         .WillOnce(Return(interrupt_reason))
264         .RetiresOnSaturation();
265     EXPECT_CALL(*input_stream_, RegisterCallback(_))
266         .RetiresOnSaturation();
267   }
268 
FinishStream(DownloadInterruptReason interrupt_reason,bool check_observer)269   void FinishStream(DownloadInterruptReason interrupt_reason,
270                     bool check_observer) {
271     ::testing::Sequence s1;
272     SetupFinishStream(interrupt_reason, s1);
273     sink_callback_.Run();
274     VerifyStreamAndSize();
275     if (check_observer) {
276       EXPECT_CALL(*(observer_.get()), DestinationCompleted(_));
277       loop_.RunUntilIdle();
278       ::testing::Mock::VerifyAndClearExpectations(observer_.get());
279       EXPECT_CALL(*(observer_.get()), DestinationUpdate(_, _, _))
280           .Times(AnyNumber())
281           .WillRepeatedly(Invoke(this,
282                                  &DownloadFileTest::SetUpdateDownloadInfo));
283     }
284   }
285 
RenameAndUniquify(const base::FilePath & full_path,base::FilePath * result_path_p)286   DownloadInterruptReason RenameAndUniquify(
287       const base::FilePath& full_path,
288       base::FilePath* result_path_p) {
289     return InvokeRenameMethodAndWaitForCallback(
290         &DownloadFile::RenameAndUniquify, full_path, result_path_p);
291   }
292 
RenameAndAnnotate(const base::FilePath & full_path,base::FilePath * result_path_p)293   DownloadInterruptReason RenameAndAnnotate(
294       const base::FilePath& full_path,
295       base::FilePath* result_path_p) {
296     return InvokeRenameMethodAndWaitForCallback(
297         &DownloadFile::RenameAndAnnotate, full_path, result_path_p);
298   }
299 
ExpectPermissionError(DownloadInterruptReason err)300   void ExpectPermissionError(DownloadInterruptReason err) {
301     EXPECT_TRUE(err == DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR ||
302                 err == DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED)
303         << "Interrupt reason = " << err;
304   }
305 
306  protected:
InvokeRenameMethodAndWaitForCallback(DownloadFileRenameMethodType method,const base::FilePath & full_path,base::FilePath * result_path_p)307   DownloadInterruptReason InvokeRenameMethodAndWaitForCallback(
308       DownloadFileRenameMethodType method,
309       const base::FilePath& full_path,
310       base::FilePath* result_path_p) {
311     DownloadInterruptReason result_reason(DOWNLOAD_INTERRUPT_REASON_NONE);
312     base::FilePath result_path;
313 
314     base::RunLoop loop_runner;
315     ((*download_file_).*method)(full_path,
316                                 base::Bind(&DownloadFileTest::SetRenameResult,
317                                            base::Unretained(this),
318                                            loop_runner.QuitClosure(),
319                                            &result_reason,
320                                            result_path_p));
321     loop_runner.Run();
322     return result_reason;
323   }
324 
325   scoped_ptr<StrictMock<MockDownloadDestinationObserver> > observer_;
326   base::WeakPtrFactory<DownloadDestinationObserver> observer_factory_;
327 
328   // DownloadFile instance we are testing.
329   scoped_ptr<DownloadFile> download_file_;
330 
331   // Stream for sending data into the download file.
332   // Owned by download_file_; will be alive for lifetime of download_file_.
333   StrictMock<MockByteStreamReader>* input_stream_;
334 
335   // Sink callback data for stream.
336   base::Closure sink_callback_;
337 
338   // Latest update sent to the observer.
339   int64 bytes_;
340   int64 bytes_per_sec_;
341   std::string hash_state_;
342 
343   base::MessageLoop loop_;
344 
345  private:
SetRenameResult(const base::Closure & closure,DownloadInterruptReason * reason_p,base::FilePath * result_path_p,DownloadInterruptReason reason,const base::FilePath & result_path)346   void SetRenameResult(const base::Closure& closure,
347                        DownloadInterruptReason* reason_p,
348                        base::FilePath* result_path_p,
349                        DownloadInterruptReason reason,
350                        const base::FilePath& result_path) {
351     if (reason_p)
352       *reason_p = reason;
353     if (result_path_p)
354       *result_path_p = result_path;
355     closure.Run();
356   }
357 
358   // UI thread.
359   BrowserThreadImpl ui_thread_;
360   // File thread to satisfy debug checks in DownloadFile.
361   BrowserThreadImpl file_thread_;
362 
363   // Keep track of what data should be saved to the disk file.
364   std::string expected_data_;
365 };
366 
367 // DownloadFile::RenameAndAnnotate and DownloadFile::RenameAndUniquify have a
368 // considerable amount of functional overlap. In order to re-use test logic, we
369 // are going to introduce this value parameterized test fixture. It will take a
370 // DownloadFileRenameMethodType value which can be either of the two rename
371 // methods.
372 class DownloadFileTestWithRename
373     : public DownloadFileTest,
374       public ::testing::WithParamInterface<DownloadFileRenameMethodType> {
375  protected:
InvokeSelectedRenameMethod(const base::FilePath & full_path,base::FilePath * result_path_p)376   DownloadInterruptReason InvokeSelectedRenameMethod(
377       const base::FilePath& full_path,
378       base::FilePath* result_path_p) {
379     return InvokeRenameMethodAndWaitForCallback(
380         GetParam(), full_path, result_path_p);
381   }
382 };
383 
384 // And now instantiate all DownloadFileTestWithRename tests using both
385 // DownloadFile rename methods. Each test of the form
386 // DownloadFileTestWithRename.<FooTest> will be instantiated once with
387 // RenameAndAnnotate as the value parameter and once with RenameAndUniquify as
388 // the value parameter.
389 INSTANTIATE_TEST_CASE_P(DownloadFile,
390                         DownloadFileTestWithRename,
391                         ::testing::Values(&DownloadFile::RenameAndAnnotate,
392                                           &DownloadFile::RenameAndUniquify));
393 
394 const char* DownloadFileTest::kTestData1 =
395     "Let's write some data to the file!\n";
396 const char* DownloadFileTest::kTestData2 = "Writing more data.\n";
397 const char* DownloadFileTest::kTestData3 = "Final line.";
398 const char* DownloadFileTest::kDataHash =
399     "CBF68BF10F8003DB86B31343AFAC8C7175BD03FB5FC905650F8C80AF087443A8";
400 
401 const uint32 DownloadFileTest::kDummyDownloadId = 23;
402 const int DownloadFileTest::kDummyChildId = 3;
403 const int DownloadFileTest::kDummyRequestId = 67;
404 
405 // Rename the file before any data is downloaded, after some has, after it all
406 // has, and after it's closed.
TEST_P(DownloadFileTestWithRename,RenameFileFinal)407 TEST_P(DownloadFileTestWithRename, RenameFileFinal) {
408   ASSERT_TRUE(CreateDownloadFile(0, true));
409   base::FilePath initial_path(download_file_->FullPath());
410   EXPECT_TRUE(base::PathExists(initial_path));
411   base::FilePath path_1(initial_path.InsertBeforeExtensionASCII("_1"));
412   base::FilePath path_2(initial_path.InsertBeforeExtensionASCII("_2"));
413   base::FilePath path_3(initial_path.InsertBeforeExtensionASCII("_3"));
414   base::FilePath path_4(initial_path.InsertBeforeExtensionASCII("_4"));
415   base::FilePath output_path;
416 
417   // Rename the file before downloading any data.
418   EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
419             InvokeSelectedRenameMethod(path_1, &output_path));
420   base::FilePath renamed_path = download_file_->FullPath();
421   EXPECT_EQ(path_1, renamed_path);
422   EXPECT_EQ(path_1, output_path);
423 
424   // Check the files.
425   EXPECT_FALSE(base::PathExists(initial_path));
426   EXPECT_TRUE(base::PathExists(path_1));
427 
428   // Download the data.
429   const char* chunks1[] = { kTestData1, kTestData2 };
430   AppendDataToFile(chunks1, 2);
431 
432   // Rename the file after downloading some data.
433   EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
434             InvokeSelectedRenameMethod(path_2, &output_path));
435   renamed_path = download_file_->FullPath();
436   EXPECT_EQ(path_2, renamed_path);
437   EXPECT_EQ(path_2, output_path);
438 
439   // Check the files.
440   EXPECT_FALSE(base::PathExists(path_1));
441   EXPECT_TRUE(base::PathExists(path_2));
442 
443   const char* chunks2[] = { kTestData3 };
444   AppendDataToFile(chunks2, 1);
445 
446   // Rename the file after downloading all the data.
447   EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
448             InvokeSelectedRenameMethod(path_3, &output_path));
449   renamed_path = download_file_->FullPath();
450   EXPECT_EQ(path_3, renamed_path);
451   EXPECT_EQ(path_3, output_path);
452 
453   // Check the files.
454   EXPECT_FALSE(base::PathExists(path_2));
455   EXPECT_TRUE(base::PathExists(path_3));
456 
457   // Should not be able to get the hash until the file is closed.
458   std::string hash;
459   EXPECT_FALSE(download_file_->GetHash(&hash));
460   FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
461   loop_.RunUntilIdle();
462 
463   // Rename the file after downloading all the data and closing the file.
464   EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
465             InvokeSelectedRenameMethod(path_4, &output_path));
466   renamed_path = download_file_->FullPath();
467   EXPECT_EQ(path_4, renamed_path);
468   EXPECT_EQ(path_4, output_path);
469 
470   // Check the files.
471   EXPECT_FALSE(base::PathExists(path_3));
472   EXPECT_TRUE(base::PathExists(path_4));
473 
474   // Check the hash.
475   EXPECT_TRUE(download_file_->GetHash(&hash));
476   EXPECT_EQ(kDataHash, base::HexEncode(hash.data(), hash.size()));
477 
478   DestroyDownloadFile(0);
479 }
480 
481 // Test to make sure the rename overwrites when requested. This is separate from
482 // the above test because it only applies to RenameAndAnnotate().
483 // RenameAndUniquify() doesn't overwrite by design.
TEST_F(DownloadFileTest,RenameOverwrites)484 TEST_F(DownloadFileTest, RenameOverwrites) {
485   ASSERT_TRUE(CreateDownloadFile(0, true));
486   base::FilePath initial_path(download_file_->FullPath());
487   EXPECT_TRUE(base::PathExists(initial_path));
488   base::FilePath path_1(initial_path.InsertBeforeExtensionASCII("_1"));
489 
490   ASSERT_FALSE(base::PathExists(path_1));
491   static const char file_data[] = "xyzzy";
492   ASSERT_EQ(static_cast<int>(sizeof(file_data)),
493             base::WriteFile(path_1, file_data, sizeof(file_data)));
494   ASSERT_TRUE(base::PathExists(path_1));
495 
496   base::FilePath new_path;
497   EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
498             RenameAndAnnotate(path_1, &new_path));
499   EXPECT_EQ(path_1.value(), new_path.value());
500 
501   std::string file_contents;
502   ASSERT_TRUE(base::ReadFileToString(new_path, &file_contents));
503   EXPECT_NE(std::string(file_data), file_contents);
504 
505   FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
506   loop_.RunUntilIdle();
507   DestroyDownloadFile(0);
508 }
509 
510 // Test to make sure the rename uniquifies if we aren't overwriting
511 // and there's a file where we're aiming. As above, not a
512 // DownloadFileTestWithRename test because this only applies to
513 // RenameAndUniquify().
TEST_F(DownloadFileTest,RenameUniquifies)514 TEST_F(DownloadFileTest, RenameUniquifies) {
515   ASSERT_TRUE(CreateDownloadFile(0, true));
516   base::FilePath initial_path(download_file_->FullPath());
517   EXPECT_TRUE(base::PathExists(initial_path));
518   base::FilePath path_1(initial_path.InsertBeforeExtensionASCII("_1"));
519   base::FilePath path_1_suffixed(path_1.InsertBeforeExtensionASCII(" (1)"));
520 
521   ASSERT_FALSE(base::PathExists(path_1));
522   static const char file_data[] = "xyzzy";
523   ASSERT_EQ(static_cast<int>(sizeof(file_data)),
524             base::WriteFile(path_1, file_data, sizeof(file_data)));
525   ASSERT_TRUE(base::PathExists(path_1));
526 
527   EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, RenameAndUniquify(path_1, NULL));
528   EXPECT_TRUE(base::PathExists(path_1_suffixed));
529 
530   FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
531   loop_.RunUntilIdle();
532   DestroyDownloadFile(0);
533 }
534 
535 // Test that RenameAndUniquify doesn't try to uniquify in the case where the
536 // target filename is the same as the current filename.
TEST_F(DownloadFileTest,RenameRecognizesSelfConflict)537 TEST_F(DownloadFileTest, RenameRecognizesSelfConflict) {
538   ASSERT_TRUE(CreateDownloadFile(0, true));
539   base::FilePath initial_path(download_file_->FullPath());
540   EXPECT_TRUE(base::PathExists(initial_path));
541 
542   base::FilePath new_path;
543   EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
544             RenameAndUniquify(initial_path, &new_path));
545   EXPECT_TRUE(base::PathExists(initial_path));
546 
547   FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
548   loop_.RunUntilIdle();
549   DestroyDownloadFile(0);
550   EXPECT_EQ(initial_path.value(), new_path.value());
551 }
552 
553 // Test to make sure we get the proper error on failure.
TEST_P(DownloadFileTestWithRename,RenameError)554 TEST_P(DownloadFileTestWithRename, RenameError) {
555   ASSERT_TRUE(CreateDownloadFile(0, true));
556   base::FilePath initial_path(download_file_->FullPath());
557 
558   // Create a subdirectory.
559   base::FilePath target_dir(
560       initial_path.DirName().Append(FILE_PATH_LITERAL("TargetDir")));
561   ASSERT_FALSE(base::DirectoryExists(target_dir));
562   ASSERT_TRUE(base::CreateDirectory(target_dir));
563   base::FilePath target_path(target_dir.Append(initial_path.BaseName()));
564 
565   // Targets
566   base::FilePath target_path_suffixed(
567       target_path.InsertBeforeExtensionASCII(" (1)"));
568   ASSERT_FALSE(base::PathExists(target_path));
569   ASSERT_FALSE(base::PathExists(target_path_suffixed));
570 
571   // Make the directory unwritable and try to rename within it.
572   {
573     base::FilePermissionRestorer restorer(target_dir);
574     ASSERT_TRUE(base::MakeFileUnwritable(target_dir));
575 
576     // Expect nulling out of further processing.
577     EXPECT_CALL(*input_stream_, RegisterCallback(IsNullCallback()));
578     ExpectPermissionError(InvokeSelectedRenameMethod(target_path, NULL));
579     EXPECT_FALSE(base::PathExists(target_path_suffixed));
580   }
581 
582   FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
583   loop_.RunUntilIdle();
584   DestroyDownloadFile(0);
585 }
586 
587 namespace {
588 
TestRenameCompletionCallback(const base::Closure & closure,bool * did_run_callback,DownloadInterruptReason interrupt_reason,const base::FilePath & new_path)589 void TestRenameCompletionCallback(const base::Closure& closure,
590                                   bool* did_run_callback,
591                                   DownloadInterruptReason interrupt_reason,
592                                   const base::FilePath& new_path) {
593   EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason);
594   *did_run_callback = true;
595   closure.Run();
596 }
597 
598 }  // namespace
599 
600 // Test that the retry logic works. This test assumes that DownloadFileImpl will
601 // post tasks to the current message loop (acting as the FILE thread)
602 // asynchronously to retry the renames. We will stuff RunLoop::QuitClosures()
603 // in between the retry tasks to stagger them and then allow the rename to
604 // succeed.
605 //
606 // Note that there is only one queue of tasks to run, and that is in the tests'
607 // base::MessageLoop::current(). Each RunLoop processes that queue until it sees
608 // a QuitClosure() targeted at itself, at which point it stops processing.
TEST_P(DownloadFileTestWithRename,RenameWithErrorRetry)609 TEST_P(DownloadFileTestWithRename, RenameWithErrorRetry) {
610   ASSERT_TRUE(CreateDownloadFile(0, true));
611   base::FilePath initial_path(download_file_->FullPath());
612 
613   // Create a subdirectory.
614   base::FilePath target_dir(
615       initial_path.DirName().Append(FILE_PATH_LITERAL("TargetDir")));
616   ASSERT_FALSE(base::DirectoryExists(target_dir));
617   ASSERT_TRUE(base::CreateDirectory(target_dir));
618   base::FilePath target_path(target_dir.Append(initial_path.BaseName()));
619 
620   bool did_run_callback = false;
621 
622   // Each RunLoop can be used the run the MessageLoop until the corresponding
623   // QuitClosure() is run. This one is used to produce the QuitClosure() that
624   // will be run when the entire rename operation is complete.
625   base::RunLoop succeeding_run;
626   {
627     // (Scope for the base::File or base::FilePermissionRestorer below.)
628 #if defined(OS_WIN)
629     // On Windows we test with an actual transient error, a sharing violation.
630     // The rename will fail because we are holding the file open for READ. On
631     // Posix this doesn't cause a failure.
632     base::File locked_file(initial_path,
633                            base::File::FLAG_OPEN | base::File::FLAG_READ);
634     ASSERT_TRUE(locked_file.IsValid());
635 #else
636     // Simulate a transient failure by revoking write permission for target_dir.
637     // The TestDownloadFileImpl class treats this error as transient even though
638     // DownloadFileImpl itself doesn't.
639     base::FilePermissionRestorer restore_permissions_for(target_dir);
640     ASSERT_TRUE(base::MakeFileUnwritable(target_dir));
641 #endif
642 
643     // The Rename() should fail here and enqueue a retry task without invoking
644     // the completion callback.
645     ((*download_file_).*GetParam())(target_path,
646                                     base::Bind(&TestRenameCompletionCallback,
647                                                succeeding_run.QuitClosure(),
648                                                &did_run_callback));
649     EXPECT_FALSE(did_run_callback);
650 
651     base::RunLoop first_failing_run;
652     // Queue the QuitClosure() on the MessageLoop now. Any tasks queued by the
653     // Rename() will be in front of the QuitClosure(). Running the message loop
654     // now causes the just the first retry task to be run. The rename still
655     // fails, so another retry task would get queued behind the QuitClosure().
656     base::MessageLoop::current()->PostTask(FROM_HERE,
657                                            first_failing_run.QuitClosure());
658     first_failing_run.Run();
659     EXPECT_FALSE(did_run_callback);
660 
661     // Running another loop should have the same effect as the above as long as
662     // kMaxRenameRetries is greater than 2.
663     base::RunLoop second_failing_run;
664     base::MessageLoop::current()->PostTask(FROM_HERE,
665                                            second_failing_run.QuitClosure());
666     second_failing_run.Run();
667     EXPECT_FALSE(did_run_callback);
668   }
669 
670   // This time the QuitClosure from succeeding_run should get executed.
671   succeeding_run.Run();
672   EXPECT_TRUE(did_run_callback);
673 
674   FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
675   loop_.RunUntilIdle();
676   DestroyDownloadFile(0);
677 }
678 
679 // Various tests of the StreamActive method.
TEST_F(DownloadFileTest,StreamEmptySuccess)680 TEST_F(DownloadFileTest, StreamEmptySuccess) {
681   ASSERT_TRUE(CreateDownloadFile(0, true));
682   base::FilePath initial_path(download_file_->FullPath());
683   EXPECT_TRUE(base::PathExists(initial_path));
684 
685   // Test that calling the sink_callback_ on an empty stream shouldn't
686   // do anything.
687   AppendDataToFile(NULL, 0);
688 
689   // Finish the download this way and make sure we see it on the
690   // observer.
691   EXPECT_CALL(*(observer_.get()), DestinationCompleted(_));
692   FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, false);
693   loop_.RunUntilIdle();
694 
695   DestroyDownloadFile(0);
696 }
697 
TEST_F(DownloadFileTest,StreamEmptyError)698 TEST_F(DownloadFileTest, StreamEmptyError) {
699   ASSERT_TRUE(CreateDownloadFile(0, true));
700   base::FilePath initial_path(download_file_->FullPath());
701   EXPECT_TRUE(base::PathExists(initial_path));
702 
703   // Finish the download in error and make sure we see it on the
704   // observer.
705   EXPECT_CALL(*(observer_.get()),
706               DestinationError(
707                   DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED))
708       .WillOnce(InvokeWithoutArgs(
709           this, &DownloadFileTest::ConfirmUpdateDownloadInfo));
710 
711   // If this next EXPECT_CALL fails flakily, it's probably a real failure.
712   // We'll be getting a stream of UpdateDownload calls from the timer, and
713   // the last one may have the correct information even if the failure
714   // doesn't produce an update, as the timer update may have triggered at the
715   // same time.
716   EXPECT_CALL(*(observer_.get()), CurrentUpdateStatus(0, _, _));
717 
718   FinishStream(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, false);
719 
720   loop_.RunUntilIdle();
721 
722   DestroyDownloadFile(0);
723 }
724 
TEST_F(DownloadFileTest,StreamNonEmptySuccess)725 TEST_F(DownloadFileTest, StreamNonEmptySuccess) {
726   ASSERT_TRUE(CreateDownloadFile(0, true));
727   base::FilePath initial_path(download_file_->FullPath());
728   EXPECT_TRUE(base::PathExists(initial_path));
729 
730   const char* chunks1[] = { kTestData1, kTestData2 };
731   ::testing::Sequence s1;
732   SetupDataAppend(chunks1, 2, s1);
733   SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, s1);
734   EXPECT_CALL(*(observer_.get()), DestinationCompleted(_));
735   sink_callback_.Run();
736   VerifyStreamAndSize();
737   loop_.RunUntilIdle();
738   DestroyDownloadFile(0);
739 }
740 
TEST_F(DownloadFileTest,StreamNonEmptyError)741 TEST_F(DownloadFileTest, StreamNonEmptyError) {
742   ASSERT_TRUE(CreateDownloadFile(0, true));
743   base::FilePath initial_path(download_file_->FullPath());
744   EXPECT_TRUE(base::PathExists(initial_path));
745 
746   const char* chunks1[] = { kTestData1, kTestData2 };
747   ::testing::Sequence s1;
748   SetupDataAppend(chunks1, 2, s1);
749   SetupFinishStream(DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED, s1);
750 
751   EXPECT_CALL(*(observer_.get()),
752               DestinationError(
753                   DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED))
754       .WillOnce(InvokeWithoutArgs(
755           this, &DownloadFileTest::ConfirmUpdateDownloadInfo));
756 
757   // If this next EXPECT_CALL fails flakily, it's probably a real failure.
758   // We'll be getting a stream of UpdateDownload calls from the timer, and
759   // the last one may have the correct information even if the failure
760   // doesn't produce an update, as the timer update may have triggered at the
761   // same time.
762   EXPECT_CALL(*(observer_.get()),
763               CurrentUpdateStatus(strlen(kTestData1) + strlen(kTestData2),
764                                   _, _));
765 
766   sink_callback_.Run();
767   loop_.RunUntilIdle();
768   VerifyStreamAndSize();
769   DestroyDownloadFile(0);
770 }
771 
772 // Send some data, wait 3/4s of a second, run the message loop, and
773 // confirm the values the observer received are correct.
TEST_F(DownloadFileTest,ConfirmUpdate)774 TEST_F(DownloadFileTest, ConfirmUpdate) {
775   CreateDownloadFile(0, true);
776 
777   const char* chunks1[] = { kTestData1, kTestData2 };
778   AppendDataToFile(chunks1, 2);
779 
780   // Run the message loops for 750ms and check for results.
781   loop_.PostDelayedTask(FROM_HERE,
782                         base::MessageLoop::QuitClosure(),
783                         base::TimeDelta::FromMilliseconds(750));
784   loop_.Run();
785 
786   EXPECT_EQ(static_cast<int64>(strlen(kTestData1) + strlen(kTestData2)),
787             bytes_);
788   EXPECT_EQ(download_file_->GetHashState(), hash_state_);
789 
790   FinishStream(DOWNLOAD_INTERRUPT_REASON_NONE, true);
791   DestroyDownloadFile(0);
792 }
793 
794 }  // namespace content
795