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/message_loop/message_loop.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/run_loop.h"
11 #include "chrome/browser/download/chrome_download_manager_delegate.h"
12 #include "chrome/browser/download/download_prefs.h"
13 #include "chrome/browser/download/download_target_info.h"
14 #include "chrome/common/pref_names.h"
15 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
16 #include "chrome/test/base/testing_pref_service_syncable.h"
17 #include "chrome/test/base/testing_profile.h"
18 #include "content/public/browser/download_interrupt_reasons.h"
19 #include "content/public/browser/web_contents.h"
20 #include "content/public/browser/web_contents_delegate.h"
21 #include "content/public/test/mock_download_item.h"
22 #include "content/public/test/mock_download_manager.h"
23 #include "content/public/test/test_renderer_host.h"
24 #include "content/public/test/web_contents_tester.h"
25 #include "testing/gmock/include/gmock/gmock.h"
26 #include "testing/gtest/include/gtest/gtest.h"
28 using ::testing::AtMost
;
29 using ::testing::Invoke
;
31 using ::testing::Return
;
32 using ::testing::ReturnPointee
;
33 using ::testing::ReturnRef
;
34 using ::testing::ReturnRefOfCopy
;
35 using ::testing::SetArgPointee
;
36 using ::testing::WithArg
;
38 using content::DownloadItem
;
42 class MockWebContentsDelegate
: public content::WebContentsDelegate
{
44 ~MockWebContentsDelegate() override
{}
47 // Google Mock action that posts a task to the current message loop that invokes
48 // the first argument of the mocked method as a callback. Said argument must be
49 // a base::Callback<void(ParamType)>. |result| must be of |ParamType| and is
50 // bound as that parameter.
54 // virtual void Foo(base::Callback<void(bool)> callback);
57 // EXPECT_CALL(mock_fooclass_instance, Foo(callback))
58 // .WillOnce(ScheduleCallback(false));
59 ACTION_P(ScheduleCallback
, result
) {
60 base::MessageLoop::current()->PostTask(FROM_HERE
, base::Bind(arg0
, result
));
63 // Similar to ScheduleCallback, but binds 2 arguments.
64 ACTION_P2(ScheduleCallback2
, result0
, result1
) {
65 base::MessageLoop::current()->PostTask(
66 FROM_HERE
, base::Bind(arg0
, result0
, result1
));
69 // Subclass of the ChromeDownloadManagerDelegate that uses a mock
70 // DownloadProtectionService.
71 class TestChromeDownloadManagerDelegate
: public ChromeDownloadManagerDelegate
{
73 explicit TestChromeDownloadManagerDelegate(Profile
* profile
)
74 : ChromeDownloadManagerDelegate(profile
) {
77 virtual ~TestChromeDownloadManagerDelegate() {}
79 virtual safe_browsing::DownloadProtectionService
*
80 GetDownloadProtectionService() override
{
84 virtual void NotifyExtensions(
85 content::DownloadItem
* download
,
86 const base::FilePath
& suggested_virtual_path
,
87 const NotifyExtensionsCallback
& callback
) override
{
88 callback
.Run(base::FilePath(),
89 DownloadPathReservationTracker::UNIQUIFY
);
92 virtual void ReserveVirtualPath(
93 content::DownloadItem
* download
,
94 const base::FilePath
& virtual_path
,
95 bool create_directory
,
96 DownloadPathReservationTracker::FilenameConflictAction conflict_action
,
97 const DownloadPathReservationTracker::ReservedPathCallback
& callback
)
99 // Pretend the path reservation succeeded without any change to
101 base::MessageLoop::current()->PostTask(
102 FROM_HERE
, base::Bind(callback
, virtual_path
, true));
105 virtual void PromptUserForDownloadPath(
106 DownloadItem
* download
,
107 const base::FilePath
& suggested_path
,
108 const DownloadTargetDeterminerDelegate::FileSelectedCallback
& callback
)
110 base::FilePath return_path
= MockPromptUserForDownloadPath(download
,
113 callback
.Run(return_path
);
117 MockPromptUserForDownloadPath
,
119 content::DownloadItem
*,
120 const base::FilePath
&,
121 const DownloadTargetDeterminerDelegate::FileSelectedCallback
&));
124 class ChromeDownloadManagerDelegateTest
125 : public ChromeRenderViewHostTestHarness
{
127 ChromeDownloadManagerDelegateTest();
130 void SetUp() override
;
131 void TearDown() override
;
133 // Verifies and clears test expectations for |delegate_| and
134 // |download_manager_|.
135 void VerifyAndClearExpectations();
137 // Creates MockDownloadItem and sets up default expectations.
138 content::MockDownloadItem
* CreateActiveDownloadItem(int32 id
);
140 // Given the relative path |path|, returns the full path under the temporary
141 // downloads directory.
142 base::FilePath
GetPathInDownloadDir(const char* path
);
144 // Set the kDownloadDefaultDirectory user preference to |path|.
145 void SetDefaultDownloadPath(const base::FilePath
& path
);
147 void DetermineDownloadTarget(DownloadItem
* download
,
148 DownloadTargetInfo
* result
);
150 // Invokes ChromeDownloadManagerDelegate::CheckForFileExistence and waits for
151 // the asynchronous callback. The result passed into
152 // content::CheckForFileExistenceCallback is the return value from this
154 bool CheckForFileExistence(DownloadItem
* download
);
156 const base::FilePath
& default_download_path() const;
157 TestChromeDownloadManagerDelegate
* delegate();
158 content::MockDownloadManager
* download_manager();
159 DownloadPrefs
* download_prefs();
162 TestingPrefServiceSyncable
* pref_service_
;
163 base::ScopedTempDir test_download_dir_
;
164 scoped_ptr
<content::MockDownloadManager
> download_manager_
;
165 scoped_ptr
<TestChromeDownloadManagerDelegate
> delegate_
;
166 MockWebContentsDelegate web_contents_delegate_
;
169 ChromeDownloadManagerDelegateTest::ChromeDownloadManagerDelegateTest()
170 : download_manager_(new ::testing::NiceMock
<content::MockDownloadManager
>) {
173 void ChromeDownloadManagerDelegateTest::SetUp() {
174 ChromeRenderViewHostTestHarness::SetUp();
177 delegate_
.reset(new TestChromeDownloadManagerDelegate(profile()));
178 delegate_
->SetDownloadManager(download_manager_
.get());
179 pref_service_
= profile()->GetTestingPrefService();
180 web_contents()->SetDelegate(&web_contents_delegate_
);
182 ASSERT_TRUE(test_download_dir_
.CreateUniqueTempDir());
183 SetDefaultDownloadPath(test_download_dir_
.path());
186 void ChromeDownloadManagerDelegateTest::TearDown() {
187 base::RunLoop().RunUntilIdle();
188 delegate_
->Shutdown();
189 ChromeRenderViewHostTestHarness::TearDown();
192 void ChromeDownloadManagerDelegateTest::VerifyAndClearExpectations() {
193 ::testing::Mock::VerifyAndClearExpectations(delegate_
.get());
196 content::MockDownloadItem
*
197 ChromeDownloadManagerDelegateTest::CreateActiveDownloadItem(int32 id
) {
198 content::MockDownloadItem
* item
=
199 new ::testing::NiceMock
<content::MockDownloadItem
>();
200 ON_CALL(*item
, GetBrowserContext())
201 .WillByDefault(Return(profile()));
202 ON_CALL(*item
, GetDangerType())
203 .WillByDefault(Return(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS
));
204 ON_CALL(*item
, GetForcedFilePath())
205 .WillByDefault(ReturnRefOfCopy(base::FilePath()));
206 ON_CALL(*item
, GetFullPath())
207 .WillByDefault(ReturnRefOfCopy(base::FilePath()));
208 ON_CALL(*item
, GetHash())
209 .WillByDefault(ReturnRefOfCopy(std::string()));
210 ON_CALL(*item
, GetId())
211 .WillByDefault(Return(id
));
212 ON_CALL(*item
, GetLastReason())
213 .WillByDefault(Return(content::DOWNLOAD_INTERRUPT_REASON_NONE
));
214 ON_CALL(*item
, GetReferrerUrl())
215 .WillByDefault(ReturnRefOfCopy(GURL()));
216 ON_CALL(*item
, GetState())
217 .WillByDefault(Return(DownloadItem::IN_PROGRESS
));
218 ON_CALL(*item
, GetTargetFilePath())
219 .WillByDefault(ReturnRefOfCopy(base::FilePath()));
220 ON_CALL(*item
, GetTransitionType())
221 .WillByDefault(Return(ui::PAGE_TRANSITION_LINK
));
222 ON_CALL(*item
, GetWebContents())
223 .WillByDefault(Return(web_contents()));
224 ON_CALL(*item
, HasUserGesture())
225 .WillByDefault(Return(false));
226 ON_CALL(*item
, IsDangerous())
227 .WillByDefault(Return(false));
228 ON_CALL(*item
, IsTemporary())
229 .WillByDefault(Return(false));
230 EXPECT_CALL(*download_manager_
, GetDownload(id
))
231 .WillRepeatedly(Return(item
));
235 base::FilePath
ChromeDownloadManagerDelegateTest::GetPathInDownloadDir(
236 const char* relative_path
) {
237 base::FilePath full_path
=
238 test_download_dir_
.path().AppendASCII(relative_path
);
239 return full_path
.NormalizePathSeparators();
242 void ChromeDownloadManagerDelegateTest::SetDefaultDownloadPath(
243 const base::FilePath
& path
) {
244 pref_service_
->SetFilePath(prefs::kDownloadDefaultDirectory
, path
);
245 pref_service_
->SetFilePath(prefs::kSaveFileDefaultDirectory
, path
);
248 void StoreDownloadTargetInfo(const base::Closure
& closure
,
249 DownloadTargetInfo
* target_info
,
250 const base::FilePath
& target_path
,
251 DownloadItem::TargetDisposition target_disposition
,
252 content::DownloadDangerType danger_type
,
253 const base::FilePath
& intermediate_path
) {
254 target_info
->target_path
= target_path
;
255 target_info
->target_disposition
= target_disposition
;
256 target_info
->danger_type
= danger_type
;
257 target_info
->intermediate_path
= intermediate_path
;
261 void ChromeDownloadManagerDelegateTest::DetermineDownloadTarget(
262 DownloadItem
* download_item
,
263 DownloadTargetInfo
* result
) {
264 base::RunLoop loop_runner
;
265 delegate()->DetermineDownloadTarget(
267 base::Bind(&StoreDownloadTargetInfo
, loop_runner
.QuitClosure(), result
));
271 void StoreBoolAndRunClosure(const base::Closure
& closure
,
272 bool* result_storage
,
274 *result_storage
= result
;
278 bool ChromeDownloadManagerDelegateTest::CheckForFileExistence(
279 DownloadItem
* download_item
) {
280 base::RunLoop loop_runner
;
282 delegate()->CheckForFileExistence(
284 base::Bind(&StoreBoolAndRunClosure
, loop_runner
.QuitClosure(), &result
));
289 const base::FilePath
& ChromeDownloadManagerDelegateTest::default_download_path()
291 return test_download_dir_
.path();
294 TestChromeDownloadManagerDelegate
*
295 ChromeDownloadManagerDelegateTest::delegate() {
296 return delegate_
.get();
299 content::MockDownloadManager
*
300 ChromeDownloadManagerDelegateTest::download_manager() {
301 return download_manager_
.get();
304 DownloadPrefs
* ChromeDownloadManagerDelegateTest::download_prefs() {
305 return delegate_
->download_prefs();
310 TEST_F(ChromeDownloadManagerDelegateTest
, StartDownload_LastSavePath
) {
311 GURL
download_url("http://example.com/foo.txt");
313 scoped_ptr
<content::MockDownloadItem
> save_as_download(
314 CreateActiveDownloadItem(0));
315 EXPECT_CALL(*save_as_download
, GetURL())
316 .Times(::testing::AnyNumber())
317 .WillRepeatedly(ReturnRef(download_url
));
318 EXPECT_CALL(*save_as_download
, GetTargetDisposition())
319 .Times(::testing::AnyNumber())
320 .WillRepeatedly(Return(DownloadItem::TARGET_DISPOSITION_PROMPT
));
322 scoped_ptr
<content::MockDownloadItem
> automatic_download(
323 CreateActiveDownloadItem(1));
324 EXPECT_CALL(*automatic_download
, GetURL())
325 .Times(::testing::AnyNumber())
326 .WillRepeatedly(ReturnRef(download_url
));
327 EXPECT_CALL(*automatic_download
, GetTargetDisposition())
328 .Times(::testing::AnyNumber())
329 .WillRepeatedly(Return(DownloadItem::TARGET_DISPOSITION_OVERWRITE
));
332 // When the prompt is displayed for the first download, the user selects a
333 // path in a different directory.
334 DownloadTargetInfo result
;
335 base::FilePath
expected_prompt_path(GetPathInDownloadDir("foo.txt"));
336 base::FilePath
user_selected_path(GetPathInDownloadDir("bar/baz.txt"));
337 EXPECT_CALL(*delegate(),
338 MockPromptUserForDownloadPath(save_as_download
.get(),
339 expected_prompt_path
, _
))
340 .WillOnce(Return(user_selected_path
));
341 DetermineDownloadTarget(save_as_download
.get(), &result
);
342 EXPECT_EQ(user_selected_path
, result
.target_path
);
343 VerifyAndClearExpectations();
347 // The prompt path for the second download is the user selected directroy
348 // from the previous download.
349 DownloadTargetInfo result
;
350 base::FilePath
expected_prompt_path(GetPathInDownloadDir("bar/foo.txt"));
351 EXPECT_CALL(*delegate(),
352 MockPromptUserForDownloadPath(save_as_download
.get(),
353 expected_prompt_path
, _
))
354 .WillOnce(Return(base::FilePath()));
355 DetermineDownloadTarget(save_as_download
.get(), &result
);
356 VerifyAndClearExpectations();
360 // Start an automatic download. This one should get the default download
361 // path since the last download path only affects Save As downloads.
362 DownloadTargetInfo result
;
363 base::FilePath
expected_path(GetPathInDownloadDir("foo.txt"));
364 DetermineDownloadTarget(automatic_download
.get(), &result
);
365 EXPECT_EQ(expected_path
, result
.target_path
);
366 VerifyAndClearExpectations();
370 // The prompt path for the next download should be the default.
371 download_prefs()->SetSaveFilePath(download_prefs()->DownloadPath());
372 DownloadTargetInfo result
;
373 base::FilePath
expected_prompt_path(GetPathInDownloadDir("foo.txt"));
374 EXPECT_CALL(*delegate(),
375 MockPromptUserForDownloadPath(save_as_download
.get(),
376 expected_prompt_path
, _
))
377 .WillOnce(Return(base::FilePath()));
378 DetermineDownloadTarget(save_as_download
.get(), &result
);
379 VerifyAndClearExpectations();
383 TEST_F(ChromeDownloadManagerDelegateTest
, CheckForFileExistence
) {
384 const char kData
[] = "helloworld";
385 const size_t kDataLength
= sizeof(kData
) - 1;
386 base::FilePath existing_path
= default_download_path().AppendASCII("foo");
387 base::FilePath non_existent_path
=
388 default_download_path().AppendASCII("bar");
389 base::WriteFile(existing_path
, kData
, kDataLength
);
391 scoped_ptr
<content::MockDownloadItem
> download_item(
392 CreateActiveDownloadItem(1));
393 EXPECT_CALL(*download_item
, GetTargetFilePath())
394 .WillRepeatedly(ReturnRef(existing_path
));
395 EXPECT_TRUE(CheckForFileExistence(download_item
.get()));
397 download_item
.reset(CreateActiveDownloadItem(1));
398 EXPECT_CALL(*download_item
, GetTargetFilePath())
399 .WillRepeatedly(ReturnRef(non_existent_path
));
400 EXPECT_FALSE(CheckForFileExistence(download_item
.get()));