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