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 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. 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 86 void SetObserver(content::DownloadManager::Observer* observer) { 87 manager_observers_[manager_observer_index_] = observer; 88 } 89 90 // Hook the specified manager into the updater. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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