summaryrefslogtreecommitdiffstats
path: root/content/browser/download/download_browsertest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'content/browser/download/download_browsertest.cc')
-rw-r--r--content/browser/download/download_browsertest.cc688
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