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

Unified Diff: headless/public/util/generic_url_request_job_test.cc

Issue 2260793002: Headless utility classes for making deterministic protocol handlers (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressing comments Created 4 years, 4 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
Index: headless/public/util/generic_url_request_job_test.cc
diff --git a/headless/public/util/generic_url_request_job_test.cc b/headless/public/util/generic_url_request_job_test.cc
new file mode 100644
index 0000000000000000000000000000000000000000..e687a6864191674762f2541c0c235741033337a7
--- /dev/null
+++ b/headless/public/util/generic_url_request_job_test.cc
@@ -0,0 +1,346 @@
+// Copyright 2016 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 "headless/public/util/generic_url_request_job.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "headless/public/util/expedited_dispatcher.h"
+#include "headless/public/util/testing/generic_url_request_mocks.h"
+#include "headless/public/util/url_fetcher.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_request_job_factory_impl.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+std::ostream& operator<<(std::ostream& os, const base::Value& value) {
+ std::string json;
+ base::JSONWriter::WriteWithOptions(
+ value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
+ os << json;
+ return os;
+}
+
+MATCHER_P(MatchesJson, json, json.c_str()) {
+ std::unique_ptr<base::Value> expected(
+ base::JSONReader::Read(json, base::JSON_PARSE_RFC));
+ return arg.Equals(expected.get());
+}
+
+namespace headless {
+
+namespace {
+
+class MockFetcher : public URLFetcher {
+ public:
+ MockFetcher(base::DictionaryValue* fetch_request,
+ const std::string& json_reply)
+ : fetch_reply_(base::JSONReader::Read(json_reply, base::JSON_PARSE_RFC)),
+ fetch_request_(fetch_request) {
+ CHECK(fetch_reply_) << "Invalid json: " << json_reply;
+ }
+
+ ~MockFetcher() override {}
+
+ void StartFetch(const GURL& url,
+ const net::HttpRequestHeaders& request_headers,
+ ResultListener* result_listener) override {
+ // Record the request.
+ fetch_request_->SetString("url", url.spec());
+ std::unique_ptr<base::DictionaryValue> headers(new base::DictionaryValue);
+ for (net::HttpRequestHeaders::Iterator it(request_headers); it.GetNext();) {
+ headers->SetString(it.name(), it.value());
+ }
+ fetch_request_->Set("headers", std::move(headers));
+
+ // Return the canned response.
+ base::DictionaryValue* reply_dictionary;
+ ASSERT_TRUE(fetch_reply_->GetAsDictionary(&reply_dictionary));
+ std::string final_url;
+ ASSERT_TRUE(reply_dictionary->GetString("url", &final_url));
+ int http_response_code;
+ ASSERT_TRUE(reply_dictionary->GetInteger("http_response_code",
+ &http_response_code));
+ ASSERT_TRUE(reply_dictionary->GetString("data", &response_data_));
+ base::DictionaryValue* reply_headers_dictionary;
+ ASSERT_TRUE(
+ reply_dictionary->GetDictionary("headers", &reply_headers_dictionary));
+ scoped_refptr<net::HttpResponseHeaders> response_headers(
+ new net::HttpResponseHeaders(""));
+ for (base::DictionaryValue::Iterator it(*reply_headers_dictionary);
+ !it.IsAtEnd(); it.Advance()) {
+ std::string value;
+ ASSERT_TRUE(it.value().GetAsString(&value));
+ response_headers->AddHeader(
+ base::StringPrintf("%s: %s", it.key().c_str(), value.c_str()));
+ }
+ result_listener->OnFetchComplete(
+ GURL(final_url), http_response_code, std::move(response_headers),
+ response_data_.c_str(), response_data_.size());
+ }
+
+ private:
+ std::unique_ptr<base::Value> fetch_reply_;
+ base::DictionaryValue* fetch_request_; // NOT OWNED
+ std::string response_data_; // Here to ensure the required lifetime.
+};
+
+class MockProtocolHandler : public net::URLRequestJobFactory::ProtocolHandler {
+ public:
+ // Details of the fetch will be stored in |fetch_request|.
+ // The fetch response will be created from parsing |json_fetch_reply_|.
+ MockProtocolHandler(base::DictionaryValue* fetch_request,
+ std::string* json_fetch_reply,
+ URLRequestDispatcher* dispatcher,
+ GenericURLRequestJob::Delegate* job_delegate)
+ : fetch_request_(fetch_request),
+ json_fetch_reply_(json_fetch_reply),
+ job_delegate_(job_delegate),
+ dispatcher_(dispatcher) {}
+
+ // net::URLRequestJobFactory::ProtocolHandler override.
+ net::URLRequestJob* MaybeCreateJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const override {
+ return new GenericURLRequestJob(
+ request, network_delegate, dispatcher_,
+ base::MakeUnique<MockFetcher>(fetch_request_, *json_fetch_reply_),
+ job_delegate_);
+ }
+
+ private:
+ base::DictionaryValue* fetch_request_; // NOT OWNED
+ std::string* json_fetch_reply_; // NOT OWNED
+ GenericURLRequestJob::Delegate* job_delegate_; // NOT OWNED
+ URLRequestDispatcher* dispatcher_; // NOT OWNED
+};
+
+} // namespace
+
+class GenericURLRequestJobTest : public testing::Test {
+ public:
+ GenericURLRequestJobTest() : dispatcher_(message_loop_.task_runner()) {
+ url_request_job_factory_.SetProtocolHandler(
+ "https", base::WrapUnique(new MockProtocolHandler(
+ &fetch_request_, &json_fetch_reply_, &dispatcher_,
+ &job_delegate_)));
+ url_request_context_.set_job_factory(&url_request_job_factory_);
+ url_request_context_.set_cookie_store(&cookie_store_);
+ }
+
+ std::unique_ptr<net::URLRequest> CreateAndCompleteJob(
+ const GURL& url,
+ const std::string& json_reply) {
+ json_fetch_reply_ = json_reply;
+
+ std::unique_ptr<net::URLRequest> request(url_request_context_.CreateRequest(
+ url, net::DEFAULT_PRIORITY, &request_delegate_));
+ request->Start();
+ message_loop_.RunUntilIdle();
+ return request;
+ }
+
+ protected:
+ base::MessageLoop message_loop_;
+ ExpeditedDispatcher dispatcher_;
+
+ net::URLRequestJobFactoryImpl url_request_job_factory_;
+ net::URLRequestContext url_request_context_;
+ MockCookieStore cookie_store_;
+
+ MockURLRequestDelegate request_delegate_;
+ base::DictionaryValue fetch_request_; // The request sent to MockFetcher.
+ std::string json_fetch_reply_; // The reply to be sent by MockFetcher.
+ MockGenericURLRequestJobDelegate job_delegate_;
+};
+
+TEST_F(GenericURLRequestJobTest, BasicRequestParams) {
+ // TODO(alexclarke): Lobby for raw string literals and use them here!
+ json_fetch_reply_ =
+ "{\"url\":\"https://example.com\","
+ " \"http_response_code\":200,"
+ " \"data\":\"Reply\","
+ " \"headers\":{\"Content-Type\":\"text/plain\"}}";
+
+ std::unique_ptr<net::URLRequest> request(url_request_context_.CreateRequest(
+ GURL("https://example.com"), net::DEFAULT_PRIORITY, &request_delegate_));
+ request->SetReferrer("https://referrer.example.com");
+ request->SetExtraRequestHeaderByName("Extra-Header", "Value", true);
+ request->SetExtraRequestHeaderByName("User-Agent", "TestBrowser", true);
+ request->SetExtraRequestHeaderByName("Accept", "text/plain", true);
+ request->Start();
+ message_loop_.RunUntilIdle();
+
+ std::string expected_request_json =
+ "{\"url\": \"https://example.com/\","
+ " \"headers\": {"
+ " \"Accept\": \"text/plain\","
+ " \"Cookie\": \"\","
+ " \"Extra-Header\": \"Value\","
+ " \"Referer\": \"https://referrer.example.com/\","
+ " \"User-Agent\": \"TestBrowser\""
+ " }"
+ "}";
+
+ EXPECT_THAT(fetch_request_, MatchesJson(expected_request_json));
+}
+
+TEST_F(GenericURLRequestJobTest, BasicRequestProperties) {
+ std::string reply =
+ "{\"url\":\"https://example.com\","
+ " \"http_response_code\":200,"
+ " \"data\":\"Reply\","
+ " \"headers\":{\"Content-Type\":\"text/html; charset=UTF-8\"}}";
+
+ std::unique_ptr<net::URLRequest> request(
+ CreateAndCompleteJob(GURL("https://example.com"), reply));
+
+ EXPECT_EQ(200, request->GetResponseCode());
+
+ std::string mime_type;
+ request->GetMimeType(&mime_type);
+ EXPECT_EQ("text/html", mime_type);
+
+ std::string charset;
+ request->GetCharset(&charset);
+ EXPECT_EQ("utf-8", charset);
+
+ std::string content_type;
+ EXPECT_TRUE(request->response_info().headers->GetNormalizedHeader(
+ "Content-Type", &content_type));
+ EXPECT_EQ("text/html; charset=UTF-8", content_type);
+}
+
+TEST_F(GenericURLRequestJobTest, BasicRequestContents) {
+ std::string reply =
+ "{\"url\":\"https://example.com\","
+ " \"http_response_code\":200,"
+ " \"data\":\"Reply\","
+ " \"headers\":{\"Content-Type\":\"text/html; charset=UTF-8\"}}";
+
+ std::unique_ptr<net::URLRequest> request(
+ CreateAndCompleteJob(GURL("https://example.com"), reply));
+
+ const int kBufferSize = 256;
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize));
+ int bytes_read;
+ EXPECT_TRUE(request->Read(buffer.get(), kBufferSize, &bytes_read));
+ EXPECT_EQ(5, bytes_read);
+ EXPECT_EQ("Reply", std::string(buffer->data(), 5));
+}
+
+TEST_F(GenericURLRequestJobTest, ReadInParts) {
+ std::string reply =
+ "{\"url\":\"https://example.com\","
+ " \"http_response_code\":200,"
+ " \"data\":\"Reply\","
+ " \"headers\":{\"Content-Type\":\"text/html; charset=UTF-8\"}}";
+
+ std::unique_ptr<net::URLRequest> request(
+ CreateAndCompleteJob(GURL("https://example.com"), reply));
+
+ const int kBufferSize = 3;
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize));
+ int bytes_read;
+ EXPECT_TRUE(request->Read(buffer.get(), kBufferSize, &bytes_read));
+ EXPECT_EQ(3, bytes_read);
+ EXPECT_EQ("Rep", std::string(buffer->data(), bytes_read));
+
+ EXPECT_TRUE(request->Read(buffer.get(), kBufferSize, &bytes_read));
+ EXPECT_EQ(2, bytes_read);
+ EXPECT_EQ("ly", std::string(buffer->data(), bytes_read));
+
+ EXPECT_TRUE(request->Read(buffer.get(), kBufferSize, &bytes_read));
+ EXPECT_EQ(0, bytes_read);
+}
+
+TEST_F(GenericURLRequestJobTest, RequestWithCookies) {
+ net::CookieList* cookies = cookie_store_.cookies();
+
+ // Basic matching cookie.
+ cookies->push_back(*net::CanonicalCookie::Create(
+ GURL("https://example.com"), "basic_cookie", "1", "example.com", "/",
+ base::Time(), base::Time(),
+ /* secure */ false,
+ /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
+ /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
+
+ // Matching secure cookie.
+ cookies->push_back(*net::CanonicalCookie::Create(
+ GURL("https://example.com"), "secure_cookie", "2", "example.com", "/",
+ base::Time(), base::Time(),
+ /* secure */ true,
+ /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
+ /* enforce_strict_secure */ true, net::COOKIE_PRIORITY_DEFAULT));
+
+ // Matching http-only cookie.
+ cookies->push_back(*net::CanonicalCookie::Create(
+ GURL("https://example.com"), "http_only_cookie", "3", "example.com", "/",
+ base::Time(), base::Time(),
+ /* secure */ false,
+ /* http_only */ true, net::CookieSameSite::NO_RESTRICTION,
+ /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
+
+ // Matching cookie with path.
+ cookies->push_back(*net::CanonicalCookie::Create(
+ GURL("https://example.com"), "cookie_with_path", "4", "example.com",
+ "/widgets", base::Time(), base::Time(),
+ /* secure */ false,
+ /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
+ /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
+
+ // Matching cookie with subdomain.
+ cookies->push_back(*net::CanonicalCookie::Create(
+ GURL("https://cdn.example.com"), "bad_subdomain_cookie", "5",
+ "cdn.example.com", "/", base::Time(), base::Time(),
+ /* secure */ false,
+ /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
+ /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
+
+ // Non-matching cookie (different site).
+ cookies->push_back(*net::CanonicalCookie::Create(
+ GURL("https://zombo.com"), "bad_site_cookie", "6", "zombo.com", "/",
+ base::Time(), base::Time(),
+ /* secure */ false,
+ /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
+ /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
+
+ // Non-matching cookie (different path).
+ cookies->push_back(*net::CanonicalCookie::Create(
+ GURL("https://example.com"), "bad_path_cookie", "7", "example.com",
+ "/gadgets", base::Time(), base::Time(),
+ /* secure */ false,
+ /* http_only */ false, net::CookieSameSite::NO_RESTRICTION,
+ /* enforce_strict_secure */ false, net::COOKIE_PRIORITY_DEFAULT));
+
+ std::string reply =
+ "{\"url\":\"https://example.com\","
+ " \"http_response_code\":200,"
+ " \"data\":\"Reply\","
+ " \"headers\":{\"Content-Type\":\"text/html; charset=UTF-8\"}}";
+
+ std::unique_ptr<net::URLRequest> request(
+ CreateAndCompleteJob(GURL("https://example.com"), reply));
+
+ std::string expected_request_json =
+ "{\"url\": \"https://example.com/\","
+ " \"headers\": {"
+ " \"Cookie\": \"basic_cookie=1; secure_cookie=2; http_only_cookie=3\","
+ " \"Referer\": \"\""
+ " }"
+ "}";
+
+ EXPECT_THAT(fetch_request_, MatchesJson(expected_request_json));
+}
+
+} // namespace headless
« no previous file with comments | « headless/public/util/generic_url_request_job.cc ('k') | headless/public/util/managed_dispatch_url_request_job.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698