Index: content/browser/download/download_browsertest.cc |
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc |
index 276f3c4587cd4588cec75db5199cfa1b60dba8e0..4045f33041bc788d892be7db85743d5bab6991b7 100644 |
--- a/content/browser/download/download_browsertest.cc |
+++ b/content/browser/download/download_browsertest.cc |
@@ -5,6 +5,8 @@ |
// This file contains download browser tests that are known to be runnable |
// in a pure content context. Over time tests should be migrated here. |
+#include <queue> |
+ |
#include "base/command_line.h" |
#include "base/file_util.h" |
#include "base/files/file_path.h" |
@@ -36,6 +38,9 @@ |
#include "content/shell/browser/shell_network_delegate.h" |
#include "content/test/net/url_request_mock_http_job.h" |
#include "content/test/net/url_request_slow_download_job.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 "net/test/spawned_test_server/spawned_test_server.h" |
#include "testing/gmock/include/gmock/gmock.h" |
#include "testing/gtest/include/gtest/gtest.h" |
@@ -503,6 +508,176 @@ bool InitialSizeFilter(int* download_size, DownloadItem* download) { |
return true; |
} |
+// RAII test server. Once instantiated, it initializes an embedded test server. |
+// Expectations can be set via the OnURL method. |
+// |
+// Call HasFatalFailure() after constructing a TestServer instance to check if |
+// the initialization was successful. |
+// |
+// Example: |
+// |
+// TEST_F(Foo, Bar) { |
+// TestServer test_server; |
+// if (HasFatalFailure()) |
+// return; |
+// |
+// test_server.OnURL("/foo").RedirectTo("/bar"); |
+// test_server.OnURL("/bar").SendBody("baz"); |
+// ... |
+// test_server.ServeFilesFromDirectory(test_file_path); |
+// ... |
+// // Do something that fetches "/foo". |
+// } |
+// |
+// Note that the order of the OnURL() calls are significant. The server expects |
+// the requests to arrive in the same order as the OnURL() calls. |
+// TODO(asanka): Consider migrating more tests to use the embedded test server. |
Randy Smith (Not in Mondays)
2014/04/23 19:12:27
I'd encourage this; this type of infrastructure lo
asanka
2014/04/23 22:59:27
I actually removed this TestServer since in the en
|
+class TestServer { |
+ public: |
+ // An expectation for a request. Created by TestServer::OnURL(). |
+ class ExpectedRequest { |
+ public: |
+ // Respond with a status code of 302 and a Location header set to |
+ // |redirect_url|. |
+ ExpectedRequest& RedirectTo(const GURL& redirect_url); |
+ |
+ // Set the body of the response to |string|. Overwrites the current body if |
+ // one had been previously set. |
+ ExpectedRequest& SendBody(const std::string& string); |
Randy Smith (Not in Mondays)
2014/04/23 19:12:27
Should you say what happens if you use both Redire
asanka
2014/04/23 22:59:27
Removed.
|
+ |
+ // Set the Content-Type to |mime_type|. |
+ ExpectedRequest& WithContentType(const char* mime_type); |
+ |
+ private: |
+ friend class TestServer; |
+ |
+ // Construct a ExpectedRequest that will expect an HTTP GET for |
+ // |relative_url|. |
+ ExpectedRequest(const std::string& relative_url); |
+ |
+ // Returns true if this expectation matches |url|. |
+ bool MatchesUrl(const std::string& url) const; |
+ |
+ // Returns the HttpResponse for this expectation. Once this is called, the |
+ // ExpectedRequest object is no longer valid. |
+ scoped_ptr<net::test_server::HttpResponse> GetResponse(); |
+ |
+ std::string relative_url_; |
+ scoped_ptr<net::test_server::BasicHttpResponse> response_; |
+ DISALLOW_COPY_AND_ASSIGN(ExpectedRequest); |
+ }; |
+ |
+ TestServer(); |
+ ~TestServer(); |
+ |
+ // Create a new expectation for receiving an HTTP GET for |relative_url|. By |
+ // default the response is an HTTP 200 with an empty body. The response can be |
+ // modified via the returned |ExpectedRequest| reference. Note that the |
+ // reference becomes invalidated once a request has been received by the |
+ // server. |
+ ExpectedRequest& OnURL(const std::string& relative_url); |
Randy Smith (Not in Mondays)
2014/04/23 19:12:27
Huh. I twitch somewhat hard at returning a non-co
asanka
2014/04/23 22:59:27
Hehe. It's a builder pattern. See for example htt
|
+ |
+ // Get the absolue URL for |relative_url|. |
+ GURL GetURL(const std::string& relative_url); |
+ |
+ // Serve files contained within |directory|. See |
+ // EmbeddedTestServer::ServeFilesFromDirectory(). |
+ void ServeFilesFromDirectory(const base::FilePath& directory); |
Randy Smith (Not in Mondays)
2014/04/23 19:12:27
Probably worthwhile calling out that any ExpectedR
asanka
2014/04/23 22:59:27
Removed.
|
+ |
+ private: |
+ scoped_ptr<net::test_server::HttpResponse> HandleRequest( |
+ const net::test_server::HttpRequest& request); |
+ void InitializeAndWaitUntilReady(); |
+ void ShutdownAndWaitUntilComplete(); |
+ |
+ net::test_server::EmbeddedTestServer embedded_test_server_; |
+ std::queue<ExpectedRequest*> expected_requests_; |
+ DISALLOW_COPY_AND_ASSIGN(TestServer); |
+}; |
+ |
+TestServer::ExpectedRequest::ExpectedRequest(const std::string& relative_url) |
+ : relative_url_(relative_url), |
+ response_(new net::test_server::BasicHttpResponse) { |
+} |
+ |
+TestServer::ExpectedRequest& TestServer::ExpectedRequest::RedirectTo( |
+ const GURL& redirect_url) { |
+ response_->set_code(net::HTTP_FOUND); |
+ response_->AddCustomHeader("Location", redirect_url.spec()); |
+ return *this; |
+} |
+ |
+TestServer::ExpectedRequest& TestServer::ExpectedRequest::SendBody( |
+ const std::string& string) { |
+ response_->set_content(string); |
+ return *this; |
+} |
+ |
+TestServer::ExpectedRequest& TestServer::ExpectedRequest::WithContentType( |
+ const char* mime_type) { |
+ response_->set_content_type(mime_type); |
+ return *this; |
+} |
+ |
+bool TestServer::ExpectedRequest::MatchesUrl(const std::string& url) const { |
+ return relative_url_ == url; |
+} |
+ |
+scoped_ptr<net::test_server::HttpResponse> |
+TestServer::ExpectedRequest::GetResponse() { |
+ return response_.PassAs<net::test_server::HttpResponse>(); |
+} |
+ |
+TestServer::TestServer() { |
+ InitializeAndWaitUntilReady(); |
+} |
+ |
+TestServer::~TestServer() { |
+ EXPECT_TRUE(expected_requests_.empty()); |
+ while (!expected_requests_.empty()) { |
+ delete expected_requests_.front(); |
+ expected_requests_.pop(); |
+ } |
+ ShutdownAndWaitUntilComplete(); |
+} |
+ |
+void TestServer::InitializeAndWaitUntilReady() { |
+ ASSERT_TRUE(embedded_test_server_.InitializeAndWaitUntilReady()); |
+ embedded_test_server_.RegisterRequestHandler( |
+ base::Bind(&TestServer::HandleRequest, base::Unretained(this))); |
+} |
+ |
+void TestServer::ShutdownAndWaitUntilComplete() { |
+ ASSERT_TRUE(embedded_test_server_.ShutdownAndWaitUntilComplete()); |
+} |
+ |
+TestServer::ExpectedRequest& TestServer::OnURL( |
+ const std::string& relative_url) { |
+ ExpectedRequest* request = new ExpectedRequest(relative_url); |
+ expected_requests_.push(request); |
+ return *request; |
+} |
+ |
+void TestServer::ServeFilesFromDirectory(const base::FilePath& directory) { |
+ embedded_test_server_.ServeFilesFromDirectory(directory); |
+} |
+ |
+GURL TestServer::GetURL(const std::string& relative_url) { |
+ return embedded_test_server_.GetURL(relative_url); |
+} |
+ |
+scoped_ptr<net::test_server::HttpResponse> TestServer::HandleRequest( |
+ const net::test_server::HttpRequest& request) { |
+ scoped_ptr<net::test_server::HttpResponse> response; |
+ if (!expected_requests_.empty() && |
+ expected_requests_.front()->MatchesUrl(request.relative_url)) { |
+ scoped_ptr<ExpectedRequest> expected_request(expected_requests_.front()); |
+ response = expected_request->GetResponse(); |
+ expected_requests_.pop(); |
+ } |
+ return response.Pass(); |
+} |
+ |
} // namespace |
class DownloadContentTest : public ContentBrowserTest { |
@@ -1651,4 +1826,87 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, CookiePolicy) { |
GURL(download))); |
} |
+// A filename suggestion specified via a @download attribute should be effective |
+// if the final download URL is in the same origin as the initial download URL. |
+// Test that this holds even if there are cross origin redirects in the middle |
+// of the redirect chain. |
+IN_PROC_BROWSER_TEST_F(DownloadContentTest, |
+ DownloadAttributeSameOriginRedirect) { |
+ TestServer origin_one; |
+ TestServer origin_two; |
+ if (HasFatalFailure()) |
+ return; |
+ |
+ // The download-attribute.html page contains an anchor element whose href is |
+ // set to the value of the query parameter (specified as |target| in the URL |
+ // below). The suggested filename for the anchor is 'suggested-filename'. When |
+ // the page is loaded, a script simulates a click on the anchor, triggering a |
+ // download of the target URL. |
+ // |
+ // We construct two test servers; origin_one and origin_two. Once started, the |
+ // server URLs will differ by the port number. Therefore they will be in |
+ // different origins. |
+ GURL download_url = origin_one.GetURL("/ping"); |
+ GURL referrer_url = origin_one.GetURL( |
+ std::string("/download-attribute.html?target=") + download_url.spec()); |
+ origin_one.ServeFilesFromDirectory(GetTestFilePath("download", "")); |
+ |
+ // <origin_one>/download-attribute.html initiates a download of |
+ // <origin_one>/ping, which redirects to <origin_two>/pong, and then finally |
+ // to <origin_one>/download. |
+ origin_one.OnURL("/ping").RedirectTo(origin_two.GetURL("/pong")); |
+ origin_two.OnURL("/pong").RedirectTo(origin_one.GetURL("/download")); |
+ origin_one.OnURL("/download").SendBody("Hello").WithContentType( |
+ "application/octet-stream"); |
+ |
+ DownloadAndWait(shell(), referrer_url, DownloadItem::COMPLETE); |
Randy Smith (Not in Mondays)
2014/04/23 19:12:27
nit: Chuckle. This relies on "DownloadAndWait()"
asanka
2014/04/23 22:59:27
I renamed the function to NavigateToURLAndWaitForD
|
+ |
+ std::vector<DownloadItem*> downloads; |
+ DownloadManagerForShell(shell())->GetAllDownloads(&downloads); |
+ ASSERT_EQ(1u, downloads.size()); |
+ |
+ EXPECT_EQ(FILE_PATH_LITERAL("suggested-filename"), |
+ downloads[0]->GetTargetFilePath().BaseName().value()); |
+} |
+ |
+// A filename suggestion specified via a @download attribute should not be |
+// effective if the final download URL is in another origin from the original |
+// download URL. |
Randy Smith (Not in Mondays)
2014/04/23 19:12:27
nit: I'd interchange the order of these two tests;
asanka
2014/04/23 22:59:27
Done.
|
+IN_PROC_BROWSER_TEST_F(DownloadContentTest, |
+ DownloadAttributeCrossOriginRedirect) { |
+ TestServer origin_one; |
+ TestServer origin_two; |
+ if (HasFatalFailure()) |
+ return; |
+ |
+ // The download-attribute.html page contains an anchor element whose href is |
+ // set to the value of the query parameter (specified as |target| in the URL |
+ // below). The suggested filename for the anchor is 'suggested-filename'. When |
+ // the page is loaded, a script simulates a click on the anchor, triggering a |
+ // download of the target URL. |
+ // |
+ // We construct two test servers; origin_one and origin_two. Once started, the |
+ // server URLs will differ by the port number. Therefore they will be in |
+ // different origins. |
+ GURL download_url = origin_one.GetURL("/ping"); |
+ GURL referrer_url = origin_one.GetURL( |
+ std::string("/download-attribute.html?target=") + download_url.spec()); |
+ |
+ // <origin_one>/download-attribute.html initiates a download of |
+ // <origin_one>/ping, which redirects to <origin_two>/download. |
+ origin_one.ServeFilesFromDirectory(GetTestFilePath("download", "")); |
+ origin_one.OnURL("/ping").RedirectTo(origin_two.GetURL("/download")); |
+ origin_two.OnURL("/download").SendBody("Hello").WithContentType( |
+ "application/octet-stream"); |
+ |
+ DownloadAndWait(shell(), referrer_url, DownloadItem::COMPLETE); |
+ |
+ std::vector<DownloadItem*> downloads; |
+ DownloadManagerForShell(shell())->GetAllDownloads(&downloads); |
+ ASSERT_EQ(1u, downloads.size()); |
+ |
+ EXPECT_EQ(FILE_PATH_LITERAL("download"), |
+ downloads[0]->GetTargetFilePath().BaseName().value()); |
+} |
+ |
} // namespace content |