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/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" 27 28using ::testing::AtMost; 29using ::testing::Invoke; 30using ::testing::Ref; 31using ::testing::Return; 32using ::testing::ReturnPointee; 33using ::testing::ReturnRef; 34using ::testing::ReturnRefOfCopy; 35using ::testing::SetArgPointee; 36using ::testing::WithArg; 37using ::testing::_; 38using content::DownloadItem; 39 40namespace { 41 42class MockWebContentsDelegate : public content::WebContentsDelegate { 43 public: 44 virtual ~MockWebContentsDelegate() {} 45}; 46 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. 51// Example: 52// class FooClass { 53// public: 54// virtual void Foo(base::Callback<void(bool)> callback); 55// }; 56// ... 57// EXPECT_CALL(mock_fooclass_instance, Foo(callback)) 58// .WillOnce(ScheduleCallback(false)); 59ACTION_P(ScheduleCallback, result) { 60 base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(arg0, result)); 61} 62 63// Similar to ScheduleCallback, but binds 2 arguments. 64ACTION_P2(ScheduleCallback2, result0, result1) { 65 base::MessageLoop::current()->PostTask( 66 FROM_HERE, base::Bind(arg0, result0, result1)); 67} 68 69// Subclass of the ChromeDownloadManagerDelegate that uses a mock 70// DownloadProtectionService. 71class TestChromeDownloadManagerDelegate : public ChromeDownloadManagerDelegate { 72 public: 73 explicit TestChromeDownloadManagerDelegate(Profile* profile) 74 : ChromeDownloadManagerDelegate(profile) { 75 } 76 77 virtual ~TestChromeDownloadManagerDelegate() {} 78 79 virtual safe_browsing::DownloadProtectionService* 80 GetDownloadProtectionService() OVERRIDE { 81 return NULL; 82 } 83 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); 90 } 91 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) 98 OVERRIDE { 99 // Pretend the path reservation succeeded without any change to 100 // |target_path|. 101 base::MessageLoop::current()->PostTask( 102 FROM_HERE, base::Bind(callback, virtual_path, true)); 103 } 104 105 virtual void PromptUserForDownloadPath( 106 DownloadItem* download, 107 const base::FilePath& suggested_path, 108 const DownloadTargetDeterminerDelegate::FileSelectedCallback& callback) 109 OVERRIDE { 110 base::FilePath return_path = MockPromptUserForDownloadPath(download, 111 suggested_path, 112 callback); 113 callback.Run(return_path); 114 } 115 116 MOCK_METHOD3( 117 MockPromptUserForDownloadPath, 118 base::FilePath( 119 content::DownloadItem*, 120 const base::FilePath&, 121 const DownloadTargetDeterminerDelegate::FileSelectedCallback&)); 122}; 123 124class ChromeDownloadManagerDelegateTest 125 : public ChromeRenderViewHostTestHarness { 126 public: 127 ChromeDownloadManagerDelegateTest(); 128 129 // ::testing::Test 130 virtual void SetUp() OVERRIDE; 131 virtual void TearDown() OVERRIDE; 132 133 // Verifies and clears test expectations for |delegate_| and 134 // |download_manager_|. 135 void VerifyAndClearExpectations(); 136 137 // Creates MockDownloadItem and sets up default expectations. 138 content::MockDownloadItem* CreateActiveDownloadItem(int32 id); 139 140 // Given the relative path |path|, returns the full path under the temporary 141 // downloads directory. 142 base::FilePath GetPathInDownloadDir(const char* path); 143 144 // Set the kDownloadDefaultDirectory user preference to |path|. 145 void SetDefaultDownloadPath(const base::FilePath& path); 146 147 void DetermineDownloadTarget(DownloadItem* download, 148 DownloadTargetInfo* result); 149 150 // Invokes ChromeDownloadManagerDelegate::CheckForFileExistence and waits for 151 // the asynchronous callback. The result passed into 152 // content::CheckForFileExistenceCallback is the return value from this 153 // method. 154 bool CheckForFileExistence(DownloadItem* download); 155 156 const base::FilePath& default_download_path() const; 157 TestChromeDownloadManagerDelegate* delegate(); 158 content::MockDownloadManager* download_manager(); 159 DownloadPrefs* download_prefs(); 160 161 private: 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_; 167}; 168 169ChromeDownloadManagerDelegateTest::ChromeDownloadManagerDelegateTest() 170 : download_manager_(new ::testing::NiceMock<content::MockDownloadManager>) { 171} 172 173void ChromeDownloadManagerDelegateTest::SetUp() { 174 ChromeRenderViewHostTestHarness::SetUp(); 175 176 CHECK(profile()); 177 delegate_.reset(new TestChromeDownloadManagerDelegate(profile())); 178 delegate_->SetDownloadManager(download_manager_.get()); 179 pref_service_ = profile()->GetTestingPrefService(); 180 web_contents()->SetDelegate(&web_contents_delegate_); 181 182 ASSERT_TRUE(test_download_dir_.CreateUniqueTempDir()); 183 SetDefaultDownloadPath(test_download_dir_.path()); 184} 185 186void ChromeDownloadManagerDelegateTest::TearDown() { 187 base::RunLoop().RunUntilIdle(); 188 delegate_->Shutdown(); 189 ChromeRenderViewHostTestHarness::TearDown(); 190} 191 192void ChromeDownloadManagerDelegateTest::VerifyAndClearExpectations() { 193 ::testing::Mock::VerifyAndClearExpectations(delegate_.get()); 194} 195 196content::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)); 232 return item; 233} 234 235base::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(); 240} 241 242void ChromeDownloadManagerDelegateTest::SetDefaultDownloadPath( 243 const base::FilePath& path) { 244 pref_service_->SetFilePath(prefs::kDownloadDefaultDirectory, path); 245 pref_service_->SetFilePath(prefs::kSaveFileDefaultDirectory, path); 246} 247 248void 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; 258 closure.Run(); 259} 260 261void ChromeDownloadManagerDelegateTest::DetermineDownloadTarget( 262 DownloadItem* download_item, 263 DownloadTargetInfo* result) { 264 base::RunLoop loop_runner; 265 delegate()->DetermineDownloadTarget( 266 download_item, 267 base::Bind(&StoreDownloadTargetInfo, loop_runner.QuitClosure(), result)); 268 loop_runner.Run(); 269} 270 271void StoreBoolAndRunClosure(const base::Closure& closure, 272 bool* result_storage, 273 bool result) { 274 *result_storage = result; 275 closure.Run(); 276} 277 278bool ChromeDownloadManagerDelegateTest::CheckForFileExistence( 279 DownloadItem* download_item) { 280 base::RunLoop loop_runner; 281 bool result = false; 282 delegate()->CheckForFileExistence( 283 download_item, 284 base::Bind(&StoreBoolAndRunClosure, loop_runner.QuitClosure(), &result)); 285 loop_runner.Run(); 286 return result; 287} 288 289const base::FilePath& ChromeDownloadManagerDelegateTest::default_download_path() 290 const { 291 return test_download_dir_.path(); 292} 293 294TestChromeDownloadManagerDelegate* 295 ChromeDownloadManagerDelegateTest::delegate() { 296 return delegate_.get(); 297} 298 299content::MockDownloadManager* 300 ChromeDownloadManagerDelegateTest::download_manager() { 301 return download_manager_.get(); 302} 303 304DownloadPrefs* ChromeDownloadManagerDelegateTest::download_prefs() { 305 return delegate_->download_prefs(); 306} 307 308} // namespace 309 310TEST_F(ChromeDownloadManagerDelegateTest, StartDownload_LastSavePath) { 311 GURL download_url("http://example.com/foo.txt"); 312 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)); 321 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)); 330 331 { 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(); 344 } 345 346 { 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(); 357 } 358 359 { 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(); 367 } 368 369 { 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(); 380 } 381} 382 383TEST_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); 390 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())); 396 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())); 401} 402