Chromium Code Reviews| Index: content/browser/loader/mojo_async_resource_handler_unittest.cc |
| diff --git a/content/browser/loader/mojo_async_resource_handler_unittest.cc b/content/browser/loader/mojo_async_resource_handler_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c1118bf330a236d599139911badc4eb4569bb3c1 |
| --- /dev/null |
| +++ b/content/browser/loader/mojo_async_resource_handler_unittest.cc |
| @@ -0,0 +1,839 @@ |
| +// 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/mojo_async_resource_handler.h" |
| + |
| +#include <string.h> |
| + |
| +#include <utility> |
| + |
| +#include "base/bind.h" |
| +#include "base/callback.h" |
| +#include "base/files/file_path.h" |
| +#include "base/memory/ptr_util.h" |
| +#include "base/memory/scoped_vector.h" |
| +#include "base/memory/weak_ptr.h" |
| +#include "base/run_loop.h" |
| +#include "content/browser/loader/mojo_async_resource_handler_test_util.h" |
| +#include "content/browser/loader/resource_dispatcher_host_impl.h" |
| +#include "content/browser/loader/resource_request_info_impl.h" |
| +#include "content/common/resource_request_completion_status.h" |
| +#include "content/common/url_loader.mojom.h" |
| +#include "content/public/browser/appcache_service.h" |
| +#include "content/public/browser/navigation_data.h" |
| +#include "content/public/browser/resource_context.h" |
| +#include "content/public/browser/resource_controller.h" |
| +#include "content/public/browser/resource_dispatcher_host_delegate.h" |
| +#include "content/public/browser/resource_throttle.h" |
| +#include "content/public/browser/stream_info.h" |
| +#include "content/public/common/resource_response.h" |
| +#include "content/public/common/resource_type.h" |
| +#include "content/public/test/test_browser_context.h" |
| +#include "content/public/test/test_browser_thread_bundle.h" |
| +#include "mojo/public/c/system/data_pipe.h" |
| +#include "mojo/public/c/system/types.h" |
| +#include "mojo/public/cpp/system/data_pipe.h" |
| +#include "net/base/auth.h" |
| +#include "net/base/net_errors.h" |
| +#include "net/http/http_response_headers.h" |
| +#include "net/http/http_response_info.h" |
| +#include "net/http/http_status_code.h" |
| +#include "net/http/http_util.h" |
| +#include "net/ssl/client_cert_store.h" |
| +#include "net/test/url_request/url_request_failed_job.h" |
| +#include "net/url_request/url_request.h" |
| +#include "net/url_request/url_request_context.h" |
| +#include "net/url_request/url_request_filter.h" |
| +#include "net/url_request/url_request_status.h" |
| +#include "net/url_request/url_request_test_util.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| +#include "ui/base/page_transition_types.h" |
| + |
| +namespace content { |
| +namespace { |
| + |
| +class FakeResourceDispatcherHostDelegate |
| + : public ResourceDispatcherHostDelegate { |
| + public: |
| + FakeResourceDispatcherHostDelegate() {} |
| + ~FakeResourceDispatcherHostDelegate() override {} |
| + |
| + bool ShouldBeginRequest(const std::string& method, |
| + const GURL& url, |
| + ResourceType resource_type, |
| + ResourceContext* resource_context) override { |
| + ADD_FAILURE() << "ShouldBeginRequest should not be called."; |
| + return false; |
| + } |
| + |
| + void RequestBeginning(net::URLRequest* request, |
| + ResourceContext* resource_context, |
| + AppCacheService* appcache_service, |
| + ResourceType resource_type, |
| + ScopedVector<ResourceThrottle>* throttles) override { |
| + ADD_FAILURE() << "RequestBeginning should not be called."; |
| + } |
| + |
| + void DownloadStarting(net::URLRequest* request, |
| + ResourceContext* resource_context, |
| + int child_id, |
| + int route_id, |
| + bool is_content_initiated, |
| + bool must_download, |
| + ScopedVector<ResourceThrottle>* throttles) override { |
| + ADD_FAILURE() << "DownloadStarting should not be called."; |
| + } |
| + |
| + ResourceDispatcherHostLoginDelegate* CreateLoginDelegate( |
| + net::AuthChallengeInfo* auth_info, |
| + net::URLRequest* request) override { |
| + ADD_FAILURE() << "CreateLoginDelegate should not be called."; |
| + return nullptr; |
| + } |
| + |
| + bool HandleExternalProtocol( |
| + const GURL& url, |
| + int child_id, |
| + const ResourceRequestInfo::WebContentsGetter& web_contents_getter, |
| + bool is_main_frame, |
| + ui::PageTransition page_transition, |
| + bool has_user_gesture, |
| + ResourceContext* resource_context) override { |
| + ADD_FAILURE() << "HandleExternalProtocol should not be called."; |
| + return false; |
| + } |
| + |
| + bool ShouldForceDownloadResource(const GURL& url, |
| + const std::string& mime_type) override { |
| + ADD_FAILURE() << "ShouldForceDownloadResource should not be called."; |
| + return false; |
| + } |
| + |
| + bool ShouldInterceptResourceAsStream(net::URLRequest* request, |
| + const base::FilePath& plugin_path, |
| + const std::string& mime_type, |
| + GURL* origin, |
| + std::string* payload) override { |
| + ADD_FAILURE() << "ShouldInterceptResourceAsStream should not be called."; |
| + return false; |
| + } |
| + |
| + void OnStreamCreated(net::URLRequest* request, |
| + std::unique_ptr<content::StreamInfo> stream) override { |
| + ADD_FAILURE() << "OnStreamCreated should not be called."; |
| + } |
| + |
| + void OnResponseStarted(net::URLRequest* request, |
| + ResourceContext* resource_context, |
| + ResourceResponse* response) override { |
| + is_on_response_started_called_ = true; |
| + } |
| + |
| + void OnRequestRedirected(const GURL& redirect_url, |
| + net::URLRequest* request, |
| + ResourceContext* resource_context, |
| + ResourceResponse* response) override { |
| + ADD_FAILURE() << "OnRequestRedirected should not be called."; |
| + } |
| + |
| + void RequestComplete(net::URLRequest* url_request) override { |
| + ADD_FAILURE() << "RequestComplete should not be called."; |
| + } |
| + |
| + bool ShouldEnableLoFiMode( |
| + const net::URLRequest& url_request, |
| + content::ResourceContext* resource_context) override { |
| + ADD_FAILURE() << "ShouldEnableLoFiMode should not be called."; |
| + return false; |
| + } |
| + |
| + NavigationData* GetNavigationData(net::URLRequest* request) const override { |
| + ADD_FAILURE() << "GetNavigationData should not be called."; |
| + return nullptr; |
| + } |
| + |
| + std::unique_ptr<net::ClientCertStore> CreateClientCertStore( |
| + ResourceContext* resource_context) override { |
| + ADD_FAILURE() << "CreateClientCertStore should not be called."; |
| + return nullptr; |
| + } |
| + |
| + bool is_on_response_started_called() const { |
| + return is_on_response_started_called_; |
| + } |
| + |
| + private: |
| + bool is_on_response_started_called_ = false; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(FakeResourceDispatcherHostDelegate); |
| +}; |
| + |
| +class FakeResourceController : public ResourceController { |
| + public: |
| + FakeResourceController() {} |
| + ~FakeResourceController() override {} |
| + |
| + void Cancel() override { ADD_FAILURE() << "Cancel should not be called."; } |
| + void CancelAndIgnore() override { |
| + ADD_FAILURE() << "CancelAndIgnore should not be called."; |
| + } |
| + void CancelWithError(int error_code) override { |
| + is_cancel_with_error_called_ = true; |
| + error_ = error_code; |
| + if (quit_closure_) |
| + quit_closure_.Run(); |
| + } |
| + void Resume() override { |
| + ++num_resume_calls_; |
| + if (quit_closure_) |
| + quit_closure_.Run(); |
| + } |
| + void set_quit_closure(const base::Closure& quit_closure) { |
| + quit_closure_ = quit_closure; |
| + } |
| + |
| + bool is_cancel_with_error_called() const { |
| + return is_cancel_with_error_called_; |
| + } |
| + int error() const { return error_; } |
| + unsigned num_resume_calls() const { return num_resume_calls_; } |
| + |
| + private: |
| + bool is_cancel_with_error_called_ = false; |
| + int error_ = net::OK; |
| + int num_resume_calls_ = 0; |
| + base::Closure quit_closure_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(FakeResourceController); |
| +}; |
| + |
| +class MojoAsyncResourceHandlerWithCustomDataPipeOperations |
| + : public MojoAsyncResourceHandler { |
| + public: |
| + MojoAsyncResourceHandlerWithCustomDataPipeOperations( |
| + net::URLRequest* request, |
| + ResourceDispatcherHostImpl* rdh, |
| + mojo::InterfaceRequest<mojom::URLLoader> mojo_request, |
| + mojom::URLLoaderClientPtr url_loader_client) |
| + : MojoAsyncResourceHandler(request, |
| + rdh, |
| + std::move(mojo_request), |
| + std::move(url_loader_client)) {} |
| + ~MojoAsyncResourceHandlerWithCustomDataPipeOperations() override {} |
| + |
| + void set_begin_write_expectation(MojoResult begin_write_expectation) { |
| + is_begin_write_expectation_set_ = true; |
| + begin_write_expectation_ = begin_write_expectation; |
| + } |
| + void set_end_write_expectation(MojoResult end_write_expectation) { |
| + is_end_write_expectation_set_ = true; |
| + end_write_expectation_ = end_write_expectation; |
| + } |
| + |
| + private: |
| + MojoResult BeginWrite(void** data, uint32_t* available) override { |
| + if (is_begin_write_expectation_set_) |
| + return begin_write_expectation_; |
| + return MojoAsyncResourceHandler::BeginWrite(data, available); |
| + } |
| + MojoResult EndWrite(uint32_t written) override { |
| + if (is_end_write_expectation_set_) |
| + return end_write_expectation_; |
| + return MojoAsyncResourceHandler::EndWrite(written); |
| + } |
| + |
| + bool is_begin_write_expectation_set_ = false; |
| + bool is_end_write_expectation_set_ = false; |
| + MojoResult begin_write_expectation_ = MOJO_RESULT_UNKNOWN; |
| + MojoResult end_write_expectation_ = MOJO_RESULT_UNKNOWN; |
| + |
| + DISALLOW_COPY_AND_ASSIGN( |
| + MojoAsyncResourceHandlerWithCustomDataPipeOperations); |
| +}; |
| + |
| +class MojoAsyncResourceHandlerTestBase { |
| + public: |
| + MojoAsyncResourceHandlerTestBase() |
| + : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP), |
| + browser_context_(new TestBrowserContext()) { |
| + MojoAsyncResourceHandler::SetAllocationSizeForTesting(32 * 1024); |
| + |
| + // Calling this function creates a request context. |
| + browser_context_->GetResourceContext()->GetRequestContext(); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + url_request_delegate_.reset(new net::TestDelegate()); |
| + net::URLRequestContext* request_context = |
| + browser_context_->GetResourceContext()->GetRequestContext(); |
| + request_ = request_context->CreateRequest( |
| + net::URLRequestFailedJob::GetMockHttpUrl(net::ERR_TIMED_OUT), |
| + net::DEFAULT_PRIORITY, url_request_delegate_.get()); |
| + ResourceRequestInfo::AllocateForTesting( |
| + request_.get(), RESOURCE_TYPE_XHR, |
| + browser_context_->GetResourceContext(), 2, 0, 0, true, false, false, |
| + true, false); |
| + handler_.reset(new MojoAsyncResourceHandlerWithCustomDataPipeOperations( |
| + request_.get(), &rdh_, nullptr, |
| + url_loader_client_.CreateInterfacePtrAndBind())); |
| + handler_->SetController(&resource_controller_); |
| + } |
| + |
| + virtual ~MojoAsyncResourceHandlerTestBase() { |
| + rdh_.SetDelegate(nullptr); |
| + net::URLRequestFilter::GetInstance()->ClearHandlers(); |
| + } |
| + |
| + void GetContexts(ResourceType resource_type, |
| + int origin_pid, |
| + ResourceContext** resource_context, |
| + net::URLRequestContext** request_context) { |
| + *resource_context = browser_context_->GetResourceContext(); |
| + *request_context = |
| + browser_context_->GetResourceContext()->GetRequestContext(); |
| + } |
| + |
| + void RunUntilNextNotification() { |
| + base::RunLoop run_loop; |
| + url_loader_client_.set_quit_closure(run_loop.QuitClosure()); |
| + resource_controller_.set_quit_closure(run_loop.QuitClosure()); |
| + run_loop.Run(); |
| + } |
| + |
| + TestBrowserThreadBundle thread_bundle_; |
| + ResourceDispatcherHostImpl rdh_; |
| + FakeURLLoaderClient url_loader_client_; |
| + FakeResourceController resource_controller_; |
| + std::unique_ptr<TestBrowserContext> browser_context_; |
| + std::unique_ptr<net::TestDelegate> url_request_delegate_; |
| + std::unique_ptr<net::URLRequest> request_; |
| + std::unique_ptr<MojoAsyncResourceHandlerWithCustomDataPipeOperations> |
| + handler_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(MojoAsyncResourceHandlerTestBase); |
| +}; |
| + |
| +class MojoAsyncResourceHandlerTest : public MojoAsyncResourceHandlerTestBase, |
| + public ::testing::Test {}; |
| + |
| +// This test class is parametrized with MojoAsyncResourceHandler's allocation |
| +// size. |
| +class MojoAsyncResourceHandlerWithAllocationSizeTest |
| + : public MojoAsyncResourceHandlerTestBase, |
| + public ::testing::TestWithParam<size_t> { |
| + protected: |
| + MojoAsyncResourceHandlerWithAllocationSizeTest() { |
| + MojoAsyncResourceHandler::SetAllocationSizeForTesting(GetParam()); |
| + } |
| +}; |
| + |
| +TEST_F(MojoAsyncResourceHandlerTest, InFlightRequests) { |
| + EXPECT_EQ(0, rdh_.num_in_flight_requests_for_testing()); |
| + handler_ = nullptr; |
| + EXPECT_EQ(0, rdh_.num_in_flight_requests_for_testing()); |
| +} |
| + |
| +TEST_F(MojoAsyncResourceHandlerTest, OnResponseStarted) { |
| + scoped_refptr<ResourceResponse> response = new ResourceResponse(); |
| + FakeResourceDispatcherHostDelegate rdh_delegate; |
| + response->head.content_length = 99; |
| + response->head.request_start = |
| + base::TimeTicks::UnixEpoch() + base::TimeDelta::FromDays(14); |
| + response->head.response_start = |
| + base::TimeTicks::UnixEpoch() + base::TimeDelta::FromDays(28); |
| + |
| + bool defer = false; |
| + |
| + base::TimeTicks now1 = base::TimeTicks::Now(); |
| + rdh_.SetDelegate(&rdh_delegate); |
| + ASSERT_TRUE(handler_->OnResponseStarted(response.get(), &defer)); |
| + rdh_.SetDelegate(nullptr); |
| + base::TimeTicks now2 = base::TimeTicks::Now(); |
| + |
| + EXPECT_FALSE(defer); |
| + EXPECT_EQ(request_->creation_time(), response->head.request_start); |
| + EXPECT_LE(now1, response->head.response_start); |
| + EXPECT_LE(response->head.response_start, now2); |
| + EXPECT_TRUE(rdh_delegate.is_on_response_started_called()); |
| + |
| + RunUntilNextNotification(); |
| + EXPECT_TRUE(url_loader_client_.has_received_response()); |
| + EXPECT_EQ(response->head.request_start, |
| + url_loader_client_.response_head().request_start); |
| + EXPECT_EQ(response->head.response_start, |
| + url_loader_client_.response_head().response_start); |
| + EXPECT_EQ(99, url_loader_client_.response_head().content_length); |
| +} |
| + |
| +TEST_F(MojoAsyncResourceHandlerTest, OnWillStart) { |
| + bool defer = false; |
| + EXPECT_TRUE(handler_->OnWillStart(request_->url(), &defer)); |
| + EXPECT_FALSE(defer); |
| +} |
| + |
| +TEST_F(MojoAsyncResourceHandlerTest, OnBeforeNetworkStart) { |
| + bool defer = false; |
| + EXPECT_TRUE(handler_->OnBeforeNetworkStart(request_->url(), &defer)); |
| + EXPECT_FALSE(defer); |
| +} |
| + |
| +TEST_F(MojoAsyncResourceHandlerTest, OnWillReadAndInFlightRequests) { |
| + EXPECT_EQ(0, rdh_.num_in_flight_requests_for_testing()); |
| + scoped_refptr<net::IOBuffer> buffer; |
| + int buf_size; |
| + EXPECT_TRUE(handler_->OnWillRead(&buffer, &buf_size, -1)); |
| + EXPECT_EQ(1, rdh_.num_in_flight_requests_for_testing()); |
| + handler_ = nullptr; |
| + EXPECT_EQ(0, rdh_.num_in_flight_requests_for_testing()); |
| +} |
| + |
| +TEST_F(MojoAsyncResourceHandlerTest, OnWillReadWithInsufficientResource) { |
| + rdh_.set_max_num_in_flight_requests_per_process(0); |
| + |
| + scoped_refptr<net::IOBuffer> buffer; |
| + int buf_size = 0; |
| + EXPECT_FALSE(handler_->OnWillRead(&buffer, &buf_size, -1)); |
| + EXPECT_FALSE(buffer); |
| + EXPECT_EQ(0, buf_size); |
| + EXPECT_EQ(1, rdh_.num_in_flight_requests_for_testing()); |
| + EXPECT_TRUE(resource_controller_.is_cancel_with_error_called()); |
| + EXPECT_EQ(net::ERR_INSUFFICIENT_RESOURCES, resource_controller_.error()); |
| + handler_ = nullptr; |
| + EXPECT_EQ(0, rdh_.num_in_flight_requests_for_testing()); |
| +} |
| + |
| +TEST_F(MojoAsyncResourceHandlerTest, OnWillReadAndOnReadCompleted) { |
| + bool defer = false; |
| + scoped_refptr<net::IOBuffer> buffer; |
| + int buf_size; |
| + |
| + ASSERT_TRUE(handler_->OnWillRead(&buffer, &buf_size, -1)); |
| + ASSERT_TRUE(buffer); |
| + // The buffer size that the mime sniffer requires implicitly. |
| + ASSERT_GE(buf_size, 2 * 1024); |
| + |
| + RunUntilNextNotification(); |
| + ASSERT_TRUE(url_loader_client_.response_body().is_valid()); |
| + |
| + buffer->data()[0] = 'A'; |
| + buffer->data()[1] = 'B'; |
| + ASSERT_TRUE(handler_->OnReadCompleted(2, &defer)); |
| + EXPECT_FALSE(defer); |
| + |
| + std::string contents; |
| + do { |
| + char buffer[16]; |
| + uint32_t read = sizeof(buffer); |
| + MojoResult r = mojo::ReadDataRaw(url_loader_client_.response_body(), buffer, |
| + &read, MOJO_READ_DATA_FLAG_NONE); |
| + if (r == MOJO_RESULT_SHOULD_WAIT) |
| + continue; |
| + contents += std::string(buffer, read); |
| + } while (contents.size() < 2); |
| + EXPECT_EQ("AB", contents); |
| +} |
| + |
| +TEST_F(MojoAsyncResourceHandlerTest, |
| + OnWillReadAndOnReadCompletedWithInsufficientInitialCapacity) { |
| + MojoAsyncResourceHandler::SetAllocationSizeForTesting(2); |
| + |
| + bool defer = false; |
| + scoped_refptr<net::IOBuffer> buffer; |
| + int buf_size; |
| + |
| + ASSERT_TRUE(handler_->OnWillRead(&buffer, &buf_size, -1)); |
| + ASSERT_TRUE(buffer); |
| + // The buffer size that the mime sniffer requires implicitly. |
| + ASSERT_GE(buf_size, 2 * 1024); |
| + |
| + RunUntilNextNotification(); |
| + ASSERT_TRUE(url_loader_client_.response_body().is_valid()); |
| + |
| + const std::string data("abcdefgh"); |
| + strcpy(buffer->data(), data.c_str()); |
| + ASSERT_TRUE(handler_->OnReadCompleted(data.size(), &defer)); |
| + EXPECT_TRUE(defer); |
| + |
| + std::string contents; |
| + do { |
| + // This is needed for ResumeIfDeferred to be called. |
| + base::RunLoop().RunUntilIdle(); |
| + char buffer[16]; |
| + uint32_t read = sizeof(buffer); |
| + MojoResult r = mojo::ReadDataRaw(url_loader_client_.response_body(), buffer, |
| + &read, MOJO_READ_DATA_FLAG_NONE); |
| + if (r == MOJO_RESULT_SHOULD_WAIT) |
| + continue; |
| + ASSERT_EQ(MOJO_RESULT_OK, r); |
| + contents += std::string(buffer, read); |
| + } while (contents.size() < data.size()); |
| + EXPECT_EQ(data, contents); |
| + EXPECT_EQ(0u, resource_controller_.num_resume_calls()); |
| +} |
| + |
| +TEST_F(MojoAsyncResourceHandlerTest, |
| + IOBufferFromOnWillReadShouldRemainValidEvenIfHandlerIsGone) { |
| + scoped_refptr<net::IOBuffer> io_buffer; |
| + int buf_size; |
| + |
| + ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
| + ASSERT_TRUE(io_buffer); |
| + // The io_buffer size that the mime sniffer requires implicitly. |
| + ASSERT_GE(buf_size, 2 * 1024); |
| + handler_ = nullptr; |
| + |
|
mmenke
2016/07/14 21:23:05
Any reason to delete url_loader_client_, too? Gue
|
| + // Hopefully ASAN checks this operation's validity. |
| + io_buffer->data()[0] = 'A'; |
| +} |
| + |
| +TEST_F(MojoAsyncResourceHandlerTest, OnResponseCompleted) { |
| + scoped_refptr<ResourceResponse> response = new ResourceResponse(); |
| + bool defer = false; |
| + ASSERT_TRUE(handler_->OnResponseStarted(response.get(), &defer)); |
| + EXPECT_FALSE(defer); |
| + RunUntilNextNotification(); |
| + EXPECT_TRUE(url_loader_client_.has_received_response()); |
| + |
| + handler_->GetRequestInfoForTesting()->set_was_ignored_by_handler(false); |
| + net::URLRequestStatus status(net::URLRequestStatus::SUCCESS, net::OK); |
| + std::string security_info = "info0"; |
| + |
| + base::TimeTicks now1 = base::TimeTicks::Now(); |
| + handler_->OnResponseCompleted(status, security_info, &defer); |
| + base::TimeTicks now2 = base::TimeTicks::Now(); |
| + EXPECT_FALSE(defer); |
| + |
| + RunUntilNextNotification(); |
| + EXPECT_TRUE(url_loader_client_.has_received_completion()); |
| + EXPECT_EQ(net::OK, url_loader_client_.completion_status().error_code); |
| + EXPECT_FALSE(url_loader_client_.completion_status().was_ignored_by_handler); |
| + EXPECT_EQ("info0", url_loader_client_.completion_status().security_info); |
| + EXPECT_LE(now1, url_loader_client_.completion_status().completion_time); |
| + EXPECT_LE(url_loader_client_.completion_status().completion_time, now2); |
| + EXPECT_EQ(request_->GetTotalReceivedBytes(), |
| + url_loader_client_.completion_status().encoded_data_length); |
| +} |
| + |
| +// This test case sets different status values from OnResponseCompleted. |
| +TEST_F(MojoAsyncResourceHandlerTest, OnResponseCompleted2) { |
| + scoped_refptr<ResourceResponse> response = new ResourceResponse(); |
| + bool defer = false; |
| + ASSERT_TRUE(handler_->OnResponseStarted(response.get(), &defer)); |
| + EXPECT_FALSE(defer); |
| + |
| + handler_->GetRequestInfoForTesting()->set_was_ignored_by_handler(true); |
| + net::URLRequestStatus status(net::URLRequestStatus::CANCELED, |
| + net::ERR_ABORTED); |
| + std::string security_info = "info1"; |
| + |
| + base::TimeTicks now1 = base::TimeTicks::Now(); |
| + handler_->OnResponseCompleted(status, security_info, &defer); |
| + base::TimeTicks now2 = base::TimeTicks::Now(); |
| + EXPECT_FALSE(defer); |
| + |
| + RunUntilNextNotification(); |
| + EXPECT_TRUE(url_loader_client_.has_received_completion()); |
| + EXPECT_EQ(net::ERR_ABORTED, |
| + url_loader_client_.completion_status().error_code); |
| + EXPECT_TRUE(url_loader_client_.completion_status().was_ignored_by_handler); |
| + EXPECT_EQ("info1", url_loader_client_.completion_status().security_info); |
| + EXPECT_LE(now1, url_loader_client_.completion_status().completion_time); |
| + EXPECT_LE(url_loader_client_.completion_status().completion_time, now2); |
| + EXPECT_EQ(request_->GetTotalReceivedBytes(), |
| + url_loader_client_.completion_status().encoded_data_length); |
| +} |
| + |
| +TEST_F(MojoAsyncResourceHandlerTest, OnResponseCompletedWithCanceledTimedOut) { |
| + net::URLRequestStatus status(net::URLRequestStatus::CANCELED, |
| + net::ERR_TIMED_OUT); |
| + bool defer = false; |
| + |
| + handler_->OnResponseCompleted(status, "security_info", &defer); |
| + EXPECT_FALSE(defer); |
| + |
| + RunUntilNextNotification(); |
| + EXPECT_TRUE(url_loader_client_.has_received_completion()); |
| + EXPECT_EQ(net::ERR_TIMED_OUT, |
| + url_loader_client_.completion_status().error_code); |
| +} |
| + |
| +TEST_F(MojoAsyncResourceHandlerTest, OnResponseCompletedWithFailedTimedOut) { |
| + net::URLRequestStatus status(net::URLRequestStatus::FAILED, |
| + net::ERR_TIMED_OUT); |
| + bool defer = false; |
| + |
| + handler_->OnResponseCompleted(status, "security_info", &defer); |
| + EXPECT_FALSE(defer); |
| + |
| + RunUntilNextNotification(); |
| + EXPECT_TRUE(url_loader_client_.has_received_completion()); |
| + EXPECT_EQ(net::ERR_TIMED_OUT, |
| + url_loader_client_.completion_status().error_code); |
| +} |
| + |
| +TEST_F(MojoAsyncResourceHandlerTest, ResponseCompletionShouldCloseDataPipe) { |
| + scoped_refptr<ResourceResponse> response = new ResourceResponse(); |
| + bool defer = false; |
| + ASSERT_TRUE(handler_->OnResponseStarted(response.get(), &defer)); |
| + EXPECT_FALSE(defer); |
| + RunUntilNextNotification(); |
| + EXPECT_TRUE(url_loader_client_.has_received_response()); |
| + |
| + scoped_refptr<net::IOBuffer> io_buffer; |
| + int buf_size; |
| + ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
| + RunUntilNextNotification(); |
| + ASSERT_TRUE(url_loader_client_.response_body().is_valid()); |
| + ASSERT_TRUE(handler_->OnReadCompleted(0, &defer)); |
| + EXPECT_FALSE(defer); |
| + |
| + net::URLRequestStatus status(net::URLRequestStatus::SUCCESS, net::OK); |
| + handler_->OnResponseCompleted(status, "security_info", &defer); |
| + EXPECT_FALSE(defer); |
| + |
| + RunUntilNextNotification(); |
| + EXPECT_TRUE(url_loader_client_.has_received_completion()); |
| + EXPECT_EQ(net::OK, url_loader_client_.completion_status().error_code); |
| + |
| + // This is needed because |*io_buffer| may keep the data producer alive. |
| + io_buffer = nullptr; |
| + |
| + char buffer[16]; |
| + uint32_t read = sizeof(buffer); |
| + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, |
| + mojo::ReadDataRaw(url_loader_client_.response_body(), buffer, &read, |
| + MOJO_READ_DATA_FLAG_NONE)); |
| +} |
| + |
| +TEST_F(MojoAsyncResourceHandlerTest, ResponseErrorDuringBodyTransmission) { |
| + scoped_refptr<ResourceResponse> response = new ResourceResponse(); |
| + bool defer = false; |
| + ASSERT_TRUE(handler_->OnResponseStarted(response.get(), &defer)); |
| + EXPECT_FALSE(defer); |
| + RunUntilNextNotification(); |
| + EXPECT_TRUE(url_loader_client_.has_received_response()); |
| + |
| + scoped_refptr<net::IOBuffer> io_buffer; |
| + int buf_size; |
| + ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
| + RunUntilNextNotification(); |
| + ASSERT_TRUE(url_loader_client_.response_body().is_valid()); |
| + ASSERT_GT(buf_size, 0); |
| + memset(io_buffer->data(), 'a', buf_size); |
| + ASSERT_TRUE(handler_->OnReadCompleted(buf_size, &defer)); |
| + |
| + defer = false; |
| + net::URLRequestStatus status(net::URLRequestStatus::FAILED, net::ERR_FAILED); |
| + handler_->OnResponseCompleted(status, "security_info", &defer); |
| + EXPECT_FALSE(defer); |
| + |
| + RunUntilNextNotification(); |
| + EXPECT_TRUE(url_loader_client_.has_received_completion()); |
| + EXPECT_EQ(net::ERR_FAILED, url_loader_client_.completion_status().error_code); |
| + |
| + // This is needed because |*io_buffer| may keep the data producer alive. |
| + io_buffer = nullptr; |
| + |
| + std::string actual; |
| + while (true) { |
| + char buf[16]; |
| + uint32_t read = sizeof(buf); |
| + MojoResult r = mojo::ReadDataRaw(url_loader_client_.response_body(), buf, |
| + &read, MOJO_READ_DATA_FLAG_NONE); |
| + if (r == MOJO_RESULT_FAILED_PRECONDITION) |
| + break; |
| + if (r == MOJO_RESULT_SHOULD_WAIT) |
| + continue; |
| + EXPECT_EQ(MOJO_RESULT_OK, r); |
| + actual += std::string(buf, read); |
| + } |
| + EXPECT_EQ(std::string(buf_size, 'a'), actual); |
| +} |
| + |
| +TEST_F(MojoAsyncResourceHandlerTest, BeginWriteFailsOnWillRead) { |
| + handler_->set_begin_write_expectation(MOJO_RESULT_UNKNOWN); |
| + scoped_refptr<net::IOBuffer> io_buffer; |
| + int buf_size = 0; |
| + ASSERT_FALSE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
| + EXPECT_FALSE(resource_controller_.is_cancel_with_error_called()); |
| +} |
| + |
| +TEST_F(MojoAsyncResourceHandlerTest, BeginWriteReturnsShouldWaitOnWillRead) { |
| + handler_->set_begin_write_expectation(MOJO_RESULT_SHOULD_WAIT); |
| + scoped_refptr<net::IOBuffer> io_buffer; |
| + int buf_size = 0; |
| + ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
| + EXPECT_TRUE(io_buffer); |
| + EXPECT_GT(buf_size, 0); |
| +} |
| + |
| +TEST_F(MojoAsyncResourceHandlerTest, |
| + EndWriteFailsOnWillReadWithInsufficientInitialCapacity) { |
| + MojoAsyncResourceHandler::SetAllocationSizeForTesting(2); |
| + handler_->set_end_write_expectation(MOJO_RESULT_UNKNOWN); |
| + scoped_refptr<net::IOBuffer> io_buffer; |
| + int buf_size = 0; |
| + ASSERT_FALSE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
| +} |
| + |
| +TEST_F(MojoAsyncResourceHandlerTest, EndWriteFailsOnReadCompleted) { |
| + scoped_refptr<net::IOBuffer> io_buffer; |
| + int buf_size = 0; |
| + bool defer = false; |
| + ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
| + |
| + handler_->set_end_write_expectation(MOJO_RESULT_SHOULD_WAIT); |
| + ASSERT_FALSE(handler_->OnReadCompleted(buf_size, &defer)); |
| +} |
| + |
| +TEST_F(MojoAsyncResourceHandlerTest, |
| + EndWriteFailsOnReadCompletedWithInsufficientInitialCapacity) { |
| + MojoAsyncResourceHandler::SetAllocationSizeForTesting(2); |
| + scoped_refptr<net::IOBuffer> io_buffer; |
| + int buf_size = 0; |
| + bool defer = false; |
| + ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
| + |
| + handler_->set_end_write_expectation(MOJO_RESULT_SHOULD_WAIT); |
| + ASSERT_FALSE(handler_->OnReadCompleted(buf_size, &defer)); |
| +} |
| + |
| +TEST_F(MojoAsyncResourceHandlerTest, |
| + EndWriteFailsOnResumeIfDeferredWithInsufficientInitialCapacity) { |
| + MojoAsyncResourceHandler::SetAllocationSizeForTesting(8); |
| + scoped_refptr<net::IOBuffer> io_buffer; |
| + int buf_size = 0; |
| + ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
| + RunUntilNextNotification(); |
| + ASSERT_TRUE(url_loader_client_.response_body().is_valid()); |
| + |
| + while (true) { |
| + bool defer = false; |
| + ASSERT_TRUE(handler_->OnReadCompleted(buf_size, &defer)); |
| + ASSERT_GE(buf_size, 0); |
| + if (defer) |
| + break; |
| + ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
| + } |
| + |
| + while (true) { |
| + char buf[16]; |
| + uint32_t read = sizeof(buf); |
| + MojoResult r = mojo::ReadDataRaw(url_loader_client_.response_body(), buf, |
| + &read, MOJO_READ_DATA_FLAG_NONE); |
| + if (r == MOJO_RESULT_SHOULD_WAIT) |
| + break; |
| + ASSERT_EQ(MOJO_RESULT_OK, r); |
| + } |
| + |
| + handler_->set_end_write_expectation(MOJO_RESULT_SHOULD_WAIT); |
| + RunUntilNextNotification(); |
| + EXPECT_FALSE(url_loader_client_.has_received_completion()); |
| + EXPECT_TRUE(resource_controller_.is_cancel_with_error_called()); |
| + EXPECT_EQ(net::ERR_FAILED, resource_controller_.error()); |
| +} |
| + |
| +TEST_P(MojoAsyncResourceHandlerWithAllocationSizeTest, |
| + OnWillReadWithLongContents) { |
| + bool defer = false; |
| + scoped_refptr<net::IOBuffer> io_buffer; |
| + int buf_size = 0; |
| + |
| + ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
| + ASSERT_TRUE(io_buffer); |
| + // The io_buffer size that the mime sniffer requires implicitly. |
| + ASSERT_GE(buf_size, 2 * 1024); |
| + const size_t contents_size = 3 * buf_size + 2; |
| + ASSERT_TRUE(handler_->OnReadCompleted(0, &defer)); |
| + ASSERT_FALSE(defer); |
| + |
| + RunUntilNextNotification(); |
| + ASSERT_TRUE(url_loader_client_.response_body().is_valid()); |
| + |
| + size_t written = 0; |
| + std::string actual; |
| + while (actual.size() < contents_size) { |
| + while (written < contents_size && !defer) { |
| + ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
| + const size_t to_be_written = |
| + std::min(static_cast<size_t>(buf_size), contents_size - written); |
| + memset(io_buffer->data(), 'a', to_be_written); |
| + ASSERT_TRUE(handler_->OnReadCompleted(to_be_written, &defer)); |
| + written += to_be_written; |
| + } |
| + |
| + char buf[16]; |
| + uint32_t read = sizeof(buf); |
| + MojoResult r = mojo::ReadDataRaw(url_loader_client_.response_body(), buf, |
| + &read, MOJO_READ_DATA_FLAG_NONE); |
| + if (r != MOJO_RESULT_SHOULD_WAIT) { |
| + ASSERT_EQ(MOJO_RESULT_OK, r); |
| + actual += std::string(buf, read); |
| + } |
| + unsigned resume_count = resource_controller_.num_resume_calls(); |
| + base::RunLoop().RunUntilIdle(); |
| + defer = (resume_count == resource_controller_.num_resume_calls()); |
| + } |
| + EXPECT_EQ(std::string(contents_size, 'a'), actual); |
| +} |
| + |
| +TEST_P(MojoAsyncResourceHandlerWithAllocationSizeTest, |
| + BeginWriteFailsOnReadCompleted) { |
| + scoped_refptr<net::IOBuffer> io_buffer; |
| + int buf_size = 0; |
| + bool defer = false; |
| + ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
| + |
| + handler_->set_begin_write_expectation(MOJO_RESULT_UNKNOWN); |
| + ASSERT_FALSE(handler_->OnReadCompleted(buf_size, &defer)); |
| +} |
| + |
| +TEST_P(MojoAsyncResourceHandlerWithAllocationSizeTest, |
| + BeginWriteReturnsShouldWaitOnReadCompleted) { |
| + scoped_refptr<net::IOBuffer> io_buffer; |
| + int buf_size = 0; |
| + bool defer = false; |
| + ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
| + |
| + handler_->set_begin_write_expectation(MOJO_RESULT_SHOULD_WAIT); |
| + ASSERT_TRUE(handler_->OnReadCompleted(buf_size, &defer)); |
| + EXPECT_TRUE(defer); |
| +} |
| + |
| +TEST_P(MojoAsyncResourceHandlerWithAllocationSizeTest, |
| + BeginWriteFailsOnResumeIfDeferred) { |
| + bool defer = false; |
| + int buf_size = 0; |
| + scoped_refptr<net::IOBuffer> io_buffer; |
| + |
| + ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
| + ASSERT_TRUE(handler_->OnReadCompleted(0, &defer)); |
| + ASSERT_FALSE(defer); |
| + RunUntilNextNotification(); |
| + ASSERT_TRUE(url_loader_client_.response_body().is_valid()); |
| + |
| + while (!defer) { |
| + ASSERT_TRUE(handler_->OnWillRead(&io_buffer, &buf_size, -1)); |
| + ASSERT_TRUE(handler_->OnReadCompleted(buf_size, &defer)); |
| + } |
| + handler_->set_begin_write_expectation(MOJO_RESULT_UNKNOWN); |
| + |
| + while (!resource_controller_.is_cancel_with_error_called()) { |
| + char buf[256]; |
| + uint32_t read = sizeof(buf); |
| + MojoResult r = mojo::ReadDataRaw(url_loader_client_.response_body(), buf, |
| + &read, MOJO_READ_DATA_FLAG_NONE); |
| + ASSERT_TRUE(r == MOJO_RESULT_OK || r == MOJO_RESULT_SHOULD_WAIT); |
| + base::RunLoop().RunUntilIdle(); |
| + } |
| + |
| + EXPECT_FALSE(url_loader_client_.has_received_completion()); |
| + EXPECT_EQ(net::ERR_FAILED, resource_controller_.error()); |
| + EXPECT_EQ(0u, resource_controller_.num_resume_calls()); |
| +} |
| + |
| +INSTANTIATE_TEST_CASE_P(MojoAsyncResourceHandlerWithAllocationSizeTest, |
| + MojoAsyncResourceHandlerWithAllocationSizeTest, |
| + ::testing::Values(8, 32 * 2014)); |
| +} // namespace |
| +} // namespace content |