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

Unified Diff: content/browser/loader/async_resource_handler_unittest.cc

Issue 2092993002: Browser process changes for Resource Timing sizes. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Stop using AllocateForTesting() Created 4 years, 5 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: content/browser/loader/async_resource_handler_unittest.cc
diff --git a/content/browser/loader/async_resource_handler_unittest.cc b/content/browser/loader/async_resource_handler_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..36da0fe864b2f42d996936341b29a45c960e8f3e
--- /dev/null
+++ b/content/browser/loader/async_resource_handler_unittest.cc
@@ -0,0 +1,394 @@
+// 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 "content/browser/loader/async_resource_handler.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <memory>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/feature_list.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/shared_memory_handle.h"
+#include "base/memory/weak_ptr.h"
+#include "base/process/process.h"
+#include "base/process/process_handle.h"
+#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "content/browser/loader/resource_dispatcher_host_impl.h"
+#include "content/browser/loader/resource_loader.h"
+#include "content/browser/loader/resource_loader_delegate.h"
+#include "content/browser/loader/resource_message_filter.h"
+#include "content/browser/loader/resource_request_info_impl.h"
+#include "content/common/resource_messages.h"
+#include "content/common/resource_request.h"
+#include "content/public/browser/resource_context.h"
+#include "content/public/browser/resource_request_info.h"
+#include "content/public/common/content_features.h"
+#include "content/public/common/process_type.h"
+#include "content/public/common/resource_type.h"
+#include "content/public/test/mock_resource_context.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "ipc/ipc_message.h"
+#include "ipc/ipc_message_macros.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_util.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_job_factory_impl.h"
+#include "net/url_request/url_request_test_job.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/page_transition_types.h"
+#include "url/gurl.h"
+
+namespace content {
+
+namespace {
+
+// This string is repeated as necessary to create a response body of the
+// required size. It is 8 bytes so that repeated doubling can achieve any power
+// of 2 size exactly.
+const char kBaseData[] = "Hello. ";
+
+// Response short enough to be inlined into a single IPC.
+const GURL kTestURLShort("test:short");
+
+// Response can be sent in a single chunk (not inlined).
+const GURL kTestURLMedium("test:medium");
+
+// Size of response to kTestURLMedium. It should be a power of 2 and larger than
+// kInlinedLeadingChunkSize.
+const size_t kMediumLength = 4096;
+
+// Response requires two chunks.
+const GURL kTestURLLarge("test:large");
+
+// Size of response to kTestURLLarge. It should be a power of 2 and larger than
+// kMaxAllocationSize.
+const size_t kLargeLength = 64 * 1024;
+
+std::string RepeatData(size_t target_size) {
+ std::string data = kBaseData;
+ data.reserve(target_size);
+ while (data.size() < target_size)
+ data += data;
+ return data;
+}
kinuko 2016/07/11 02:35:24 Why can't we use simple method like std::string(ta
Adam Rice 2016/07/11 05:16:36 Oh. Yes. Sorry. Done.
+
+// This test job adds a Content-Length header and implements
+// GetTotalReceivedBytes().
+class TestJob : public net::URLRequestTestJob {
+ public:
+ TestJob(net::URLRequest* request, net::NetworkDelegate* network_delegate)
+ : net::URLRequestTestJob(request, network_delegate, true) {}
+
+ // URLRequestJob implementation:
kinuko 2016/07/11 02:35:25 Should we have a TODO to implement this in URLRequ
Adam Rice 2016/07/11 05:16:36 Done.
+ int64_t GetTotalReceivedBytes() const override {
+ std::string http_headers = net::HttpUtil::ConvertHeadersBackToHTTPResponse(
+ response_headers_->raw_headers());
+ return http_headers.size() + offset_;
+ }
+
+ protected:
+ // Override URLRequestTestJob behaviour.
+ void StartAsync() override {
kinuko 2016/07/11 02:35:25 I think it's easier to extend / more readable if w
Adam Rice 2016/07/11 05:16:36 That is simpler. Done.
+ std::string data;
+ if (request_->url() == kTestURLShort) {
+ data = kBaseData;
+ } else if (request_->url() == kTestURLMedium) {
+ data = RepeatData(kMediumLength);
+ } else if (request_->url() == kTestURLLarge) {
+ data = RepeatData(kLargeLength);
+ }
+ if (data.empty()) {
+ URLRequestTestJob::StartAsync();
+ return;
+ }
+
+ std::string headers = test_headers();
+ response_headers_ = new net::HttpResponseHeaders(
+ net::HttpUtil::AssembleRawHeaders(headers.data(), headers.size()));
+
+ response_data_ = data;
+ response_headers_->AddHeader("Content-Length: " +
+ base::SizeTToString(data.size()));
+ URLRequestTestJob::StartAsync();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestJob);
+};
+
+class TestJobProtocolHandler
+ : public net::URLRequestJobFactory::ProtocolHandler {
+ public:
+ net::URLRequestJob* MaybeCreateJob(
+ net::URLRequest* request,
+ net::NetworkDelegate* network_delegate) const override {
+ return new TestJob(request, network_delegate);
+ }
+};
+
+// A subclass of ResourceMessageFilter that records IPC messages that are sent.
+class RecordingResourceMessageFilter : public ResourceMessageFilter {
+ public:
+ RecordingResourceMessageFilter(ResourceContext* resource_context,
+ net::URLRequestContext* request_context)
+ : ResourceMessageFilter(
+ 0,
+ PROCESS_TYPE_RENDERER,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ base::Bind(&RecordingResourceMessageFilter::GetContexts,
+ base::Unretained(this))),
+ resource_context_(resource_context),
+ request_context_(request_context) {
+ set_peer_process_for_testing(base::Process::Current());
+ }
+
+ const std::vector<std::unique_ptr<IPC::Message>>& messages() const {
+ return messages_;
+ }
+
+ // IPC::Sender implementation
+ bool Send(IPC::Message* message) override {
+ if (message->type() == ResourceMsg_SetDataBuffer::ID)
+ ConsumeHandle(*message);
+ messages_.push_back(base::WrapUnique(message));
+ return true;
+ }
+
+ private:
+ ~RecordingResourceMessageFilter() override {}
+
+ void GetContexts(ResourceType resource_type,
+ int origin_pid,
+ ResourceContext** resource_context,
+ net::URLRequestContext** request_context) {
+ *resource_context = resource_context_;
+ *request_context = request_context_;
+ }
+
+ // Unpickle the base::SharedMemoryHandle to avoid warnings about
+ // "MessageAttachmentSet destroyed with unconsumed descriptors".
+ void ConsumeHandle(const IPC::Message& message) {
+ IPC_BEGIN_MESSAGE_MAP(RecordingResourceMessageFilter, message)
+ IPC_MESSAGE_HANDLER(ResourceMsg_SetDataBuffer, OnSetDataBuffer)
+ IPC_MESSAGE_UNHANDLED(NOTREACHED())
+ IPC_END_MESSAGE_MAP()
kinuko 2016/07/11 02:35:25 Isn't it more compact / easier to read if we just
Adam Rice 2016/07/11 05:16:36 I didn't think that would work. I should have trie
+ }
+
+ void OnSetDataBuffer(int request_id,
+ base::SharedMemoryHandle shm_handle,
+ int shm_size,
+ base::ProcessId renderer_pid) {}
+
+ ResourceContext* const resource_context_;
+ net::URLRequestContext* const request_context_;
+ std::vector<std::unique_ptr<IPC::Message>> messages_;
+};
+
+class AsyncResourceHandlerTest : public ::testing::Test {
+ protected:
+ AsyncResourceHandlerTest()
+ : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP), context_(true) {}
+
+ void CreateRequest(const GURL& url) {
+ test_job_factory_.SetProtocolHandler(
+ "test", base::MakeUnique<TestJobProtocolHandler>());
+ context_.set_job_factory(&test_job_factory_);
+ context_.Init();
+ std::unique_ptr<net::URLRequest> request =
+ context_.CreateRequest(GURL(url), net::DEFAULT_PRIORITY, nullptr);
+ resource_context_ = base::MakeUnique<MockResourceContext>(&context_);
+ filter_ = make_scoped_refptr(
+ new RecordingResourceMessageFilter(resource_context_.get(), &context_));
+ ResourceRequestInfoImpl* info = new ResourceRequestInfoImpl(
Mike West 2016/07/08 13:14:37 Nit: Wrap this in `// clang format off` at the top
Adam Rice 2016/07/11 02:07:59 Actually, the comments on each line are sufficient
kinuko 2016/07/11 02:35:24 Note that ResourceRequestInfo::AllocateForTesting
Adam Rice 2016/07/11 05:16:36 ResourceRequestInfo::AllocateForTesting() only set
kinuko 2016/07/11 06:43:38 I see... then we probably need to live with this l
+ PROCESS_TYPE_RENDERER, // process_type
+ 0, // child_id
+ 0, // route_id
+ -1, // frame_tree_node_id
+ 0, // origin_pid
+ 0, // request_id
+ 0, // render_frame_id
+ false, // is_main_frame
+ false, // parent_is_main_frame
+ RESOURCE_TYPE_IMAGE, // resource_type
+ ui::PAGE_TRANSITION_LINK, // transition_type
+ false, // should_replace_current_entry
+ false, // is_download
+ false, // is_stream
+ false, // allow_download
+ false, // has_user_gesture
+ false, // enable load timing
+ false, // enable upload progress
+ false, // do_not_prompt_for_login
+ blink::WebReferrerPolicyDefault, // referrer_policy
+ blink::WebPageVisibilityStateVisible, // visibility_state
+ resource_context_.get(), // context
+ filter_->GetWeakPtr(), // filter
+ false, // report_raw_headers
+ true, // is_async
+ false, // is_using_lofi
+ std::string(), // original_headers
+ nullptr); // body
+ info->AssociateWithRequest(request.get());
+ std::unique_ptr<AsyncResourceHandler> handler =
+ base::MakeUnique<AsyncResourceHandler>(request.get(), &rdh_);
+ loader_delegate_.reset(new TrivialResourceLoaderDelegate(this));
+ loader_ =
+ base::MakeUnique<ResourceLoader>(std::move(request), std::move(handler),
+ nullptr, loader_delegate_.get());
+ }
+
+ void StartRequest() { loader_->StartRequest(); }
+
+ void WaitForFinish() { finish_waiter_.Run(); }
kinuko 2016/07/11 02:35:25 StartRequest and WaitForFinish are called only fro
Adam Rice 2016/07/11 05:16:36 Done.
+
+ void CreateStartAndWait(const GURL& url) {
+ CreateRequest(url);
+ StartRequest();
+ WaitForFinish();
+ }
+
+ TestBrowserThreadBundle thread_bundle_;
+ ResourceDispatcherHostImpl rdh_;
+ net::TestURLRequestContext context_;
+ net::URLRequestJobFactoryImpl test_job_factory_;
+ std::unique_ptr<MockResourceContext> resource_context_;
+ scoped_refptr<RecordingResourceMessageFilter> filter_;
+ std::unique_ptr<ResourceLoader> loader_;
+ base::RunLoop finish_waiter_;
kinuko 2016/07/11 02:35:24 Looks like all fields other than filter_ can be ac
Adam Rice 2016/07/11 05:16:36 Done.
+
+ private:
+ class TrivialResourceLoaderDelegate : public ResourceLoaderDelegate {
kinuko 2016/07/11 02:35:24 I think AsyncResourceHandlerTest can directly impl
Adam Rice 2016/07/11 05:16:36 Done.
+ public:
+ explicit TrivialResourceLoaderDelegate(AsyncResourceHandlerTest* fixture)
+ : fixture_(fixture) {}
+ ResourceDispatcherHostLoginDelegate* CreateLoginDelegate(
+ ResourceLoader* loader,
+ net::AuthChallengeInfo* auth_info) override {
+ return nullptr;
+ }
+
+ bool HandleExternalProtocol(ResourceLoader* loader,
+ const GURL& url) override {
+ return false;
+ }
+ void DidStartRequest(ResourceLoader* loader) override {}
+ void DidReceiveRedirect(ResourceLoader* loader,
+ const GURL& new_url) override {}
+ void DidReceiveResponse(ResourceLoader* loader) override {}
+ void DidFinishLoading(ResourceLoader* loader) override {
+ fixture_->DidFinishLoading();
+ }
+
+ private:
+ AsyncResourceHandlerTest* fixture_;
+ };
+
+ void DidFinishLoading() {
+ loader_.reset();
+ finish_waiter_.Quit();
+ }
+
+ std::unique_ptr<TrivialResourceLoaderDelegate> loader_delegate_;
+};
+
+std::unique_ptr<ResourceMsg_DataReceived::Param> UnpackDataReceivedIPC(
+ const IPC::Message* msg) {
+ if (ResourceMsg_DataReceived::ID != msg->type())
+ return nullptr;
+ std::unique_ptr<ResourceMsg_DataReceived::Param> params =
+ base::MakeUnique<ResourceMsg_DataReceived::Param>();
+ ResourceMsg_DataReceived::Read(msg, params.get());
+ return params;
+}
+
+std::unique_ptr<ResourceMsg_InlinedDataChunkReceived::Param>
+UnpackInlinedDataChunkReceivedIPC(const IPC::Message* msg) {
+ if (ResourceMsg_InlinedDataChunkReceived::ID != msg->type())
+ return nullptr;
+ std::unique_ptr<ResourceMsg_InlinedDataChunkReceived::Param> params =
+ base::MakeUnique<ResourceMsg_InlinedDataChunkReceived::Param>();
+ ResourceMsg_InlinedDataChunkReceived::Read(msg, params.get());
+ return params;
+}
+
+TEST_F(AsyncResourceHandlerTest, Construct) {
+ CreateRequest(net::URLRequestTestJob::test_url_1());
+}
+
+TEST_F(AsyncResourceHandlerTest, OneChunkLengths) {
+ CreateStartAndWait(kTestURLMedium);
+ const auto& messages = filter_->messages();
+ ASSERT_EQ(4u, messages.size());
+ auto params = UnpackDataReceivedIPC(messages[2].get());
+ ASSERT_TRUE(params);
kinuko 2016/07/11 02:35:24 I have a feeling that these two lines can be repla
Adam Rice 2016/07/11 05:16:36 Read() doesn't check the message type, so I had to
+
+ int encoded_data_length = std::get<3>(*params);
+ EXPECT_EQ(4163, encoded_data_length);
+ int encoded_body_length = std::get<4>(*params);
+ EXPECT_EQ(4096, encoded_body_length);
+}
+
+TEST_F(AsyncResourceHandlerTest, InlinedChunkLengths) {
+ // TODO(ricea): Remove this Feature-enabling code once the feature is on by
+ // default.
+ auto feature_list = base::MakeUnique<base::FeatureList>();
+ feature_list->InitializeFromCommandLine(
+ features::kOptimizeLoadingIPCForSmallResources.name, "");
+ base::FeatureList::ClearInstanceForTesting();
+ base::FeatureList::SetInstance(std::move(feature_list));
+
+ CreateStartAndWait(kTestURLShort);
+ const auto& messages = filter_->messages();
+ ASSERT_EQ(3u, messages.size());
+ auto params = UnpackInlinedDataChunkReceivedIPC(messages[1].get());
+ ASSERT_TRUE(params);
+
+ int encoded_data_length = std::get<2>(*params);
+ EXPECT_EQ(72, encoded_data_length);
+ int encoded_body_length = std::get<3>(*params);
+ EXPECT_EQ(8, encoded_body_length);
+
+ base::FeatureList::ClearInstanceForTesting();
+ base::FeatureList::SetInstance(base::MakeUnique<base::FeatureList>());
+}
+
+TEST_F(AsyncResourceHandlerTest, TwoChunksLengths) {
+ CreateStartAndWait(kTestURLLarge);
+ const auto& messages = filter_->messages();
+ ASSERT_EQ(5u, messages.size());
+ auto params = UnpackDataReceivedIPC(messages[2].get());
+ ASSERT_TRUE(params);
+
+ int encoded_data_length = std::get<3>(*params);
+ EXPECT_EQ(32836, encoded_data_length);
+ int encoded_body_length = std::get<4>(*params);
+ EXPECT_EQ(32768, encoded_body_length);
+
+ params = UnpackDataReceivedIPC(messages[3].get());
+ ASSERT_TRUE(params);
+ encoded_data_length = std::get<3>(*params);
+ EXPECT_EQ(32768, encoded_data_length);
+ encoded_body_length = std::get<4>(*params);
+ EXPECT_EQ(32768, encoded_body_length);
+}
+
+} // namespace
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698