OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 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 <stddef.h> |
| 6 #include <stdint.h> |
| 7 #include <utility> |
| 8 |
| 9 #include "base/callback_helpers.h" |
| 10 #include "base/files/file_path.h" |
| 11 #include "base/files/file_util.h" |
| 12 #include "base/files/scoped_temp_dir.h" |
| 13 #include "base/macros.h" |
| 14 #include "base/memory/ref_counted.h" |
| 15 #include "base/message_loop/message_loop.h" |
| 16 #include "base/threading/platform_thread.h" |
| 17 #include "base/threading/sequenced_worker_pool.h" |
| 18 #include "base/threading/thread_restrictions.h" |
| 19 #include "content/browser/byte_stream.h" |
| 20 #include "content/browser/download/download_file_factory.h" |
| 21 #include "content/browser/download/download_file_impl.h" |
| 22 #include "content/browser/download/download_item_impl.h" |
| 23 #include "content/browser/download/download_manager_impl.h" |
| 24 #include "content/browser/web_contents/web_contents_impl.h" |
| 25 #include "content/public/test/browser_test_utils.h" |
| 26 #include "content/public/test/content_browser_test.h" |
| 27 #include "content/public/test/content_browser_test_utils.h" |
| 28 #include "content/public/test/download_test_observer.h" |
| 29 #include "content/public/test/test_utils.h" |
| 30 #include "headless/lib/browser/headless_browser_context_impl.h" |
| 31 #include "headless/lib/browser/headless_download_manager_delegate.h" |
| 32 #include "headless/public/headless_browser.h" |
| 33 #include "headless/test/headless_browser_test.h" |
| 34 #include "net/dns/mock_host_resolver.h" |
| 35 #include "net/test/embedded_test_server/embedded_test_server.h" |
| 36 #include "net/test/embedded_test_server/http_request.h" |
| 37 #include "net/test/embedded_test_server/http_response.h" |
| 38 #include "net/test/url_request/url_request_mock_http_job.h" |
| 39 #include "net/test/url_request/url_request_slow_download_job.h" |
| 40 #include "testing/gmock/include/gmock/gmock.h" |
| 41 #include "testing/gtest/include/gtest/gtest.h" |
| 42 #include "url/gurl.h" |
| 43 |
| 44 using namespace content; |
| 45 |
| 46 namespace net { |
| 47 class NetLogWithSource; |
| 48 } |
| 49 |
| 50 namespace headless { |
| 51 |
| 52 namespace { |
| 53 |
| 54 const std::string kOriginOne = "one.example"; |
| 55 const std::string kOriginTwo = "two.example"; |
| 56 |
| 57 DownloadManagerImpl* DownloadManagerForBrowser( |
| 58 HeadlessBrowser* browser) { |
| 59 // We're in a content_browsertest; we know that the DownloadManager |
| 60 // is a DownloadManagerImpl. |
| 61 HeadlessBrowserContextImpl* ctx = |
| 62 HeadlessBrowserContextImpl::From(browser->GetDefaultBrowserContext()); |
| 63 |
| 64 return static_cast<DownloadManagerImpl*>( |
| 65 BrowserContext::GetDownloadManager(ctx)); |
| 66 } |
| 67 |
| 68 class CountingDownloadFile : public DownloadFileImpl { |
| 69 public: |
| 70 CountingDownloadFile(std::unique_ptr<DownloadSaveInfo> save_info, |
| 71 const base::FilePath& default_downloads_directory, |
| 72 std::unique_ptr<ByteStreamReader> stream, |
| 73 const net::NetLogWithSource& net_log, |
| 74 base::WeakPtr<DownloadDestinationObserver> observer) |
| 75 : DownloadFileImpl(std::move(save_info), |
| 76 default_downloads_directory, |
| 77 std::move(stream), |
| 78 net_log, |
| 79 observer) {} |
| 80 |
| 81 ~CountingDownloadFile() override { |
| 82 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 83 active_files_--; |
| 84 } |
| 85 |
| 86 void Initialize(const InitializeCallback& callback, |
| 87 const CancelRequestCallback& cancel_request_callback, |
| 88 const DownloadItem::ReceivedSlices& received_slices, |
| 89 bool is_parallelizable) override { |
| 90 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 91 active_files_++; |
| 92 DownloadFileImpl::Initialize(callback, cancel_request_callback, |
| 93 received_slices, is_parallelizable); |
| 94 } |
| 95 |
| 96 static void GetNumberActiveFiles(int* result) { |
| 97 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 98 *result = active_files_; |
| 99 } |
| 100 |
| 101 // Can be called on any thread, and will block (running message loop) |
| 102 // until data is returned. |
| 103 static int GetNumberActiveFilesFromFileThread() { |
| 104 int result = -1; |
| 105 BrowserThread::PostTaskAndReply( |
| 106 BrowserThread::FILE, FROM_HERE, |
| 107 base::Bind(&CountingDownloadFile::GetNumberActiveFiles, &result), |
| 108 base::MessageLoop::current()->QuitWhenIdleClosure()); |
| 109 base::RunLoop().Run(); |
| 110 DCHECK_NE(-1, result); |
| 111 return result; |
| 112 } |
| 113 |
| 114 private: |
| 115 static int active_files_; |
| 116 }; |
| 117 |
| 118 int CountingDownloadFile::active_files_ = 0; |
| 119 |
| 120 class CountingDownloadFileFactory : public DownloadFileFactory { |
| 121 public: |
| 122 CountingDownloadFileFactory() {} |
| 123 ~CountingDownloadFileFactory() override {} |
| 124 |
| 125 // DownloadFileFactory interface. |
| 126 DownloadFile* CreateFile( |
| 127 std::unique_ptr<DownloadSaveInfo> save_info, |
| 128 const base::FilePath& default_downloads_directory, |
| 129 std::unique_ptr<ByteStreamReader> stream, |
| 130 const net::NetLogWithSource& net_log, |
| 131 base::WeakPtr<DownloadDestinationObserver> observer) override { |
| 132 return new CountingDownloadFile(std::move(save_info), |
| 133 default_downloads_directory, |
| 134 std::move(stream), net_log, observer); |
| 135 } |
| 136 }; |
| 137 |
| 138 // Get the next created download. |
| 139 class DownloadCreateObserver : DownloadManager::Observer { |
| 140 public: |
| 141 DownloadCreateObserver(DownloadManager* manager) |
| 142 : manager_(manager), item_(nullptr) { |
| 143 manager_->AddObserver(this); |
| 144 } |
| 145 |
| 146 ~DownloadCreateObserver() override { |
| 147 if (manager_) |
| 148 manager_->RemoveObserver(this); |
| 149 manager_ = nullptr; |
| 150 } |
| 151 |
| 152 void ManagerGoingDown(DownloadManager* manager) override { |
| 153 DCHECK_EQ(manager_, manager); |
| 154 manager_->RemoveObserver(this); |
| 155 manager_ = nullptr; |
| 156 } |
| 157 |
| 158 void OnDownloadCreated(DownloadManager* manager, |
| 159 DownloadItem* download) override { |
| 160 if (!item_) |
| 161 item_ = download; |
| 162 |
| 163 if (!completion_closure_.is_null()) |
| 164 base::ResetAndReturn(&completion_closure_).Run(); |
| 165 } |
| 166 |
| 167 DownloadItem* WaitForFinished() { |
| 168 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 169 if (!item_) { |
| 170 base::RunLoop run_loop; |
| 171 completion_closure_ = run_loop.QuitClosure(); |
| 172 run_loop.Run(); |
| 173 } |
| 174 return item_; |
| 175 } |
| 176 |
| 177 private: |
| 178 DownloadManager* manager_; |
| 179 DownloadItem* item_; |
| 180 base::Closure completion_closure_; |
| 181 }; |
| 182 |
| 183 bool IsDownloadInState(DownloadItem::DownloadState state, DownloadItem* item) { |
| 184 return item->GetState() == state; |
| 185 } |
| 186 |
| 187 class HeadlessDownloadContentTest : public HeadlessBrowserTest { |
| 188 protected: |
| 189 void SetUpOnMainThread() override { |
| 190 base::ThreadRestrictions::SetIOAllowed(true); |
| 191 ASSERT_TRUE(downloads_directory_.CreateUniqueTempDir()); |
| 192 |
| 193 headless::HeadlessBrowserContext::Builder context_builder = |
| 194 browser()->CreateBrowserContextBuilder(); |
| 195 headless::HeadlessBrowserContext* browser_context = context_builder.Build(); |
| 196 browser()->SetDefaultBrowserContext(browser_context); |
| 197 |
| 198 test_delegate_.reset(new HeadlessDownloadManagerDelegate()); |
| 199 test_delegate_->SetDownloadBehaviorForTesting( |
| 200 downloads_directory_.GetPath()); |
| 201 DownloadManager* manager = DownloadManagerForBrowser(browser()); |
| 202 manager->GetDelegate()->Shutdown(); |
| 203 manager->SetDelegate(test_delegate_.get()); |
| 204 test_delegate_->SetDownloadManager(manager); |
| 205 |
| 206 BrowserThread::PostTask( |
| 207 BrowserThread::IO, FROM_HERE, |
| 208 base::Bind(&net::URLRequestSlowDownloadJob::AddUrlHandler)); |
| 209 base::FilePath mock_base(GetTestFilePath("download", "")); |
| 210 BrowserThread::PostTask( |
| 211 BrowserThread::IO, FROM_HERE, |
| 212 base::Bind( |
| 213 &net::URLRequestMockHTTPJob::AddUrlHandlers, mock_base, |
| 214 make_scoped_refptr(content::BrowserThread::GetBlockingPool()))); |
| 215 ASSERT_TRUE(embedded_test_server()->Start()); |
| 216 const std::string real_host = |
| 217 embedded_test_server()->host_port_pair().host(); |
| 218 host_resolver()->AddRule(kOriginOne, real_host); |
| 219 host_resolver()->AddRule(kOriginTwo, real_host); |
| 220 } |
| 221 |
| 222 // Create a DownloadTestObserverTerminal that will wait for the |
| 223 // specified number of downloads to finish. |
| 224 DownloadTestObserver* CreateWaiter(HeadlessBrowser* browser, |
| 225 int num_downloads) { |
| 226 DownloadManager* download_manager = DownloadManagerForBrowser(browser); |
| 227 return new DownloadTestObserverTerminal( |
| 228 download_manager, num_downloads, |
| 229 DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL); |
| 230 } |
| 231 |
| 232 void WaitForCompletion(DownloadItem* download) { |
| 233 DownloadUpdatedObserver( |
| 234 download, base::Bind(&IsDownloadInState, DownloadItem::COMPLETE)) |
| 235 .WaitForEvent(); |
| 236 } |
| 237 |
| 238 // Note: Cannot be used with other alternative DownloadFileFactorys |
| 239 void SetupEnsureNoPendingDownloads() { |
| 240 DownloadManagerForBrowser(browser())->SetDownloadFileFactoryForTesting( |
| 241 std::unique_ptr<DownloadFileFactory>( |
| 242 new CountingDownloadFileFactory())); |
| 243 } |
| 244 |
| 245 bool EnsureNoPendingDownloads() { |
| 246 bool result = true; |
| 247 BrowserThread::PostTask( |
| 248 BrowserThread::IO, FROM_HERE, |
| 249 base::Bind(&EnsureNoPendingDownloadJobsOnIO, &result)); |
| 250 base::RunLoop().Run(); |
| 251 return result && |
| 252 (CountingDownloadFile::GetNumberActiveFilesFromFileThread() == 0); |
| 253 } |
| 254 |
| 255 HeadlessWebContents* CreateWebContentsForURL(HeadlessBrowser* browser, |
| 256 const GURL& url) { |
| 257 return browser->GetDefaultBrowserContext() |
| 258 ->CreateWebContentsBuilder() |
| 259 .SetInitialURL(url) |
| 260 .Build(); |
| 261 } |
| 262 |
| 263 // Checks that |path| is has |file_size| bytes, and matches the |value| |
| 264 // string. |
| 265 bool VerifyFile(const base::FilePath& path, |
| 266 const std::string& value, |
| 267 const int64_t file_size) { |
| 268 std::string file_contents; |
| 269 |
| 270 { |
| 271 base::ThreadRestrictions::ScopedAllowIO allow_io_during_test_verification; |
| 272 bool read = base::ReadFileToString(path, &file_contents); |
| 273 EXPECT_TRUE(read) << "Failed reading file: " << path.value() << std::endl; |
| 274 if (!read) |
| 275 return false; // Couldn't read the file. |
| 276 } |
| 277 |
| 278 // Note: we don't handle really large files (more than size_t can hold) |
| 279 // so we will fail in that case. |
| 280 size_t expected_size = static_cast<size_t>(file_size); |
| 281 |
| 282 // Check the size. |
| 283 EXPECT_EQ(expected_size, file_contents.size()); |
| 284 if (expected_size != file_contents.size()) |
| 285 return false; |
| 286 |
| 287 // Check the contents. |
| 288 EXPECT_EQ(value, file_contents); |
| 289 if (memcmp(file_contents.c_str(), value.c_str(), expected_size) != 0) |
| 290 return false; |
| 291 |
| 292 return true; |
| 293 } |
| 294 |
| 295 // Start a download and return the item. |
| 296 DownloadItem* StartDownloadAndReturnItem(HeadlessBrowser* browser, GURL url) { |
| 297 std::unique_ptr<DownloadCreateObserver> observer( |
| 298 new DownloadCreateObserver(DownloadManagerForBrowser(browser))); |
| 299 |
| 300 CreateWebContentsForURL(browser, url); |
| 301 return observer->WaitForFinished(); |
| 302 } |
| 303 |
| 304 private: |
| 305 static void EnsureNoPendingDownloadJobsOnIO(bool* result) { |
| 306 if (net::URLRequestSlowDownloadJob::NumberOutstandingRequests()) |
| 307 *result = false; |
| 308 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| 309 base::MessageLoop::QuitWhenIdleClosure()); |
| 310 } |
| 311 |
| 312 // Location of the downloads directory for these tests |
| 313 base::ScopedTempDir downloads_directory_; |
| 314 std::unique_ptr<HeadlessDownloadManagerDelegate> test_delegate_; |
| 315 }; |
| 316 |
| 317 } // namespace |
| 318 |
| 319 IN_PROC_BROWSER_TEST_F(HeadlessDownloadContentTest, DownloadCancelled) { |
| 320 base::ThreadRestrictions::SetIOAllowed(true); |
| 321 SetupEnsureNoPendingDownloads(); |
| 322 |
| 323 // Create a download, wait until it's started, and confirm |
| 324 // we're in the expected state. |
| 325 DownloadItem* download = StartDownloadAndReturnItem( |
| 326 browser(), GURL(net::URLRequestSlowDownloadJob::kUnknownSizeUrl)); |
| 327 ASSERT_EQ(DownloadItem::IN_PROGRESS, download->GetState()); |
| 328 |
| 329 // Cancel the download and wait for download system quiesce. |
| 330 download->Cancel(true); |
| 331 scoped_refptr<DownloadTestFlushObserver> flush_observer( |
| 332 new DownloadTestFlushObserver(DownloadManagerForBrowser(browser()))); |
| 333 flush_observer->WaitForFlush(); |
| 334 |
| 335 // Get the important info from other threads and check it. |
| 336 EXPECT_TRUE(EnsureNoPendingDownloads()); |
| 337 } |
| 338 |
| 339 // Check that downloading a single file works. |
| 340 IN_PROC_BROWSER_TEST_F(HeadlessDownloadContentTest, SingleDownload) { |
| 341 base::ThreadRestrictions::SetIOAllowed(true); |
| 342 SetupEnsureNoPendingDownloads(); |
| 343 |
| 344 // Create a download, wait until it's started, and confirm |
| 345 // we're in the expected state. |
| 346 DownloadItem* download1 = StartDownloadAndReturnItem( |
| 347 browser(), |
| 348 GURL(net::URLRequestMockHTTPJob::GetMockUrl("download-test.lib"))); |
| 349 ASSERT_EQ(DownloadItem::IN_PROGRESS, download1->GetState()); |
| 350 |
| 351 WaitForCompletion(download1); |
| 352 ASSERT_EQ(DownloadItem::COMPLETE, download1->GetState()); |
| 353 } |
| 354 |
| 355 // Check that downloading multiple (in this case, 2) files does not result in |
| 356 // corrupted files. |
| 357 IN_PROC_BROWSER_TEST_F(HeadlessDownloadContentTest, MultiDownload) { |
| 358 SetupEnsureNoPendingDownloads(); |
| 359 |
| 360 // Create a download, wait until it's started, and confirm |
| 361 // we're in the expected state. |
| 362 DownloadItem* download1 = StartDownloadAndReturnItem( |
| 363 browser(), GURL(net::URLRequestSlowDownloadJob::kUnknownSizeUrl)); |
| 364 ASSERT_EQ(DownloadItem::IN_PROGRESS, download1->GetState()); |
| 365 |
| 366 // Start the second download and wait until it's done. |
| 367 GURL url(net::URLRequestMockHTTPJob::GetMockUrl("download-test.lib")); |
| 368 DownloadItem* download2 = StartDownloadAndReturnItem(browser(), url); |
| 369 WaitForCompletion(download2); |
| 370 |
| 371 ASSERT_EQ(DownloadItem::IN_PROGRESS, download1->GetState()); |
| 372 ASSERT_EQ(DownloadItem::COMPLETE, download2->GetState()); |
| 373 |
| 374 // Allow the first request to finish. |
| 375 std::unique_ptr<DownloadTestObserver> observer2(CreateWaiter(browser(), 1)); |
| 376 CreateWebContentsForURL( |
| 377 browser(), GURL(net::URLRequestSlowDownloadJob::kFinishDownloadUrl)); |
| 378 observer2->WaitForFinished(); // Wait for the third request. |
| 379 EXPECT_EQ(1u, observer2->NumDownloadsSeenInState(DownloadItem::COMPLETE)); |
| 380 |
| 381 // Get the important info from other threads and check it. |
| 382 EXPECT_TRUE(EnsureNoPendingDownloads()); |
| 383 |
| 384 // The |DownloadItem|s should now be done and have the final file names. |
| 385 // Verify that the files have the expected data and size. |
| 386 // |file1| should be full of '*'s, and |file2| should be the same as the |
| 387 // source file. |
| 388 base::FilePath file1(download1->GetTargetFilePath()); |
| 389 size_t file_size1 = net::URLRequestSlowDownloadJob::kFirstDownloadSize + |
| 390 net::URLRequestSlowDownloadJob::kSecondDownloadSize; |
| 391 std::string expected_contents(file_size1, '*'); |
| 392 ASSERT_TRUE(VerifyFile(file1, expected_contents, file_size1)); |
| 393 |
| 394 base::FilePath file2(download2->GetTargetFilePath()); |
| 395 ASSERT_TRUE(base::ContentsEqual( |
| 396 file2, GetTestFilePath("download", "download-test.lib"))); |
| 397 } |
| 398 |
| 399 } // namespace headless |
OLD | NEW |