diff options
Diffstat (limited to 'chromium/content/browser/appcache/appcache_request_handler_unittest.cc')
-rw-r--r-- | chromium/content/browser/appcache/appcache_request_handler_unittest.cc | 1014 |
1 files changed, 1014 insertions, 0 deletions
diff --git a/chromium/content/browser/appcache/appcache_request_handler_unittest.cc b/chromium/content/browser/appcache/appcache_request_handler_unittest.cc new file mode 100644 index 00000000000..df6fb330d24 --- /dev/null +++ b/chromium/content/browser/appcache/appcache_request_handler_unittest.cc @@ -0,0 +1,1014 @@ +// Copyright 2014 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 <stack> +#include <string> +#include <vector> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/callback.h" +#include "base/message_loop/message_loop.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" +#include "content/browser/appcache/mock_appcache_policy.h" +#include "content/browser/appcache/mock_appcache_service.h" +#include "net/base/net_errors.h" +#include "net/base/request_priority.h" +#include "net/http/http_response_headers.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_error_job.h" +#include "net/url_request/url_request_job_factory.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webkit/browser/appcache/appcache.h" +#include "webkit/browser/appcache/appcache_backend_impl.h" +#include "webkit/browser/appcache/appcache_request_handler.h" +#include "webkit/browser/appcache/appcache_url_request_job.h" + +using appcache::AppCache; +using appcache::AppCacheBackendImpl; +using appcache::AppCacheEntry; +using appcache::AppCacheFrontend; +using appcache::AppCacheGroup; +using appcache::AppCacheHost; +using appcache::AppCacheInfo; +using appcache::AppCacheRequestHandler; +using appcache::AppCacheURLRequestJob; +using appcache::kAppCacheNoCacheId; + +namespace content { + +static const int kMockProcessId = 1; + +class AppCacheRequestHandlerTest : public testing::Test { + public: + class MockFrontend : public AppCacheFrontend { + public: + virtual void OnCacheSelected( + int host_id, const appcache::AppCacheInfo& info) OVERRIDE {} + + virtual void OnStatusChanged(const std::vector<int>& host_ids, + appcache::AppCacheStatus status) OVERRIDE {} + + virtual void OnEventRaised(const std::vector<int>& host_ids, + appcache::AppCacheEventID event_id) OVERRIDE {} + + virtual void OnErrorEventRaised( + const std::vector<int>& host_ids, + const appcache::AppCacheErrorDetails& details) OVERRIDE {} + + virtual void OnProgressEventRaised(const std::vector<int>& host_ids, + const GURL& url, + int num_total, + int num_complete) OVERRIDE { + } + + virtual void OnLogMessage(int host_id, + appcache::AppCacheLogLevel log_level, + const std::string& message) OVERRIDE { + } + + virtual void OnContentBlocked(int host_id, + const GURL& manifest_url) OVERRIDE {} + }; + + // Helper callback to run a test on our io_thread. The io_thread is spun up + // once and reused for all tests. + template <class Method> + void MethodWrapper(Method method) { + SetUpTest(); + (this->*method)(); + } + + // Subclasses to simulate particular responses so test cases can + // exercise fallback code paths. + + class MockURLRequestDelegate : public net::URLRequest::Delegate { + virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE {} + virtual void OnReadCompleted(net::URLRequest* request, + int bytes_read) OVERRIDE { + } + }; + + class MockURLRequestJob : public net::URLRequestJob { + public: + MockURLRequestJob(net::URLRequest* request, + net::NetworkDelegate* network_delegate, + int response_code) + : net::URLRequestJob(request, network_delegate), + response_code_(response_code), + has_response_info_(false) {} + MockURLRequestJob(net::URLRequest* request, + net::NetworkDelegate* network_delegate, + const net::HttpResponseInfo& info) + : net::URLRequestJob(request, network_delegate), + response_code_(info.headers->response_code()), + has_response_info_(true), + response_info_(info) {} + + protected: + virtual ~MockURLRequestJob() {} + virtual void Start() OVERRIDE { + NotifyHeadersComplete(); + } + virtual int GetResponseCode() const OVERRIDE { + return response_code_; + } + virtual void GetResponseInfo( + net::HttpResponseInfo* info) OVERRIDE { + if (!has_response_info_) + return; + *info = response_info_; + } + + private: + int response_code_; + bool has_response_info_; + net::HttpResponseInfo response_info_; + }; + + class MockURLRequestJobFactory : public net::URLRequestJobFactory { + public: + MockURLRequestJobFactory() : job_(NULL) { + } + + virtual ~MockURLRequestJobFactory() { + DCHECK(!job_); + } + + void SetJob(net::URLRequestJob* job) { + job_ = job; + } + + virtual net::URLRequestJob* MaybeCreateJobWithProtocolHandler( + const std::string& scheme, + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const OVERRIDE { + if (job_) { + net::URLRequestJob* temp = job_; + job_ = NULL; + return temp; + } else { + // Some of these tests trigger UpdateJobs which start URLRequests. + // We short circuit those be returning error jobs. + return new net::URLRequestErrorJob(request, + network_delegate, + net::ERR_INTERNET_DISCONNECTED); + } + } + + virtual bool IsHandledProtocol(const std::string& scheme) const OVERRIDE { + return scheme == "http"; + }; + + virtual bool IsHandledURL(const GURL& url) const OVERRIDE { + return url.SchemeIs("http"); + } + + virtual bool IsSafeRedirectTarget(const GURL& location) const OVERRIDE { + return false; + } + + private: + mutable net::URLRequestJob* job_; + }; + + class MockURLRequest : public net::URLRequest { + public: + MockURLRequest(const GURL& url, net::URLRequestContext* context) + : net::URLRequest(url, net::DEFAULT_PRIORITY, NULL, context) {} + + + MockURLRequestDelegate delegate_; + }; + + static void SetUpTestCase() { + io_thread_.reset(new base::Thread("AppCacheRequestHandlerTest Thread")); + base::Thread::Options options(base::MessageLoop::TYPE_IO, 0); + io_thread_->StartWithOptions(options); + } + + static void TearDownTestCase() { + io_thread_.reset(NULL); + } + + // Test harness -------------------------------------------------- + + AppCacheRequestHandlerTest() : host_(NULL) {} + + template <class Method> + void RunTestOnIOThread(Method method) { + test_finished_event_ .reset(new base::WaitableEvent(false, false)); + io_thread_->message_loop()->PostTask( + FROM_HERE, + base::Bind(&AppCacheRequestHandlerTest::MethodWrapper<Method>, + base::Unretained(this), method)); + test_finished_event_->Wait(); + } + + void SetUpTest() { + DCHECK(base::MessageLoop::current() == io_thread_->message_loop()); + mock_service_.reset(new MockAppCacheService); + mock_service_->set_request_context(&empty_context_); + mock_policy_.reset(new MockAppCachePolicy); + mock_service_->set_appcache_policy(mock_policy_.get()); + mock_frontend_.reset(new MockFrontend); + backend_impl_.reset(new AppCacheBackendImpl); + backend_impl_->Initialize(mock_service_.get(), mock_frontend_.get(), + kMockProcessId); + const int kHostId = 1; + backend_impl_->RegisterHost(kHostId); + host_ = backend_impl_->GetHost(kHostId); + job_factory_.reset(new MockURLRequestJobFactory()); + empty_context_.set_job_factory(job_factory_.get()); + } + + void TearDownTest() { + DCHECK(base::MessageLoop::current() == io_thread_->message_loop()); + job_ = NULL; + handler_.reset(); + request_.reset(); + backend_impl_.reset(); + mock_frontend_.reset(); + mock_service_.reset(); + mock_policy_.reset(); + job_factory_.reset(); + host_ = NULL; + } + + void TestFinished() { + // We unwind the stack prior to finishing up to let stack + // based objects get deleted. + DCHECK(base::MessageLoop::current() == io_thread_->message_loop()); + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&AppCacheRequestHandlerTest::TestFinishedUnwound, + base::Unretained(this))); + } + + void TestFinishedUnwound() { + TearDownTest(); + test_finished_event_->Signal(); + } + + void PushNextTask(const base::Closure& task) { + task_stack_.push(task); + } + + void ScheduleNextTask() { + DCHECK(base::MessageLoop::current() == io_thread_->message_loop()); + if (task_stack_.empty()) { + TestFinished(); + return; + } + base::MessageLoop::current()->PostTask(FROM_HERE, task_stack_.top()); + task_stack_.pop(); + } + + // MainResource_Miss -------------------------------------------------- + + void MainResource_Miss() { + PushNextTask( + base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Miss, + base::Unretained(this))); + + request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); + handler_.reset(host_->CreateRequestHandler(request_.get(), + ResourceType::MAIN_FRAME)); + EXPECT_TRUE(handler_.get()); + + job_ = handler_->MaybeLoadResource(request_.get(), + request_->context()->network_delegate()); + EXPECT_TRUE(job_.get()); + EXPECT_TRUE(job_->is_waiting()); + + // We have to wait for completion of storage->FindResponseForMainRequest. + ScheduleNextTask(); + } + + void Verify_MainResource_Miss() { + EXPECT_FALSE(job_->is_waiting()); + EXPECT_TRUE(job_->is_delivering_network_response()); + + int64 cache_id = kAppCacheNoCacheId; + GURL manifest_url; + handler_->GetExtraResponseInfo(&cache_id, &manifest_url); + EXPECT_EQ(kAppCacheNoCacheId, cache_id); + EXPECT_EQ(GURL(), manifest_url); + EXPECT_EQ(0, handler_->found_group_id_); + + AppCacheURLRequestJob* fallback_job; + fallback_job = handler_->MaybeLoadFallbackForRedirect( + request_.get(), + request_->context()->network_delegate(), + GURL("http://blah/redirect")); + EXPECT_FALSE(fallback_job); + fallback_job = handler_->MaybeLoadFallbackForResponse( + request_.get(), request_->context()->network_delegate()); + EXPECT_FALSE(fallback_job); + + EXPECT_TRUE(host_->preferred_manifest_url().is_empty()); + + TestFinished(); + } + + // MainResource_Hit -------------------------------------------------- + + void MainResource_Hit() { + PushNextTask( + base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Hit, + base::Unretained(this))); + + request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); + handler_.reset(host_->CreateRequestHandler(request_.get(), + ResourceType::MAIN_FRAME)); + EXPECT_TRUE(handler_.get()); + + mock_storage()->SimulateFindMainResource( + AppCacheEntry(AppCacheEntry::EXPLICIT, 1), + GURL(), AppCacheEntry(), + 1, 2, GURL("http://blah/manifest/")); + + job_ = handler_->MaybeLoadResource(request_.get(), + request_->context()->network_delegate()); + EXPECT_TRUE(job_.get()); + EXPECT_TRUE(job_->is_waiting()); + + // We have to wait for completion of storage->FindResponseForMainRequest. + ScheduleNextTask(); + } + + void Verify_MainResource_Hit() { + EXPECT_FALSE(job_->is_waiting()); + EXPECT_TRUE(job_->is_delivering_appcache_response()); + + int64 cache_id = kAppCacheNoCacheId; + GURL manifest_url; + handler_->GetExtraResponseInfo(&cache_id, &manifest_url); + EXPECT_EQ(1, cache_id); + EXPECT_EQ(GURL("http://blah/manifest/"), manifest_url); + EXPECT_EQ(2, handler_->found_group_id_); + + AppCacheURLRequestJob* fallback_job; + fallback_job = handler_->MaybeLoadFallbackForResponse( + request_.get(), request_->context()->network_delegate()); + EXPECT_FALSE(fallback_job); + + EXPECT_EQ(GURL("http://blah/manifest/"), + host_->preferred_manifest_url()); + + TestFinished(); + } + + // MainResource_Fallback -------------------------------------------------- + + void MainResource_Fallback() { + PushNextTask( + base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Fallback, + base::Unretained(this))); + + request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); + handler_.reset(host_->CreateRequestHandler(request_.get(), + ResourceType::MAIN_FRAME)); + EXPECT_TRUE(handler_.get()); + + mock_storage()->SimulateFindMainResource( + AppCacheEntry(), + GURL("http://blah/fallbackurl"), + AppCacheEntry(AppCacheEntry::EXPLICIT, 1), + 1, 2, GURL("http://blah/manifest/")); + + job_ = handler_->MaybeLoadResource(request_.get(), + request_->context()->network_delegate()); + EXPECT_TRUE(job_.get()); + EXPECT_TRUE(job_->is_waiting()); + + // We have to wait for completion of storage->FindResponseForMainRequest. + ScheduleNextTask(); + } + + void SimulateResponseCode(int response_code) { + job_factory_->SetJob( + new MockURLRequestJob( + request_.get(), + request_->context()->network_delegate(), + response_code)); + request_->Start(); + // All our simulation needs to satisfy are the following two DCHECKs + DCHECK(request_->status().is_success()); + DCHECK_EQ(response_code, request_->GetResponseCode()); + } + + void SimulateResponseInfo(const net::HttpResponseInfo& info) { + job_factory_->SetJob( + new MockURLRequestJob( + request_.get(), + request_->context()->network_delegate(), info)); + request_->set_delegate(&request_->delegate_); + request_->Start(); + } + + void Verify_MainResource_Fallback() { + EXPECT_FALSE(job_->is_waiting()); + EXPECT_TRUE(job_->is_delivering_network_response()); + + // When the request is restarted, the existing job is dropped so a + // real network job gets created. We expect NULL here which will cause + // the net library to create a real job. + job_ = handler_->MaybeLoadResource(request_.get(), + request_->context()->network_delegate()); + EXPECT_FALSE(job_.get()); + + // Simulate an http error of the real network job. + SimulateResponseCode(500); + + job_ = handler_->MaybeLoadFallbackForResponse( + request_.get(), request_->context()->network_delegate()); + EXPECT_TRUE(job_.get()); + EXPECT_TRUE(job_->is_delivering_appcache_response()); + + int64 cache_id = kAppCacheNoCacheId; + GURL manifest_url; + handler_->GetExtraResponseInfo(&cache_id, &manifest_url); + EXPECT_EQ(1, cache_id); + EXPECT_EQ(GURL("http://blah/manifest/"), manifest_url); + EXPECT_TRUE(host_->main_resource_was_namespace_entry_); + EXPECT_EQ(GURL("http://blah/fallbackurl"), host_->namespace_entry_url_); + + EXPECT_EQ(GURL("http://blah/manifest/"), + host_->preferred_manifest_url()); + + TestFinished(); + } + + // MainResource_FallbackOverride -------------------------------------------- + + void MainResource_FallbackOverride() { + PushNextTask(base::Bind( + &AppCacheRequestHandlerTest::Verify_MainResource_FallbackOverride, + base::Unretained(this))); + + request_.reset(new MockURLRequest(GURL("http://blah/fallback-override"), + &empty_context_)); + handler_.reset(host_->CreateRequestHandler(request_.get(), + ResourceType::MAIN_FRAME)); + EXPECT_TRUE(handler_.get()); + + mock_storage()->SimulateFindMainResource( + AppCacheEntry(), + GURL("http://blah/fallbackurl"), + AppCacheEntry(AppCacheEntry::EXPLICIT, 1), + 1, 2, GURL("http://blah/manifest/")); + + job_ = handler_->MaybeLoadResource(request_.get(), + request_->context()->network_delegate()); + EXPECT_TRUE(job_.get()); + EXPECT_TRUE(job_->is_waiting()); + + // We have to wait for completion of storage->FindResponseForMainRequest. + ScheduleNextTask(); + } + + void Verify_MainResource_FallbackOverride() { + EXPECT_FALSE(job_->is_waiting()); + EXPECT_TRUE(job_->is_delivering_network_response()); + + // When the request is restarted, the existing job is dropped so a + // real network job gets created. We expect NULL here which will cause + // the net library to create a real job. + job_ = handler_->MaybeLoadResource(request_.get(), + request_->context()->network_delegate()); + EXPECT_FALSE(job_.get()); + + // Simulate an http error of the real network job, but with custom + // headers that override the fallback behavior. + const char kOverrideHeaders[] = + "HTTP/1.1 404 BOO HOO\0" + "x-chromium-appcache-fallback-override: disallow-fallback\0" + "\0"; + net::HttpResponseInfo info; + info.headers = new net::HttpResponseHeaders( + std::string(kOverrideHeaders, arraysize(kOverrideHeaders))); + SimulateResponseInfo(info); + + job_ = handler_->MaybeLoadFallbackForResponse( + request_.get(), request_->context()->network_delegate()); + EXPECT_FALSE(job_.get()); + + TestFinished(); + } + + // SubResource_Miss_WithNoCacheSelected ---------------------------------- + + void SubResource_Miss_WithNoCacheSelected() { + request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); + handler_.reset(host_->CreateRequestHandler(request_.get(), + ResourceType::SUB_RESOURCE)); + + // We avoid creating handler when possible, sub-resource requests are not + // subject to retrieval from an appcache when there's no associated cache. + EXPECT_FALSE(handler_.get()); + + TestFinished(); + } + + // SubResource_Miss_WithCacheSelected ---------------------------------- + + void SubResource_Miss_WithCacheSelected() { + // A sub-resource load where the resource is not in an appcache, or + // in a network or fallback namespace, should result in a failed request. + host_->AssociateCompleteCache(MakeNewCache()); + + request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); + handler_.reset(host_->CreateRequestHandler(request_.get(), + ResourceType::SUB_RESOURCE)); + EXPECT_TRUE(handler_.get()); + + job_ = handler_->MaybeLoadResource(request_.get(), + request_->context()->network_delegate()); + EXPECT_TRUE(job_.get()); + EXPECT_TRUE(job_->is_delivering_error_response()); + + AppCacheURLRequestJob* fallback_job; + fallback_job = handler_->MaybeLoadFallbackForRedirect( + request_.get(), + request_->context()->network_delegate(), + GURL("http://blah/redirect")); + EXPECT_FALSE(fallback_job); + fallback_job = handler_->MaybeLoadFallbackForResponse( + request_.get(), request_->context()->network_delegate()); + EXPECT_FALSE(fallback_job); + + TestFinished(); + } + + // SubResource_Miss_WithWaitForCacheSelection ----------------------------- + + void SubResource_Miss_WithWaitForCacheSelection() { + // Precondition, the host is waiting on cache selection. + scoped_refptr<AppCache> cache(MakeNewCache()); + host_->pending_selected_cache_id_ = cache->cache_id(); + host_->set_preferred_manifest_url(cache->owning_group()->manifest_url()); + + request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); + handler_.reset(host_->CreateRequestHandler(request_.get(), + ResourceType::SUB_RESOURCE)); + EXPECT_TRUE(handler_.get()); + job_ = handler_->MaybeLoadResource(request_.get(), + request_->context()->network_delegate()); + EXPECT_TRUE(job_.get()); + EXPECT_TRUE(job_->is_waiting()); + + host_->FinishCacheSelection(cache.get(), NULL); + EXPECT_FALSE(job_->is_waiting()); + EXPECT_TRUE(job_->is_delivering_error_response()); + + AppCacheURLRequestJob* fallback_job; + fallback_job = handler_->MaybeLoadFallbackForRedirect( + request_.get(), + request_->context()->network_delegate(), + GURL("http://blah/redirect")); + EXPECT_FALSE(fallback_job); + fallback_job = handler_->MaybeLoadFallbackForResponse( + request_.get(), request_->context()->network_delegate()); + EXPECT_FALSE(fallback_job); + + TestFinished(); + } + + // SubResource_Hit ----------------------------- + + void SubResource_Hit() { + host_->AssociateCompleteCache(MakeNewCache()); + + mock_storage()->SimulateFindSubResource( + AppCacheEntry(AppCacheEntry::EXPLICIT, 1), AppCacheEntry(), false); + + request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); + handler_.reset(host_->CreateRequestHandler(request_.get(), + ResourceType::SUB_RESOURCE)); + EXPECT_TRUE(handler_.get()); + job_ = handler_->MaybeLoadResource(request_.get(), + request_->context()->network_delegate()); + EXPECT_TRUE(job_.get()); + EXPECT_TRUE(job_->is_delivering_appcache_response()); + + AppCacheURLRequestJob* fallback_job; + fallback_job = handler_->MaybeLoadFallbackForRedirect( + request_.get(), + request_->context()->network_delegate(), + GURL("http://blah/redirect")); + EXPECT_FALSE(fallback_job); + fallback_job = handler_->MaybeLoadFallbackForResponse( + request_.get(), request_->context()->network_delegate()); + EXPECT_FALSE(fallback_job); + + TestFinished(); + } + + // SubResource_RedirectFallback ----------------------------- + + void SubResource_RedirectFallback() { + // Redirects to resources in the a different origin are subject to + // fallback namespaces. + host_->AssociateCompleteCache(MakeNewCache()); + + mock_storage()->SimulateFindSubResource( + AppCacheEntry(), AppCacheEntry(AppCacheEntry::EXPLICIT, 1), false); + + request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); + handler_.reset(host_->CreateRequestHandler(request_.get(), + ResourceType::SUB_RESOURCE)); + EXPECT_TRUE(handler_.get()); + job_ = handler_->MaybeLoadResource(request_.get(), + request_->context()->network_delegate()); + EXPECT_FALSE(job_.get()); + + job_ = handler_->MaybeLoadFallbackForRedirect( + request_.get(), + request_->context()->network_delegate(), + GURL("http://not_blah/redirect")); + EXPECT_TRUE(job_.get()); + EXPECT_TRUE(job_->is_delivering_appcache_response()); + + AppCacheURLRequestJob* fallback_job; + fallback_job = handler_->MaybeLoadFallbackForResponse( + request_.get(), request_->context()->network_delegate()); + EXPECT_FALSE(fallback_job); + + TestFinished(); + } + + // SubResource_NoRedirectFallback ----------------------------- + + void SubResource_NoRedirectFallback() { + // Redirects to resources in the same-origin are not subject to + // fallback namespaces. + host_->AssociateCompleteCache(MakeNewCache()); + + mock_storage()->SimulateFindSubResource( + AppCacheEntry(), AppCacheEntry(AppCacheEntry::EXPLICIT, 1), false); + + request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); + handler_.reset(host_->CreateRequestHandler(request_.get(), + ResourceType::SUB_RESOURCE)); + EXPECT_TRUE(handler_.get()); + job_ = handler_->MaybeLoadResource(request_.get(), + request_->context()->network_delegate()); + EXPECT_FALSE(job_.get()); + + AppCacheURLRequestJob* fallback_job; + fallback_job = handler_->MaybeLoadFallbackForRedirect( + request_.get(), + request_->context()->network_delegate(), + GURL("http://blah/redirect")); + EXPECT_FALSE(fallback_job); + + SimulateResponseCode(200); + fallback_job = handler_->MaybeLoadFallbackForResponse( + request_.get(), request_->context()->network_delegate()); + EXPECT_FALSE(fallback_job); + + TestFinished(); + } + + // SubResource_Network ----------------------------- + + void SubResource_Network() { + // A sub-resource load where the resource is in a network namespace, + // should result in the system using a 'real' job to do the network + // retrieval. + host_->AssociateCompleteCache(MakeNewCache()); + + mock_storage()->SimulateFindSubResource( + AppCacheEntry(), AppCacheEntry(), true); + + request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); + handler_.reset(host_->CreateRequestHandler(request_.get(), + ResourceType::SUB_RESOURCE)); + EXPECT_TRUE(handler_.get()); + job_ = handler_->MaybeLoadResource(request_.get(), + request_->context()->network_delegate()); + EXPECT_FALSE(job_.get()); + + AppCacheURLRequestJob* fallback_job; + fallback_job = handler_->MaybeLoadFallbackForRedirect( + request_.get(), + request_->context()->network_delegate(), + GURL("http://blah/redirect")); + EXPECT_FALSE(fallback_job); + fallback_job = handler_->MaybeLoadFallbackForResponse( + request_.get(), request_->context()->network_delegate()); + EXPECT_FALSE(fallback_job); + + TestFinished(); + } + + // DestroyedHost ----------------------------- + + void DestroyedHost() { + host_->AssociateCompleteCache(MakeNewCache()); + + mock_storage()->SimulateFindSubResource( + AppCacheEntry(AppCacheEntry::EXPLICIT, 1), AppCacheEntry(), false); + + request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); + handler_.reset(host_->CreateRequestHandler(request_.get(), + ResourceType::SUB_RESOURCE)); + EXPECT_TRUE(handler_.get()); + + backend_impl_->UnregisterHost(1); + host_ = NULL; + + EXPECT_FALSE(handler_->MaybeLoadResource( + request_.get(), request_->context()->network_delegate())); + EXPECT_FALSE(handler_->MaybeLoadFallbackForRedirect( + request_.get(), + request_->context()->network_delegate(), + GURL("http://blah/redirect"))); + EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse( + request_.get(), request_->context()->network_delegate())); + + TestFinished(); + } + + // DestroyedHostWithWaitingJob ----------------------------- + + void DestroyedHostWithWaitingJob() { + // Precondition, the host is waiting on cache selection. + host_->pending_selected_cache_id_ = 1; + + request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); + handler_.reset(host_->CreateRequestHandler(request_.get(), + ResourceType::SUB_RESOURCE)); + EXPECT_TRUE(handler_.get()); + + job_ = handler_->MaybeLoadResource(request_.get(), + request_->context()->network_delegate()); + EXPECT_TRUE(job_.get()); + EXPECT_TRUE(job_->is_waiting()); + + backend_impl_->UnregisterHost(1); + host_ = NULL; + EXPECT_TRUE(job_->has_been_killed()); + + EXPECT_FALSE(handler_->MaybeLoadResource( + request_.get(), request_->context()->network_delegate())); + EXPECT_FALSE(handler_->MaybeLoadFallbackForRedirect( + request_.get(), + request_->context()->network_delegate(), + GURL("http://blah/redirect"))); + EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse( + request_.get(), request_->context()->network_delegate())); + + TestFinished(); + } + + // UnsupportedScheme ----------------------------- + + void UnsupportedScheme() { + // Precondition, the host is waiting on cache selection. + host_->pending_selected_cache_id_ = 1; + + request_.reset(new MockURLRequest(GURL("ftp://blah/"), &empty_context_)); + handler_.reset(host_->CreateRequestHandler(request_.get(), + ResourceType::SUB_RESOURCE)); + EXPECT_TRUE(handler_.get()); // we could redirect to http (conceivably) + + EXPECT_FALSE(handler_->MaybeLoadResource( + request_.get(), request_->context()->network_delegate())); + EXPECT_FALSE(handler_->MaybeLoadFallbackForRedirect( + request_.get(), + request_->context()->network_delegate(), + GURL("ftp://blah/redirect"))); + EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse( + request_.get(), request_->context()->network_delegate())); + + TestFinished(); + } + + // CanceledRequest ----------------------------- + + void CanceledRequest() { + request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); + handler_.reset(host_->CreateRequestHandler(request_.get(), + ResourceType::MAIN_FRAME)); + EXPECT_TRUE(handler_.get()); + + job_ = handler_->MaybeLoadResource(request_.get(), + request_->context()->network_delegate()); + EXPECT_TRUE(job_.get()); + EXPECT_TRUE(job_->is_waiting()); + EXPECT_FALSE(job_->has_been_started()); + + job_factory_->SetJob(job_); + request_->Start(); + EXPECT_TRUE(job_->has_been_started()); + + request_->Cancel(); + EXPECT_TRUE(job_->has_been_killed()); + + EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse( + request_.get(), request_->context()->network_delegate())); + + TestFinished(); + } + + // WorkerRequest ----------------------------- + + void WorkerRequest() { + EXPECT_TRUE(AppCacheRequestHandler::IsMainResourceType( + ResourceType::MAIN_FRAME)); + EXPECT_TRUE(AppCacheRequestHandler::IsMainResourceType( + ResourceType::SUB_FRAME)); + EXPECT_TRUE(AppCacheRequestHandler::IsMainResourceType( + ResourceType::SHARED_WORKER)); + EXPECT_FALSE(AppCacheRequestHandler::IsMainResourceType( + ResourceType::WORKER)); + + request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); + + const int kParentHostId = host_->host_id(); + const int kWorkerHostId = 2; + const int kAbandonedWorkerHostId = 3; + const int kNonExsitingHostId = 700; + + backend_impl_->RegisterHost(kWorkerHostId); + AppCacheHost* worker_host = backend_impl_->GetHost(kWorkerHostId); + worker_host->SelectCacheForWorker(kParentHostId, kMockProcessId); + handler_.reset(worker_host->CreateRequestHandler( + request_.get(), ResourceType::SHARED_WORKER)); + EXPECT_TRUE(handler_.get()); + // Verify that the handler is associated with the parent host. + EXPECT_EQ(host_, handler_->host_); + + // Create a new worker host, but associate it with a parent host that + // does not exists to simulate the host having been torn down. + backend_impl_->UnregisterHost(kWorkerHostId); + backend_impl_->RegisterHost(kAbandonedWorkerHostId); + worker_host = backend_impl_->GetHost(kAbandonedWorkerHostId); + EXPECT_EQ(NULL, backend_impl_->GetHost(kNonExsitingHostId)); + worker_host->SelectCacheForWorker(kNonExsitingHostId, kMockProcessId); + handler_.reset(worker_host->CreateRequestHandler( + request_.get(), ResourceType::SHARED_WORKER)); + EXPECT_FALSE(handler_.get()); + + TestFinished(); + } + + // MainResource_Blocked -------------------------------------------------- + + void MainResource_Blocked() { + PushNextTask( + base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Blocked, + base::Unretained(this))); + + request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_)); + handler_.reset(host_->CreateRequestHandler(request_.get(), + ResourceType::MAIN_FRAME)); + EXPECT_TRUE(handler_.get()); + + mock_policy_->can_load_return_value_ = false; + mock_storage()->SimulateFindMainResource( + AppCacheEntry(AppCacheEntry::EXPLICIT, 1), + GURL(), AppCacheEntry(), + 1, 2, GURL("http://blah/manifest/")); + + job_ = handler_->MaybeLoadResource(request_.get(), + request_->context()->network_delegate()); + EXPECT_TRUE(job_.get()); + EXPECT_TRUE(job_->is_waiting()); + + // We have to wait for completion of storage->FindResponseForMainRequest. + ScheduleNextTask(); + } + + void Verify_MainResource_Blocked() { + EXPECT_FALSE(job_->is_waiting()); + EXPECT_FALSE(job_->is_delivering_appcache_response()); + + EXPECT_EQ(0, handler_->found_cache_id_); + EXPECT_EQ(0, handler_->found_group_id_); + EXPECT_TRUE(handler_->found_manifest_url_.is_empty()); + EXPECT_TRUE(host_->preferred_manifest_url().is_empty()); + EXPECT_TRUE(host_->main_resource_blocked_); + EXPECT_TRUE(host_->blocked_manifest_url_ == GURL("http://blah/manifest/")); + + TestFinished(); + } + + // Test case helpers -------------------------------------------------- + + AppCache* MakeNewCache() { + AppCache* cache = new AppCache( + mock_storage(), mock_storage()->NewCacheId()); + cache->set_complete(true); + AppCacheGroup* group = new AppCacheGroup( + mock_storage(), GURL("http://blah/manifest"), + mock_storage()->NewGroupId()); + group->AddCache(cache); + return cache; + } + + MockAppCacheStorage* mock_storage() { + return reinterpret_cast<MockAppCacheStorage*>(mock_service_->storage()); + } + + // Data members -------------------------------------------------- + + scoped_ptr<base::WaitableEvent> test_finished_event_; + std::stack<base::Closure> task_stack_; + scoped_ptr<MockAppCacheService> mock_service_; + scoped_ptr<AppCacheBackendImpl> backend_impl_; + scoped_ptr<MockFrontend> mock_frontend_; + scoped_ptr<MockAppCachePolicy> mock_policy_; + AppCacheHost* host_; + net::URLRequestContext empty_context_; + scoped_ptr<MockURLRequestJobFactory> job_factory_; + scoped_ptr<MockURLRequest> request_; + scoped_ptr<AppCacheRequestHandler> handler_; + scoped_refptr<AppCacheURLRequestJob> job_; + + static scoped_ptr<base::Thread> io_thread_; +}; + +// static +scoped_ptr<base::Thread> AppCacheRequestHandlerTest::io_thread_; + +TEST_F(AppCacheRequestHandlerTest, MainResource_Miss) { + RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Miss); +} + +TEST_F(AppCacheRequestHandlerTest, MainResource_Hit) { + RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Hit); +} + +TEST_F(AppCacheRequestHandlerTest, MainResource_Fallback) { + RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Fallback); +} + +TEST_F(AppCacheRequestHandlerTest, MainResource_FallbackOverride) { + RunTestOnIOThread( + &AppCacheRequestHandlerTest::MainResource_FallbackOverride); +} + +TEST_F(AppCacheRequestHandlerTest, SubResource_Miss_WithNoCacheSelected) { + RunTestOnIOThread( + &AppCacheRequestHandlerTest::SubResource_Miss_WithNoCacheSelected); +} + +TEST_F(AppCacheRequestHandlerTest, SubResource_Miss_WithCacheSelected) { + RunTestOnIOThread( + &AppCacheRequestHandlerTest::SubResource_Miss_WithCacheSelected); +} + +TEST_F(AppCacheRequestHandlerTest, + SubResource_Miss_WithWaitForCacheSelection) { + RunTestOnIOThread( + &AppCacheRequestHandlerTest::SubResource_Miss_WithWaitForCacheSelection); +} + +TEST_F(AppCacheRequestHandlerTest, SubResource_Hit) { + RunTestOnIOThread(&AppCacheRequestHandlerTest::SubResource_Hit); +} + +TEST_F(AppCacheRequestHandlerTest, SubResource_RedirectFallback) { + RunTestOnIOThread(&AppCacheRequestHandlerTest::SubResource_RedirectFallback); +} + +TEST_F(AppCacheRequestHandlerTest, SubResource_NoRedirectFallback) { + RunTestOnIOThread( + &AppCacheRequestHandlerTest::SubResource_NoRedirectFallback); +} + +TEST_F(AppCacheRequestHandlerTest, SubResource_Network) { + RunTestOnIOThread(&AppCacheRequestHandlerTest::SubResource_Network); +} + +TEST_F(AppCacheRequestHandlerTest, DestroyedHost) { + RunTestOnIOThread(&AppCacheRequestHandlerTest::DestroyedHost); +} + +TEST_F(AppCacheRequestHandlerTest, DestroyedHostWithWaitingJob) { + RunTestOnIOThread(&AppCacheRequestHandlerTest::DestroyedHostWithWaitingJob); +} + +TEST_F(AppCacheRequestHandlerTest, UnsupportedScheme) { + RunTestOnIOThread(&AppCacheRequestHandlerTest::UnsupportedScheme); +} + +TEST_F(AppCacheRequestHandlerTest, CanceledRequest) { + RunTestOnIOThread(&AppCacheRequestHandlerTest::CanceledRequest); +} + +TEST_F(AppCacheRequestHandlerTest, WorkerRequest) { + RunTestOnIOThread(&AppCacheRequestHandlerTest::WorkerRequest); +} + +TEST_F(AppCacheRequestHandlerTest, MainResource_Blocked) { + RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Blocked); +} + +} // namespace content |