1 // Copyright 2013 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/bind.h" 6 #include "base/callback.h" 7 #include "base/files/file_path.h" 8 #include "base/memory/ref_counted.h" 9 #include "base/memory/scoped_ptr.h" 10 #include "base/memory/weak_ptr.h" 11 #include "base/observer_list.h" 12 #include "chrome/browser/download/download_history.h" 13 #include "chrome/browser/download/download_service.h" 14 #include "chrome/browser/download/download_service_factory.h" 15 #include "chrome/browser/download/download_ui_controller.h" 16 #include "chrome/browser/history/download_row.h" 17 #include "chrome/browser/profiles/profile.h" 18 #include "chrome/test/base/chrome_render_view_host_test_harness.h" 19 #include "content/public/test/mock_download_item.h" 20 #include "content/public/test/mock_download_manager.h" 21 #include "testing/gmock/include/gmock/gmock.h" 22 #include "testing/gtest/include/gtest/gtest.h" 23 24 using content::MockDownloadItem; 25 using content::MockDownloadManager; 26 using testing::AnyNumber; 27 using testing::Assign; 28 using testing::Return; 29 using testing::ReturnRefOfCopy; 30 using testing::SaveArg; 31 using testing::_; 32 33 namespace { 34 35 // A DownloadUIController::Delegate that stores the DownloadItem* for the last 36 // download that was sent to the UI. 37 class TestDelegate : public DownloadUIController::Delegate { 38 public: 39 explicit TestDelegate(base::WeakPtr<content::DownloadItem*> receiver); 40 virtual ~TestDelegate() {} 41 42 private: 43 virtual void OnNewDownloadReady(content::DownloadItem* item) OVERRIDE; 44 45 base::WeakPtr<content::DownloadItem*> receiver_; 46 }; 47 48 TestDelegate::TestDelegate(base::WeakPtr<content::DownloadItem*> receiver) 49 : receiver_(receiver) { 50 } 51 52 void TestDelegate::OnNewDownloadReady(content::DownloadItem* item) { 53 if (receiver_.get()) 54 *receiver_ = item; 55 } 56 57 // A DownloadService that returns a custom DownloadHistory. 58 class TestDownloadService : public DownloadService { 59 public: 60 explicit TestDownloadService(Profile* profile); 61 virtual ~TestDownloadService(); 62 63 void set_download_history(scoped_ptr<DownloadHistory> download_history) { 64 download_history_.swap(download_history); 65 } 66 virtual DownloadHistory* GetDownloadHistory() OVERRIDE; 67 68 private: 69 scoped_ptr<DownloadHistory> download_history_; 70 }; 71 72 TestDownloadService::TestDownloadService(Profile* profile) 73 : DownloadService(profile) { 74 } 75 76 TestDownloadService::~TestDownloadService() { 77 } 78 79 DownloadHistory* TestDownloadService::GetDownloadHistory() { 80 return download_history_.get(); 81 } 82 83 // The test fixture: 84 class DownloadUIControllerTest : public ChromeRenderViewHostTestHarness { 85 public: 86 DownloadUIControllerTest(); 87 88 protected: 89 // testing::Test 90 virtual void SetUp() OVERRIDE; 91 92 // Returns a TestDelegate. Invoking OnNewDownloadReady on the returned 93 // delegate results in the DownloadItem* being stored in |notified_item_|. 94 scoped_ptr<DownloadUIController::Delegate> GetTestDelegate(); 95 96 MockDownloadManager* manager() { return manager_.get(); } 97 98 // Returns the DownloadManager::Observer registered by a test case. This is 99 // the DownloadUIController's observer for all current test cases. 100 content::DownloadManager::Observer* manager_observer() { 101 return manager_observer_; 102 } 103 104 // The most recent DownloadItem that was passed into OnNewDownloadReady(). 105 content::DownloadItem* notified_item() { return notified_item_; } 106 107 // DownloadHistory performs a query of existing downloads when it is first 108 // instantiated. This method returns the completion callback for that query. 109 // It can be used to inject history downloads. 110 const HistoryService::DownloadQueryCallback& history_query_callback() const { 111 return history_adapter_->download_query_callback_; 112 } 113 114 // DownloadManager::Observer registered by DownloadHistory. 115 content::DownloadManager::Observer* download_history_manager_observer() { 116 return download_history_manager_observer_; 117 } 118 119 scoped_ptr<MockDownloadItem> CreateMockInProgressDownload(); 120 121 private: 122 // A private history adapter that stores the DownloadQueryCallback when 123 // QueryDownloads is called. 124 class HistoryAdapter : public DownloadHistory::HistoryAdapter { 125 public: 126 HistoryAdapter() : DownloadHistory::HistoryAdapter(NULL) {} 127 HistoryService::DownloadQueryCallback download_query_callback_; 128 129 private: 130 virtual void QueryDownloads( 131 const HistoryService::DownloadQueryCallback& callback) OVERRIDE { 132 download_query_callback_ = callback; 133 } 134 }; 135 136 // Constructs and returns a TestDownloadService. 137 static KeyedService* TestingDownloadServiceFactory( 138 content::BrowserContext* browser_context); 139 140 scoped_ptr<MockDownloadManager> manager_; 141 content::DownloadManager::Observer* download_history_manager_observer_; 142 content::DownloadManager::Observer* manager_observer_; 143 content::DownloadItem* notified_item_; 144 base::WeakPtrFactory<content::DownloadItem*> notified_item_receiver_factory_; 145 146 HistoryAdapter* history_adapter_; 147 }; 148 149 // static 150 KeyedService* DownloadUIControllerTest::TestingDownloadServiceFactory( 151 content::BrowserContext* browser_context) { 152 return new TestDownloadService(Profile::FromBrowserContext(browser_context)); 153 } 154 155 DownloadUIControllerTest::DownloadUIControllerTest() 156 : download_history_manager_observer_(NULL), 157 manager_observer_(NULL), 158 notified_item_(NULL), 159 notified_item_receiver_factory_(¬ified_item_) { 160 } 161 162 void DownloadUIControllerTest::SetUp() { 163 ChromeRenderViewHostTestHarness::SetUp(); 164 165 manager_.reset(new testing::StrictMock<MockDownloadManager>()); 166 EXPECT_CALL(*manager_, AddObserver(_)) 167 .WillOnce(SaveArg<0>(&download_history_manager_observer_)); 168 EXPECT_CALL(*manager_, 169 RemoveObserver(testing::Eq( 170 testing::ByRef(download_history_manager_observer_)))) 171 .WillOnce(testing::Assign( 172 &download_history_manager_observer_, 173 static_cast<content::DownloadManager::Observer*>(NULL))); 174 EXPECT_CALL(*manager_, GetAllDownloads(_)).Times(AnyNumber()); 175 176 scoped_ptr<HistoryAdapter> history_adapter(new HistoryAdapter); 177 history_adapter_ = history_adapter.get(); 178 scoped_ptr<DownloadHistory> download_history(new DownloadHistory( 179 manager_.get(), 180 history_adapter.PassAs<DownloadHistory::HistoryAdapter>())); 181 ASSERT_TRUE(download_history_manager_observer_); 182 183 EXPECT_CALL(*manager_, AddObserver(_)) 184 .WillOnce(SaveArg<0>(&manager_observer_)); 185 EXPECT_CALL(*manager_, 186 RemoveObserver(testing::Eq(testing::ByRef(manager_observer_)))) 187 .WillOnce(testing::Assign( 188 &manager_observer_, 189 static_cast<content::DownloadManager::Observer*>(NULL))); 190 TestDownloadService* download_service = static_cast<TestDownloadService*>( 191 DownloadServiceFactory::GetInstance()->SetTestingFactoryAndUse( 192 browser_context(), &TestingDownloadServiceFactory)); 193 ASSERT_TRUE(download_service); 194 download_service->set_download_history(download_history.Pass()); 195 } 196 197 scoped_ptr<MockDownloadItem> 198 DownloadUIControllerTest::CreateMockInProgressDownload() { 199 scoped_ptr<MockDownloadItem> item( 200 new testing::StrictMock<MockDownloadItem>()); 201 EXPECT_CALL(*item, GetBrowserContext()) 202 .WillRepeatedly(Return(browser_context())); 203 EXPECT_CALL(*item, GetId()).WillRepeatedly(Return(1)); 204 EXPECT_CALL(*item, GetTargetFilePath()).WillRepeatedly( 205 ReturnRefOfCopy(base::FilePath(FILE_PATH_LITERAL("foo")))); 206 EXPECT_CALL(*item, GetFullPath()).WillRepeatedly( 207 ReturnRefOfCopy(base::FilePath(FILE_PATH_LITERAL("foo")))); 208 EXPECT_CALL(*item, GetState()) 209 .WillRepeatedly(Return(content::DownloadItem::IN_PROGRESS)); 210 EXPECT_CALL(*item, GetUrlChain()) 211 .WillRepeatedly(testing::ReturnRefOfCopy(std::vector<GURL>())); 212 EXPECT_CALL(*item, GetReferrerUrl()) 213 .WillRepeatedly(testing::ReturnRefOfCopy(GURL())); 214 EXPECT_CALL(*item, GetStartTime()).WillRepeatedly(Return(base::Time())); 215 EXPECT_CALL(*item, GetEndTime()).WillRepeatedly(Return(base::Time())); 216 EXPECT_CALL(*item, GetETag()).WillRepeatedly(ReturnRefOfCopy(std::string())); 217 EXPECT_CALL(*item, GetLastModifiedTime()) 218 .WillRepeatedly(ReturnRefOfCopy(std::string())); 219 EXPECT_CALL(*item, GetDangerType()) 220 .WillRepeatedly(Return(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS)); 221 EXPECT_CALL(*item, GetLastReason()) 222 .WillRepeatedly(Return(content::DOWNLOAD_INTERRUPT_REASON_NONE)); 223 EXPECT_CALL(*item, GetReceivedBytes()).WillRepeatedly(Return(0)); 224 EXPECT_CALL(*item, GetTotalBytes()).WillRepeatedly(Return(0)); 225 EXPECT_CALL(*item, GetTargetDisposition()).WillRepeatedly( 226 Return(content::DownloadItem::TARGET_DISPOSITION_OVERWRITE)); 227 EXPECT_CALL(*item, GetOpened()).WillRepeatedly(Return(false)); 228 EXPECT_CALL(*item, GetMimeType()).WillRepeatedly(Return(std::string())); 229 EXPECT_CALL(*item, GetURL()).WillRepeatedly(testing::ReturnRefOfCopy(GURL())); 230 EXPECT_CALL(*item, IsTemporary()).WillRepeatedly(Return(false)); 231 return item.Pass(); 232 } 233 234 scoped_ptr<DownloadUIController::Delegate> 235 DownloadUIControllerTest::GetTestDelegate() { 236 scoped_ptr<DownloadUIController::Delegate> delegate( 237 new TestDelegate(notified_item_receiver_factory_.GetWeakPtr())); 238 return delegate.Pass(); 239 } 240 241 // New downloads should be presented to the UI when GetTargetFilePath() returns 242 // a non-empty path. I.e. once the download target has been determined. 243 TEST_F(DownloadUIControllerTest, DownloadUIController_NotifyBasic) { 244 scoped_ptr<MockDownloadItem> item(CreateMockInProgressDownload()); 245 DownloadUIController controller(manager(), GetTestDelegate()); 246 EXPECT_CALL(*item, GetTargetFilePath()) 247 .WillOnce(ReturnRefOfCopy(base::FilePath())); 248 249 ASSERT_TRUE(manager_observer()); 250 manager_observer()->OnDownloadCreated(manager(), item.get()); 251 252 // The destination for the download hasn't been determined yet. It should not 253 // be displayed. 254 EXPECT_FALSE(notified_item()); 255 256 // Once the destination has been determined, then it should be displayed. 257 EXPECT_CALL(*item, GetTargetFilePath()) 258 .WillOnce(ReturnRefOfCopy(base::FilePath(FILE_PATH_LITERAL("foo")))); 259 item->NotifyObserversDownloadUpdated(); 260 261 EXPECT_EQ(static_cast<content::DownloadItem*>(item.get()), notified_item()); 262 } 263 264 // A download that's created in an interrupted state should also be displayed. 265 TEST_F(DownloadUIControllerTest, DownloadUIController_NotifyBasic_Interrupted) { 266 scoped_ptr<MockDownloadItem> item = CreateMockInProgressDownload(); 267 DownloadUIController controller(manager(), GetTestDelegate()); 268 EXPECT_CALL(*item, GetState()) 269 .WillRepeatedly(Return(content::DownloadItem::INTERRUPTED)); 270 271 ASSERT_TRUE(manager_observer()); 272 manager_observer()->OnDownloadCreated(manager(), item.get()); 273 EXPECT_EQ(static_cast<content::DownloadItem*>(item.get()), notified_item()); 274 } 275 276 // Downloads that have a target path on creation and are in the IN_PROGRESS 277 // state should be displayed in the UI immediately without requiring an 278 // additional OnDownloadUpdated() notification. 279 TEST_F(DownloadUIControllerTest, DownloadUIController_NotifyReadyOnCreate) { 280 scoped_ptr<MockDownloadItem> item(CreateMockInProgressDownload()); 281 DownloadUIController controller(manager(), GetTestDelegate()); 282 283 ASSERT_TRUE(manager_observer()); 284 manager_observer()->OnDownloadCreated(manager(), item.get()); 285 EXPECT_EQ(static_cast<content::DownloadItem*>(item.get()), notified_item()); 286 } 287 288 // The UI shouldn't be notified of downloads that were restored from history. 289 TEST_F(DownloadUIControllerTest, DownloadUIController_HistoryDownload) { 290 DownloadUIController controller(manager(), GetTestDelegate()); 291 // DownloadHistory should already have been created. It performs a query of 292 // existing downloads upon creation. We'll use the callback to inject a 293 // history download. 294 ASSERT_FALSE(history_query_callback().is_null()); 295 296 // download_history_manager_observer is the DownloadManager::Observer 297 // registered by the DownloadHistory. DownloadHistory relies on the 298 // OnDownloadCreated notification to mark a download as having been restored 299 // from history. 300 ASSERT_TRUE(download_history_manager_observer()); 301 302 scoped_ptr<std::vector<history::DownloadRow> > history_downloads; 303 history_downloads.reset(new std::vector<history::DownloadRow>()); 304 history_downloads->push_back(history::DownloadRow()); 305 history_downloads->front().id = 1; 306 307 std::vector<GURL> url_chain; 308 GURL url; 309 scoped_ptr<MockDownloadItem> item = CreateMockInProgressDownload(); 310 311 EXPECT_CALL(*item, GetOriginalMimeType()); 312 EXPECT_CALL(*manager(), CheckForHistoryFilesRemoval()); 313 314 { 315 testing::InSequence s; 316 testing::MockFunction<void()> mock_function; 317 // DownloadHistory will immediately try to create a download using the info 318 // we push through the query callback. When DownloadManager::CreateDownload 319 // is called, we need to first invoke the OnDownloadCreated callback for 320 // DownloadHistory before returning the DownloadItem since that's the 321 // sequence of events expected by DownloadHistory. 322 base::Closure history_on_created_callback = 323 base::Bind(&content::DownloadManager::Observer::OnDownloadCreated, 324 base::Unretained(download_history_manager_observer()), 325 manager(), 326 item.get()); 327 EXPECT_CALL(*manager(), MockCreateDownloadItem(_)).WillOnce( 328 testing::DoAll(testing::InvokeWithoutArgs(&history_on_created_callback, 329 &base::Closure::Run), 330 Return(item.get()))); 331 EXPECT_CALL(mock_function, Call()); 332 333 history_query_callback().Run(history_downloads.Pass()); 334 mock_function.Call(); 335 } 336 337 // Now pass along the notification to the OnDownloadCreated observer from 338 // DownloadUIController. It should ignore the download since it's marked as 339 // being restored from history. 340 ASSERT_TRUE(manager_observer()); 341 manager_observer()->OnDownloadCreated(manager(), item.get()); 342 343 // Finally, the expectation we've been waiting for: 344 EXPECT_FALSE(notified_item()); 345 } 346 347 } // namespace 348