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/memory/scoped_vector.h"
      6 #include "base/memory/weak_ptr.h"
      7 #include "base/message_loop/message_loop.h"
      8 #include "base/stl_util.h"
      9 #include "chrome/browser/download/download_status_updater.h"
     10 #include "content/public/test/mock_download_item.h"
     11 #include "content/public/test/mock_download_manager.h"
     12 #include "content/public/test/test_browser_thread.h"
     13 #include "testing/gmock/include/gmock/gmock.h"
     14 #include "testing/gtest/include/gtest/gtest.h"
     15 
     16 using testing::AtLeast;
     17 using testing::Invoke;
     18 using testing::Mock;
     19 using testing::Return;
     20 using testing::SetArgPointee;
     21 using testing::StrictMock;
     22 using testing::WithArg;
     23 using testing::_;
     24 
     25 class TestDownloadStatusUpdater : public DownloadStatusUpdater {
     26  public:
     27   TestDownloadStatusUpdater() : notification_count_(0),
     28                                 acceptable_notification_item_(NULL) {
     29   }
     30   void SetAcceptableNotificationItem(content::DownloadItem* item) {
     31     acceptable_notification_item_ = item;
     32   }
     33   size_t NotificationCount() {
     34     return notification_count_;
     35   }
     36  protected:
     37   virtual void UpdateAppIconDownloadProgress(
     38       content::DownloadItem* download) OVERRIDE {
     39     ++notification_count_;
     40     if (acceptable_notification_item_)
     41       EXPECT_EQ(acceptable_notification_item_, download);
     42   }
     43  private:
     44   size_t notification_count_;
     45   content::DownloadItem* acceptable_notification_item_;
     46 };
     47 
     48 class DownloadStatusUpdaterTest : public testing::Test {
     49  public:
     50   DownloadStatusUpdaterTest()
     51       : updater_(new TestDownloadStatusUpdater()),
     52         ui_thread_(content::BrowserThread::UI, &loop_) {}
     53 
     54   virtual ~DownloadStatusUpdaterTest() {
     55     for (size_t mgr_idx = 0; mgr_idx < managers_.size(); ++mgr_idx) {
     56       EXPECT_CALL(*Manager(mgr_idx), RemoveObserver(_));
     57       for (size_t item_idx = 0; item_idx < manager_items_[mgr_idx].size();
     58            ++item_idx) {
     59         EXPECT_CALL(*Item(mgr_idx, item_idx), RemoveObserver(_));
     60       }
     61     }
     62 
     63     delete updater_;
     64     updater_ = NULL;
     65     VerifyAndClearExpectations();
     66 
     67     managers_.clear();
     68     for (std::vector<Items>::iterator it = manager_items_.begin();
     69          it != manager_items_.end(); ++it)
     70       STLDeleteContainerPointers(it->begin(), it->end());
     71 
     72     loop_.RunUntilIdle();  // Allow DownloadManager destruction.
     73   }
     74 
     75  protected:
     76   // Attach some number of DownloadManagers to the updater.
     77   void SetupManagers(int manager_count) {
     78     DCHECK_EQ(0U, managers_.size());
     79     for (int i = 0; i < manager_count; ++i) {
     80       content::MockDownloadManager* mgr =
     81           new StrictMock<content::MockDownloadManager>;
     82       managers_.push_back(mgr);
     83     }
     84   }
     85 
     86   void SetObserver(content::DownloadManager::Observer* observer) {
     87     manager_observers_[manager_observer_index_] = observer;
     88   }
     89 
     90   // Hook the specified manager into the updater.
     91   void LinkManager(int i) {
     92     content::MockDownloadManager* mgr = managers_[i];
     93     manager_observer_index_ = i;
     94     while (manager_observers_.size() <= static_cast<size_t>(i)) {
     95       manager_observers_.push_back(NULL);
     96     }
     97     EXPECT_CALL(*mgr, AddObserver(_))
     98         .WillOnce(WithArg<0>(Invoke(
     99             this, &DownloadStatusUpdaterTest::SetObserver)));
    100     updater_->AddManager(mgr);
    101   }
    102 
    103   // Add some number of Download items to a particular manager.
    104   void AddItems(int manager_index, int item_count, int in_progress_count) {
    105     DCHECK_GT(managers_.size(), static_cast<size_t>(manager_index));
    106     content::MockDownloadManager* manager = managers_[manager_index];
    107 
    108     if (manager_items_.size() <= static_cast<size_t>(manager_index))
    109       manager_items_.resize(manager_index+1);
    110 
    111     std::vector<content::DownloadItem*> item_list;
    112     for (int i = 0; i < item_count; ++i) {
    113       content::MockDownloadItem* item =
    114           new StrictMock<content::MockDownloadItem>;
    115       content::DownloadItem::DownloadState state =
    116           i < in_progress_count ? content::DownloadItem::IN_PROGRESS
    117               : content::DownloadItem::CANCELLED;
    118       EXPECT_CALL(*item, GetState()).WillRepeatedly(Return(state));
    119       EXPECT_CALL(*item, AddObserver(_))
    120           .WillOnce(Return());
    121       manager_items_[manager_index].push_back(item);
    122     }
    123     EXPECT_CALL(*manager, GetAllDownloads(_))
    124         .WillRepeatedly(SetArgPointee<0>(manager_items_[manager_index]));
    125   }
    126 
    127   // Return the specified manager.
    128   content::MockDownloadManager* Manager(int manager_index) {
    129     DCHECK_GT(managers_.size(), static_cast<size_t>(manager_index));
    130     return managers_[manager_index];
    131   }
    132 
    133   // Return the specified item.
    134   content::MockDownloadItem* Item(int manager_index, int item_index) {
    135     DCHECK_GT(manager_items_.size(), static_cast<size_t>(manager_index));
    136     DCHECK_GT(manager_items_[manager_index].size(),
    137               static_cast<size_t>(item_index));
    138     // All DownloadItems in manager_items_ are MockDownloadItems.
    139     return static_cast<content::MockDownloadItem*>(
    140         manager_items_[manager_index][item_index]);
    141   }
    142 
    143   // Set return values relevant to |DownloadStatusUpdater::GetProgress()|
    144   // for the specified item.
    145   void SetItemValues(int manager_index, int item_index,
    146                      int received_bytes, int total_bytes, bool notify) {
    147     content::MockDownloadItem* item(Item(manager_index, item_index));
    148     EXPECT_CALL(*item, GetReceivedBytes())
    149         .WillRepeatedly(Return(received_bytes));
    150     EXPECT_CALL(*item, GetTotalBytes())
    151         .WillRepeatedly(Return(total_bytes));
    152     if (notify)
    153       updater_->OnDownloadUpdated(managers_[manager_index], item);
    154   }
    155 
    156   // Transition specified item to completed.
    157   void CompleteItem(int manager_index, int item_index) {
    158     content::MockDownloadItem* item(Item(manager_index, item_index));
    159     EXPECT_CALL(*item, GetState())
    160         .WillRepeatedly(Return(content::DownloadItem::COMPLETE));
    161     updater_->OnDownloadUpdated(managers_[manager_index], item);
    162   }
    163 
    164   // Verify and clear all mocks expectations.
    165   void VerifyAndClearExpectations() {
    166     for (ScopedVector<content::MockDownloadManager>::iterator it
    167              = managers_.begin(); it != managers_.end(); ++it)
    168       Mock::VerifyAndClearExpectations(*it);
    169     for (std::vector<Items>::iterator it = manager_items_.begin();
    170          it != manager_items_.end(); ++it)
    171       for (Items::iterator sit = it->begin(); sit != it->end(); ++sit)
    172         Mock::VerifyAndClearExpectations(*sit);
    173   }
    174 
    175   ScopedVector<content::MockDownloadManager> managers_;
    176   // DownloadItem so that it can be assigned to the result of SearchDownloads.
    177   typedef std::vector<content::DownloadItem*> Items;
    178   std::vector<Items> manager_items_;
    179   int manager_observer_index_;
    180 
    181   std::vector<content::DownloadManager::Observer*> manager_observers_;
    182 
    183   // Pointer so we can verify that destruction triggers appropriate
    184   // changes.
    185   TestDownloadStatusUpdater *updater_;
    186 
    187   // Thread so that the DownloadManager (which is a DeleteOnUIThread
    188   // object) can be deleted.
    189   // TODO(rdsmith): This can be removed when the DownloadManager
    190   // is no longer required to be deleted on the UI thread.
    191   base::MessageLoop loop_;
    192   content::TestBrowserThread ui_thread_;
    193 };
    194 
    195 // Test null updater.
    196 TEST_F(DownloadStatusUpdaterTest, Basic) {
    197   float progress = -1;
    198   int download_count = -1;
    199   EXPECT_TRUE(updater_->GetProgress(&progress, &download_count));
    200   EXPECT_FLOAT_EQ(0.0f, progress);
    201   EXPECT_EQ(0, download_count);
    202 }
    203 
    204 // Test updater with null manager.
    205 TEST_F(DownloadStatusUpdaterTest, OneManagerNoItems) {
    206   SetupManagers(1);
    207   AddItems(0, 0, 0);
    208   LinkManager(0);
    209   VerifyAndClearExpectations();
    210 
    211   float progress = -1;
    212   int download_count = -1;
    213   EXPECT_CALL(*managers_[0], GetAllDownloads(_))
    214       .WillRepeatedly(SetArgPointee<0>(manager_items_[0]));
    215   EXPECT_TRUE(updater_->GetProgress(&progress, &download_count));
    216   EXPECT_FLOAT_EQ(0.0f, progress);
    217   EXPECT_EQ(0, download_count);
    218 }
    219 
    220 // Test updater with non-null manager, including transition an item to
    221 // |content::DownloadItem::COMPLETE| and adding a new item.
    222 TEST_F(DownloadStatusUpdaterTest, OneManagerManyItems) {
    223   SetupManagers(1);
    224   AddItems(0, 3, 2);
    225   LinkManager(0);
    226 
    227   // Prime items
    228   SetItemValues(0, 0, 10, 20, false);
    229   SetItemValues(0, 1, 50, 60, false);
    230   SetItemValues(0, 2, 90, 90, false);
    231 
    232   float progress = -1;
    233   int download_count = -1;
    234   EXPECT_TRUE(updater_->GetProgress(&progress, &download_count));
    235   EXPECT_FLOAT_EQ((10+50)/(20.0f+60), progress);
    236   EXPECT_EQ(2, download_count);
    237 
    238   // Transition one item to completed and confirm progress is updated
    239   // properly.
    240   CompleteItem(0, 0);
    241   EXPECT_TRUE(updater_->GetProgress(&progress, &download_count));
    242   EXPECT_FLOAT_EQ(50/60.0f, progress);
    243   EXPECT_EQ(1, download_count);
    244 
    245   // Add a new item to manager and confirm progress is updated properly.
    246   AddItems(0, 1, 1);
    247   SetItemValues(0, 3, 150, 200, false);
    248   manager_observers_[0]->OnDownloadCreated(
    249       managers_[0], manager_items_[0][manager_items_[0].size()-1]);
    250 
    251   EXPECT_TRUE(updater_->GetProgress(&progress, &download_count));
    252   EXPECT_FLOAT_EQ((50+150)/(60+200.0f), progress);
    253   EXPECT_EQ(2, download_count);
    254 }
    255 
    256 // Test to ensure that the download progress notification is called correctly.
    257 TEST_F(DownloadStatusUpdaterTest, ProgressNotification) {
    258   size_t expected_notifications = updater_->NotificationCount();
    259   SetupManagers(1);
    260   AddItems(0, 2, 2);
    261   LinkManager(0);
    262 
    263   // Expect two notifications, one for each item; which item will come first
    264   // isn't defined so it cannot be tested.
    265   expected_notifications += 2;
    266   ASSERT_EQ(expected_notifications, updater_->NotificationCount());
    267 
    268   // Make progress on the first item.
    269   updater_->SetAcceptableNotificationItem(Item(0, 0));
    270   SetItemValues(0, 0, 10, 20, true);
    271   ++expected_notifications;
    272   ASSERT_EQ(expected_notifications, updater_->NotificationCount());
    273 
    274   // Second item completes!
    275   updater_->SetAcceptableNotificationItem(Item(0, 1));
    276   CompleteItem(0, 1);
    277   ++expected_notifications;
    278   ASSERT_EQ(expected_notifications, updater_->NotificationCount());
    279 
    280   // First item completes.
    281   updater_->SetAcceptableNotificationItem(Item(0, 0));
    282   CompleteItem(0, 0);
    283   ++expected_notifications;
    284   ASSERT_EQ(expected_notifications, updater_->NotificationCount());
    285 
    286   updater_->SetAcceptableNotificationItem(NULL);
    287 }
    288 
    289 // Confirm we recognize the situation where we have an unknown size.
    290 TEST_F(DownloadStatusUpdaterTest, UnknownSize) {
    291   SetupManagers(1);
    292   AddItems(0, 2, 2);
    293   LinkManager(0);
    294 
    295   // Prime items
    296   SetItemValues(0, 0, 10, 20, false);
    297   SetItemValues(0, 1, 50, -1, false);
    298 
    299   float progress = -1;
    300   int download_count = -1;
    301   EXPECT_FALSE(updater_->GetProgress(&progress, &download_count));
    302 }
    303 
    304 // Test many null managers.
    305 TEST_F(DownloadStatusUpdaterTest, ManyManagersNoItems) {
    306   SetupManagers(1);
    307   AddItems(0, 0, 0);
    308   LinkManager(0);
    309 
    310   float progress = -1;
    311   int download_count = -1;
    312   EXPECT_TRUE(updater_->GetProgress(&progress, &download_count));
    313   EXPECT_FLOAT_EQ(0.0f, progress);
    314   EXPECT_EQ(0, download_count);
    315 }
    316 
    317 // Test many managers with all items complete.
    318 TEST_F(DownloadStatusUpdaterTest, ManyManagersEmptyItems) {
    319   SetupManagers(2);
    320   AddItems(0, 3, 0);
    321   LinkManager(0);
    322   AddItems(1, 3, 0);
    323   LinkManager(1);
    324 
    325   float progress = -1;
    326   int download_count = -1;
    327   EXPECT_TRUE(updater_->GetProgress(&progress, &download_count));
    328   EXPECT_FLOAT_EQ(0.0f, progress);
    329   EXPECT_EQ(0, download_count);
    330 }
    331 
    332 // Test many managers with some non-complete items.
    333 TEST_F(DownloadStatusUpdaterTest, ManyManagersMixedItems) {
    334   SetupManagers(2);
    335   AddItems(0, 3, 2);
    336   LinkManager(0);
    337   AddItems(1, 3, 1);
    338   LinkManager(1);
    339 
    340   SetItemValues(0, 0, 10, 20, false);
    341   SetItemValues(0, 1, 50, 60, false);
    342   SetItemValues(1, 0, 80, 90, false);
    343 
    344   float progress = -1;
    345   int download_count = -1;
    346   EXPECT_TRUE(updater_->GetProgress(&progress, &download_count));
    347   EXPECT_FLOAT_EQ((10+50+80)/(20.0f+60+90), progress);
    348   EXPECT_EQ(3, download_count);
    349 }
    350