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
26using ::testing::_;
27using ::testing::AnyNumber;
28using ::testing::DoAll;
29using ::testing::InSequence;
30using ::testing::Return;
31using ::testing::SetArgPointee;
32using ::testing::StrictMock;
33
34namespace content {
35namespace {
36
37class MockByteStreamReader : public ByteStreamReader {
38 public:
39  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
49class 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
62MATCHER(IsNullCallback, "") { return (arg.is_null()); }
63
64typedef 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.
70class 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) {}
88
89 protected:
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.
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
108class 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
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
130  virtual ~DownloadFileTest() {
131  }
132
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
140  void ConfirmUpdateDownloadInfo() {
141    observer_->CurrentUpdateStatus(bytes_, bytes_per_sec_, hash_state_);
142  }
143
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.
151  void RegisterCallback(const base::Closure& sink_callback) {
152    sink_callback_ = sink_callback;
153  }
154
155  void SetInterruptReasonCallback(const base::Closure& closure,
156                                  DownloadInterruptReason* reason_p,
157                                  DownloadInterruptReason reason) {
158    *reason_p = reason;
159    closure.Run();
160  }
161
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
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.
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
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.
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
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
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
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
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
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:
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:
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.
372class DownloadFileTestWithRename
373    : public DownloadFileTest,
374      public ::testing::WithParamInterface<DownloadFileRenameMethodType> {
375 protected:
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.
389INSTANTIATE_TEST_CASE_P(DownloadFile,
390                        DownloadFileTestWithRename,
391                        ::testing::Values(&DownloadFile::RenameAndAnnotate,
392                                          &DownloadFile::RenameAndUniquify));
393
394const char* DownloadFileTest::kTestData1 =
395    "Let's write some data to the file!\n";
396const char* DownloadFileTest::kTestData2 = "Writing more data.\n";
397const char* DownloadFileTest::kTestData3 = "Final line.";
398const char* DownloadFileTest::kDataHash =
399    "CBF68BF10F8003DB86B31343AFAC8C7175BD03FB5FC905650F8C80AF087443A8";
400
401const uint32 DownloadFileTest::kDummyDownloadId = 23;
402const int DownloadFileTest::kDummyChildId = 3;
403const 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.
407TEST_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.
484TEST_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().
514TEST_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.
537TEST_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.
554TEST_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
587namespace {
588
589void 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.
609TEST_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.
680TEST_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
698TEST_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
725TEST_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
741TEST_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.
774TEST_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