Home | History | Annotate | Download | only in download
      1 // Copyright 2013 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/bind.h"
      6 #include "base/callback.h"
      7 #include "base/files/file_path.h"
      8 #include "base/memory/ref_counted.h"
      9 #include "base/memory/scoped_ptr.h"
     10 #include "base/memory/weak_ptr.h"
     11 #include "base/observer_list.h"
     12 #include "chrome/browser/download/download_history.h"
     13 #include "chrome/browser/download/download_service.h"
     14 #include "chrome/browser/download/download_service_factory.h"
     15 #include "chrome/browser/download/download_ui_controller.h"
     16 #include "chrome/browser/history/download_row.h"
     17 #include "chrome/browser/profiles/profile.h"
     18 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
     19 #include "content/public/test/mock_download_item.h"
     20 #include "content/public/test/mock_download_manager.h"
     21 #include "testing/gmock/include/gmock/gmock.h"
     22 #include "testing/gtest/include/gtest/gtest.h"
     23 
     24 using content::MockDownloadItem;
     25 using content::MockDownloadManager;
     26 using testing::AnyNumber;
     27 using testing::Assign;
     28 using testing::Return;
     29 using testing::ReturnRefOfCopy;
     30 using testing::SaveArg;
     31 using testing::_;
     32 
     33 namespace {
     34 
     35 // A DownloadUIController::Delegate that stores the DownloadItem* for the last
     36 // download that was sent to the UI.
     37 class TestDelegate : public DownloadUIController::Delegate {
     38  public:
     39   explicit TestDelegate(base::WeakPtr<content::DownloadItem*> receiver);
     40   virtual ~TestDelegate() {}
     41 
     42  private:
     43   virtual void OnNewDownloadReady(content::DownloadItem* item) OVERRIDE;
     44 
     45   base::WeakPtr<content::DownloadItem*> receiver_;
     46 };
     47 
     48 TestDelegate::TestDelegate(base::WeakPtr<content::DownloadItem*> receiver)
     49     : receiver_(receiver) {
     50 }
     51 
     52 void TestDelegate::OnNewDownloadReady(content::DownloadItem* item) {
     53   if (receiver_.get())
     54     *receiver_ = item;
     55 }
     56 
     57 // A DownloadService that returns a custom DownloadHistory.
     58 class TestDownloadService : public DownloadService {
     59  public:
     60   explicit TestDownloadService(Profile* profile);
     61   virtual ~TestDownloadService();
     62 
     63   void set_download_history(scoped_ptr<DownloadHistory> download_history) {
     64     download_history_.swap(download_history);
     65   }
     66   virtual DownloadHistory* GetDownloadHistory() OVERRIDE;
     67 
     68  private:
     69   scoped_ptr<DownloadHistory> download_history_;
     70 };
     71 
     72 TestDownloadService::TestDownloadService(Profile* profile)
     73     : DownloadService(profile) {
     74 }
     75 
     76 TestDownloadService::~TestDownloadService() {
     77 }
     78 
     79 DownloadHistory* TestDownloadService::GetDownloadHistory() {
     80   return download_history_.get();
     81 }
     82 
     83 // The test fixture:
     84 class DownloadUIControllerTest : public ChromeRenderViewHostTestHarness {
     85  public:
     86   DownloadUIControllerTest();
     87 
     88  protected:
     89   // testing::Test
     90   virtual void SetUp() OVERRIDE;
     91 
     92   // Returns a TestDelegate. Invoking OnNewDownloadReady on the returned
     93   // delegate results in the DownloadItem* being stored in |notified_item_|.
     94   scoped_ptr<DownloadUIController::Delegate> GetTestDelegate();
     95 
     96   MockDownloadManager* manager() { return manager_.get(); }
     97 
     98   // Returns the DownloadManager::Observer registered by a test case. This is
     99   // the DownloadUIController's observer for all current test cases.
    100   content::DownloadManager::Observer* manager_observer() {
    101     return manager_observer_;
    102   }
    103 
    104   // The most recent DownloadItem that was passed into OnNewDownloadReady().
    105   content::DownloadItem* notified_item() { return notified_item_; }
    106 
    107   // DownloadHistory performs a query of existing downloads when it is first
    108   // instantiated. This method returns the completion callback for that query.
    109   // It can be used to inject history downloads.
    110   const HistoryService::DownloadQueryCallback& history_query_callback() const {
    111     return history_adapter_->download_query_callback_;
    112   }
    113 
    114   // DownloadManager::Observer registered by DownloadHistory.
    115   content::DownloadManager::Observer* download_history_manager_observer() {
    116     return download_history_manager_observer_;
    117   }
    118 
    119   scoped_ptr<MockDownloadItem> CreateMockInProgressDownload();
    120 
    121  private:
    122   // A private history adapter that stores the DownloadQueryCallback when
    123   // QueryDownloads is called.
    124   class HistoryAdapter : public DownloadHistory::HistoryAdapter {
    125    public:
    126     HistoryAdapter() : DownloadHistory::HistoryAdapter(NULL) {}
    127     HistoryService::DownloadQueryCallback download_query_callback_;
    128 
    129    private:
    130     virtual void QueryDownloads(
    131         const HistoryService::DownloadQueryCallback& callback) OVERRIDE {
    132       download_query_callback_ = callback;
    133     }
    134   };
    135 
    136   // Constructs and returns a TestDownloadService.
    137   static KeyedService* TestingDownloadServiceFactory(
    138       content::BrowserContext* browser_context);
    139 
    140   scoped_ptr<MockDownloadManager> manager_;
    141   content::DownloadManager::Observer* download_history_manager_observer_;
    142   content::DownloadManager::Observer* manager_observer_;
    143   content::DownloadItem* notified_item_;
    144   base::WeakPtrFactory<content::DownloadItem*> notified_item_receiver_factory_;
    145 
    146   HistoryAdapter* history_adapter_;
    147 };
    148 
    149 // static
    150 KeyedService* DownloadUIControllerTest::TestingDownloadServiceFactory(
    151     content::BrowserContext* browser_context) {
    152   return new TestDownloadService(Profile::FromBrowserContext(browser_context));
    153 }
    154 
    155 DownloadUIControllerTest::DownloadUIControllerTest()
    156     : download_history_manager_observer_(NULL),
    157       manager_observer_(NULL),
    158       notified_item_(NULL),
    159       notified_item_receiver_factory_(&notified_item_) {
    160 }
    161 
    162 void DownloadUIControllerTest::SetUp() {
    163   ChromeRenderViewHostTestHarness::SetUp();
    164 
    165   manager_.reset(new testing::StrictMock<MockDownloadManager>());
    166   EXPECT_CALL(*manager_, AddObserver(_))
    167       .WillOnce(SaveArg<0>(&download_history_manager_observer_));
    168   EXPECT_CALL(*manager_,
    169               RemoveObserver(testing::Eq(
    170                   testing::ByRef(download_history_manager_observer_))))
    171       .WillOnce(testing::Assign(
    172           &download_history_manager_observer_,
    173           static_cast<content::DownloadManager::Observer*>(NULL)));
    174   EXPECT_CALL(*manager_, GetAllDownloads(_)).Times(AnyNumber());
    175 
    176   scoped_ptr<HistoryAdapter> history_adapter(new HistoryAdapter);
    177   history_adapter_ = history_adapter.get();
    178   scoped_ptr<DownloadHistory> download_history(new DownloadHistory(
    179       manager_.get(),
    180       history_adapter.PassAs<DownloadHistory::HistoryAdapter>()));
    181   ASSERT_TRUE(download_history_manager_observer_);
    182 
    183   EXPECT_CALL(*manager_, AddObserver(_))
    184       .WillOnce(SaveArg<0>(&manager_observer_));
    185   EXPECT_CALL(*manager_,
    186               RemoveObserver(testing::Eq(testing::ByRef(manager_observer_))))
    187       .WillOnce(testing::Assign(
    188           &manager_observer_,
    189           static_cast<content::DownloadManager::Observer*>(NULL)));
    190   TestDownloadService* download_service = static_cast<TestDownloadService*>(
    191       DownloadServiceFactory::GetInstance()->SetTestingFactoryAndUse(
    192           browser_context(), &TestingDownloadServiceFactory));
    193   ASSERT_TRUE(download_service);
    194   download_service->set_download_history(download_history.Pass());
    195 }
    196 
    197 scoped_ptr<MockDownloadItem>
    198 DownloadUIControllerTest::CreateMockInProgressDownload() {
    199   scoped_ptr<MockDownloadItem> item(
    200       new testing::StrictMock<MockDownloadItem>());
    201   EXPECT_CALL(*item, GetBrowserContext())
    202       .WillRepeatedly(Return(browser_context()));
    203   EXPECT_CALL(*item, GetId()).WillRepeatedly(Return(1));
    204   EXPECT_CALL(*item, GetTargetFilePath()).WillRepeatedly(
    205       ReturnRefOfCopy(base::FilePath(FILE_PATH_LITERAL("foo"))));
    206   EXPECT_CALL(*item, GetFullPath()).WillRepeatedly(
    207       ReturnRefOfCopy(base::FilePath(FILE_PATH_LITERAL("foo"))));
    208   EXPECT_CALL(*item, GetState())
    209       .WillRepeatedly(Return(content::DownloadItem::IN_PROGRESS));
    210   EXPECT_CALL(*item, GetUrlChain())
    211       .WillRepeatedly(testing::ReturnRefOfCopy(std::vector<GURL>()));
    212   EXPECT_CALL(*item, GetReferrerUrl())
    213       .WillRepeatedly(testing::ReturnRefOfCopy(GURL()));
    214   EXPECT_CALL(*item, GetStartTime()).WillRepeatedly(Return(base::Time()));
    215   EXPECT_CALL(*item, GetEndTime()).WillRepeatedly(Return(base::Time()));
    216   EXPECT_CALL(*item, GetETag()).WillRepeatedly(ReturnRefOfCopy(std::string()));
    217   EXPECT_CALL(*item, GetLastModifiedTime())
    218       .WillRepeatedly(ReturnRefOfCopy(std::string()));
    219   EXPECT_CALL(*item, GetDangerType())
    220       .WillRepeatedly(Return(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS));
    221   EXPECT_CALL(*item, GetLastReason())
    222       .WillRepeatedly(Return(content::DOWNLOAD_INTERRUPT_REASON_NONE));
    223   EXPECT_CALL(*item, GetReceivedBytes()).WillRepeatedly(Return(0));
    224   EXPECT_CALL(*item, GetTotalBytes()).WillRepeatedly(Return(0));
    225   EXPECT_CALL(*item, GetTargetDisposition()).WillRepeatedly(
    226       Return(content::DownloadItem::TARGET_DISPOSITION_OVERWRITE));
    227   EXPECT_CALL(*item, GetOpened()).WillRepeatedly(Return(false));
    228   EXPECT_CALL(*item, GetMimeType()).WillRepeatedly(Return(std::string()));
    229   EXPECT_CALL(*item, GetURL()).WillRepeatedly(testing::ReturnRefOfCopy(GURL()));
    230   EXPECT_CALL(*item, IsTemporary()).WillRepeatedly(Return(false));
    231   return item.Pass();
    232 }
    233 
    234 scoped_ptr<DownloadUIController::Delegate>
    235 DownloadUIControllerTest::GetTestDelegate() {
    236   scoped_ptr<DownloadUIController::Delegate> delegate(
    237       new TestDelegate(notified_item_receiver_factory_.GetWeakPtr()));
    238   return delegate.Pass();
    239 }
    240 
    241 // New downloads should be presented to the UI when GetTargetFilePath() returns
    242 // a non-empty path.  I.e. once the download target has been determined.
    243 TEST_F(DownloadUIControllerTest, DownloadUIController_NotifyBasic) {
    244   scoped_ptr<MockDownloadItem> item(CreateMockInProgressDownload());
    245   DownloadUIController controller(manager(), GetTestDelegate());
    246   EXPECT_CALL(*item, GetTargetFilePath())
    247       .WillOnce(ReturnRefOfCopy(base::FilePath()));
    248 
    249   ASSERT_TRUE(manager_observer());
    250   manager_observer()->OnDownloadCreated(manager(), item.get());
    251 
    252   // The destination for the download hasn't been determined yet. It should not
    253   // be displayed.
    254   EXPECT_FALSE(notified_item());
    255 
    256   // Once the destination has been determined, then it should be displayed.
    257   EXPECT_CALL(*item, GetTargetFilePath())
    258       .WillOnce(ReturnRefOfCopy(base::FilePath(FILE_PATH_LITERAL("foo"))));
    259   item->NotifyObserversDownloadUpdated();
    260 
    261   EXPECT_EQ(static_cast<content::DownloadItem*>(item.get()), notified_item());
    262 }
    263 
    264 // A download that's created in an interrupted state should also be displayed.
    265 TEST_F(DownloadUIControllerTest, DownloadUIController_NotifyBasic_Interrupted) {
    266   scoped_ptr<MockDownloadItem> item = CreateMockInProgressDownload();
    267   DownloadUIController controller(manager(), GetTestDelegate());
    268   EXPECT_CALL(*item, GetState())
    269       .WillRepeatedly(Return(content::DownloadItem::INTERRUPTED));
    270 
    271   ASSERT_TRUE(manager_observer());
    272   manager_observer()->OnDownloadCreated(manager(), item.get());
    273   EXPECT_EQ(static_cast<content::DownloadItem*>(item.get()), notified_item());
    274 }
    275 
    276 // Downloads that have a target path on creation and are in the IN_PROGRESS
    277 // state should be displayed in the UI immediately without requiring an
    278 // additional OnDownloadUpdated() notification.
    279 TEST_F(DownloadUIControllerTest, DownloadUIController_NotifyReadyOnCreate) {
    280   scoped_ptr<MockDownloadItem> item(CreateMockInProgressDownload());
    281   DownloadUIController controller(manager(), GetTestDelegate());
    282 
    283   ASSERT_TRUE(manager_observer());
    284   manager_observer()->OnDownloadCreated(manager(), item.get());
    285   EXPECT_EQ(static_cast<content::DownloadItem*>(item.get()), notified_item());
    286 }
    287 
    288 // The UI shouldn't be notified of downloads that were restored from history.
    289 TEST_F(DownloadUIControllerTest, DownloadUIController_HistoryDownload) {
    290   DownloadUIController controller(manager(), GetTestDelegate());
    291   // DownloadHistory should already have been created. It performs a query of
    292   // existing downloads upon creation. We'll use the callback to inject a
    293   // history download.
    294   ASSERT_FALSE(history_query_callback().is_null());
    295 
    296   // download_history_manager_observer is the DownloadManager::Observer
    297   // registered by the DownloadHistory. DownloadHistory relies on the
    298   // OnDownloadCreated notification to mark a download as having been restored
    299   // from history.
    300   ASSERT_TRUE(download_history_manager_observer());
    301 
    302   scoped_ptr<std::vector<history::DownloadRow> > history_downloads;
    303   history_downloads.reset(new std::vector<history::DownloadRow>());
    304   history_downloads->push_back(history::DownloadRow());
    305   history_downloads->front().id = 1;
    306 
    307   std::vector<GURL> url_chain;
    308   GURL url;
    309   scoped_ptr<MockDownloadItem> item = CreateMockInProgressDownload();
    310 
    311   EXPECT_CALL(*item, GetOriginalMimeType());
    312   EXPECT_CALL(*manager(), CheckForHistoryFilesRemoval());
    313 
    314   {
    315     testing::InSequence s;
    316     testing::MockFunction<void()> mock_function;
    317     // DownloadHistory will immediately try to create a download using the info
    318     // we push through the query callback. When DownloadManager::CreateDownload
    319     // is called, we need to first invoke the OnDownloadCreated callback for
    320     // DownloadHistory before returning the DownloadItem since that's the
    321     // sequence of events expected by DownloadHistory.
    322     base::Closure history_on_created_callback =
    323         base::Bind(&content::DownloadManager::Observer::OnDownloadCreated,
    324                    base::Unretained(download_history_manager_observer()),
    325                    manager(),
    326                    item.get());
    327     EXPECT_CALL(*manager(), MockCreateDownloadItem(_)).WillOnce(
    328         testing::DoAll(testing::InvokeWithoutArgs(&history_on_created_callback,
    329                                                   &base::Closure::Run),
    330                        Return(item.get())));
    331     EXPECT_CALL(mock_function, Call());
    332 
    333     history_query_callback().Run(history_downloads.Pass());
    334     mock_function.Call();
    335   }
    336 
    337   // Now pass along the notification to the OnDownloadCreated observer from
    338   // DownloadUIController. It should ignore the download since it's marked as
    339   // being restored from history.
    340   ASSERT_TRUE(manager_observer());
    341   manager_observer()->OnDownloadCreated(manager(), item.get());
    342 
    343   // Finally, the expectation we've been waiting for:
    344   EXPECT_FALSE(notified_item());
    345 }
    346 
    347 } // namespace
    348