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

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

Issue 2668603003: Make ResourceHandler::OnWillRead able to complete asynchronously. (Closed)
Patch Set: Fix merge (x2) Created 3 years, 9 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/detachable_resource_handler_unittest.cc
diff --git a/content/browser/loader/detachable_resource_handler_unittest.cc b/content/browser/loader/detachable_resource_handler_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..2905662abec0e1db0ce8be0afd1b64d289e1a975
--- /dev/null
+++ b/content/browser/loader/detachable_resource_handler_unittest.cc
@@ -0,0 +1,354 @@
+// Copyright 2017 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/detachable_resource_handler.h"
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "content/browser/loader/mock_resource_loader.h"
+#include "content/browser/loader/resource_controller.h"
+#include "content/browser/loader/test_resource_handler.h"
+#include "content/public/browser/resource_request_info.h"
+#include "content/public/common/resource_response.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "net/base/net_errors.h"
+#include "net/url_request/redirect_info.h"
+#include "net/url_request/url_request_context.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 "url/gurl.h"
+
+namespace content {
+
+namespace {
+
+// Full response body.
+const char kResponseBody[] = "Nifty response body.";
+// Two separate reads allow for testing cancellation in the middle of one read,
+// and between reads.
+const char kFirstBodyRead[] = "Nifty";
+const char kSecondBodyRead[] = " response body.";
+
+enum class DetachPhase {
+ DETACHED_FROM_CREATION,
+ ON_WILL_START,
+ REQUEST_REDIRECTED,
+ ON_RESPONSE_STARTED,
+ FIRST_ON_WILL_READ,
+ FIRST_ON_READ_COMPLETED,
+ SECOND_ON_WILL_READ,
+ SECOND_ON_READ_COMPLETED,
+ ON_READ_EOF,
+ ON_RESPONSE_COMPLETED,
+ NEVER_DETACH,
+};
+
+class DetachableResourceHandlerTest
+ : public testing::TestWithParam<DetachPhase> {
+ public:
+ DetachableResourceHandlerTest()
+ : request_(context_.CreateRequest(GURL("http://foo/"),
+ net::DEFAULT_PRIORITY,
+ nullptr)) {
+ ResourceRequestInfo::AllocateForTesting(request_.get(),
+ RESOURCE_TYPE_MAIN_FRAME,
+ nullptr, // context
+ 0, // render_process_id
+ 0, // render_view_id
+ 0, // render_frame_id
+ true, // is_main_frame
+ false, // parent_is_main_frame
+ true, // allow_download
+ true, // is_async
+ PREVIEWS_OFF); // previews_state
+
+ std::unique_ptr<TestResourceHandler> test_handler;
+ if (GetParam() != DetachPhase::DETACHED_FROM_CREATION) {
+ test_handler = base::MakeUnique<TestResourceHandler>();
+ test_handler_ = test_handler->GetWeakPtr();
+ }
+ // TODO(mmenke): This file currently has no timeout tests. Should it?
+ detachable_handler_ = base::MakeUnique<DetachableResourceHandler>(
+ request_.get(), base::TimeDelta::FromMinutes(30),
+ std::move(test_handler));
+ mock_loader_ =
+ base::MakeUnique<MockResourceLoader>(detachable_handler_.get());
+ }
+
+ // If the DetachableResourceHandler is supposed to detach the next handler at
+ // |phase|, attempts to detach the request.
+ void MaybeSyncDetachAtPhase(DetachPhase phase) {
+ if (GetParam() == phase) {
+ detachable_handler_->Detach();
+ EXPECT_FALSE(test_handler_);
+ }
+ }
+
+ // Returns true if the DetachableResourceHandler should have detached the next
+ // handler at or before the specified phase. Also checks that |test_handler_|
+ // is nullptr iff the request should have been detached by the specified
+ // phase.
+ bool WasDetachedBy(DetachPhase phase) {
+ if (GetParam() <= phase) {
+ EXPECT_FALSE(test_handler_);
+ return true;
+ }
+ EXPECT_TRUE(test_handler_);
+ return false;
+ }
+
+ // If the DetachableResourceHandler is supposed to detach the next handler at
+ // |phase|, attempts to detach the request. Expected to be called in sync
+ // tests after the specified phase has started. Performs additional sanity
+ // checks based on that assumption.
+ void MaybeAsyncDetachAt(DetachPhase phase) {
+ if (GetParam() < phase) {
+ EXPECT_FALSE(test_handler_);
+ EXPECT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->status());
+ return;
+ }
+
+ EXPECT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
+ mock_loader_->status());
+
+ if (GetParam() == phase) {
+ detachable_handler_->Detach();
+ EXPECT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->status());
+ EXPECT_FALSE(test_handler_);
+ return;
+ }
+
+ test_handler_->Resume();
+ }
+
+ protected:
+ TestBrowserThreadBundle thread_bundle_;
+ net::TestURLRequestContext context_;
+ std::unique_ptr<net::URLRequest> request_;
+
+ base::WeakPtr<TestResourceHandler> test_handler_;
+
+ std::unique_ptr<DetachableResourceHandler> detachable_handler_;
+ std::unique_ptr<MockResourceLoader> mock_loader_;
+};
+
+// Tests where ResourceHandler completes synchronously. Handler is detached
+// just before the phase indicated by the DetachPhase parameter.
+TEST_P(DetachableResourceHandlerTest, Sync) {
+ MaybeSyncDetachAtPhase(DetachPhase::ON_WILL_START);
+ ASSERT_EQ(MockResourceLoader::Status::IDLE,
+ mock_loader_->OnWillStart(request_->url()));
+ if (!WasDetachedBy(DetachPhase::ON_WILL_START)) {
+ EXPECT_EQ(1, test_handler_->on_will_start_called());
+ EXPECT_EQ(0, test_handler_->on_request_redirected_called());
+ }
+
+ MaybeSyncDetachAtPhase(DetachPhase::REQUEST_REDIRECTED);
+ ASSERT_EQ(
+ MockResourceLoader::Status::IDLE,
+ mock_loader_->OnRequestRedirected(
+ net::RedirectInfo(), make_scoped_refptr(new ResourceResponse())));
+ if (!WasDetachedBy(DetachPhase::REQUEST_REDIRECTED)) {
+ EXPECT_EQ(1, test_handler_->on_request_redirected_called());
+ EXPECT_EQ(0, test_handler_->on_response_started_called());
+ }
+
+ MaybeSyncDetachAtPhase(DetachPhase::ON_RESPONSE_STARTED);
+ ASSERT_EQ(MockResourceLoader::Status::IDLE,
+ mock_loader_->OnResponseStarted(
+ make_scoped_refptr(new ResourceResponse())));
+ if (!WasDetachedBy(DetachPhase::ON_RESPONSE_STARTED)) {
+ EXPECT_EQ(1, test_handler_->on_request_redirected_called());
+ EXPECT_EQ(1, test_handler_->on_response_started_called());
+ EXPECT_EQ(0, test_handler_->on_will_read_called());
+ }
+
+ MaybeSyncDetachAtPhase(DetachPhase::FIRST_ON_WILL_READ);
+ ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead());
+ if (!WasDetachedBy(DetachPhase::FIRST_ON_WILL_READ)) {
+ EXPECT_EQ(1, test_handler_->on_will_read_called());
+ EXPECT_EQ(0, test_handler_->on_read_completed_called());
+ }
+
+ MaybeSyncDetachAtPhase(DetachPhase::FIRST_ON_READ_COMPLETED);
+ ASSERT_EQ(MockResourceLoader::Status::IDLE,
+ mock_loader_->OnReadCompleted(kFirstBodyRead));
+ if (!WasDetachedBy(DetachPhase::FIRST_ON_READ_COMPLETED)) {
+ EXPECT_EQ(1, test_handler_->on_read_completed_called());
+ EXPECT_EQ(kFirstBodyRead, test_handler_->body());
+ }
+
+ MaybeSyncDetachAtPhase(DetachPhase::SECOND_ON_WILL_READ);
+ ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead());
+ if (!WasDetachedBy(DetachPhase::SECOND_ON_WILL_READ)) {
+ EXPECT_EQ(2, test_handler_->on_will_read_called());
+ EXPECT_EQ(1, test_handler_->on_read_completed_called());
+ }
+
+ MaybeSyncDetachAtPhase(DetachPhase::SECOND_ON_READ_COMPLETED);
+ ASSERT_EQ(MockResourceLoader::Status::IDLE,
+ mock_loader_->OnReadCompleted(kSecondBodyRead));
+ if (!WasDetachedBy(DetachPhase::SECOND_ON_READ_COMPLETED)) {
+ EXPECT_EQ(2, test_handler_->on_will_read_called());
+ EXPECT_EQ(2, test_handler_->on_read_completed_called());
+ EXPECT_EQ(kResponseBody, test_handler_->body());
+ }
+
+ ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead());
+ if (!WasDetachedBy(DetachPhase::SECOND_ON_READ_COMPLETED)) {
+ EXPECT_EQ(3, test_handler_->on_will_read_called());
+ EXPECT_EQ(2, test_handler_->on_read_completed_called());
+ EXPECT_EQ(0, test_handler_->on_response_completed_called());
+ }
+
+ MaybeSyncDetachAtPhase(DetachPhase::ON_READ_EOF);
+ ASSERT_EQ(MockResourceLoader::Status::IDLE,
+ mock_loader_->OnReadCompleted(""));
+ if (!WasDetachedBy(DetachPhase::ON_READ_EOF)) {
+ EXPECT_EQ(3, test_handler_->on_read_completed_called());
+ EXPECT_EQ(1, test_handler_->on_read_eof_called());
+ EXPECT_EQ(0, test_handler_->on_response_completed_called());
+ }
+
+ MaybeSyncDetachAtPhase(DetachPhase::ON_RESPONSE_COMPLETED);
+ ASSERT_EQ(MockResourceLoader::Status::IDLE,
+ mock_loader_->OnResponseCompleted(
+ net::URLRequestStatus::FromError(net::OK)));
+ if (!WasDetachedBy(DetachPhase::ON_RESPONSE_COMPLETED)) {
+ EXPECT_EQ(1, test_handler_->on_response_completed_called());
+ EXPECT_EQ(kResponseBody, test_handler_->body());
+ }
+}
+
+// Tests where ResourceHandler completes asynchronously. Handler is detached
+// during the phase indicated by the DetachPhase parameter. Async cases where
+// the handler is detached between phases are similar enough to the sync tests
+// that they wouldn't provide meaningfully better test coverage.
+//
+// Before the handler is detached, all calls complete asynchronously.
+// Afterwards, they all complete synchronously.
+TEST_P(DetachableResourceHandlerTest, Async) {
+ if (GetParam() != DetachPhase::DETACHED_FROM_CREATION) {
+ test_handler_->set_defer_on_will_start(true);
+ test_handler_->set_defer_on_request_redirected(true);
+ test_handler_->set_defer_on_response_started(true);
+ test_handler_->set_defer_on_will_read(true);
+ test_handler_->set_defer_on_read_completed(true);
+ test_handler_->set_defer_on_read_eof(true);
+ // Note: Can't set |defer_on_response_completed|, since the
+ // DetachableResourceHandler DCHECKs when the next handler tries to defer
+ // the ERR_ABORTED message it sends downstream.
+ }
+
+ mock_loader_->OnWillStart(request_->url());
+ if (test_handler_) {
+ EXPECT_EQ(1, test_handler_->on_will_start_called());
+ EXPECT_EQ(0, test_handler_->on_request_redirected_called());
+ }
+ MaybeAsyncDetachAt(DetachPhase::ON_WILL_START);
+
+ mock_loader_->OnRequestRedirected(net::RedirectInfo(),
+ make_scoped_refptr(new ResourceResponse()));
+ if (test_handler_) {
+ EXPECT_EQ(1, test_handler_->on_request_redirected_called());
+ EXPECT_EQ(0, test_handler_->on_response_started_called());
+ }
+ MaybeAsyncDetachAt(DetachPhase::REQUEST_REDIRECTED);
+
+ mock_loader_->OnResponseStarted(make_scoped_refptr(new ResourceResponse()));
+ if (test_handler_) {
+ EXPECT_EQ(1, test_handler_->on_request_redirected_called());
+ EXPECT_EQ(1, test_handler_->on_response_started_called());
+ EXPECT_EQ(0, test_handler_->on_will_read_called());
+ }
+ MaybeAsyncDetachAt(DetachPhase::ON_RESPONSE_STARTED);
+
+ mock_loader_->OnWillRead();
+ if (test_handler_) {
+ EXPECT_EQ(1, test_handler_->on_will_read_called());
+ EXPECT_EQ(0, test_handler_->on_read_completed_called());
+ }
+ MaybeAsyncDetachAt(DetachPhase::FIRST_ON_WILL_READ);
+
+ mock_loader_->OnReadCompleted(kFirstBodyRead);
+ if (test_handler_) {
+ EXPECT_EQ(1, test_handler_->on_read_completed_called());
+ EXPECT_EQ(kFirstBodyRead, test_handler_->body());
+ }
+ MaybeAsyncDetachAt(DetachPhase::FIRST_ON_READ_COMPLETED);
+
+ if (test_handler_)
+ test_handler_->set_defer_on_will_read(true);
+ mock_loader_->OnWillRead();
+ if (test_handler_) {
+ EXPECT_EQ(2, test_handler_->on_will_read_called());
+ EXPECT_EQ(1, test_handler_->on_read_completed_called());
+ }
+ MaybeAsyncDetachAt(DetachPhase::SECOND_ON_WILL_READ);
+
+ if (test_handler_)
+ test_handler_->set_defer_on_read_completed(true);
+ mock_loader_->OnReadCompleted(kSecondBodyRead);
+ if (test_handler_) {
+ EXPECT_EQ(2, test_handler_->on_will_read_called());
+ EXPECT_EQ(2, test_handler_->on_read_completed_called());
+ EXPECT_EQ(kResponseBody, test_handler_->body());
+ }
+ MaybeAsyncDetachAt(DetachPhase::SECOND_ON_READ_COMPLETED);
+
+ // Test doesn't check detaching on the third OnWillRead call.
+ ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead());
+ if (GetParam() > DetachPhase::SECOND_ON_READ_COMPLETED) {
+ EXPECT_EQ(3, test_handler_->on_will_read_called());
+ EXPECT_EQ(2, test_handler_->on_read_completed_called());
+ EXPECT_EQ(0, test_handler_->on_response_completed_called());
+ } else {
+ EXPECT_FALSE(test_handler_);
+ }
+
+ if (test_handler_)
+ test_handler_->set_defer_on_read_completed(true);
+ mock_loader_->OnReadCompleted("");
+ if (test_handler_) {
+ EXPECT_EQ(3, test_handler_->on_read_completed_called());
+ EXPECT_EQ(1, test_handler_->on_read_eof_called());
+ EXPECT_EQ(0, test_handler_->on_response_completed_called());
+ }
+ MaybeAsyncDetachAt(DetachPhase::ON_READ_EOF);
+
+ if (test_handler_)
+ test_handler_->set_defer_on_response_completed(true);
+ mock_loader_->OnResponseCompleted(net::URLRequestStatus::FromError(net::OK));
+ if (test_handler_) {
+ EXPECT_EQ(1, test_handler_->on_response_completed_called());
+ EXPECT_EQ(kResponseBody, test_handler_->body());
+ }
+ MaybeAsyncDetachAt(DetachPhase::ON_RESPONSE_COMPLETED);
+}
+
+INSTANTIATE_TEST_CASE_P(/* No prefix needed*/,
+ DetachableResourceHandlerTest,
+ testing::Values(DetachPhase::DETACHED_FROM_CREATION,
+ DetachPhase::ON_WILL_START,
+ DetachPhase::REQUEST_REDIRECTED,
+ DetachPhase::ON_RESPONSE_STARTED,
+ DetachPhase::FIRST_ON_WILL_READ,
+ DetachPhase::FIRST_ON_READ_COMPLETED,
+ DetachPhase::SECOND_ON_WILL_READ,
+ DetachPhase::SECOND_ON_READ_COMPLETED,
+ DetachPhase::ON_READ_EOF,
+ DetachPhase::ON_RESPONSE_COMPLETED,
+ DetachPhase::NEVER_DETACH));
+
+} // namespace
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698