// Copyright (c) 2011 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 #include #include "base/bind.h" #include "base/bind_helpers.h" #include "base/callback.h" #include "base/compiler_specific.h" #include "base/pickle.h" #include "base/synchronization/waitable_event.h" #include "base/threading/thread.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.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 "testing/gtest/include/gtest/gtest.h" #include "webkit/appcache/appcache_response.h" #include "webkit/appcache/appcache_url_request_job.h" #include "webkit/appcache/mock_appcache_service.h" using net::IOBuffer; using net::WrappedIOBuffer; namespace appcache { static const char kHttpBasicHeaders[] = "HTTP/1.0 200 OK\0Content-Length: 5\0\0"; static const char kHttpBasicBody[] = "Hello"; static const int kNumBlocks = 4; static const int kBlockSize = 1024; class AppCacheURLRequestJobTest : public testing::Test { public: // Test Harness ------------------------------------------------------------- // TODO(michaeln): share this test harness with AppCacheResponseTest class MockStorageDelegate : public AppCacheStorage::Delegate { public: explicit MockStorageDelegate(AppCacheURLRequestJobTest* test) : loaded_info_id_(0), test_(test) { } virtual void OnResponseInfoLoaded(AppCacheResponseInfo* info, int64 response_id) OVERRIDE { loaded_info_ = info; loaded_info_id_ = response_id; test_->ScheduleNextTask(); } scoped_refptr loaded_info_; int64 loaded_info_id_; AppCacheURLRequestJobTest* test_; }; class MockURLRequestDelegate : public net::URLRequest::Delegate { public: explicit MockURLRequestDelegate(AppCacheURLRequestJobTest* test) : test_(test), received_data_(new net::IOBuffer(kNumBlocks * kBlockSize)), did_receive_headers_(false), amount_received_(0), kill_after_amount_received_(0), kill_with_io_pending_(false) { } virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE { amount_received_ = 0; did_receive_headers_ = false; if (request->status().is_success()) { EXPECT_TRUE(request->response_headers()); did_receive_headers_ = true; received_info_ = request->response_info(); ReadSome(request); } else { RequestComplete(); } } virtual void OnReadCompleted(net::URLRequest* request, int bytes_read) OVERRIDE { if (bytes_read > 0) { amount_received_ += bytes_read; if (kill_after_amount_received_ && !kill_with_io_pending_) { if (amount_received_ >= kill_after_amount_received_) { request->Cancel(); return; } } ReadSome(request); if (kill_after_amount_received_ && kill_with_io_pending_) { if (amount_received_ >= kill_after_amount_received_) { request->Cancel(); return; } } } else { RequestComplete(); } } void ReadSome(net::URLRequest* request) { DCHECK(amount_received_ + kBlockSize <= kNumBlocks * kBlockSize); scoped_refptr wrapped_buffer( new net::WrappedIOBuffer(received_data_->data() + amount_received_)); int bytes_read = 0; EXPECT_FALSE(request->Read(wrapped_buffer, kBlockSize, &bytes_read)); EXPECT_EQ(0, bytes_read); } void RequestComplete() { test_->ScheduleNextTask(); } AppCacheURLRequestJobTest* test_; net::HttpResponseInfo received_info_; scoped_refptr received_data_; bool did_receive_headers_; int amount_received_; int kill_after_amount_received_; bool kill_with_io_pending_; }; static net::URLRequestJob* MockHttpJobFactory( net::URLRequest* request, net::NetworkDelegate* network_delegate, const std::string& scheme) { if (mock_factory_job_) { net::URLRequestJob* temp = mock_factory_job_; mock_factory_job_ = NULL; return temp; } else { return new net::URLRequestErrorJob(request, network_delegate, net::ERR_INTERNET_DISCONNECTED); } } // Helper callback to run a test on our io_thread. The io_thread is spun up // once and reused for all tests. template void MethodWrapper(Method method) { SetUpTest(); (this->*method)(); } static void SetUpTestCase() { io_thread_.reset(new base::Thread("AppCacheURLRequestJobTest Thread")); base::Thread::Options options(MessageLoop::TYPE_IO, 0); io_thread_->StartWithOptions(options); } static void TearDownTestCase() { io_thread_.reset(NULL); } AppCacheURLRequestJobTest() {} template void RunTestOnIOThread(Method method) { test_finished_event_ .reset(new base::WaitableEvent(false, false)); io_thread_->message_loop()->PostTask( FROM_HERE, base::Bind(&AppCacheURLRequestJobTest::MethodWrapper, base::Unretained(this), method)); test_finished_event_->Wait(); } void SetUpTest() { DCHECK(MessageLoop::current() == io_thread_->message_loop()); DCHECK(task_stack_.empty()); orig_http_factory_ = net::URLRequest::Deprecated::RegisterProtocolFactory( "http", MockHttpJobFactory); url_request_delegate_.reset(new MockURLRequestDelegate(this)); storage_delegate_.reset(new MockStorageDelegate(this)); service_.reset(new MockAppCacheService()); expected_read_result_ = 0; expected_write_result_ = 0; written_response_id_ = 0; reader_deletion_count_down_ = 0; writer_deletion_count_down_ = 0; } void TearDownTest() { DCHECK(MessageLoop::current() == io_thread_->message_loop()); net::URLRequest::Deprecated::RegisterProtocolFactory( "http", orig_http_factory_); orig_http_factory_ = NULL; request_.reset(); url_request_delegate_.reset(); DCHECK(!mock_factory_job_); while (!task_stack_.empty()) task_stack_.pop(); reader_.reset(); read_buffer_ = NULL; read_info_buffer_ = NULL; writer_.reset(); write_buffer_ = NULL; write_info_buffer_ = NULL; storage_delegate_.reset(); service_.reset(); } void TestFinished() { // We unwind the stack prior to finishing up to let stack // based objects get deleted. DCHECK(MessageLoop::current() == io_thread_->message_loop()); MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&AppCacheURLRequestJobTest::TestFinishedUnwound, base::Unretained(this))); } void TestFinishedUnwound() { TearDownTest(); test_finished_event_->Signal(); } void PushNextTask(const base::Closure& task) { task_stack_.push(std::pair(task, false)); } void PushNextTaskAsImmediate(const base::Closure& task) { task_stack_.push(std::pair(task, true)); } void ScheduleNextTask() { DCHECK(MessageLoop::current() == io_thread_->message_loop()); if (task_stack_.empty()) { TestFinished(); return; } base::Closure task =task_stack_.top().first; bool immediate = task_stack_.top().second; task_stack_.pop(); if (immediate) task.Run(); else MessageLoop::current()->PostTask(FROM_HERE, task); } // Wrappers to call AppCacheResponseReader/Writer Read and Write methods void WriteBasicResponse() { scoped_refptr body(new WrappedIOBuffer(kHttpBasicBody)); std::string raw_headers(kHttpBasicHeaders, arraysize(kHttpBasicHeaders)); WriteResponse(MakeHttpResponseInfo(raw_headers), body, strlen(kHttpBasicBody)); } void WriteResponse(net::HttpResponseInfo* head, IOBuffer* body, int body_len) { DCHECK(body); scoped_refptr body_ref(body); PushNextTask(base::Bind(&AppCacheURLRequestJobTest::WriteResponseBody, base::Unretained(this), body_ref, body_len)); WriteResponseHead(head); } void WriteResponseHead(net::HttpResponseInfo* head) { EXPECT_FALSE(writer_->IsWritePending()); expected_write_result_ = GetHttpResponseInfoSize(head); write_info_buffer_ = new HttpResponseInfoIOBuffer(head); writer_->WriteInfo( write_info_buffer_, base::Bind(&AppCacheURLRequestJobTest::OnWriteInfoComplete, base::Unretained(this))); } void WriteResponseBody(scoped_refptr io_buffer, int buf_len) { EXPECT_FALSE(writer_->IsWritePending()); write_buffer_ = io_buffer; expected_write_result_ = buf_len; writer_->WriteData( write_buffer_, buf_len, base::Bind(&AppCacheURLRequestJobTest::OnWriteComplete, base::Unretained(this))); } void ReadResponseBody(scoped_refptr io_buffer, int buf_len) { EXPECT_FALSE(reader_->IsReadPending()); read_buffer_ = io_buffer; expected_read_result_ = buf_len; reader_->ReadData( read_buffer_, buf_len, base::Bind(&AppCacheURLRequestJobTest::OnReadComplete, base::Unretained(this))); } // AppCacheResponseReader / Writer completion callbacks void OnWriteInfoComplete(int result) { EXPECT_FALSE(writer_->IsWritePending()); EXPECT_EQ(expected_write_result_, result); ScheduleNextTask(); } void OnWriteComplete(int result) { EXPECT_FALSE(writer_->IsWritePending()); EXPECT_EQ(expected_write_result_, result); ScheduleNextTask(); } void OnReadInfoComplete(int result) { EXPECT_FALSE(reader_->IsReadPending()); EXPECT_EQ(expected_read_result_, result); ScheduleNextTask(); } void OnReadComplete(int result) { EXPECT_FALSE(reader_->IsReadPending()); EXPECT_EQ(expected_read_result_, result); ScheduleNextTask(); } // Helpers to work with HttpResponseInfo objects net::HttpResponseInfo* MakeHttpResponseInfo(const std::string& raw_headers) { net::HttpResponseInfo* info = new net::HttpResponseInfo; info->request_time = base::Time::Now(); info->response_time = base::Time::Now(); info->was_cached = false; info->headers = new net::HttpResponseHeaders(raw_headers); return info; } int GetHttpResponseInfoSize(const net::HttpResponseInfo* info) { Pickle pickle; return PickleHttpResonseInfo(&pickle, info); } bool CompareHttpResponseInfos(const net::HttpResponseInfo* info1, const net::HttpResponseInfo* info2) { Pickle pickle1; Pickle pickle2; PickleHttpResonseInfo(&pickle1, info1); PickleHttpResonseInfo(&pickle2, info2); return (pickle1.size() == pickle2.size()) && (0 == memcmp(pickle1.data(), pickle2.data(), pickle1.size())); } int PickleHttpResonseInfo(Pickle* pickle, const net::HttpResponseInfo* info) { const bool kSkipTransientHeaders = true; const bool kTruncated = false; info->Persist(pickle, kSkipTransientHeaders, kTruncated); return pickle->size(); } // Helpers to fill and verify blocks of memory with a value void FillData(char value, char* data, int data_len) { memset(data, value, data_len); } bool CheckData(char value, const char* data, int data_len) { for (int i = 0; i < data_len; ++i, ++data) { if (*data != value) return false; } return true; } // Individual Tests --------------------------------------------------------- // Some of the individual tests involve multiple async steps. Each test // is delineated with a section header. // Basic ------------------------------------------------------------------- void Basic() { AppCacheStorage* storage = service_->storage(); net::URLRequest request(GURL("http://blah/"), NULL, &empty_context_); scoped_refptr job; // Create an instance and see that it looks as expected. job = new AppCacheURLRequestJob( &request, empty_context_.network_delegate(), storage); EXPECT_TRUE(job->is_waiting()); EXPECT_FALSE(job->is_delivering_appcache_response()); EXPECT_FALSE(job->is_delivering_network_response()); EXPECT_FALSE(job->is_delivering_error_response()); EXPECT_FALSE(job->has_been_started()); EXPECT_FALSE(job->has_been_killed()); EXPECT_EQ(GURL(), job->manifest_url()); EXPECT_EQ(kNoCacheId, job->cache_id()); EXPECT_FALSE(job->entry().has_response_id()); TestFinished(); } // DeliveryOrders ----------------------------------------------------- void DeliveryOrders() { AppCacheStorage* storage = service_->storage(); net::URLRequest request(GURL("http://blah/"), NULL, &empty_context_); scoped_refptr job; // Create an instance, give it a delivery order and see that // it looks as expected. job = new AppCacheURLRequestJob( &request, empty_context_.network_delegate(), storage); job->DeliverErrorResponse(); EXPECT_TRUE(job->is_delivering_error_response()); EXPECT_FALSE(job->has_been_started()); job = new AppCacheURLRequestJob( &request, empty_context_.network_delegate(), storage); job->DeliverNetworkResponse(); EXPECT_TRUE(job->is_delivering_network_response()); EXPECT_FALSE(job->has_been_started()); job = new AppCacheURLRequestJob( &request, empty_context_.network_delegate(), storage); const GURL kManifestUrl("http://blah/"); const int64 kCacheId(1); const int64 kGroupId(1); const AppCacheEntry kEntry(AppCacheEntry::EXPLICIT, 1); job->DeliverAppCachedResponse(kManifestUrl, kCacheId, kGroupId, kEntry, false); EXPECT_FALSE(job->is_waiting()); EXPECT_TRUE(job->is_delivering_appcache_response()); EXPECT_FALSE(job->has_been_started()); EXPECT_EQ(kManifestUrl, job->manifest_url()); EXPECT_EQ(kCacheId, job->cache_id()); EXPECT_EQ(kGroupId, job->group_id()); EXPECT_EQ(kEntry.types(), job->entry().types()); EXPECT_EQ(kEntry.response_id(), job->entry().response_id()); TestFinished(); } // DeliverNetworkResponse -------------------------------------------------- void DeliverNetworkResponse() { // This test has async steps. PushNextTask( base::Bind(&AppCacheURLRequestJobTest::VerifyDeliverNetworkResponse, base::Unretained(this))); AppCacheStorage* storage = service_->storage(); request_.reset(empty_context_.CreateRequest( GURL("http://blah/"), url_request_delegate_.get())); // Setup to create an AppCacheURLRequestJob with orders to deliver // a network response. mock_factory_job_ = new AppCacheURLRequestJob( request_.get(), empty_context_.network_delegate(), storage); mock_factory_job_->DeliverNetworkResponse(); EXPECT_TRUE(mock_factory_job_->is_delivering_network_response()); EXPECT_FALSE(mock_factory_job_->has_been_started()); // Start the request. request_->Start(); // The job should have been picked up. EXPECT_FALSE(mock_factory_job_); // Completion is async. } void VerifyDeliverNetworkResponse() { EXPECT_EQ(request_->status().error(), net::ERR_INTERNET_DISCONNECTED); TestFinished(); } // DeliverErrorResponse -------------------------------------------------- void DeliverErrorResponse() { // This test has async steps. PushNextTask( base::Bind(&AppCacheURLRequestJobTest::VerifyDeliverErrorResponse, base::Unretained(this))); AppCacheStorage* storage = service_->storage(); request_.reset(empty_context_.CreateRequest(GURL( "http://blah/"), url_request_delegate_.get())); // Setup to create an AppCacheURLRequestJob with orders to deliver // a network response. mock_factory_job_ = new AppCacheURLRequestJob( request_.get(), empty_context_.network_delegate(), storage); mock_factory_job_->DeliverErrorResponse(); EXPECT_TRUE(mock_factory_job_->is_delivering_error_response()); EXPECT_FALSE(mock_factory_job_->has_been_started()); // Start the request. request_->Start(); // The job should have been picked up. EXPECT_FALSE(mock_factory_job_); // Completion is async. } void VerifyDeliverErrorResponse() { EXPECT_EQ(request_->status().error(), net::ERR_FAILED); TestFinished(); } // DeliverSmallAppCachedResponse -------------------------------------- // "Small" being small enough to read completely in a single // request->Read call. void DeliverSmallAppCachedResponse() { // This test has several async steps. // 1. Write a small response to response storage. // 2. Use net::URLRequest to retrieve it. // 3. Verify we received what we expected to receive. PushNextTask(base::Bind( &AppCacheURLRequestJobTest::VerifyDeliverSmallAppCachedResponse, base::Unretained(this))); PushNextTask( base::Bind(&AppCacheURLRequestJobTest::RequestAppCachedResource, base::Unretained(this), false)); writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0)); written_response_id_ = writer_->response_id(); WriteBasicResponse(); // Continues async } void RequestAppCachedResource(bool start_after_delivery_orders) { AppCacheStorage* storage = service_->storage(); request_.reset(empty_context_.CreateRequest( GURL("http://blah/"), url_request_delegate_.get())); // Setup to create an AppCacheURLRequestJob with orders to deliver // a network response. scoped_refptr job(new AppCacheURLRequestJob( request_.get(), empty_context_.network_delegate(), storage)); if (start_after_delivery_orders) { job->DeliverAppCachedResponse( GURL(), 0, 111, AppCacheEntry(AppCacheEntry::EXPLICIT, written_response_id_), false); EXPECT_TRUE(job->is_delivering_appcache_response()); } // Start the request. EXPECT_FALSE(job->has_been_started()); mock_factory_job_ = job; request_->Start(); EXPECT_FALSE(mock_factory_job_); EXPECT_TRUE(job->has_been_started()); if (!start_after_delivery_orders) { job->DeliverAppCachedResponse( GURL(), 0, 111, AppCacheEntry(AppCacheEntry::EXPLICIT, written_response_id_), false); EXPECT_TRUE(job->is_delivering_appcache_response()); } // Completion is async. } void VerifyDeliverSmallAppCachedResponse() { EXPECT_TRUE(request_->status().is_success()); EXPECT_TRUE(CompareHttpResponseInfos( write_info_buffer_->http_info.get(), &url_request_delegate_->received_info_)); EXPECT_EQ(5, url_request_delegate_->amount_received_); EXPECT_EQ(0, memcmp(kHttpBasicBody, url_request_delegate_->received_data_->data(), strlen(kHttpBasicBody))); TestFinished(); } // DeliverLargeAppCachedResponse -------------------------------------- // "Large" enough to require multiple calls to request->Read to complete. void DeliverLargeAppCachedResponse() { // This test has several async steps. // 1. Write a large response to response storage. // 2. Use net::URLRequest to retrieve it. // 3. Verify we received what we expected to receive. PushNextTask(base::Bind( &AppCacheURLRequestJobTest::VerifyDeliverLargeAppCachedResponse, base::Unretained(this))); PushNextTask(base::Bind( &AppCacheURLRequestJobTest::RequestAppCachedResource, base::Unretained(this), true)); writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0)); written_response_id_ = writer_->response_id(); WriteLargeResponse(); // Continues async } void WriteLargeResponse() { // 3, 1k blocks static const char kHttpHeaders[] = "HTTP/1.0 200 OK\0Content-Length: 3072\0\0"; scoped_refptr body(new IOBuffer(kBlockSize * 3)); char* p = body->data(); for (int i = 0; i < 3; ++i, p += kBlockSize) FillData(i + 1, p, kBlockSize); std::string raw_headers(kHttpHeaders, arraysize(kHttpHeaders)); WriteResponse(MakeHttpResponseInfo(raw_headers), body, kBlockSize * 3); } void VerifyDeliverLargeAppCachedResponse() { EXPECT_TRUE(request_->status().is_success()); EXPECT_TRUE(CompareHttpResponseInfos( write_info_buffer_->http_info.get(), &url_request_delegate_->received_info_)); EXPECT_EQ(3072, url_request_delegate_->amount_received_); char* p = url_request_delegate_->received_data_->data(); for (int i = 0; i < 3; ++i, p += kBlockSize) EXPECT_TRUE(CheckData(i + 1, p, kBlockSize)); TestFinished(); } // DeliverPartialResponse -------------------------------------- void DeliverPartialResponse() { // This test has several async steps. // 1. Write a small response to response storage. // 2. Use net::URLRequest to retrieve it a subset using a range request // 3. Verify we received what we expected to receive. PushNextTask(base::Bind( &AppCacheURLRequestJobTest::VerifyDeliverPartialResponse, base::Unretained(this))); PushNextTask(base::Bind( &AppCacheURLRequestJobTest::MakeRangeRequest, base::Unretained(this))); writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0)); written_response_id_ = writer_->response_id(); WriteBasicResponse(); // Continues async } void MakeRangeRequest() { AppCacheStorage* storage = service_->storage(); request_.reset(empty_context_.CreateRequest( GURL("http://blah/"), url_request_delegate_.get())); // Request a range, the 3 middle chars out of 'Hello' net::HttpRequestHeaders extra_headers; extra_headers.SetHeader("Range", "bytes= 1-3"); request_->SetExtraRequestHeaders(extra_headers); // Create job with orders to deliver an appcached entry. scoped_refptr job(new AppCacheURLRequestJob( request_.get(), empty_context_.network_delegate(), storage)); job->DeliverAppCachedResponse( GURL(), 0, 111, AppCacheEntry(AppCacheEntry::EXPLICIT, written_response_id_), false); EXPECT_TRUE(job->is_delivering_appcache_response()); // Start the request. EXPECT_FALSE(job->has_been_started()); mock_factory_job_ = job; request_->Start(); EXPECT_FALSE(mock_factory_job_); EXPECT_TRUE(job->has_been_started()); // Completion is async. } void VerifyDeliverPartialResponse() { EXPECT_TRUE(request_->status().is_success()); EXPECT_EQ(3, url_request_delegate_->amount_received_); EXPECT_EQ(0, memcmp(kHttpBasicBody + 1, url_request_delegate_->received_data_->data(), 3)); net::HttpResponseHeaders* headers = url_request_delegate_->received_info_.headers.get(); EXPECT_EQ(206, headers->response_code()); EXPECT_EQ(3, headers->GetContentLength()); int64 range_start, range_end, object_size; EXPECT_TRUE( headers->GetContentRange(&range_start, &range_end, &object_size)); EXPECT_EQ(1, range_start); EXPECT_EQ(3, range_end); EXPECT_EQ(5, object_size); TestFinished(); } // CancelRequest -------------------------------------- void CancelRequest() { // This test has several async steps. // 1. Write a large response to response storage. // 2. Use net::URLRequest to retrieve it. // 3. Cancel the request after data starts coming in. PushNextTask(base::Bind( &AppCacheURLRequestJobTest::VerifyCancel, base::Unretained(this))); PushNextTask(base::Bind( &AppCacheURLRequestJobTest::RequestAppCachedResource, base::Unretained(this), true)); writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0)); written_response_id_ = writer_->response_id(); WriteLargeResponse(); url_request_delegate_->kill_after_amount_received_ = kBlockSize; url_request_delegate_->kill_with_io_pending_ = false; // Continues async } void VerifyCancel() { EXPECT_EQ(net::URLRequestStatus::CANCELED, request_->status().status()); TestFinished(); } // CancelRequestWithIOPending -------------------------------------- void CancelRequestWithIOPending() { // This test has several async steps. // 1. Write a large response to response storage. // 2. Use net::URLRequest to retrieve it. // 3. Cancel the request after data starts coming in. PushNextTask(base::Bind( &AppCacheURLRequestJobTest::VerifyCancel, base::Unretained(this))); PushNextTask(base::Bind( &AppCacheURLRequestJobTest::RequestAppCachedResource, base::Unretained(this), true)); writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0)); written_response_id_ = writer_->response_id(); WriteLargeResponse(); url_request_delegate_->kill_after_amount_received_ = kBlockSize; url_request_delegate_->kill_with_io_pending_ = true; // Continues async } // Data members -------------------------------------------------------- scoped_ptr test_finished_event_; scoped_ptr storage_delegate_; scoped_ptr service_; std::stack > task_stack_; scoped_ptr reader_; scoped_refptr read_info_buffer_; scoped_refptr read_buffer_; int expected_read_result_; int reader_deletion_count_down_; int64 written_response_id_; scoped_ptr writer_; scoped_refptr write_info_buffer_; scoped_refptr write_buffer_; int expected_write_result_; int writer_deletion_count_down_; net::URLRequest::ProtocolFactory* orig_http_factory_; net::URLRequestContext empty_context_; scoped_ptr request_; scoped_ptr url_request_delegate_; static scoped_ptr io_thread_; static AppCacheURLRequestJob* mock_factory_job_; }; // static scoped_ptr AppCacheURLRequestJobTest::io_thread_; AppCacheURLRequestJob* AppCacheURLRequestJobTest::mock_factory_job_ = NULL; TEST_F(AppCacheURLRequestJobTest, Basic) { RunTestOnIOThread(&AppCacheURLRequestJobTest::Basic); } TEST_F(AppCacheURLRequestJobTest, DeliveryOrders) { RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliveryOrders); } TEST_F(AppCacheURLRequestJobTest, DeliverNetworkResponse) { RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverNetworkResponse); } TEST_F(AppCacheURLRequestJobTest, DeliverErrorResponse) { RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverErrorResponse); } TEST_F(AppCacheURLRequestJobTest, DeliverSmallAppCachedResponse) { RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverSmallAppCachedResponse); } TEST_F(AppCacheURLRequestJobTest, DeliverLargeAppCachedResponse) { RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverLargeAppCachedResponse); } TEST_F(AppCacheURLRequestJobTest, DeliverPartialResponse) { RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverPartialResponse); } TEST_F(AppCacheURLRequestJobTest, CancelRequest) { RunTestOnIOThread(&AppCacheURLRequestJobTest::CancelRequest); } TEST_F(AppCacheURLRequestJobTest, CancelRequestWithIOPending) { RunTestOnIOThread(&AppCacheURLRequestJobTest::CancelRequestWithIOPending); } } // namespace appcache