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