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 16using testing::AtLeast; 17using testing::Invoke; 18using testing::Mock; 19using testing::Return; 20using testing::SetArgPointee; 21using testing::StrictMock; 22using testing::WithArg; 23using testing::_; 24 25class 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 48class 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 } 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. 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 82 void SetObserver(content::DownloadManager::Observer* observer) { 83 manager_observers_[manager_observer_index_] = observer; 84 } 85 86 // Hook the specified manager into the updater. 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. 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. 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. 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. 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. 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. 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. 190TEST_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. 199TEST_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. 216TEST_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. 251TEST_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. 284TEST_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. 299TEST_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. 312TEST_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. 327TEST_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