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