• 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     }
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.
SetupManagers(int manager_count)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 
SetObserver(content::DownloadManager::Observer * observer)82   void SetObserver(content::DownloadManager::Observer* observer) {
83     manager_observers_[manager_observer_index_] = observer;
84   }
85 
86   // Hook the specified manager into the updater.
LinkManager(int i)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.
AddItems(int manager_index,int item_count,int in_progress_count)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.
Manager(int manager_index)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.
Item(int manager_index,int item_index)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.
SetItemValues(int manager_index,int item_index,int received_bytes,int total_bytes,bool notify)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.
CompleteItem(int manager_index,int item_index)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.
VerifyAndClearExpectations()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.
TEST_F(DownloadStatusUpdaterTest,Basic)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.
TEST_F(DownloadStatusUpdaterTest,OneManagerNoItems)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.
TEST_F(DownloadStatusUpdaterTest,OneManagerManyItems)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.
TEST_F(DownloadStatusUpdaterTest,ProgressNotification)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.
TEST_F(DownloadStatusUpdaterTest,UnknownSize)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.
TEST_F(DownloadStatusUpdaterTest,ManyManagersNoItems)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.
TEST_F(DownloadStatusUpdaterTest,ManyManagersEmptyItems)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.
TEST_F(DownloadStatusUpdaterTest,ManyManagersMixedItems)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