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