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/scoped_temp_dir.h" 7 #include "base/message_loop/message_loop.h" 8 #include "base/prefs/pref_service.h" 9 #include "base/run_loop.h" 10 #include "chrome/browser/download/chrome_download_manager_delegate.h" 11 #include "chrome/browser/download/download_prefs.h" 12 #include "chrome/common/pref_names.h" 13 #include "chrome/test/base/chrome_render_view_host_test_harness.h" 14 #include "chrome/test/base/testing_pref_service_syncable.h" 15 #include "chrome/test/base/testing_profile.h" 16 #include "content/public/browser/download_interrupt_reasons.h" 17 #include "content/public/browser/web_contents.h" 18 #include "content/public/browser/web_contents_delegate.h" 19 #include "content/public/test/mock_download_item.h" 20 #include "content/public/test/mock_download_manager.h" 21 #include "content/public/test/test_browser_thread.h" 22 #include "content/public/test/test_renderer_host.h" 23 #include "content/public/test/web_contents_tester.h" 24 #include "testing/gmock/include/gmock/gmock.h" 25 #include "testing/gtest/include/gtest/gtest.h" 26 27 using ::testing::AtMost; 28 using ::testing::Invoke; 29 using ::testing::Ref; 30 using ::testing::Return; 31 using ::testing::ReturnPointee; 32 using ::testing::ReturnRef; 33 using ::testing::ReturnRefOfCopy; 34 using ::testing::SetArgPointee; 35 using ::testing::WithArg; 36 using ::testing::_; 37 using content::DownloadItem; 38 39 namespace { 40 41 class MockWebContentsDelegate : public content::WebContentsDelegate { 42 public: 43 virtual ~MockWebContentsDelegate() {} 44 }; 45 46 // Google Mock action that posts a task to the current message loop that invokes 47 // the first argument of the mocked method as a callback. Said argument must be 48 // a base::Callback<void(ParamType)>. |result| must be of |ParamType| and is 49 // bound as that parameter. 50 // Example: 51 // class FooClass { 52 // public: 53 // virtual void Foo(base::Callback<void(bool)> callback); 54 // }; 55 // ... 56 // EXPECT_CALL(mock_fooclass_instance, Foo(callback)) 57 // .WillOnce(ScheduleCallback(false)); 58 ACTION_P(ScheduleCallback, result) { 59 base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(arg0, result)); 60 } 61 62 // Similar to ScheduleCallback, but binds 2 arguments. 63 ACTION_P2(ScheduleCallback2, result0, result1) { 64 base::MessageLoop::current()->PostTask( 65 FROM_HERE, base::Bind(arg0, result0, result1)); 66 } 67 68 struct DownloadTarget { 69 base::FilePath target_path; 70 base::FilePath intermediate_path; 71 DownloadItem::TargetDisposition target_disposition; 72 content::DownloadDangerType danger_type; 73 }; 74 75 // Subclass of the ChromeDownloadManagerDelegate that uses a mock 76 // DownloadProtectionService. 77 class TestChromeDownloadManagerDelegate : public ChromeDownloadManagerDelegate { 78 public: 79 explicit TestChromeDownloadManagerDelegate(Profile* profile) 80 : ChromeDownloadManagerDelegate(profile) { 81 } 82 83 virtual safe_browsing::DownloadProtectionService* 84 GetDownloadProtectionService() OVERRIDE { 85 return NULL; 86 } 87 88 virtual void NotifyExtensions( 89 content::DownloadItem* download, 90 const base::FilePath& suggested_virtual_path, 91 const NotifyExtensionsCallback& callback) OVERRIDE { 92 callback.Run(base::FilePath(), 93 DownloadPathReservationTracker::UNIQUIFY); 94 } 95 96 virtual void ReserveVirtualPath( 97 content::DownloadItem* download, 98 const base::FilePath& virtual_path, 99 bool create_directory, 100 DownloadPathReservationTracker::FilenameConflictAction conflict_action, 101 const DownloadPathReservationTracker::ReservedPathCallback& callback) 102 OVERRIDE { 103 // Pretend the path reservation succeeded without any change to 104 // |target_path|. 105 base::MessageLoop::current()->PostTask( 106 FROM_HERE, base::Bind(callback, virtual_path, true)); 107 } 108 109 virtual void PromptUserForDownloadPath( 110 DownloadItem* download, 111 const base::FilePath& suggested_path, 112 const DownloadTargetDeterminerDelegate::FileSelectedCallback& callback) 113 OVERRIDE { 114 base::FilePath return_path = MockPromptUserForDownloadPath(download, 115 suggested_path, 116 callback); 117 callback.Run(return_path); 118 } 119 120 MOCK_METHOD3( 121 MockPromptUserForDownloadPath, 122 base::FilePath( 123 content::DownloadItem*, 124 const base::FilePath&, 125 const DownloadTargetDeterminerDelegate::FileSelectedCallback&)); 126 127 private: 128 ~TestChromeDownloadManagerDelegate() {} 129 }; 130 131 class ChromeDownloadManagerDelegateTest : 132 public ChromeRenderViewHostTestHarness { 133 public: 134 ChromeDownloadManagerDelegateTest(); 135 136 // ::testing::Test 137 virtual void SetUp() OVERRIDE; 138 virtual void TearDown() OVERRIDE; 139 140 // Verifies and clears test expectations for |delegate_| and 141 // |download_manager_|. 142 void VerifyAndClearExpectations(); 143 144 // Creates MockDownloadItem and sets up default expectations. 145 content::MockDownloadItem* CreateActiveDownloadItem(int32 id); 146 147 // Given the relative path |path|, returns the full path under the temporary 148 // downloads directory. 149 base::FilePath GetPathInDownloadDir(const char* path); 150 151 // Set the kDownloadDefaultDirectory user preference to |path|. 152 void SetDefaultDownloadPath(const base::FilePath& path); 153 154 void DetermineDownloadTarget(DownloadItem* download, 155 DownloadTarget* result); 156 157 const base::FilePath& default_download_path() const; 158 TestChromeDownloadManagerDelegate* delegate(); 159 content::MockDownloadManager* download_manager(); 160 DownloadPrefs* download_prefs(); 161 162 private: 163 void OnDownloadTargetDone(DownloadTarget* result, 164 const base::FilePath& target_path, 165 DownloadItem::TargetDisposition disposition, 166 content::DownloadDangerType danger_type, 167 const base::FilePath& intermediate_path); 168 169 TestingPrefServiceSyncable* pref_service_; 170 base::ScopedTempDir test_download_dir_; 171 scoped_ptr<content::MockDownloadManager> download_manager_; 172 scoped_refptr<TestChromeDownloadManagerDelegate> delegate_; 173 MockWebContentsDelegate web_contents_delegate_; 174 }; 175 176 ChromeDownloadManagerDelegateTest::ChromeDownloadManagerDelegateTest() 177 : download_manager_(new ::testing::NiceMock<content::MockDownloadManager>) { 178 } 179 180 void ChromeDownloadManagerDelegateTest::SetUp() { 181 ChromeRenderViewHostTestHarness::SetUp(); 182 183 CHECK(profile()); 184 delegate_ = new TestChromeDownloadManagerDelegate(profile()); 185 delegate_->SetDownloadManager(download_manager_.get()); 186 pref_service_ = profile()->GetTestingPrefService(); 187 web_contents()->SetDelegate(&web_contents_delegate_); 188 189 ASSERT_TRUE(test_download_dir_.CreateUniqueTempDir()); 190 SetDefaultDownloadPath(test_download_dir_.path()); 191 } 192 193 void ChromeDownloadManagerDelegateTest::TearDown() { 194 base::RunLoop().RunUntilIdle(); 195 delegate_->Shutdown(); 196 ChromeRenderViewHostTestHarness::TearDown(); 197 } 198 199 void ChromeDownloadManagerDelegateTest::VerifyAndClearExpectations() { 200 ::testing::Mock::VerifyAndClearExpectations(delegate_.get()); 201 } 202 203 content::MockDownloadItem* 204 ChromeDownloadManagerDelegateTest::CreateActiveDownloadItem(int32 id) { 205 content::MockDownloadItem* item = 206 new ::testing::NiceMock<content::MockDownloadItem>(); 207 ON_CALL(*item, GetBrowserContext()) 208 .WillByDefault(Return(profile())); 209 ON_CALL(*item, GetDangerType()) 210 .WillByDefault(Return(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS)); 211 ON_CALL(*item, GetForcedFilePath()) 212 .WillByDefault(ReturnRefOfCopy(base::FilePath())); 213 ON_CALL(*item, GetFullPath()) 214 .WillByDefault(ReturnRefOfCopy(base::FilePath())); 215 ON_CALL(*item, GetHash()) 216 .WillByDefault(ReturnRefOfCopy(std::string())); 217 ON_CALL(*item, GetId()) 218 .WillByDefault(Return(id)); 219 ON_CALL(*item, GetLastReason()) 220 .WillByDefault(Return(content::DOWNLOAD_INTERRUPT_REASON_NONE)); 221 ON_CALL(*item, GetReferrerUrl()) 222 .WillByDefault(ReturnRefOfCopy(GURL())); 223 ON_CALL(*item, GetState()) 224 .WillByDefault(Return(DownloadItem::IN_PROGRESS)); 225 ON_CALL(*item, GetTargetFilePath()) 226 .WillByDefault(ReturnRefOfCopy(base::FilePath())); 227 ON_CALL(*item, GetTransitionType()) 228 .WillByDefault(Return(content::PAGE_TRANSITION_LINK)); 229 ON_CALL(*item, GetWebContents()) 230 .WillByDefault(Return(web_contents())); 231 ON_CALL(*item, HasUserGesture()) 232 .WillByDefault(Return(false)); 233 ON_CALL(*item, IsDangerous()) 234 .WillByDefault(Return(false)); 235 ON_CALL(*item, IsTemporary()) 236 .WillByDefault(Return(false)); 237 EXPECT_CALL(*download_manager_, GetDownload(id)) 238 .WillRepeatedly(Return(item)); 239 return item; 240 } 241 242 base::FilePath ChromeDownloadManagerDelegateTest::GetPathInDownloadDir( 243 const char* relative_path) { 244 base::FilePath full_path = 245 test_download_dir_.path().AppendASCII(relative_path); 246 return full_path.NormalizePathSeparators(); 247 } 248 249 void ChromeDownloadManagerDelegateTest::SetDefaultDownloadPath( 250 const base::FilePath& path) { 251 pref_service_->SetFilePath(prefs::kDownloadDefaultDirectory, path); 252 pref_service_->SetFilePath(prefs::kSaveFileDefaultDirectory, path); 253 } 254 255 void ChromeDownloadManagerDelegateTest::DetermineDownloadTarget( 256 DownloadItem* download_item, 257 DownloadTarget* result) { 258 base::WeakPtrFactory<ChromeDownloadManagerDelegateTest> factory(this); 259 delegate()->DetermineDownloadTarget( 260 download_item, 261 base::Bind(&ChromeDownloadManagerDelegateTest::OnDownloadTargetDone, 262 factory.GetWeakPtr(), base::Unretained(result))); 263 base::RunLoop loop_runner; 264 loop_runner.RunUntilIdle(); 265 } 266 267 void ChromeDownloadManagerDelegateTest::OnDownloadTargetDone( 268 DownloadTarget* result, 269 const base::FilePath& target_path, 270 DownloadItem::TargetDisposition target_disposition, 271 content::DownloadDangerType danger_type, 272 const base::FilePath& intermediate_path) { 273 result->target_path = target_path; 274 result->intermediate_path = intermediate_path; 275 result->target_disposition = target_disposition; 276 result->danger_type = danger_type; 277 } 278 279 const base::FilePath& ChromeDownloadManagerDelegateTest::default_download_path() 280 const { 281 return test_download_dir_.path(); 282 } 283 284 TestChromeDownloadManagerDelegate* 285 ChromeDownloadManagerDelegateTest::delegate() { 286 return delegate_.get(); 287 } 288 289 content::MockDownloadManager* 290 ChromeDownloadManagerDelegateTest::download_manager() { 291 return download_manager_.get(); 292 } 293 294 DownloadPrefs* ChromeDownloadManagerDelegateTest::download_prefs() { 295 return delegate_->download_prefs(); 296 } 297 298 } // namespace 299 300 TEST_F(ChromeDownloadManagerDelegateTest, StartDownload_LastSavePath) { 301 GURL download_url("http://example.com/foo.txt"); 302 303 scoped_ptr<content::MockDownloadItem> save_as_download( 304 CreateActiveDownloadItem(0)); 305 EXPECT_CALL(*save_as_download, GetURL()) 306 .Times(::testing::AnyNumber()) 307 .WillRepeatedly(ReturnRef(download_url)); 308 EXPECT_CALL(*save_as_download, GetTargetDisposition()) 309 .Times(::testing::AnyNumber()) 310 .WillRepeatedly(Return(DownloadItem::TARGET_DISPOSITION_PROMPT)); 311 312 scoped_ptr<content::MockDownloadItem> automatic_download( 313 CreateActiveDownloadItem(1)); 314 EXPECT_CALL(*automatic_download, GetURL()) 315 .Times(::testing::AnyNumber()) 316 .WillRepeatedly(ReturnRef(download_url)); 317 EXPECT_CALL(*automatic_download, GetTargetDisposition()) 318 .Times(::testing::AnyNumber()) 319 .WillRepeatedly(Return(DownloadItem::TARGET_DISPOSITION_OVERWRITE)); 320 321 { 322 // When the prompt is displayed for the first download, the user selects a 323 // path in a different directory. 324 DownloadTarget result; 325 base::FilePath expected_prompt_path(GetPathInDownloadDir("foo.txt")); 326 base::FilePath user_selected_path(GetPathInDownloadDir("bar/baz.txt")); 327 EXPECT_CALL(*delegate(), 328 MockPromptUserForDownloadPath(save_as_download.get(), 329 expected_prompt_path, _)) 330 .WillOnce(Return(user_selected_path)); 331 DetermineDownloadTarget(save_as_download.get(), &result); 332 EXPECT_EQ(user_selected_path, result.target_path); 333 VerifyAndClearExpectations(); 334 } 335 336 { 337 // The prompt path for the second download is the user selected directroy 338 // from the previous download. 339 DownloadTarget result; 340 base::FilePath expected_prompt_path(GetPathInDownloadDir("bar/foo.txt")); 341 EXPECT_CALL(*delegate(), 342 MockPromptUserForDownloadPath(save_as_download.get(), 343 expected_prompt_path, _)) 344 .WillOnce(Return(base::FilePath())); 345 DetermineDownloadTarget(save_as_download.get(), &result); 346 VerifyAndClearExpectations(); 347 } 348 349 { 350 // Start an automatic download. This one should get the default download 351 // path since the last download path only affects Save As downloads. 352 DownloadTarget result; 353 base::FilePath expected_path(GetPathInDownloadDir("foo.txt")); 354 DetermineDownloadTarget(automatic_download.get(), &result); 355 EXPECT_EQ(expected_path, result.target_path); 356 VerifyAndClearExpectations(); 357 } 358 359 { 360 // The prompt path for the next download should be the default. 361 download_prefs()->SetSaveFilePath(download_prefs()->DownloadPath()); 362 DownloadTarget result; 363 base::FilePath expected_prompt_path(GetPathInDownloadDir("foo.txt")); 364 EXPECT_CALL(*delegate(), 365 MockPromptUserForDownloadPath(save_as_download.get(), 366 expected_prompt_path, _)) 367 .WillOnce(Return(base::FilePath())); 368 DetermineDownloadTarget(save_as_download.get(), &result); 369 VerifyAndClearExpectations(); 370 } 371 } 372