summaryrefslogtreecommitdiffstats
path: root/webkit/appcache/appcache_url_request_job_unittest.cc
diff options
context:
space:
mode:
authormichaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-09 00:09:21 +0000
committermichaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-09 00:09:21 +0000
commit3367fc1dd134eeaf9ab6e62397f05ad3964319fe (patch)
tree78fb4d7ac912bbaf47b64f0aaefef331906b5fca /webkit/appcache/appcache_url_request_job_unittest.cc
parent9f9e1fcbbd52267fa657b3b0b80ece683ceb6dd6 (diff)
downloadchromium_src-3367fc1dd134eeaf9ab6e62397f05ad3964319fe.zip
chromium_src-3367fc1dd134eeaf9ab6e62397f05ad3964319fe.tar.gz
chromium_src-3367fc1dd134eeaf9ab6e62397f05ad3964319fe.tar.bz2
AppCache changes to the networking model. Added new classes AppCacheRequestHandler and AppCacheURLRequestJob.
TEST=appcache_url_request_job_unittest.cc, appcache_request_handler_unittest.cc BUG=none Review URL: http://codereview.chromium.org/338034 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@31411 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/appcache/appcache_url_request_job_unittest.cc')
-rw-r--r--webkit/appcache/appcache_url_request_job_unittest.cc745
1 files changed, 745 insertions, 0 deletions
diff --git a/webkit/appcache/appcache_url_request_job_unittest.cc b/webkit/appcache/appcache_url_request_job_unittest.cc
new file mode 100644
index 0000000..b7b452f
--- /dev/null
+++ b/webkit/appcache/appcache_url_request_job_unittest.cc
@@ -0,0 +1,745 @@
+// Copyright (c) 2009 The Chromium Authos. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/compiler_specific.h"
+#include "base/pickle.h"
+#include "base/thread.h"
+#include "base/waitable_event.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_error_job.h"
+#include "net/base/io_buffer.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;
+static const int kNoSuchResponseId = 123;
+
+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) {
+ loaded_info_ = info;
+ loaded_info_id_ = response_id;
+ test_->ScheduleNextTask();
+ }
+
+ scoped_refptr<AppCacheResponseInfo> loaded_info_;
+ int64 loaded_info_id_;
+ AppCacheURLRequestJobTest* test_;
+ };
+
+ class MockURLRequestDelegate : public 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(URLRequest* request) {
+ 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(URLRequest* request, int bytes_read) {
+ 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(URLRequest* request) {
+ DCHECK(amount_received_ + kBlockSize <= kNumBlocks * kBlockSize);
+ scoped_refptr<IOBuffer> 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<net::IOBuffer> received_data_;
+ bool did_receive_headers_;
+ int amount_received_;
+ int kill_after_amount_received_;
+ bool kill_with_io_pending_;
+ };
+
+ static URLRequestJob* MockHttpJobFactory(URLRequest* request,
+ const std::string& scheme) {
+ if (mock_factory_job_) {
+ URLRequestJob* temp = mock_factory_job_;
+ mock_factory_job_ = NULL;
+ return temp;
+ } else {
+ return new URLRequestErrorJob(request, net::ERR_INTERNET_DISCONNECTED);
+ }
+ }
+
+ // Helper class run a test on our io_thread. The io_thread
+ // is spun up once and reused for all tests.
+ template <class Method>
+ class WrapperTask : public Task {
+ public:
+ WrapperTask(AppCacheURLRequestJobTest* test, Method method)
+ : test_(test), method_(method) {
+ }
+
+ virtual void Run() {
+ test_->SetUpTest();
+ (test_->*method_)();
+ }
+
+ private:
+ AppCacheURLRequestJobTest* test_;
+ Method 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()
+ : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(read_callback_(
+ this, &AppCacheURLRequestJobTest::OnReadComplete)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(read_info_callback_(
+ this, &AppCacheURLRequestJobTest::OnReadInfoComplete)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(write_callback_(
+ this, &AppCacheURLRequestJobTest::OnWriteComplete)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(write_info_callback_(
+ this, &AppCacheURLRequestJobTest::OnWriteInfoComplete)) {
+ }
+
+ template <class Method>
+ void RunTestOnIOThread(Method method) {
+ test_finished_event_ .reset(new base::WaitableEvent(false, false));
+ io_thread_->message_loop()->PostTask(
+ FROM_HERE, new WrapperTask<Method>(this, method));
+ test_finished_event_->Wait();
+ }
+
+ void SetUpTest() {
+ DCHECK(MessageLoop::current() == io_thread_->message_loop());
+ DCHECK(task_stack_.empty());
+ orig_http_factory_ = URLRequest::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;
+ should_delete_reader_in_completion_callback_ = false;
+ should_delete_writer_in_completion_callback_ = false;
+ reader_deletion_count_down_ = 0;
+ writer_deletion_count_down_ = 0;
+ read_callback_was_called_ = false;
+ write_callback_was_called_ = false;
+ }
+
+ void TearDownTest() {
+ DCHECK(MessageLoop::current() == io_thread_->message_loop());
+ URLRequest::RegisterProtocolFactory("http", orig_http_factory_);
+ orig_http_factory_ = NULL;
+ request_.reset();
+ url_request_delegate_.reset();
+ DCHECK(!mock_factory_job_);
+
+ while (!task_stack_.empty()) {
+ delete task_stack_.top().first;
+ 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,
+ method_factory_.NewRunnableMethod(
+ &AppCacheURLRequestJobTest::TestFinishedUnwound));
+ }
+
+ void TestFinishedUnwound() {
+ TearDownTest();
+ test_finished_event_->Signal();
+ }
+
+ void PushNextTask(Task* task) {
+ task_stack_.push(std::pair<Task*, bool>(task, false));
+ }
+
+ void PushNextTaskAsImmediate(Task* task) {
+ task_stack_.push(std::pair<Task*, bool>(task, true));
+ }
+
+ void ScheduleNextTask() {
+ DCHECK(MessageLoop::current() == io_thread_->message_loop());
+ if (task_stack_.empty()) {
+ TestFinished();
+ return;
+ }
+ scoped_ptr<Task> 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.release());
+ }
+
+ // Wrappers to call AppCacheResponseReader/Writer Read and Write methods
+
+ void WriteBasicResponse() {
+ scoped_refptr<IOBuffer> 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<IOBuffer> body_ref(body);
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheURLRequestJobTest::WriteResponseBody,
+ 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_, &write_info_callback_);
+ }
+
+ void WriteResponseBody(scoped_refptr<IOBuffer> io_buffer, int buf_len) {
+ EXPECT_FALSE(writer_->IsWritePending());
+ write_buffer_ = io_buffer;
+ expected_write_result_ = buf_len;
+ writer_->WriteData(
+ write_buffer_, buf_len, &write_callback_);
+ }
+
+ void ReadResponseBody(scoped_refptr<IOBuffer> io_buffer, int buf_len) {
+ EXPECT_FALSE(reader_->IsReadPending());
+ read_buffer_ = io_buffer;
+ expected_read_result_ = buf_len;
+ reader_->ReadData(
+ read_buffer_, buf_len, &read_callback_);
+ }
+
+ // 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());
+ write_callback_was_called_ = true;
+ EXPECT_EQ(expected_write_result_, result);
+ if (should_delete_writer_in_completion_callback_ &&
+ --writer_deletion_count_down_ == 0) {
+ writer_.reset();
+ }
+ ScheduleNextTask();
+ }
+
+ void OnReadInfoComplete(int result) {
+ EXPECT_FALSE(reader_->IsReadPending());
+ EXPECT_EQ(expected_read_result_, result);
+ ScheduleNextTask();
+ }
+
+ void OnReadComplete(int result) {
+ EXPECT_FALSE(reader_->IsReadPending());
+ read_callback_was_called_ = true;
+ EXPECT_EQ(expected_read_result_, result);
+ if (should_delete_reader_in_completion_callback_ &&
+ --reader_deletion_count_down_ == 0) {
+ reader_.reset();
+ }
+ 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();
+ URLRequest request(GURL("http://blah/"), NULL);
+ scoped_refptr<AppCacheURLRequestJob> job;
+
+ // Create an instance and see that it looks as expected.
+
+ job = new AppCacheURLRequestJob(&request, 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::EmptyGURL(), job->manifest_url());
+ EXPECT_EQ(kNoCacheId, job->cache_id());
+ EXPECT_FALSE(job->entry().has_response_id());
+
+ TestFinished();
+ }
+
+ // DeliveryOrders -----------------------------------------------------
+ void DeliveryOrders() {
+ AppCacheStorage* storage = service_->storage();
+ URLRequest request(GURL("http://blah/"), NULL);
+ scoped_refptr<AppCacheURLRequestJob> job;
+
+ // Create an instance, give it a delivery order and see that
+ // it looks as expected.
+
+ job = new AppCacheURLRequestJob(&request, storage);
+ job->DeliverErrorResponse();
+ EXPECT_TRUE(job->is_delivering_error_response());
+ EXPECT_FALSE(job->has_been_started());
+
+ job = new AppCacheURLRequestJob(&request, storage);
+ job->DeliverNetworkResponse();
+ EXPECT_TRUE(job->is_delivering_network_response());
+ EXPECT_FALSE(job->has_been_started());
+
+ job = new AppCacheURLRequestJob(&request, storage);
+ const GURL kManifestUrl("http://blah/");
+ const int64 kCacheId(1);
+ const AppCacheEntry kEntry(AppCacheEntry::EXPLICIT, 1);
+ job->DeliverAppCachedResponse(kManifestUrl, kCacheId, kEntry);
+ 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(kEntry.types(), job->entry().types());
+ EXPECT_EQ(kEntry.response_id(), job->entry().response_id());
+
+ TestFinished();
+ }
+
+ // DeliverNetworkResponse --------------------------------------------------
+
+ void DeliverNetworkResponse() {
+ // This test has async steps.
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheURLRequestJobTest::VerifyDeliverNetworkResponse));
+
+ AppCacheStorage* storage = service_->storage();
+ request_.reset(
+ new URLRequest(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(), 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().os_error(),
+ net::ERR_INTERNET_DISCONNECTED);
+ TestFinished();
+ }
+
+ // DeliverErrorResponse --------------------------------------------------
+
+ void DeliverErrorResponse() {
+ // This test has async steps.
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheURLRequestJobTest::VerifyDeliverErrorResponse));
+
+ AppCacheStorage* storage = service_->storage();
+ request_.reset(
+ new URLRequest(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(), 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().os_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 URLRequest to retrieve it.
+ // 3. Verify we received what we expected to receive.
+
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheURLRequestJobTest::VerifyDeliverSmallAppCachedResponse));
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheURLRequestJobTest::RequestAppCachedResource, false));
+
+ writer_.reset(service_->storage()->CreateResponseWriter(GURL()));
+ written_response_id_ = writer_->response_id();
+ WriteBasicResponse();
+ // Continues async
+ }
+
+ void RequestAppCachedResource(bool start_after_delivery_orders) {
+ AppCacheStorage* storage = service_->storage();
+ request_.reset(
+ new URLRequest(GURL("http://blah/"), url_request_delegate_.get()));
+
+ // Setup to create an AppCacheURLRequestJob with orders to deliver
+ // a network response.
+ scoped_refptr<AppCacheURLRequestJob> job(
+ new AppCacheURLRequestJob(request_.get(), storage));
+
+ if (start_after_delivery_orders) {
+ job->DeliverAppCachedResponse(
+ GURL(), 111,
+ AppCacheEntry(AppCacheEntry::EXPLICIT, written_response_id_));
+ 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(), 111,
+ AppCacheEntry(AppCacheEntry::EXPLICIT, written_response_id_));
+ 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 URLRequest to retrieve it.
+ // 3. Verify we received what we expected to receive.
+
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheURLRequestJobTest::VerifyDeliverLargeAppCachedResponse));
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheURLRequestJobTest::RequestAppCachedResource, true));
+
+ writer_.reset(service_->storage()->CreateResponseWriter(GURL()));
+ 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<IOBuffer> 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();
+ }
+
+ // CancelRequest --------------------------------------
+
+ void CancelRequest() {
+ // This test has several async steps.
+ // 1. Write a large response to response storage.
+ // 2. Use URLRequest to retrieve it.
+ // 3. Cancel the request after data starts coming in.
+
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheURLRequestJobTest::VerifyCancel));
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheURLRequestJobTest::RequestAppCachedResource, true));
+
+ writer_.reset(service_->storage()->CreateResponseWriter(GURL()));
+ 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(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 URLRequest to retrieve it.
+ // 3. Cancel the request after data starts coming in.
+
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheURLRequestJobTest::VerifyCancel));
+ PushNextTask(method_factory_.NewRunnableMethod(
+ &AppCacheURLRequestJobTest::RequestAppCachedResource, true));
+
+ writer_.reset(service_->storage()->CreateResponseWriter(GURL()));
+ 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 --------------------------------------------------------
+
+ ScopedRunnableMethodFactory<AppCacheURLRequestJobTest> method_factory_;
+ scoped_ptr<base::WaitableEvent> test_finished_event_;
+ scoped_ptr<MockStorageDelegate> storage_delegate_;
+ scoped_ptr<MockAppCacheService> service_;
+ std::stack<std::pair<Task*, bool> > task_stack_;
+
+ scoped_ptr<AppCacheResponseReader> reader_;
+ scoped_refptr<HttpResponseInfoIOBuffer> read_info_buffer_;
+ scoped_refptr<IOBuffer> read_buffer_;
+ int expected_read_result_;
+ net::CompletionCallbackImpl<AppCacheURLRequestJobTest> read_callback_;
+ net::CompletionCallbackImpl<AppCacheURLRequestJobTest> read_info_callback_;
+ bool should_delete_reader_in_completion_callback_;
+ int reader_deletion_count_down_;
+ bool read_callback_was_called_;
+
+ int64 written_response_id_;
+ scoped_ptr<AppCacheResponseWriter> writer_;
+ scoped_refptr<HttpResponseInfoIOBuffer> write_info_buffer_;
+ scoped_refptr<IOBuffer> write_buffer_;
+ int expected_write_result_;
+ net::CompletionCallbackImpl<AppCacheURLRequestJobTest> write_callback_;
+ net::CompletionCallbackImpl<AppCacheURLRequestJobTest> write_info_callback_;
+ bool should_delete_writer_in_completion_callback_;
+ int writer_deletion_count_down_;
+ bool write_callback_was_called_;
+
+ URLRequest::ProtocolFactory* orig_http_factory_;
+ scoped_ptr<URLRequest> request_;
+ scoped_ptr<MockURLRequestDelegate> url_request_delegate_;
+
+ static scoped_ptr<base::Thread> io_thread_;
+ static AppCacheURLRequestJob* mock_factory_job_;
+};
+
+// static
+scoped_ptr<base::Thread> 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, CancelRequest) {
+ RunTestOnIOThread(&AppCacheURLRequestJobTest::CancelRequest);
+}
+
+TEST_F(AppCacheURLRequestJobTest, CancelRequestWithIOPending) {
+ RunTestOnIOThread(&AppCacheURLRequestJobTest::CancelRequestWithIOPending);
+}
+
+} // namespace appcache
+