diff options
Diffstat (limited to 'content/browser/download/download_browsertest.cc')
-rw-r--r-- | content/browser/download/download_browsertest.cc | 688 |
1 files changed, 595 insertions, 93 deletions
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc index 9e8921c..a62cd89 100644 --- a/content/browser/download/download_browsertest.cc +++ b/content/browser/download/download_browsertest.cc @@ -19,6 +19,7 @@ #include "content/public/browser/power_save_blocker.h" #include "content/public/common/content_switches.h" #include "content/public/test/download_test_observer.h" +#include "content/public/test/test_file_error_injector.h" #include "content/public/test/test_utils.h" #include "content/shell/shell.h" #include "content/shell/shell_browser_context.h" @@ -349,23 +350,113 @@ class TestShellDownloadManagerDelegate : public ShellDownloadManagerDelegate { std::vector<DownloadOpenDelayedCallback> delayed_callbacks_; }; -// Filter for handling resumption; don't return true until -// we see first a transition to IN_PROGRESS then a transition to -// some final state. Since this is a stateful filter, we -// must provide a flag in which to store the state; that flag -// must be false on initialization. The flag must be the first argument -// so that Bind() can be used to produce the callback expected by -// DownloadUpdatedObserver. -bool DownloadResumptionFilter(bool* state_flag, DownloadItem* download) { - if (*state_flag && DownloadItem::IN_PROGRESS != download->GetState()) { - return true; +// Record all state transitions and byte counts on the observed download. +class RecordingDownloadObserver : DownloadItem::Observer { + public: + struct RecordStruct { + DownloadItem::DownloadState state; + int bytes_received; + }; + + typedef std::vector<RecordStruct> RecordVector; + + RecordingDownloadObserver(DownloadItem* download) + : download_(download) { + last_state_.state = download->GetState(); + last_state_.bytes_received = download->GetReceivedBytes(); + download_->AddObserver(this); } - if (DownloadItem::IN_PROGRESS == download->GetState()) - *state_flag = true; + virtual ~RecordingDownloadObserver() { + RemoveObserver(); + } + + void CompareToExpectedRecord(const RecordStruct expected[], size_t size) { + EXPECT_EQ(size, record_.size()); + int min = size > record_.size() ? record_.size() : size; + for (int i = 0; i < min; ++i) { + EXPECT_EQ(expected[i].state, record_[i].state) << "Iteration " << i; + EXPECT_EQ(expected[i].bytes_received, record_[i].bytes_received) + << "Iteration " << i; + } + } + + private: + virtual void OnDownloadUpdated(DownloadItem* download) OVERRIDE { + DCHECK_EQ(download_, download); + DownloadItem::DownloadState state = download->GetState(); + int bytes = download->GetReceivedBytes(); + if (last_state_.state != state || last_state_.bytes_received > bytes) { + last_state_.state = state; + last_state_.bytes_received = bytes; + record_.push_back(last_state_); + } + } + + virtual void OnDownloadDestroyed(DownloadItem* download) OVERRIDE { + DCHECK_EQ(download_, download); + RemoveObserver(); + } + + void RemoveObserver() { + if (download_) { + download_->RemoveObserver(this); + download_ = NULL; + } + } + + DownloadItem* download_; + RecordStruct last_state_; + RecordVector record_; +}; + +// Get the next created download. +class DownloadCreateObserver : DownloadManager::Observer { + public: + DownloadCreateObserver(DownloadManager* manager) + : manager_(manager), + item_(NULL), + waiting_(false) { + manager_->AddObserver(this); + } + + ~DownloadCreateObserver() { + if (manager_) + manager_->RemoveObserver(this); + manager_ = NULL; + } + + virtual void ManagerGoingDown(DownloadManager* manager) { + DCHECK_EQ(manager_, manager); + manager_->RemoveObserver(this); + manager_ = NULL; + } + + virtual void OnDownloadCreated(DownloadManager* manager, + DownloadItem* download) { + if (!item_) + item_ = download; + + if (waiting_) + MessageLoopForUI::current()->Quit(); + } + + DownloadItem* WaitForFinished() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (!item_) { + waiting_ = true; + RunMessageLoop(); + waiting_ = false; + } + return item_; + } + + private: + DownloadManager* manager_; + DownloadItem* item_; + bool waiting_; +}; - return false; -} // Filter for waiting for intermediate file rename. bool IntermediateFileRenameFilter(DownloadItem* download) { @@ -377,10 +468,36 @@ bool DataReceivedFilter(int number_of_bytes, DownloadItem* download) { return download->GetReceivedBytes() >= number_of_bytes; } +// Filter for download completion. +bool DownloadCompleteFilter(DownloadItem* download) { + return download->GetState() == DownloadItem::COMPLETE; +} + +// Filter for saving the size of the download when the first IN_PROGRESS +// is hit. +bool InitialSizeFilter(int* download_size, DownloadItem* download) { + if (download->GetState() != DownloadItem::IN_PROGRESS) + return false; + + *download_size = download->GetReceivedBytes(); + return true; +} + } // namespace class DownloadContentTest : public ContentBrowserTest { protected: + // An initial send from a website of at least this size will not be + // help up by buffering in the underlying downloads ByteStream data + // transfer. This is important because on resumption tests we wait + // until we've gotten the data we expect before allowing the test server + // to send its reset, to get around hard close semantics on the Windows + // socket layer implementation. + int GetSafeBufferChunk() const { + return (DownloadResourceHandler::kDownloadByteStreamSize / + ByteStreamWriter::kFractionBufferBeforeSending) + 1; + } + virtual void SetUpOnMainThread() OVERRIDE { ASSERT_TRUE(downloads_directory_.CreateUniqueTempDir()); @@ -417,10 +534,10 @@ class DownloadContentTest : public ContentBrowserTest { // Create a DownloadTestObserverInProgress that will wait for the // specified number of downloads to start. - DownloadTestObserver* CreateInProgressWaiter( + DownloadCreateObserver* CreateInProgressWaiter( Shell* shell, int num_downloads) { DownloadManager* download_manager = DownloadManagerForShell(shell); - return new DownloadTestObserverInProgress(download_manager, num_downloads); + return new DownloadCreateObserver(download_manager); } // Note: Cannot be used with other alternative DownloadFileFactorys @@ -477,6 +594,70 @@ class DownloadContentTest : public ContentBrowserTest { return true; } + // Start a download and return the item. + DownloadItem* StartDownloadAndReturnItem(GURL url) { + scoped_ptr<DownloadCreateObserver> observer( + CreateInProgressWaiter(shell(), 1)); + NavigateToURL(shell(), url); + observer->WaitForFinished(); + std::vector<DownloadItem*> downloads; + DownloadManagerForShell(shell())->GetAllDownloads(&downloads); + EXPECT_EQ(1u, downloads.size()); + if (1u != downloads.size()) + return NULL; + return downloads[0]; + } + + // Wait for data + void WaitForData(DownloadItem* download, int size) { + DownloadUpdatedObserver data_observer( + download, base::Bind(&DataReceivedFilter, size)); + data_observer.WaitForEvent(); + ASSERT_EQ(size, download->GetReceivedBytes()); + ASSERT_EQ(DownloadItem::IN_PROGRESS, download->GetState()); + } + + // Tell the test server to release a pending RST and confirm + // that the interrupt is received properly (for download resumption + // testing). + void ReleaseRSTAndConfirmInterruptForResume(DownloadItem* download) { + scoped_ptr<DownloadTestObserver> rst_observer(CreateWaiter(shell(), 1)); + NavigateToURL(shell(), test_server()->GetURL("download-finish")); + rst_observer->WaitForFinished(); + EXPECT_EQ(DownloadItem::INTERRUPTED, download->GetState()); + } + + // Confirm file status expected for the given location in a stream + // provided by the resume test server. + void ConfirmFileStatusForResume( + DownloadItem* download, bool file_exists, + int received_bytes, int total_bytes, + const base::FilePath& expected_filename) { + EXPECT_EQ(received_bytes, download->GetReceivedBytes()); + EXPECT_EQ(total_bytes, download->GetTotalBytes()); + EXPECT_EQ(expected_filename.value(), + download->GetFullPath().BaseName().value()); + EXPECT_EQ(file_exists, + (!download->GetFullPath().empty() && + file_util::PathExists(download->GetFullPath()))); + + if (file_exists) { + std::string file_contents; + EXPECT_TRUE(file_util::ReadFileToString( + download->GetFullPath(), &file_contents)); + + ASSERT_EQ(static_cast<size_t>(received_bytes), file_contents.size()); + for (int i = 0; i < received_bytes; ++i) { + EXPECT_EQ(static_cast<char>((i * 2 + 15) % 256), file_contents[i]) + << "File contents diverged at position " << i + << " for " << expected_filename.value(); + + if (static_cast<char>((i * 2 + 15) % 256) != file_contents[i]) + return; + } + } + } + private: static void EnsureNoPendingDownloadJobsOnIO(bool* result) { if (URLRequestSlowDownloadJob::NumberOutstandingRequests()) @@ -494,7 +675,8 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadCancelled) { // Create a download, wait until it's started, and confirm // we're in the expected state. - scoped_ptr<DownloadTestObserver> observer(CreateInProgressWaiter(shell(), 1)); + scoped_ptr<DownloadCreateObserver> observer( + CreateInProgressWaiter(shell(), 1)); NavigateToURL(shell(), GURL(URLRequestSlowDownloadJob::kUnknownSizeUrl)); observer->WaitForFinished(); @@ -520,7 +702,7 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, MultiDownload) { // Create a download, wait until it's started, and confirm // we're in the expected state. - scoped_ptr<DownloadTestObserver> observer1( + scoped_ptr<DownloadCreateObserver> observer1( CreateInProgressWaiter(shell(), 1)); NavigateToURL(shell(), GURL(URLRequestSlowDownloadJob::kUnknownSizeUrl)); observer1->WaitForFinished(); @@ -676,7 +858,8 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, CancelAtRelease) { // works properly. IN_PROC_BROWSER_TEST_F(DownloadContentTest, ShutdownInProgress) { // Create a download that won't complete. - scoped_ptr<DownloadTestObserver> observer(CreateInProgressWaiter(shell(), 1)); + scoped_ptr<DownloadCreateObserver> observer( + CreateInProgressWaiter(shell(), 1)); NavigateToURL(shell(), GURL(URLRequestSlowDownloadJob::kUnknownSizeUrl)); observer->WaitForFinished(); @@ -773,87 +956,406 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, ResumeInterruptedDownload) { switches::kEnableDownloadResumption); ASSERT_TRUE(test_server()->Start()); - // Figure out the size of the first chunk to send so that it makes it - // through the buffering between DownloadResourceHandler and - // DownloadFileImpl. - int initial_chunk = (DownloadResourceHandler::kDownloadByteStreamSize / - ByteStreamWriter::kFractionBufferBeforeSending) + 1; - GURL url = test_server()->GetURL( StringPrintf("rangereset?size=%d&rst_boundary=%d", - initial_chunk * 2, initial_chunk)); + GetSafeBufferChunk() * 3, GetSafeBufferChunk())); - // Download and wait for file determination. - scoped_ptr<DownloadTestObserver> observer(CreateInProgressWaiter(shell(), 1)); - NavigateToURL(shell(), url); - observer->WaitForFinished(); + DownloadItem* download(StartDownloadAndReturnItem(url)); + WaitForData(download, GetSafeBufferChunk()); - std::vector<DownloadItem*> downloads; - DownloadManagerForShell(shell())->GetAllDownloads(&downloads); - ASSERT_EQ(1u, downloads.size()); - DownloadItem* download(downloads[0]); - - // Wait for intermediate name, then for all expected data to arrive. - // TODO(rdsmith): Figure out how to handle the races needed - // so that we don't have to wait for the target name determination. - DownloadUpdatedObserver intermediate_observer( - download, base::Bind(&IntermediateFileRenameFilter)); - intermediate_observer.WaitForEvent(); - - DownloadUpdatedObserver data_observer( - download, base::Bind(&DataReceivedFilter, initial_chunk)); - data_observer.WaitForEvent(); - - // Shouldn't have received any extra data. - ASSERT_EQ(initial_chunk, download->GetReceivedBytes()); - - // Unleash the RST! - scoped_ptr<DownloadTestObserver> rst_observer(CreateWaiter(shell(), 1)); - GURL release_url = test_server()->GetURL("download-finish"); - NavigateToURL(shell(), release_url); - rst_observer->WaitForFinished(); - EXPECT_EQ(DownloadItem::INTERRUPTED, download->GetState()); - EXPECT_EQ(initial_chunk, download->GetReceivedBytes()); - EXPECT_EQ(initial_chunk * 2, download->GetTotalBytes()); - EXPECT_EQ(FILE_PATH_LITERAL("rangereset.crdownload"), - download->GetFullPath().BaseName().value()); + // Confirm resumption while in progress doesn't do anything. + download->ResumeInterruptedDownload(); + ASSERT_EQ(GetSafeBufferChunk(), download->GetReceivedBytes()); + ASSERT_EQ(DownloadItem::IN_PROGRESS, download->GetState()); + + // Tell the server to send the RST and confirm the interrupt happens. + ReleaseRSTAndConfirmInterruptForResume(download); + ConfirmFileStatusForResume( + download, true, GetSafeBufferChunk(), GetSafeBufferChunk() * 3, + base::FilePath(FILE_PATH_LITERAL("rangereset.crdownload"))); + + // Resume, confirming received bytes on resumption is correct. + int initial_size = 0; + DownloadUpdatedObserver initial_size_observer( + download, base::Bind(&InitialSizeFilter, &initial_size)); + download->ResumeInterruptedDownload(); + initial_size_observer.WaitForEvent(); + EXPECT_EQ(GetSafeBufferChunk(), initial_size); - { - std::string file_contents; - std::string expected_contents(initial_chunk, 'X'); - ASSERT_TRUE(file_util::ReadFileToString( - download->GetFullPath(), &file_contents)); - EXPECT_EQ(static_cast<size_t>(initial_chunk), file_contents.size()); - // In conditional to avoid spamming the console with two very long strings. - if (expected_contents != file_contents) - EXPECT_TRUE(false) << "File contents do not have expected value."; - } - - // Resume; should get entire file (note that a restart will fail - // here because it'll produce another RST). - bool flag(false); - DownloadUpdatedObserver complete_observer( - download, base::Bind(&DownloadResumptionFilter, &flag)); + // and wait for expected data. + WaitForData(download, GetSafeBufferChunk() * 2); + + // Tell the server to send the RST and confirm the interrupt happens. + ReleaseRSTAndConfirmInterruptForResume(download); + ConfirmFileStatusForResume( + download, true, GetSafeBufferChunk() * 2, GetSafeBufferChunk() * 3, + base::FilePath(FILE_PATH_LITERAL("rangereset.crdownload"))); + + // Resume and wait for completion. + DownloadUpdatedObserver completion_observer( + download, base::Bind(DownloadCompleteFilter)); download->ResumeInterruptedDownload(); - NavigateToURL(shell(), release_url); // Needed to get past hold. - complete_observer.WaitForEvent(); - EXPECT_EQ(DownloadItem::COMPLETE, download->GetState()); - EXPECT_EQ(initial_chunk * 2, download->GetReceivedBytes()); - EXPECT_EQ(initial_chunk * 2, download->GetTotalBytes()); - EXPECT_EQ(FILE_PATH_LITERAL("rangereset"), - download->GetFullPath().BaseName().value()) - << "Target path name: " << download->GetTargetFilePath().value(); + completion_observer.WaitForEvent(); - { - std::string file_contents; - std::string expected_contents(initial_chunk * 2, 'X'); - ASSERT_TRUE(file_util::ReadFileToString( - download->GetFullPath(), &file_contents)); - EXPECT_EQ(static_cast<size_t>(initial_chunk * 2), file_contents.size()); - // In conditional to avoid spamming the console with two 800 char strings. - if (expected_contents != file_contents) - EXPECT_TRUE(false) << "File contents do not have expected value."; - } + ConfirmFileStatusForResume( + download, true, GetSafeBufferChunk() * 3, GetSafeBufferChunk() * 3, + base::FilePath(FILE_PATH_LITERAL("rangereset"))); + + // Confirm resumption while complete doesn't do anything. + download->ResumeInterruptedDownload(); + ASSERT_EQ(GetSafeBufferChunk() * 3, download->GetReceivedBytes()); + ASSERT_EQ(DownloadItem::COMPLETE, download->GetState()); + RunAllPendingInMessageLoop(); + ASSERT_EQ(GetSafeBufferChunk() * 3, download->GetReceivedBytes()); + ASSERT_EQ(DownloadItem::COMPLETE, download->GetState()); +} + +// Confirm restart fallback happens if a range request is bounced. +IN_PROC_BROWSER_TEST_F(DownloadContentTest, ResumeInterruptedDownloadNoRange) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableDownloadResumption); + ASSERT_TRUE(test_server()->Start()); + + // Auto-restart if server doesn't handle ranges. + GURL url = test_server()->GetURL( + StringPrintf( + // First download hits an RST, rest don't, no ranges. + "rangereset?size=%d&rst_boundary=%d&" + "token=NoRange&rst_limit=1&bounce_range", + GetSafeBufferChunk() * 3, GetSafeBufferChunk())); + + // Start the download and wait for first data chunk. + DownloadItem* download(StartDownloadAndReturnItem(url)); + WaitForData(download, GetSafeBufferChunk()); + + RecordingDownloadObserver recorder(download); + + ReleaseRSTAndConfirmInterruptForResume(download); + ConfirmFileStatusForResume( + download, true, GetSafeBufferChunk(), GetSafeBufferChunk() * 3, + base::FilePath(FILE_PATH_LITERAL("rangereset.crdownload"))); + + DownloadUpdatedObserver completion_observer( + download, base::Bind(DownloadCompleteFilter)); + download->ResumeInterruptedDownload(); + completion_observer.WaitForEvent(); + + ConfirmFileStatusForResume( + download, true, GetSafeBufferChunk() * 3, GetSafeBufferChunk() * 3, + base::FilePath(FILE_PATH_LITERAL("rangereset"))); + + static const RecordingDownloadObserver::RecordStruct expected_record[] = { + // Result of RST + {DownloadItem::INTERRUPTED, GetSafeBufferChunk()}, + // Starting continuation + {DownloadItem::IN_PROGRESS, GetSafeBufferChunk()}, + // Notification of receiving whole file. + {DownloadItem::IN_PROGRESS, 0}, + // Completion. + {DownloadItem::COMPLETE, GetSafeBufferChunk() * 3}, + }; + + recorder.CompareToExpectedRecord(expected_record, arraysize(expected_record)); +} + +// Confirm restart fallback happens if a precondition is failed. +IN_PROC_BROWSER_TEST_F(DownloadContentTest, + ResumeInterruptedDownloadBadPrecondition) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableDownloadResumption); + ASSERT_TRUE(test_server()->Start()); + + GURL url = test_server()->GetURL( + StringPrintf( + // First download hits an RST, rest don't, precondition fail. + "rangereset?size=%d&rst_boundary=%d&" + "token=NoRange&rst_limit=1&fail_precondition=2", + GetSafeBufferChunk() * 3, GetSafeBufferChunk())); + + // Start the download and wait for first data chunk. + DownloadItem* download(StartDownloadAndReturnItem(url)); + WaitForData(download, GetSafeBufferChunk()); + + RecordingDownloadObserver recorder(download); + + ReleaseRSTAndConfirmInterruptForResume(download); + ConfirmFileStatusForResume( + download, true, GetSafeBufferChunk(), GetSafeBufferChunk() * 3, + base::FilePath(FILE_PATH_LITERAL("rangereset.crdownload"))); + + DownloadUpdatedObserver completion_observer( + download, base::Bind(DownloadCompleteFilter)); + download->ResumeInterruptedDownload(); + completion_observer.WaitForEvent(); + + ConfirmFileStatusForResume( + download, true, GetSafeBufferChunk() * 3, GetSafeBufferChunk() * 3, + base::FilePath(FILE_PATH_LITERAL("rangereset"))); + + static const RecordingDownloadObserver::RecordStruct expected_record[] = { + // Result of RST + {DownloadItem::INTERRUPTED, GetSafeBufferChunk()}, + // Starting continuation + {DownloadItem::IN_PROGRESS, GetSafeBufferChunk()}, + // Server precondition fail. + {DownloadItem::INTERRUPTED, GetSafeBufferChunk()}, + // Notification of successful restart. + {DownloadItem::IN_PROGRESS, 0}, + // Completion. + {DownloadItem::COMPLETE, GetSafeBufferChunk() * 3}, + }; + + recorder.CompareToExpectedRecord(expected_record, arraysize(expected_record)); +} + +// Confirm we don't try to resume if we don't have a verifier. +IN_PROC_BROWSER_TEST_F(DownloadContentTest, + ResumeInterruptedDownloadNoVerifiers) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableDownloadResumption); + ASSERT_TRUE(test_server()->Start()); + + GURL url = test_server()->GetURL( + StringPrintf( + // First download hits an RST, rest don't, no verifiers. + "rangereset?size=%d&rst_boundary=%d&" + "token=NoRange&rst_limit=1&no_verifiers", + GetSafeBufferChunk() * 3, GetSafeBufferChunk())); + + // Start the download and wait for first data chunk. + DownloadItem* download(StartDownloadAndReturnItem(url)); + WaitForData(download, GetSafeBufferChunk()); + + RecordingDownloadObserver recorder(download); + + ReleaseRSTAndConfirmInterruptForResume(download); + ConfirmFileStatusForResume( + download, false, GetSafeBufferChunk(), GetSafeBufferChunk() * 3, + base::FilePath(FILE_PATH_LITERAL("rangereset.crdownload"))); + + DownloadUpdatedObserver completion_observer( + download, base::Bind(DownloadCompleteFilter)); + download->ResumeInterruptedDownload(); + completion_observer.WaitForEvent(); + + ConfirmFileStatusForResume( + download, true, GetSafeBufferChunk() * 3, GetSafeBufferChunk() * 3, + base::FilePath(FILE_PATH_LITERAL("rangereset"))); + + static const RecordingDownloadObserver::RecordStruct expected_record[] = { + // Result of RST + {DownloadItem::INTERRUPTED, GetSafeBufferChunk()}, + // Restart for lack of verifiers + {DownloadItem::IN_PROGRESS, 0}, + // Completion. + {DownloadItem::COMPLETE, GetSafeBufferChunk() * 3}, + }; + + recorder.CompareToExpectedRecord(expected_record, arraysize(expected_record)); +} + +IN_PROC_BROWSER_TEST_F(DownloadContentTest, ResumeWithDeletedFile) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableDownloadResumption); + ASSERT_TRUE(test_server()->Start()); + + GURL url = test_server()->GetURL( + StringPrintf( + // First download hits an RST, rest don't + "rangereset?size=%d&rst_boundary=%d&" + "token=NoRange&rst_limit=1", + GetSafeBufferChunk() * 3, GetSafeBufferChunk())); + + // Start the download and wait for first data chunk. + DownloadItem* download(StartDownloadAndReturnItem(url)); + WaitForData(download, GetSafeBufferChunk()); + + RecordingDownloadObserver recorder(download); + + ReleaseRSTAndConfirmInterruptForResume(download); + ConfirmFileStatusForResume( + download, true, GetSafeBufferChunk(), GetSafeBufferChunk() * 3, + base::FilePath(FILE_PATH_LITERAL("rangereset.crdownload"))); + + // Delete the intermediate file. + file_util::Delete(download->GetFullPath(), false); + + DownloadUpdatedObserver completion_observer( + download, base::Bind(DownloadCompleteFilter)); + download->ResumeInterruptedDownload(); + completion_observer.WaitForEvent(); + + ConfirmFileStatusForResume( + download, true, GetSafeBufferChunk() * 3, GetSafeBufferChunk() * 3, + base::FilePath(FILE_PATH_LITERAL("rangereset"))); + + static const RecordingDownloadObserver::RecordStruct expected_record[] = { + // Result of RST + {DownloadItem::INTERRUPTED, GetSafeBufferChunk()}, + // Starting continuation + {DownloadItem::IN_PROGRESS, GetSafeBufferChunk()}, + // Error because file isn't there. + {DownloadItem::INTERRUPTED, GetSafeBufferChunk()}, + // Restart + {DownloadItem::IN_PROGRESS, 0}, + // Completion. + {DownloadItem::COMPLETE, GetSafeBufferChunk() * 3}, + }; + + recorder.CompareToExpectedRecord(expected_record, arraysize(expected_record)); +} + +IN_PROC_BROWSER_TEST_F(DownloadContentTest, ResumeWithFileInitError) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableDownloadResumption); + base::FilePath file(FILE_PATH_LITERAL("download-test.lib")); + GURL url(URLRequestMockHTTPJob::GetMockUrl(file)); + + // Setup the error injector. + scoped_refptr<TestFileErrorInjector> injector( + TestFileErrorInjector::Create(DownloadManagerForShell(shell()))); + + TestFileErrorInjector::FileErrorInfo err = { + url.spec(), + TestFileErrorInjector::FILE_OPERATION_INITIALIZE, + 0, + DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE + }; + injector->AddError(err); + injector->InjectErrors(); + + // Start and watch for interrupt. + scoped_ptr<DownloadTestObserver> int_observer(CreateWaiter(shell(), 1)); + DownloadItem* download(StartDownloadAndReturnItem(url)); + int_observer->WaitForFinished(); + ASSERT_EQ(DownloadItem::INTERRUPTED, download->GetState()); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, + download->GetLastReason()); + EXPECT_EQ(0, download->GetReceivedBytes()); + EXPECT_TRUE(download->GetFullPath().empty()); + EXPECT_TRUE(download->GetTargetFilePath().empty()); + + // We need to make sure that any cross-thread downloads communication has + // quiesced before clearing and injecting the new errors, as the + // InjectErrors() routine alters the currently in use download file + // factory, which is a file thread object. + RunAllPendingInMessageLoop(BrowserThread::FILE); + RunAllPendingInMessageLoop(); + + // Clear the old errors list. + injector->ClearErrors(); + injector->InjectErrors(); + + // Resume and watch completion. + DownloadUpdatedObserver completion_observer( + download, base::Bind(DownloadCompleteFilter)); + download->ResumeInterruptedDownload(); + completion_observer.WaitForEvent(); + EXPECT_EQ(download->GetState(), DownloadItem::COMPLETE); +} + +IN_PROC_BROWSER_TEST_F(DownloadContentTest, + ResumeWithFileIntermediateRenameError) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableDownloadResumption); + base::FilePath file(FILE_PATH_LITERAL("download-test.lib")); + GURL url(URLRequestMockHTTPJob::GetMockUrl(file)); + + // Setup the error injector. + scoped_refptr<TestFileErrorInjector> injector( + TestFileErrorInjector::Create(DownloadManagerForShell(shell()))); + + TestFileErrorInjector::FileErrorInfo err = { + url.spec(), + TestFileErrorInjector::FILE_OPERATION_RENAME_UNIQUIFY, + 0, + DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE + }; + injector->AddError(err); + injector->InjectErrors(); + + // Start and watch for interrupt. + scoped_ptr<DownloadTestObserver> int_observer(CreateWaiter(shell(), 1)); + DownloadItem* download(StartDownloadAndReturnItem(url)); + int_observer->WaitForFinished(); + ASSERT_EQ(DownloadItem::INTERRUPTED, download->GetState()); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, + download->GetLastReason()); + EXPECT_TRUE(download->GetFullPath().empty()); + // Target path will have been set after file name determination, + // and reset when the intermediate rename fails, as that suggests + // we should re-do file name determination. + EXPECT_TRUE(download->GetTargetFilePath().empty()); + + // We need to make sure that any cross-thread downloads communication has + // quiesced before clearing and injecting the new errors, as the + // InjectErrors() routine alters the currently in use download file + // factory, which is a file thread object. + RunAllPendingInMessageLoop(BrowserThread::FILE); + RunAllPendingInMessageLoop(); + + // Clear the old errors list. + injector->ClearErrors(); + injector->InjectErrors(); + + // Resume and watch completion. + DownloadUpdatedObserver completion_observer( + download, base::Bind(DownloadCompleteFilter)); + download->ResumeInterruptedDownload(); + completion_observer.WaitForEvent(); + EXPECT_EQ(download->GetState(), DownloadItem::COMPLETE); +} + +IN_PROC_BROWSER_TEST_F(DownloadContentTest, ResumeWithFileFinalRenameError) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableDownloadResumption); + base::FilePath file(FILE_PATH_LITERAL("download-test.lib")); + GURL url(URLRequestMockHTTPJob::GetMockUrl(file)); + + // Setup the error injector. + scoped_refptr<TestFileErrorInjector> injector( + TestFileErrorInjector::Create(DownloadManagerForShell(shell()))); + + DownloadManagerForShell(shell())->RemoveAllDownloads(); + TestFileErrorInjector::FileErrorInfo err = { + url.spec(), + TestFileErrorInjector::FILE_OPERATION_RENAME_ANNOTATE, + 0, + DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE + }; + injector->AddError(err); + injector->InjectErrors(); + + // Start and watch for interrupt. + scoped_ptr<DownloadTestObserver> int_observer(CreateWaiter(shell(), 1)); + DownloadItem* download(StartDownloadAndReturnItem(url)); + int_observer->WaitForFinished(); + ASSERT_EQ(DownloadItem::INTERRUPTED, download->GetState()); + EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_FILE_NO_SPACE, + download->GetLastReason()); + EXPECT_TRUE(download->GetFullPath().empty()); + // Target path will have been set after file name determination, + // and reset when the rename fails, as that suggests + // we should re-do file name determination. + EXPECT_TRUE(download->GetTargetFilePath().empty()); + + // We need to make sure that any cross-thread downloads communication has + // quiesced before clearing and injecting the new errors, as the + // InjectErrors() routine alters the currently in use download file + // factory, which is a file thread object. + RunAllPendingInMessageLoop(BrowserThread::FILE); + RunAllPendingInMessageLoop(); + + // Clear the old errors list. + injector->ClearErrors(); + injector->InjectErrors(); + + // Resume and watch completion. + DownloadUpdatedObserver completion_observer( + download, base::Bind(DownloadCompleteFilter)); + download->ResumeInterruptedDownload(); + completion_observer.WaitForEvent(); + EXPECT_EQ(download->GetState(), DownloadItem::COMPLETE); } } // namespace content |