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