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.
5 #include "base/files/file_path.h"
6 #include "base/files/file_util.h"
7 #include "base/files/scoped_temp_dir.h"
8 #include "base/location.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/run_loop.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "chrome/browser/download/chrome_download_manager_delegate.h"
15 #include "chrome/browser/download/download_prefs.h"
16 #include "chrome/browser/download/download_target_info.h"
17 #include "chrome/common/pref_names.h"
18 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
19 #include "chrome/test/base/testing_profile.h"
20 #include "components/syncable_prefs/testing_pref_service_syncable.h"
21 #include "content/public/browser/download_interrupt_reasons.h"
22 #include "content/public/browser/web_contents.h"
23 #include "content/public/browser/web_contents_delegate.h"
24 #include "content/public/test/mock_download_item.h"
25 #include "content/public/test/mock_download_manager.h"
26 #include "content/public/test/test_renderer_host.h"
27 #include "content/public/test/web_contents_tester.h"
28 #include "testing/gmock/include/gmock/gmock.h"
29 #include "testing/gtest/include/gtest/gtest.h"
31 using ::testing::AtMost
;
32 using ::testing::Invoke
;
34 using ::testing::Return
;
35 using ::testing::ReturnPointee
;
36 using ::testing::ReturnRef
;
37 using ::testing::ReturnRefOfCopy
;
38 using ::testing::SetArgPointee
;
39 using ::testing::WithArg
;
41 using content::DownloadItem
;
45 class MockWebContentsDelegate
: public content::WebContentsDelegate
{
47 ~MockWebContentsDelegate() override
{}
50 // Google Mock action that posts a task to the current message loop that invokes
51 // the first argument of the mocked method as a callback. Said argument must be
52 // a base::Callback<void(ParamType)>. |result| must be of |ParamType| and is
53 // bound as that parameter.
57 // virtual void Foo(base::Callback<void(bool)> callback);
60 // EXPECT_CALL(mock_fooclass_instance, Foo(callback))
61 // .WillOnce(ScheduleCallback(false));
62 ACTION_P(ScheduleCallback
, result
) {
63 base::MessageLoop::current()->PostTask(FROM_HERE
, base::Bind(arg0
, result
));
66 // Similar to ScheduleCallback, but binds 2 arguments.
67 ACTION_P2(ScheduleCallback2
, result0
, result1
) {
68 base::MessageLoop::current()->PostTask(
69 FROM_HERE
, base::Bind(arg0
, result0
, result1
));
72 // Subclass of the ChromeDownloadManagerDelegate that uses a mock
73 // DownloadProtectionService.
74 class TestChromeDownloadManagerDelegate
: public ChromeDownloadManagerDelegate
{
76 explicit TestChromeDownloadManagerDelegate(Profile
* profile
)
77 : ChromeDownloadManagerDelegate(profile
) {
80 ~TestChromeDownloadManagerDelegate() override
{}
82 safe_browsing::DownloadProtectionService
*
83 GetDownloadProtectionService() override
{
87 void NotifyExtensions(content::DownloadItem
* download
,
88 const base::FilePath
& suggested_virtual_path
,
89 const NotifyExtensionsCallback
& callback
) override
{
90 callback
.Run(base::FilePath(),
91 DownloadPathReservationTracker::UNIQUIFY
);
94 void ReserveVirtualPath(
95 content::DownloadItem
* download
,
96 const base::FilePath
& virtual_path
,
97 bool create_directory
,
98 DownloadPathReservationTracker::FilenameConflictAction conflict_action
,
99 const DownloadPathReservationTracker::ReservedPathCallback
& callback
)
101 // Pretend the path reservation succeeded without any change to
103 base::ThreadTaskRunnerHandle::Get()->PostTask(
104 FROM_HERE
, base::Bind(callback
, virtual_path
, true));
107 void PromptUserForDownloadPath(
108 DownloadItem
* download
,
109 const base::FilePath
& suggested_path
,
110 const DownloadTargetDeterminerDelegate::FileSelectedCallback
& callback
)
112 base::FilePath return_path
= MockPromptUserForDownloadPath(download
,
115 callback
.Run(return_path
);
119 MockPromptUserForDownloadPath
,
121 content::DownloadItem
*,
122 const base::FilePath
&,
123 const DownloadTargetDeterminerDelegate::FileSelectedCallback
&));
126 class ChromeDownloadManagerDelegateTest
127 : public ChromeRenderViewHostTestHarness
{
129 ChromeDownloadManagerDelegateTest();
132 void SetUp() override
;
133 void TearDown() override
;
135 // Verifies and clears test expectations for |delegate_| and
136 // |download_manager_|.
137 void VerifyAndClearExpectations();
139 // Creates MockDownloadItem and sets up default expectations.
140 content::MockDownloadItem
* CreateActiveDownloadItem(int32 id
);
142 // Given the relative path |path|, returns the full path under the temporary
143 // downloads directory.
144 base::FilePath
GetPathInDownloadDir(const char* path
);
146 // Set the kDownloadDefaultDirectory user preference to |path|.
147 void SetDefaultDownloadPath(const base::FilePath
& path
);
149 void DetermineDownloadTarget(DownloadItem
* download
,
150 DownloadTargetInfo
* result
);
152 // Invokes ChromeDownloadManagerDelegate::CheckForFileExistence and waits for
153 // the asynchronous callback. The result passed into
154 // content::CheckForFileExistenceCallback is the return value from this
156 bool CheckForFileExistence(DownloadItem
* download
);
158 const base::FilePath
& default_download_path() const;
159 TestChromeDownloadManagerDelegate
* delegate();
160 content::MockDownloadManager
* download_manager();
161 DownloadPrefs
* download_prefs();
164 syncable_prefs::TestingPrefServiceSyncable
* pref_service_
;
165 base::ScopedTempDir test_download_dir_
;
166 scoped_ptr
<content::MockDownloadManager
> download_manager_
;
167 scoped_ptr
<TestChromeDownloadManagerDelegate
> delegate_
;
168 MockWebContentsDelegate web_contents_delegate_
;
172 ChromeDownloadManagerDelegateTest::ChromeDownloadManagerDelegateTest()
173 : download_manager_(new ::testing::NiceMock
<content::MockDownloadManager
>) {
176 void ChromeDownloadManagerDelegateTest::SetUp() {
177 ChromeRenderViewHostTestHarness::SetUp();
180 delegate_
.reset(new TestChromeDownloadManagerDelegate(profile()));
181 delegate_
->SetDownloadManager(download_manager_
.get());
182 pref_service_
= profile()->GetTestingPrefService();
183 web_contents()->SetDelegate(&web_contents_delegate_
);
185 ASSERT_TRUE(test_download_dir_
.CreateUniqueTempDir());
186 SetDefaultDownloadPath(test_download_dir_
.path());
189 void ChromeDownloadManagerDelegateTest::TearDown() {
190 base::RunLoop().RunUntilIdle();
191 delegate_
->Shutdown();
192 ChromeRenderViewHostTestHarness::TearDown();
195 void ChromeDownloadManagerDelegateTest::VerifyAndClearExpectations() {
196 ::testing::Mock::VerifyAndClearExpectations(delegate_
.get());
199 content::MockDownloadItem
*
200 ChromeDownloadManagerDelegateTest::CreateActiveDownloadItem(int32 id
) {
201 content::MockDownloadItem
* item
=
202 new ::testing::NiceMock
<content::MockDownloadItem
>();
203 ON_CALL(*item
, GetBrowserContext())
204 .WillByDefault(Return(profile()));
205 ON_CALL(*item
, GetDangerType())
206 .WillByDefault(Return(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS
));
207 ON_CALL(*item
, GetForcedFilePath())
208 .WillByDefault(ReturnRefOfCopy(base::FilePath()));
209 ON_CALL(*item
, GetFullPath())
210 .WillByDefault(ReturnRefOfCopy(base::FilePath()));
211 ON_CALL(*item
, GetHash())
212 .WillByDefault(ReturnRefOfCopy(std::string()));
213 ON_CALL(*item
, GetId())
214 .WillByDefault(Return(id
));
215 ON_CALL(*item
, GetLastReason())
216 .WillByDefault(Return(content::DOWNLOAD_INTERRUPT_REASON_NONE
));
217 ON_CALL(*item
, GetReferrerUrl())
218 .WillByDefault(ReturnRefOfCopy(GURL()));
219 ON_CALL(*item
, GetState())
220 .WillByDefault(Return(DownloadItem::IN_PROGRESS
));
221 ON_CALL(*item
, GetTargetFilePath())
222 .WillByDefault(ReturnRefOfCopy(base::FilePath()));
223 ON_CALL(*item
, GetTransitionType())
224 .WillByDefault(Return(ui::PAGE_TRANSITION_LINK
));
225 ON_CALL(*item
, GetWebContents())
226 .WillByDefault(Return(web_contents()));
227 ON_CALL(*item
, HasUserGesture())
228 .WillByDefault(Return(false));
229 ON_CALL(*item
, IsDangerous())
230 .WillByDefault(Return(false));
231 ON_CALL(*item
, IsTemporary())
232 .WillByDefault(Return(false));
233 EXPECT_CALL(*download_manager_
, GetDownload(id
))
234 .WillRepeatedly(Return(item
));
238 base::FilePath
ChromeDownloadManagerDelegateTest::GetPathInDownloadDir(
239 const char* relative_path
) {
240 base::FilePath full_path
=
241 test_download_dir_
.path().AppendASCII(relative_path
);
242 return full_path
.NormalizePathSeparators();
245 void ChromeDownloadManagerDelegateTest::SetDefaultDownloadPath(
246 const base::FilePath
& path
) {
247 pref_service_
->SetFilePath(prefs::kDownloadDefaultDirectory
, path
);
248 pref_service_
->SetFilePath(prefs::kSaveFileDefaultDirectory
, path
);
251 void StoreDownloadTargetInfo(const base::Closure
& closure
,
252 DownloadTargetInfo
* target_info
,
253 const base::FilePath
& target_path
,
254 DownloadItem::TargetDisposition target_disposition
,
255 content::DownloadDangerType danger_type
,
256 const base::FilePath
& intermediate_path
) {
257 target_info
->target_path
= target_path
;
258 target_info
->target_disposition
= target_disposition
;
259 target_info
->danger_type
= danger_type
;
260 target_info
->intermediate_path
= intermediate_path
;
264 void ChromeDownloadManagerDelegateTest::DetermineDownloadTarget(
265 DownloadItem
* download_item
,
266 DownloadTargetInfo
* result
) {
267 base::RunLoop loop_runner
;
268 delegate()->DetermineDownloadTarget(
270 base::Bind(&StoreDownloadTargetInfo
, loop_runner
.QuitClosure(), result
));
274 void StoreBoolAndRunClosure(const base::Closure
& closure
,
275 bool* result_storage
,
277 *result_storage
= result
;
281 bool ChromeDownloadManagerDelegateTest::CheckForFileExistence(
282 DownloadItem
* download_item
) {
283 base::RunLoop loop_runner
;
285 delegate()->CheckForFileExistence(
287 base::Bind(&StoreBoolAndRunClosure
, loop_runner
.QuitClosure(), &result
));
292 const base::FilePath
& ChromeDownloadManagerDelegateTest::default_download_path()
294 return test_download_dir_
.path();
297 TestChromeDownloadManagerDelegate
*
298 ChromeDownloadManagerDelegateTest::delegate() {
299 return delegate_
.get();
302 content::MockDownloadManager
*
303 ChromeDownloadManagerDelegateTest::download_manager() {
304 return download_manager_
.get();
307 DownloadPrefs
* ChromeDownloadManagerDelegateTest::download_prefs() {
308 return delegate_
->download_prefs();
313 TEST_F(ChromeDownloadManagerDelegateTest
, StartDownload_LastSavePath
) {
314 GURL
download_url("http://example.com/foo.txt");
316 scoped_ptr
<content::MockDownloadItem
> save_as_download(
317 CreateActiveDownloadItem(0));
318 EXPECT_CALL(*save_as_download
, GetURL())
319 .Times(::testing::AnyNumber())
320 .WillRepeatedly(ReturnRef(download_url
));
321 EXPECT_CALL(*save_as_download
, GetTargetDisposition())
322 .Times(::testing::AnyNumber())
323 .WillRepeatedly(Return(DownloadItem::TARGET_DISPOSITION_PROMPT
));
325 scoped_ptr
<content::MockDownloadItem
> automatic_download(
326 CreateActiveDownloadItem(1));
327 EXPECT_CALL(*automatic_download
, GetURL())
328 .Times(::testing::AnyNumber())
329 .WillRepeatedly(ReturnRef(download_url
));
330 EXPECT_CALL(*automatic_download
, GetTargetDisposition())
331 .Times(::testing::AnyNumber())
332 .WillRepeatedly(Return(DownloadItem::TARGET_DISPOSITION_OVERWRITE
));
335 // When the prompt is displayed for the first download, the user selects a
336 // path in a different directory.
337 DownloadTargetInfo result
;
338 base::FilePath
expected_prompt_path(GetPathInDownloadDir("foo.txt"));
339 base::FilePath
user_selected_path(GetPathInDownloadDir("bar/baz.txt"));
340 EXPECT_CALL(*delegate(),
341 MockPromptUserForDownloadPath(save_as_download
.get(),
342 expected_prompt_path
, _
))
343 .WillOnce(Return(user_selected_path
));
344 DetermineDownloadTarget(save_as_download
.get(), &result
);
345 EXPECT_EQ(user_selected_path
, result
.target_path
);
346 VerifyAndClearExpectations();
350 // The prompt path for the second download is the user selected directroy
351 // from the previous download.
352 DownloadTargetInfo result
;
353 base::FilePath
expected_prompt_path(GetPathInDownloadDir("bar/foo.txt"));
354 EXPECT_CALL(*delegate(),
355 MockPromptUserForDownloadPath(save_as_download
.get(),
356 expected_prompt_path
, _
))
357 .WillOnce(Return(base::FilePath()));
358 DetermineDownloadTarget(save_as_download
.get(), &result
);
359 VerifyAndClearExpectations();
363 // Start an automatic download. This one should get the default download
364 // path since the last download path only affects Save As downloads.
365 DownloadTargetInfo result
;
366 base::FilePath
expected_path(GetPathInDownloadDir("foo.txt"));
367 DetermineDownloadTarget(automatic_download
.get(), &result
);
368 EXPECT_EQ(expected_path
, result
.target_path
);
369 VerifyAndClearExpectations();
373 // The prompt path for the next download should be the default.
374 download_prefs()->SetSaveFilePath(download_prefs()->DownloadPath());
375 DownloadTargetInfo result
;
376 base::FilePath
expected_prompt_path(GetPathInDownloadDir("foo.txt"));
377 EXPECT_CALL(*delegate(),
378 MockPromptUserForDownloadPath(save_as_download
.get(),
379 expected_prompt_path
, _
))
380 .WillOnce(Return(base::FilePath()));
381 DetermineDownloadTarget(save_as_download
.get(), &result
);
382 VerifyAndClearExpectations();
386 TEST_F(ChromeDownloadManagerDelegateTest
, CheckForFileExistence
) {
387 const char kData
[] = "helloworld";
388 const size_t kDataLength
= sizeof(kData
) - 1;
389 base::FilePath existing_path
= default_download_path().AppendASCII("foo");
390 base::FilePath non_existent_path
=
391 default_download_path().AppendASCII("bar");
392 base::WriteFile(existing_path
, kData
, kDataLength
);
394 scoped_ptr
<content::MockDownloadItem
> download_item(
395 CreateActiveDownloadItem(1));
396 EXPECT_CALL(*download_item
, GetTargetFilePath())
397 .WillRepeatedly(ReturnRef(existing_path
));
398 EXPECT_TRUE(CheckForFileExistence(download_item
.get()));
400 download_item
.reset(CreateActiveDownloadItem(1));
401 EXPECT_CALL(*download_item
, GetTargetFilePath())
402 .WillRepeatedly(ReturnRef(non_existent_path
));
403 EXPECT_FALSE(CheckForFileExistence(download_item
.get()));