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.
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_factory.h"
14 #include "chrome/browser/download/download_service_impl.h"
15 #include "chrome/browser/download/download_ui_controller.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
18 #include "components/history/core/browser/download_row.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"
24 using content::MockDownloadItem
;
25 using content::MockDownloadManager
;
26 using history::HistoryService
;
27 using testing::AnyNumber
;
28 using testing::Assign
;
29 using testing::Return
;
30 using testing::ReturnRefOfCopy
;
31 using testing::SaveArg
;
36 // A DownloadUIController::Delegate that stores the DownloadItem* for the last
37 // download that was sent to the UI.
38 class TestDelegate
: public DownloadUIController::Delegate
{
40 explicit TestDelegate(base::WeakPtr
<content::DownloadItem
*> receiver
);
41 ~TestDelegate() override
{}
44 void OnNewDownloadReady(content::DownloadItem
* item
) override
;
46 base::WeakPtr
<content::DownloadItem
*> receiver_
;
49 TestDelegate::TestDelegate(base::WeakPtr
<content::DownloadItem
*> receiver
)
50 : receiver_(receiver
) {
53 void TestDelegate::OnNewDownloadReady(content::DownloadItem
* item
) {
58 // A DownloadService that returns a custom DownloadHistory.
59 class TestDownloadService
: public DownloadServiceImpl
{
61 explicit TestDownloadService(Profile
* profile
);
62 ~TestDownloadService() override
;
64 void set_download_history(scoped_ptr
<DownloadHistory
> download_history
) {
65 download_history_
.swap(download_history
);
67 DownloadHistory
* GetDownloadHistory() override
;
70 scoped_ptr
<DownloadHistory
> download_history_
;
73 TestDownloadService::TestDownloadService(Profile
* profile
)
74 : DownloadServiceImpl(profile
) {
77 TestDownloadService::~TestDownloadService() {
80 DownloadHistory
* TestDownloadService::GetDownloadHistory() {
81 return download_history_
.get();
85 class DownloadUIControllerTest
: public ChromeRenderViewHostTestHarness
{
87 DownloadUIControllerTest();
91 void SetUp() override
;
93 // Returns a TestDelegate. Invoking OnNewDownloadReady on the returned
94 // delegate results in the DownloadItem* being stored in |notified_item_|.
95 scoped_ptr
<DownloadUIController::Delegate
> GetTestDelegate();
97 MockDownloadManager
* manager() { return manager_
.get(); }
99 // Returns the DownloadManager::Observer registered by a test case. This is
100 // the DownloadUIController's observer for all current test cases.
101 content::DownloadManager::Observer
* manager_observer() {
102 return manager_observer_
;
105 // The most recent DownloadItem that was passed into OnNewDownloadReady().
106 content::DownloadItem
* notified_item() { return notified_item_
; }
108 // DownloadHistory performs a query of existing downloads when it is first
109 // instantiated. This method returns the completion callback for that query.
110 // It can be used to inject history downloads.
111 const HistoryService::DownloadQueryCallback
& history_query_callback() const {
112 return history_adapter_
->download_query_callback_
;
115 // DownloadManager::Observer registered by DownloadHistory.
116 content::DownloadManager::Observer
* download_history_manager_observer() {
117 return download_history_manager_observer_
;
120 scoped_ptr
<MockDownloadItem
> CreateMockInProgressDownload();
123 // A private history adapter that stores the DownloadQueryCallback when
124 // QueryDownloads is called.
125 class HistoryAdapter
: public DownloadHistory::HistoryAdapter
{
127 HistoryAdapter() : DownloadHistory::HistoryAdapter(NULL
) {}
128 HistoryService::DownloadQueryCallback download_query_callback_
;
132 const HistoryService::DownloadQueryCallback
& callback
) override
{
133 download_query_callback_
= callback
;
137 // Constructs and returns a TestDownloadService.
138 static scoped_ptr
<KeyedService
> TestingDownloadServiceFactory(
139 content::BrowserContext
* browser_context
);
141 scoped_ptr
<MockDownloadManager
> manager_
;
142 content::DownloadManager::Observer
* download_history_manager_observer_
;
143 content::DownloadManager::Observer
* manager_observer_
;
144 content::DownloadItem
* notified_item_
;
145 base::WeakPtrFactory
<content::DownloadItem
*> notified_item_receiver_factory_
;
147 HistoryAdapter
* history_adapter_
;
151 scoped_ptr
<KeyedService
>
152 DownloadUIControllerTest::TestingDownloadServiceFactory(
153 content::BrowserContext
* browser_context
) {
154 return make_scoped_ptr(
155 new TestDownloadService(Profile::FromBrowserContext(browser_context
)));
158 DownloadUIControllerTest::DownloadUIControllerTest()
159 : download_history_manager_observer_(NULL
),
160 manager_observer_(NULL
),
161 notified_item_(NULL
),
162 notified_item_receiver_factory_(¬ified_item_
) {
165 void DownloadUIControllerTest::SetUp() {
166 ChromeRenderViewHostTestHarness::SetUp();
168 manager_
.reset(new testing::StrictMock
<MockDownloadManager
>());
169 EXPECT_CALL(*manager_
, AddObserver(_
))
170 .WillOnce(SaveArg
<0>(&download_history_manager_observer_
));
171 EXPECT_CALL(*manager_
,
172 RemoveObserver(testing::Eq(
173 testing::ByRef(download_history_manager_observer_
))))
174 .WillOnce(testing::Assign(
175 &download_history_manager_observer_
,
176 static_cast<content::DownloadManager::Observer
*>(NULL
)));
177 EXPECT_CALL(*manager_
, GetAllDownloads(_
)).Times(AnyNumber());
179 scoped_ptr
<HistoryAdapter
> history_adapter(new HistoryAdapter
);
180 history_adapter_
= history_adapter
.get();
181 scoped_ptr
<DownloadHistory
> download_history(
182 new DownloadHistory(manager_
.get(), history_adapter
.Pass()));
183 ASSERT_TRUE(download_history_manager_observer_
);
185 EXPECT_CALL(*manager_
, AddObserver(_
))
186 .WillOnce(SaveArg
<0>(&manager_observer_
));
187 EXPECT_CALL(*manager_
,
188 RemoveObserver(testing::Eq(testing::ByRef(manager_observer_
))))
189 .WillOnce(testing::Assign(
191 static_cast<content::DownloadManager::Observer
*>(NULL
)));
192 TestDownloadService
* download_service
= static_cast<TestDownloadService
*>(
193 DownloadServiceFactory::GetInstance()->SetTestingFactoryAndUse(
194 browser_context(), &TestingDownloadServiceFactory
));
195 ASSERT_TRUE(download_service
);
196 download_service
->set_download_history(download_history
.Pass());
199 scoped_ptr
<MockDownloadItem
>
200 DownloadUIControllerTest::CreateMockInProgressDownload() {
201 scoped_ptr
<MockDownloadItem
> item(
202 new testing::StrictMock
<MockDownloadItem
>());
203 EXPECT_CALL(*item
, GetBrowserContext())
204 .WillRepeatedly(Return(browser_context()));
205 EXPECT_CALL(*item
, GetId()).WillRepeatedly(Return(1));
206 EXPECT_CALL(*item
, GetTargetFilePath()).WillRepeatedly(
207 ReturnRefOfCopy(base::FilePath(FILE_PATH_LITERAL("foo"))));
208 EXPECT_CALL(*item
, GetFullPath()).WillRepeatedly(
209 ReturnRefOfCopy(base::FilePath(FILE_PATH_LITERAL("foo"))));
210 EXPECT_CALL(*item
, GetState())
211 .WillRepeatedly(Return(content::DownloadItem::IN_PROGRESS
));
212 EXPECT_CALL(*item
, GetUrlChain())
213 .WillRepeatedly(testing::ReturnRefOfCopy(std::vector
<GURL
>()));
214 EXPECT_CALL(*item
, GetReferrerUrl())
215 .WillRepeatedly(testing::ReturnRefOfCopy(GURL()));
216 EXPECT_CALL(*item
, GetStartTime()).WillRepeatedly(Return(base::Time()));
217 EXPECT_CALL(*item
, GetEndTime()).WillRepeatedly(Return(base::Time()));
218 EXPECT_CALL(*item
, GetETag()).WillRepeatedly(ReturnRefOfCopy(std::string()));
219 EXPECT_CALL(*item
, GetLastModifiedTime())
220 .WillRepeatedly(ReturnRefOfCopy(std::string()));
221 EXPECT_CALL(*item
, GetDangerType())
222 .WillRepeatedly(Return(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS
));
223 EXPECT_CALL(*item
, GetLastReason())
224 .WillRepeatedly(Return(content::DOWNLOAD_INTERRUPT_REASON_NONE
));
225 EXPECT_CALL(*item
, GetReceivedBytes()).WillRepeatedly(Return(0));
226 EXPECT_CALL(*item
, GetTotalBytes()).WillRepeatedly(Return(0));
227 EXPECT_CALL(*item
, GetTargetDisposition()).WillRepeatedly(
228 Return(content::DownloadItem::TARGET_DISPOSITION_OVERWRITE
));
229 EXPECT_CALL(*item
, GetOpened()).WillRepeatedly(Return(false));
230 EXPECT_CALL(*item
, GetMimeType()).WillRepeatedly(Return(std::string()));
231 EXPECT_CALL(*item
, GetURL()).WillRepeatedly(testing::ReturnRefOfCopy(GURL()));
232 EXPECT_CALL(*item
, GetWebContents()).WillRepeatedly(Return(nullptr));
233 EXPECT_CALL(*item
, IsTemporary()).WillRepeatedly(Return(false));
237 scoped_ptr
<DownloadUIController::Delegate
>
238 DownloadUIControllerTest::GetTestDelegate() {
239 scoped_ptr
<DownloadUIController::Delegate
> delegate(
240 new TestDelegate(notified_item_receiver_factory_
.GetWeakPtr()));
241 return delegate
.Pass();
244 // New downloads should be presented to the UI when GetTargetFilePath() returns
245 // a non-empty path. I.e. once the download target has been determined.
246 TEST_F(DownloadUIControllerTest
, DownloadUIController_NotifyBasic
) {
247 scoped_ptr
<MockDownloadItem
> item(CreateMockInProgressDownload());
248 DownloadUIController
controller(manager(), GetTestDelegate());
249 EXPECT_CALL(*item
, GetTargetFilePath())
250 .WillOnce(ReturnRefOfCopy(base::FilePath()));
252 ASSERT_TRUE(manager_observer());
253 manager_observer()->OnDownloadCreated(manager(), item
.get());
255 // The destination for the download hasn't been determined yet. It should not
257 EXPECT_FALSE(notified_item());
259 // Once the destination has been determined, then it should be displayed.
260 EXPECT_CALL(*item
, GetTargetFilePath())
261 .WillOnce(ReturnRefOfCopy(base::FilePath(FILE_PATH_LITERAL("foo"))));
262 item
->NotifyObserversDownloadUpdated();
264 EXPECT_EQ(static_cast<content::DownloadItem
*>(item
.get()), notified_item());
267 // A download that's created in an interrupted state should also be displayed.
268 TEST_F(DownloadUIControllerTest
, DownloadUIController_NotifyBasic_Interrupted
) {
269 scoped_ptr
<MockDownloadItem
> item
= CreateMockInProgressDownload();
270 DownloadUIController
controller(manager(), GetTestDelegate());
271 EXPECT_CALL(*item
, GetState())
272 .WillRepeatedly(Return(content::DownloadItem::INTERRUPTED
));
274 ASSERT_TRUE(manager_observer());
275 manager_observer()->OnDownloadCreated(manager(), item
.get());
276 EXPECT_EQ(static_cast<content::DownloadItem
*>(item
.get()), notified_item());
279 // Downloads that have a target path on creation and are in the IN_PROGRESS
280 // state should be displayed in the UI immediately without requiring an
281 // additional OnDownloadUpdated() notification.
282 TEST_F(DownloadUIControllerTest
, DownloadUIController_NotifyReadyOnCreate
) {
283 scoped_ptr
<MockDownloadItem
> item(CreateMockInProgressDownload());
284 DownloadUIController
controller(manager(), GetTestDelegate());
286 ASSERT_TRUE(manager_observer());
287 manager_observer()->OnDownloadCreated(manager(), item
.get());
288 EXPECT_EQ(static_cast<content::DownloadItem
*>(item
.get()), notified_item());
291 // The UI shouldn't be notified of downloads that were restored from history.
292 TEST_F(DownloadUIControllerTest
, DownloadUIController_HistoryDownload
) {
293 DownloadUIController
controller(manager(), GetTestDelegate());
294 // DownloadHistory should already have been created. It performs a query of
295 // existing downloads upon creation. We'll use the callback to inject a
297 ASSERT_FALSE(history_query_callback().is_null());
299 // download_history_manager_observer is the DownloadManager::Observer
300 // registered by the DownloadHistory. DownloadHistory relies on the
301 // OnDownloadCreated notification to mark a download as having been restored
303 ASSERT_TRUE(download_history_manager_observer());
305 scoped_ptr
<std::vector
<history::DownloadRow
> > history_downloads
;
306 history_downloads
.reset(new std::vector
<history::DownloadRow
>());
307 history_downloads
->push_back(history::DownloadRow());
308 history_downloads
->front().id
= 1;
310 std::vector
<GURL
> url_chain
;
312 scoped_ptr
<MockDownloadItem
> item
= CreateMockInProgressDownload();
314 EXPECT_CALL(*item
, GetOriginalMimeType());
315 EXPECT_CALL(*manager(), CheckForHistoryFilesRemoval());
318 testing::InSequence s
;
319 testing::MockFunction
<void()> mock_function
;
320 // DownloadHistory will immediately try to create a download using the info
321 // we push through the query callback. When DownloadManager::CreateDownload
322 // is called, we need to first invoke the OnDownloadCreated callback for
323 // DownloadHistory before returning the DownloadItem since that's the
324 // sequence of events expected by DownloadHistory.
325 base::Closure history_on_created_callback
=
326 base::Bind(&content::DownloadManager::Observer::OnDownloadCreated
,
327 base::Unretained(download_history_manager_observer()),
330 EXPECT_CALL(*manager(), MockCreateDownloadItem(_
)).WillOnce(
331 testing::DoAll(testing::InvokeWithoutArgs(&history_on_created_callback
,
332 &base::Closure::Run
),
333 Return(item
.get())));
334 EXPECT_CALL(mock_function
, Call());
336 history_query_callback().Run(history_downloads
.Pass());
337 mock_function
.Call();
340 // Now pass along the notification to the OnDownloadCreated observer from
341 // DownloadUIController. It should ignore the download since it's marked as
342 // being restored from history.
343 ASSERT_TRUE(manager_observer());
344 manager_observer()->OnDownloadCreated(manager(), item
.get());
346 // Finally, the expectation we've been waiting for:
347 EXPECT_FALSE(notified_item());