Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2396)

Unified Diff: content/browser/download/url_downloader_unittest.cc

Issue 23496076: WIP - Refactor programmatic downloads Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « content/browser/download/url_downloader.cc ('k') | content/browser/indexed_db/indexed_db_internals_ui.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/browser/download/url_downloader_unittest.cc
diff --git a/content/browser/download/url_downloader_unittest.cc b/content/browser/download/url_downloader_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..fc53237b9162867e11d05c93e2e9d82a581f9442
--- /dev/null
+++ b/content/browser/download/url_downloader_unittest.cc
@@ -0,0 +1,397 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "content/browser/byte_stream.h"
+#include "content/browser/download/download_create_info.h"
+#include "content/browser/download/download_interrupt_reasons_impl.h"
+#include "content/browser/download/url_downloader.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/download_interrupt_reasons.h"
+#include "content/public/browser/download_url_parameters.h"
+#include "content/public/test/test_browser_context.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "net/base/net_errors.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AllOf;
+using ::testing::Return;
+using ::testing::_;
+
+namespace content {
+namespace {
+
+class ByteStreamSlurper {
+ public:
+ typedef base::Callback<void(scoped_ptr<ByteStreamSlurper>)>
+ CompletionCallback;
+
+ ByteStreamSlurper(scoped_ptr<ByteStreamReader> reader,
+ const CompletionCallback& callback);
+
+ bool is_done() const { return is_done_; }
+ int state() const { return state_; }
+ const std::string& content() const { return content_; }
+
+ void Start();
+
+ private:
+ void Slurp();
+
+ scoped_ptr<ByteStreamReader> reader_;
+ CompletionCallback callback_;
+ bool is_done_;
+ int state_;
+ std::string content_;
+};
+
+ByteStreamSlurper::ByteStreamSlurper(scoped_ptr<ByteStreamReader> reader,
+ const CompletionCallback& callback)
+ : reader_(reader.Pass()),
+ callback_(callback),
+ is_done_(false),
+ state_(0) {
+}
+
+void ByteStreamSlurper::Start() {
+ reader_->RegisterCallback(
+ base::Bind(&ByteStreamSlurper::Slurp,
+ base::Unretained(this)));
+ // Initial slurp.
+ Slurp();
+}
+
+void ByteStreamSlurper::Slurp() {
+ while (!is_done_) {
+ scoped_refptr<net::IOBuffer> buffer;
+ size_t length = 0;
+ ByteStreamReader::StreamState state = reader_->Read(&buffer, &length);
+ switch (state) {
+ case ByteStreamReader::STREAM_EMPTY:
+ return; // We should get called when there's data available.
+
+ case ByteStreamReader::STREAM_COMPLETE: {
+ DCHECK_EQ(0u, length);
+ is_done_ = true;
+ state_ = reader_->GetStatus();
+ scoped_ptr<ByteStreamSlurper> owned_this(this);
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(callback_, base::Passed(&owned_this)));
+ break;
+ }
+
+ case ByteStreamReader::STREAM_HAS_DATA:
+ content_.append(buffer->data(), length);
+ break;
+ }
+ }
+}
+
+class Response : public net::test_server::HttpResponse {
+ public:
+ Response() : data_(new Data()) {}
+ Response(const Response& that) : data_(that.data_) {}
+ Response& Code(net::HttpStatusCode code) {
+ data_->response_.set_code(code);
+ return *this;
+ }
+ Response& Header(const char* name, const char* value) {
+ data_->response_.AddCustomHeader(name, value);
+ return *this;
+ }
+ Response& Content(const char* content) {
+ data_->response_.set_content(content);
+ return *this;
+ }
+ Response& ContentType(const char* content_type) {
+ data_->response_.set_content_type(content_type);
+ return *this;
+ }
+
+ virtual std::string ToResponseString() const OVERRIDE {
+ return data_->response_.ToResponseString();
+ }
+
+ private:
+ class Data : public base::RefCounted<Data> {
+ public:
+ Data() {}
+ net::test_server::BasicHttpResponse response_;
+
+ private:
+ friend class base::RefCounted<Data>;
+ virtual ~Data() {}
+ };
+
+ scoped_refptr<Data> data_;
+};
+
+class RequestHandler {
+ public:
+ net::test_server::EmbeddedTestServer::HandleRequestCallback GetCallback() {
+ return base::Bind(&RequestHandler::HandleRequest, base::Unretained(this));
+ }
+
+ MOCK_METHOD3(Request,
+ Response(const std::string&,
+ const std::map<std::string, std::string>&,
+ const std::string&));
+
+ private:
+ scoped_ptr<net::test_server::HttpResponse> HandleRequest(
+ const net::test_server::HttpRequest& request);
+};
+
+MATCHER_P2(HasHeader, name, value, "") {
+ return arg.find(name) != arg.end() && arg.find(name)->second == value;
+}
+
+scoped_ptr<net::test_server::HttpResponse> RequestHandler::HandleRequest(
+ const net::test_server::HttpRequest& request) {
+ scoped_ptr<Response> response(new Response(
+ Request(request.relative_url, request.headers, request.content)));
+ return response.PassAs<net::test_server::HttpResponse>();
+}
+
+class UrlDownloaderTest : public testing::Test {
+ public:
+ UrlDownloaderTest() {}
+ virtual ~UrlDownloaderTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ test_server_.reset(new net::test_server::EmbeddedTestServer(
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)));
+ test_server_->RegisterRequestHandler(handler_.GetCallback());
+ }
+
+ virtual void TearDown() OVERRIDE {
+ ASSERT_TRUE(test_server_->ShutdownAndWaitUntilComplete());
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+ }
+
+ DownloadInterruptReason StartRequestAndWaitForCallback(
+ DownloadRequestHandle* request_handle,
+ scoped_ptr<DownloadCreateInfo>* info) {
+ base::RunLoop run_loop;
+ DownloadInterruptReason result = DOWNLOAD_INTERRUPT_REASON_NONE;
+ request_handle->Start(base::Bind(&UrlDownloaderTest::StartRequestCallback,
+ &result,
+ info,
+ run_loop.QuitClosure()));
+ run_loop.Run();
+ return result;
+ }
+
+ DownloadInterruptReason SlurpByteStream(
+ scoped_ptr<ByteStreamReader> stream_reader,
+ std::string* content) {
+ int status = 0;
+ base::RunLoop run_loop;
+ scoped_ptr<ByteStreamSlurper> slurper(
+ new ByteStreamSlurper(stream_reader.Pass(),
+ base::Bind(&UrlDownloaderTest::SlurpDoneCallback,
+ &status,
+ content,
+ run_loop.QuitClosure())));
+ BrowserThread::PostTask(BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&ByteStreamSlurper::Start,
+ base::Unretained(slurper.release())));
+ run_loop.Run();
+ return static_cast<DownloadInterruptReason>(status);
+ }
+
+ TestBrowserContext* test_browser_context() { return &test_browser_context_; }
+
+ RequestHandler* request_handler() { return &handler_; }
+
+ net::test_server::EmbeddedTestServer* test_server() {
+ return test_server_.get();
+ }
+
+ private:
+ static void StartRequestCallback(DownloadInterruptReason* store_result,
+ scoped_ptr<DownloadCreateInfo>* store_info,
+ const base::Closure& closure,
+ DownloadInterruptReason result,
+ scoped_ptr<DownloadCreateInfo> create_info) {
+ *store_result = result;
+ store_info->swap(create_info);
+ closure.Run();
+ }
+
+ static void SlurpDoneCallback(int* status,
+ std::string* content,
+ const base::Closure& closure,
+ scoped_ptr<ByteStreamSlurper> slurper) {
+ *status = slurper->state();
+ *content = slurper->content();
+ BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, slurper.release());
+ closure.Run();
+ }
+
+ TestBrowserThreadBundle thread_bundle_;
+ TestBrowserContext test_browser_context_;
+ scoped_ptr<net::test_server::EmbeddedTestServer> test_server_;
+ RequestHandler handler_;
+};
+
+const char* const kDummyURL = "/foo";
+const char* const kTextContentType = "text/plain";
+const char* const kDummyContent = "Hello world";
+
+TEST_F(UrlDownloaderTest, UrlDownloader_Start) {
+ const char* kETag = "abcd";
+ ASSERT_TRUE(test_server()->InitializeAndWaitUntilReady());
+ GURL url = test_server()->GetURL(kDummyURL);
+ scoped_ptr<DownloadUrlParameters> params(new DownloadUrlParameters(
+ url, -1, -1, test_browser_context()->GetResourceContext()));
+ scoped_ptr<DownloadRequestHandle> request_handle(
+ UrlDownloader::CreateDownloadRequest(params.Pass()));
+
+ EXPECT_CALL(*request_handler(), Request(kDummyURL, _, _))
+ .WillOnce(Return(Response()
+ .Code(net::HTTP_OK)
+ .Header("etag", kETag)
+ .ContentType(kTextContentType)
+ .Content(kDummyContent)));
+ scoped_ptr<DownloadCreateInfo> info;
+ DownloadInterruptReason result =
+ StartRequestAndWaitForCallback(request_handle.get(), &info);
+ ASSERT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, result);
+ ASSERT_TRUE(info);
+ EXPECT_STREQ(kETag, info->etag.c_str());
+ EXPECT_STREQ(kTextContentType, info->mime_type.c_str());
+
+ std::string content;
+ ASSERT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE,
+ SlurpByteStream(info->stream_reader.Pass(), &content));
+ EXPECT_EQ(kDummyContent, content);
+}
+
+TEST_F(UrlDownloaderTest, UrlDownloader_ValidRedirects) {
+ ASSERT_TRUE(test_server()->InitializeAndWaitUntilReady());
+ static const char* const kURL0 = "/foo";
+ static const char* const kURL1 = "/bar";
+ static const char* const kURL2 = "/baz";
+ std::vector<GURL> urls;
+ urls.push_back(test_server()->GetURL(kURL0));
+ urls.push_back(test_server()->GetURL(kURL1));
+ urls.push_back(test_server()->GetURL(kURL2));
+
+ scoped_ptr<DownloadUrlParameters> params(
+ new DownloadUrlParameters(urls[0], -1, -1,
+ test_browser_context()->GetResourceContext()));
+ scoped_ptr<DownloadRequestHandle> request_handle(
+ UrlDownloader::CreateDownloadRequest(params.Pass()));
+
+ EXPECT_CALL(*request_handler(), Request(kURL0, _, _))
+ .WillOnce(Return(Response()
+ .Code(net::HTTP_TEMPORARY_REDIRECT)
+ .Header("location", urls[1].spec().c_str())));
+ EXPECT_CALL(*request_handler(), Request(kURL1, _, _))
+ .WillOnce(Return(Response()
+ .Code(net::HTTP_TEMPORARY_REDIRECT)
+ .Header("location", urls[2].spec().c_str())));
+ EXPECT_CALL(*request_handler(), Request(kURL2, _, _))
+ .WillOnce(Return(Response()
+ .Code(net::HTTP_OK)
+ .ContentType(kTextContentType)
+ .Content(kDummyContent)));
+ scoped_ptr<DownloadCreateInfo> info;
+ DownloadInterruptReason result =
+ StartRequestAndWaitForCallback(request_handle.get(), &info);
+ ASSERT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, result);
+ ASSERT_TRUE(info);
+ EXPECT_EQ(urls, info->url_chain);
+}
+
+TEST_F(UrlDownloaderTest, UrlDownloader_InvalidRedirects) {
+ ASSERT_TRUE(test_server()->InitializeAndWaitUntilReady());
+ static const char* const kURL0 = "/foo";
+ static const char* const kURL1 = "/bar";
+ static const char* const kURL2 = "notavalidscheme://baz";
+ std::vector<GURL> urls;
+ urls.push_back(test_server()->GetURL(kURL0));
+ urls.push_back(test_server()->GetURL(kURL1));
+ urls.push_back(GURL(kURL2));
+
+ scoped_ptr<DownloadUrlParameters> params(
+ new DownloadUrlParameters(urls[0], -1, -1,
+ test_browser_context()->GetResourceContext()));
+ scoped_ptr<DownloadRequestHandle> request_handle(
+ UrlDownloader::CreateDownloadRequest(params.Pass()));
+
+ EXPECT_CALL(*request_handler(), Request(kURL0, _, _))
+ .WillOnce(Return(Response()
+ .Code(net::HTTP_TEMPORARY_REDIRECT)
+ .Header("location", urls[1].spec().c_str())));
+ EXPECT_CALL(*request_handler(), Request(kURL1, _, _))
+ .WillOnce(Return(Response()
+ .Code(net::HTTP_TEMPORARY_REDIRECT)
+ .Header("location", urls[2].spec().c_str())));
+ scoped_ptr<DownloadCreateInfo> info;
+ DownloadInterruptReason result =
+ StartRequestAndWaitForCallback(request_handle.get(), &info);
+ EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST, result);
+ EXPECT_FALSE(info);
+}
+
+TEST_F(UrlDownloaderTest, UrlDownloader_404Handling) {
+ ASSERT_TRUE(test_server()->InitializeAndWaitUntilReady());
+ scoped_ptr<DownloadUrlParameters> params(
+ new DownloadUrlParameters(test_server()->GetURL(kDummyURL),
+ -1,
+ -1,
+ test_browser_context()->GetResourceContext()));
+ scoped_ptr<DownloadRequestHandle> request_handle(
+ UrlDownloader::CreateDownloadRequest(params.Pass()));
+ EXPECT_CALL(*request_handler(), Request(kDummyURL, _, _))
+ .WillOnce(Return(Response().Code(net::HTTP_NOT_FOUND)));
+ scoped_ptr<DownloadCreateInfo> info;
+ DownloadInterruptReason reason =
+ StartRequestAndWaitForCallback(request_handle.get(), &info);
+ EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NONE, reason);
+ EXPECT_TRUE(info);
+
+ std::string content;
+ EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_SERVER_BAD_CONTENT,
+ SlurpByteStream(info->stream_reader.Pass(), &content));
+ EXPECT_TRUE(content.empty());
+}
+
+TEST_F(UrlDownloaderTest, UrlDownloader_InvalidScheme) {
+ GURL url("notarealscheme://foo/bar");
+ scoped_ptr<DownloadUrlParameters> params(new DownloadUrlParameters(
+ url, -1, -1, test_browser_context()->GetResourceContext()));
+ scoped_ptr<DownloadRequestHandle> request_handle(
+ UrlDownloader::CreateDownloadRequest(params.Pass()));
+ scoped_ptr<DownloadCreateInfo> info;
+ DownloadInterruptReason reason =
+ StartRequestAndWaitForCallback(request_handle.get(), &info);
+ EXPECT_EQ(DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST, reason);
+ EXPECT_FALSE(info);
+}
+
+// TODO(asanka): Test that back pressure is commuted correctly.
+// TODO(asanka): Test that a successful response results in a
+// DownloadCreateInfo and that the response data is available.
+// TODO(asanka): Test that responses with errors are handled correctly.
+// TODO(asanka): Test that request headers are set correctly.
+// TODO(asanka): Test that pausing and resuming work correctly.
+} // namespace
+} // namespace content
« no previous file with comments | « content/browser/download/url_downloader.cc ('k') | content/browser/indexed_db/indexed_db_internals_ui.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698