• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • 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:
TestDownloadStatusUpdater()27   TestDownloadStatusUpdater() : notification_count_(0),
28                                 acceptable_notification_item_(NULL) {
29   }
SetAcceptableNotificationItem(content::DownloadItem * item)30   void SetAcceptableNotificationItem(content::DownloadItem* item) {
31     acceptable_notification_item_ = item;
32   }
NotificationCount()33   size_t NotificationCount() {
34     return notification_count_;
35   }
36  protected:
UpdateAppIconDownloadProgress(content::DownloadItem * download)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:
DownloadStatusUpdaterTest()50   DownloadStatusUpdaterTest()
51       : updater_(new TestDownloadStatusUpdater()),
52         ui_thread_(content::BrowserThread::UI, &loop_) {}
53 
~DownloadStatusUpdaterTest()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.
SetupManagers(int manager_count)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 
SetObserver(content::DownloadManager::Observer * observer)86   void SetObserver(content::DownloadManager::Observer* observer) {
87     manager_observers_[manager_observer_index_] = observer;
88   }
89 
90   // Hook the specified manager into the updater.
LinkManager(int i)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.
AddItems(int manager_index,int item_count,int in_progress_count)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.
Manager(int manager_index)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.
Item(int manager_index,int item_index)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.
SetItemValues(int manager_index,int item_index,int received_bytes,int total_bytes,bool notify)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.
CompleteItem(int manager_index,int item_index)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.
VerifyAndClearExpectations()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.
TEST_F(DownloadStatusUpdaterTest,Basic)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.
TEST_F(DownloadStatusUpdaterTest,OneManagerNoItems)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.
TEST_F(DownloadStatusUpdaterTest,OneManagerManyItems)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.
TEST_F(DownloadStatusUpdaterTest,ProgressNotification)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.
TEST_F(DownloadStatusUpdaterTest,UnknownSize)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.
TEST_F(DownloadStatusUpdaterTest,ManyManagersNoItems)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.
TEST_F(DownloadStatusUpdaterTest,ManyManagersEmptyItems)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.
TEST_F(DownloadStatusUpdaterTest,ManyManagersMixedItems)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