Home | History | Annotate | Download | only in 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_path.h"
      6 #include "base/files/scoped_temp_dir.h"
      7 #include "base/message_loop/message_loop.h"
      8 #include "base/prefs/pref_service.h"
      9 #include "base/run_loop.h"
     10 #include "chrome/browser/download/chrome_download_manager_delegate.h"
     11 #include "chrome/browser/download/download_prefs.h"
     12 #include "chrome/common/pref_names.h"
     13 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
     14 #include "chrome/test/base/testing_pref_service_syncable.h"
     15 #include "chrome/test/base/testing_profile.h"
     16 #include "content/public/browser/download_interrupt_reasons.h"
     17 #include "content/public/browser/web_contents.h"
     18 #include "content/public/browser/web_contents_delegate.h"
     19 #include "content/public/test/mock_download_item.h"
     20 #include "content/public/test/mock_download_manager.h"
     21 #include "content/public/test/test_browser_thread.h"
     22 #include "content/public/test/test_renderer_host.h"
     23 #include "content/public/test/web_contents_tester.h"
     24 #include "testing/gmock/include/gmock/gmock.h"
     25 #include "testing/gtest/include/gtest/gtest.h"
     26 
     27 using ::testing::AtMost;
     28 using ::testing::Invoke;
     29 using ::testing::Ref;
     30 using ::testing::Return;
     31 using ::testing::ReturnPointee;
     32 using ::testing::ReturnRef;
     33 using ::testing::ReturnRefOfCopy;
     34 using ::testing::SetArgPointee;
     35 using ::testing::WithArg;
     36 using ::testing::_;
     37 using content::DownloadItem;
     38 
     39 namespace {
     40 
     41 class MockWebContentsDelegate : public content::WebContentsDelegate {
     42  public:
     43   virtual ~MockWebContentsDelegate() {}
     44 };
     45 
     46 // Google Mock action that posts a task to the current message loop that invokes
     47 // the first argument of the mocked method as a callback. Said argument must be
     48 // a base::Callback<void(ParamType)>. |result| must be of |ParamType| and is
     49 // bound as that parameter.
     50 // Example:
     51 //   class FooClass {
     52 //    public:
     53 //     virtual void Foo(base::Callback<void(bool)> callback);
     54 //   };
     55 //   ...
     56 //   EXPECT_CALL(mock_fooclass_instance, Foo(callback))
     57 //     .WillOnce(ScheduleCallback(false));
     58 ACTION_P(ScheduleCallback, result) {
     59   base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(arg0, result));
     60 }
     61 
     62 // Similar to ScheduleCallback, but binds 2 arguments.
     63 ACTION_P2(ScheduleCallback2, result0, result1) {
     64   base::MessageLoop::current()->PostTask(
     65       FROM_HERE, base::Bind(arg0, result0, result1));
     66 }
     67 
     68 struct DownloadTarget {
     69   base::FilePath target_path;
     70   base::FilePath intermediate_path;
     71   DownloadItem::TargetDisposition target_disposition;
     72   content::DownloadDangerType danger_type;
     73 };
     74 
     75 // Subclass of the ChromeDownloadManagerDelegate that uses a mock
     76 // DownloadProtectionService.
     77 class TestChromeDownloadManagerDelegate : public ChromeDownloadManagerDelegate {
     78  public:
     79   explicit TestChromeDownloadManagerDelegate(Profile* profile)
     80       : ChromeDownloadManagerDelegate(profile) {
     81   }
     82 
     83   virtual safe_browsing::DownloadProtectionService*
     84       GetDownloadProtectionService() OVERRIDE {
     85     return NULL;
     86   }
     87 
     88   virtual void NotifyExtensions(
     89       content::DownloadItem* download,
     90       const base::FilePath& suggested_virtual_path,
     91       const NotifyExtensionsCallback& callback) OVERRIDE {
     92     callback.Run(base::FilePath(),
     93                  DownloadPathReservationTracker::UNIQUIFY);
     94   }
     95 
     96   virtual void ReserveVirtualPath(
     97       content::DownloadItem* download,
     98       const base::FilePath& virtual_path,
     99       bool create_directory,
    100       DownloadPathReservationTracker::FilenameConflictAction conflict_action,
    101       const DownloadPathReservationTracker::ReservedPathCallback& callback)
    102       OVERRIDE {
    103     // Pretend the path reservation succeeded without any change to
    104     // |target_path|.
    105     base::MessageLoop::current()->PostTask(
    106         FROM_HERE, base::Bind(callback, virtual_path, true));
    107   }
    108 
    109   virtual void PromptUserForDownloadPath(
    110       DownloadItem* download,
    111       const base::FilePath& suggested_path,
    112       const DownloadTargetDeterminerDelegate::FileSelectedCallback& callback)
    113       OVERRIDE {
    114     base::FilePath return_path = MockPromptUserForDownloadPath(download,
    115                                                                suggested_path,
    116                                                                callback);
    117     callback.Run(return_path);
    118   }
    119 
    120   MOCK_METHOD3(
    121       MockPromptUserForDownloadPath,
    122       base::FilePath(
    123           content::DownloadItem*,
    124           const base::FilePath&,
    125           const DownloadTargetDeterminerDelegate::FileSelectedCallback&));
    126 
    127  private:
    128   ~TestChromeDownloadManagerDelegate() {}
    129 };
    130 
    131 class ChromeDownloadManagerDelegateTest :
    132       public ChromeRenderViewHostTestHarness {
    133  public:
    134   ChromeDownloadManagerDelegateTest();
    135 
    136   // ::testing::Test
    137   virtual void SetUp() OVERRIDE;
    138   virtual void TearDown() OVERRIDE;
    139 
    140   // Verifies and clears test expectations for |delegate_| and
    141   // |download_manager_|.
    142   void VerifyAndClearExpectations();
    143 
    144   // Creates MockDownloadItem and sets up default expectations.
    145   content::MockDownloadItem* CreateActiveDownloadItem(int32 id);
    146 
    147   // Given the relative path |path|, returns the full path under the temporary
    148   // downloads directory.
    149   base::FilePath GetPathInDownloadDir(const char* path);
    150 
    151   // Set the kDownloadDefaultDirectory user preference to |path|.
    152   void SetDefaultDownloadPath(const base::FilePath& path);
    153 
    154   void DetermineDownloadTarget(DownloadItem* download,
    155                                DownloadTarget* result);
    156 
    157   const base::FilePath& default_download_path() const;
    158   TestChromeDownloadManagerDelegate* delegate();
    159   content::MockDownloadManager* download_manager();
    160   DownloadPrefs* download_prefs();
    161 
    162  private:
    163   void OnDownloadTargetDone(DownloadTarget* result,
    164                             const base::FilePath& target_path,
    165                             DownloadItem::TargetDisposition disposition,
    166                             content::DownloadDangerType danger_type,
    167                             const base::FilePath& intermediate_path);
    168 
    169   TestingPrefServiceSyncable* pref_service_;
    170   base::ScopedTempDir test_download_dir_;
    171   scoped_ptr<content::MockDownloadManager> download_manager_;
    172   scoped_refptr<TestChromeDownloadManagerDelegate> delegate_;
    173   MockWebContentsDelegate web_contents_delegate_;
    174 };
    175 
    176 ChromeDownloadManagerDelegateTest::ChromeDownloadManagerDelegateTest()
    177     : download_manager_(new ::testing::NiceMock<content::MockDownloadManager>) {
    178 }
    179 
    180 void ChromeDownloadManagerDelegateTest::SetUp() {
    181   ChromeRenderViewHostTestHarness::SetUp();
    182 
    183   CHECK(profile());
    184   delegate_ = new TestChromeDownloadManagerDelegate(profile());
    185   delegate_->SetDownloadManager(download_manager_.get());
    186   pref_service_ = profile()->GetTestingPrefService();
    187   web_contents()->SetDelegate(&web_contents_delegate_);
    188 
    189   ASSERT_TRUE(test_download_dir_.CreateUniqueTempDir());
    190   SetDefaultDownloadPath(test_download_dir_.path());
    191 }
    192 
    193 void ChromeDownloadManagerDelegateTest::TearDown() {
    194   base::RunLoop().RunUntilIdle();
    195   delegate_->Shutdown();
    196   ChromeRenderViewHostTestHarness::TearDown();
    197 }
    198 
    199 void ChromeDownloadManagerDelegateTest::VerifyAndClearExpectations() {
    200   ::testing::Mock::VerifyAndClearExpectations(delegate_.get());
    201 }
    202 
    203 content::MockDownloadItem*
    204     ChromeDownloadManagerDelegateTest::CreateActiveDownloadItem(int32 id) {
    205   content::MockDownloadItem* item =
    206       new ::testing::NiceMock<content::MockDownloadItem>();
    207   ON_CALL(*item, GetBrowserContext())
    208       .WillByDefault(Return(profile()));
    209   ON_CALL(*item, GetDangerType())
    210       .WillByDefault(Return(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS));
    211   ON_CALL(*item, GetForcedFilePath())
    212       .WillByDefault(ReturnRefOfCopy(base::FilePath()));
    213   ON_CALL(*item, GetFullPath())
    214       .WillByDefault(ReturnRefOfCopy(base::FilePath()));
    215   ON_CALL(*item, GetHash())
    216       .WillByDefault(ReturnRefOfCopy(std::string()));
    217   ON_CALL(*item, GetId())
    218       .WillByDefault(Return(id));
    219   ON_CALL(*item, GetLastReason())
    220       .WillByDefault(Return(content::DOWNLOAD_INTERRUPT_REASON_NONE));
    221   ON_CALL(*item, GetReferrerUrl())
    222       .WillByDefault(ReturnRefOfCopy(GURL()));
    223   ON_CALL(*item, GetState())
    224       .WillByDefault(Return(DownloadItem::IN_PROGRESS));
    225   ON_CALL(*item, GetTargetFilePath())
    226       .WillByDefault(ReturnRefOfCopy(base::FilePath()));
    227   ON_CALL(*item, GetTransitionType())
    228       .WillByDefault(Return(content::PAGE_TRANSITION_LINK));
    229   ON_CALL(*item, GetWebContents())
    230       .WillByDefault(Return(web_contents()));
    231   ON_CALL(*item, HasUserGesture())
    232       .WillByDefault(Return(false));
    233   ON_CALL(*item, IsDangerous())
    234       .WillByDefault(Return(false));
    235   ON_CALL(*item, IsTemporary())
    236       .WillByDefault(Return(false));
    237   EXPECT_CALL(*download_manager_, GetDownload(id))
    238       .WillRepeatedly(Return(item));
    239   return item;
    240 }
    241 
    242 base::FilePath ChromeDownloadManagerDelegateTest::GetPathInDownloadDir(
    243     const char* relative_path) {
    244   base::FilePath full_path =
    245       test_download_dir_.path().AppendASCII(relative_path);
    246   return full_path.NormalizePathSeparators();
    247 }
    248 
    249 void ChromeDownloadManagerDelegateTest::SetDefaultDownloadPath(
    250     const base::FilePath& path) {
    251   pref_service_->SetFilePath(prefs::kDownloadDefaultDirectory, path);
    252   pref_service_->SetFilePath(prefs::kSaveFileDefaultDirectory, path);
    253 }
    254 
    255 void ChromeDownloadManagerDelegateTest::DetermineDownloadTarget(
    256     DownloadItem* download_item,
    257     DownloadTarget* result) {
    258   base::WeakPtrFactory<ChromeDownloadManagerDelegateTest> factory(this);
    259   delegate()->DetermineDownloadTarget(
    260       download_item,
    261       base::Bind(&ChromeDownloadManagerDelegateTest::OnDownloadTargetDone,
    262                  factory.GetWeakPtr(), base::Unretained(result)));
    263   base::RunLoop loop_runner;
    264   loop_runner.RunUntilIdle();
    265 }
    266 
    267 void ChromeDownloadManagerDelegateTest::OnDownloadTargetDone(
    268     DownloadTarget* result,
    269     const base::FilePath& target_path,
    270     DownloadItem::TargetDisposition target_disposition,
    271     content::DownloadDangerType danger_type,
    272     const base::FilePath& intermediate_path) {
    273   result->target_path = target_path;
    274   result->intermediate_path = intermediate_path;
    275   result->target_disposition = target_disposition;
    276   result->danger_type = danger_type;
    277 }
    278 
    279 const base::FilePath& ChromeDownloadManagerDelegateTest::default_download_path()
    280     const {
    281   return test_download_dir_.path();
    282 }
    283 
    284 TestChromeDownloadManagerDelegate*
    285     ChromeDownloadManagerDelegateTest::delegate() {
    286   return delegate_.get();
    287 }
    288 
    289 content::MockDownloadManager*
    290     ChromeDownloadManagerDelegateTest::download_manager() {
    291   return download_manager_.get();
    292 }
    293 
    294 DownloadPrefs* ChromeDownloadManagerDelegateTest::download_prefs() {
    295   return delegate_->download_prefs();
    296 }
    297 
    298 }  // namespace
    299 
    300 TEST_F(ChromeDownloadManagerDelegateTest, StartDownload_LastSavePath) {
    301   GURL download_url("http://example.com/foo.txt");
    302 
    303   scoped_ptr<content::MockDownloadItem> save_as_download(
    304       CreateActiveDownloadItem(0));
    305   EXPECT_CALL(*save_as_download, GetURL())
    306       .Times(::testing::AnyNumber())
    307       .WillRepeatedly(ReturnRef(download_url));
    308   EXPECT_CALL(*save_as_download, GetTargetDisposition())
    309       .Times(::testing::AnyNumber())
    310       .WillRepeatedly(Return(DownloadItem::TARGET_DISPOSITION_PROMPT));
    311 
    312   scoped_ptr<content::MockDownloadItem> automatic_download(
    313       CreateActiveDownloadItem(1));
    314   EXPECT_CALL(*automatic_download, GetURL())
    315       .Times(::testing::AnyNumber())
    316       .WillRepeatedly(ReturnRef(download_url));
    317   EXPECT_CALL(*automatic_download, GetTargetDisposition())
    318       .Times(::testing::AnyNumber())
    319       .WillRepeatedly(Return(DownloadItem::TARGET_DISPOSITION_OVERWRITE));
    320 
    321   {
    322     // When the prompt is displayed for the first download, the user selects a
    323     // path in a different directory.
    324     DownloadTarget result;
    325     base::FilePath expected_prompt_path(GetPathInDownloadDir("foo.txt"));
    326     base::FilePath user_selected_path(GetPathInDownloadDir("bar/baz.txt"));
    327     EXPECT_CALL(*delegate(),
    328                 MockPromptUserForDownloadPath(save_as_download.get(),
    329                                               expected_prompt_path, _))
    330         .WillOnce(Return(user_selected_path));
    331     DetermineDownloadTarget(save_as_download.get(), &result);
    332     EXPECT_EQ(user_selected_path, result.target_path);
    333     VerifyAndClearExpectations();
    334   }
    335 
    336   {
    337     // The prompt path for the second download is the user selected directroy
    338     // from the previous download.
    339     DownloadTarget result;
    340     base::FilePath expected_prompt_path(GetPathInDownloadDir("bar/foo.txt"));
    341     EXPECT_CALL(*delegate(),
    342                 MockPromptUserForDownloadPath(save_as_download.get(),
    343                                               expected_prompt_path, _))
    344         .WillOnce(Return(base::FilePath()));
    345     DetermineDownloadTarget(save_as_download.get(), &result);
    346     VerifyAndClearExpectations();
    347   }
    348 
    349   {
    350     // Start an automatic download. This one should get the default download
    351     // path since the last download path only affects Save As downloads.
    352     DownloadTarget result;
    353     base::FilePath expected_path(GetPathInDownloadDir("foo.txt"));
    354     DetermineDownloadTarget(automatic_download.get(), &result);
    355     EXPECT_EQ(expected_path, result.target_path);
    356     VerifyAndClearExpectations();
    357   }
    358 
    359   {
    360     // The prompt path for the next download should be the default.
    361     download_prefs()->SetSaveFilePath(download_prefs()->DownloadPath());
    362     DownloadTarget result;
    363     base::FilePath expected_prompt_path(GetPathInDownloadDir("foo.txt"));
    364     EXPECT_CALL(*delegate(),
    365                 MockPromptUserForDownloadPath(save_as_download.get(),
    366                                               expected_prompt_path, _))
    367         .WillOnce(Return(base::FilePath()));
    368     DetermineDownloadTarget(save_as_download.get(), &result);
    369     VerifyAndClearExpectations();
    370   }
    371 }
    372