Home | History | Annotate | Download | only in appcache
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <stack>
      6 #include <utility>
      7 
      8 #include "base/bind.h"
      9 #include "base/bind_helpers.h"
     10 #include "base/callback.h"
     11 #include "base/compiler_specific.h"
     12 #include "base/logging.h"
     13 #include "base/memory/scoped_ptr.h"
     14 #include "base/pickle.h"
     15 #include "base/synchronization/waitable_event.h"
     16 #include "base/threading/thread.h"
     17 #include "content/browser/appcache/appcache_response.h"
     18 #include "content/browser/appcache/appcache_url_request_job.h"
     19 #include "content/browser/appcache/mock_appcache_service.h"
     20 #include "net/base/io_buffer.h"
     21 #include "net/base/net_errors.h"
     22 #include "net/base/request_priority.h"
     23 #include "net/http/http_response_headers.h"
     24 #include "net/url_request/url_request.h"
     25 #include "net/url_request/url_request_context.h"
     26 #include "net/url_request/url_request_error_job.h"
     27 #include "net/url_request/url_request_job_factory.h"
     28 #include "testing/gtest/include/gtest/gtest.h"
     29 #include "url/gurl.h"
     30 
     31 using net::IOBuffer;
     32 using net::WrappedIOBuffer;
     33 
     34 namespace content {
     35 
     36 namespace {
     37 
     38 const char kHttpBasicHeaders[] = "HTTP/1.0 200 OK\0Content-Length: 5\0\0";
     39 const char kHttpBasicBody[] = "Hello";
     40 
     41 const int kNumBlocks = 4;
     42 const int kBlockSize = 1024;
     43 
     44 class MockURLRequestJobFactory : public net::URLRequestJobFactory {
     45  public:
     46   MockURLRequestJobFactory() : job_(NULL) {
     47   }
     48 
     49   virtual ~MockURLRequestJobFactory() {
     50     DCHECK(!job_);
     51   }
     52 
     53   void SetJob(net::URLRequestJob* job) {
     54     job_ = job;
     55   }
     56 
     57   bool has_job() const {
     58     return job_ != NULL;
     59   }
     60 
     61   // net::URLRequestJobFactory implementation.
     62   virtual net::URLRequestJob* MaybeCreateJobWithProtocolHandler(
     63       const std::string& scheme,
     64       net::URLRequest* request,
     65       net::NetworkDelegate* network_delegate) const OVERRIDE {
     66     if (job_) {
     67       net::URLRequestJob* temp = job_;
     68       job_ = NULL;
     69       return temp;
     70     } else {
     71       return new net::URLRequestErrorJob(request,
     72                                          network_delegate,
     73                                          net::ERR_INTERNET_DISCONNECTED);
     74     }
     75   }
     76 
     77   virtual bool IsHandledProtocol(const std::string& scheme) const OVERRIDE {
     78     return scheme == "http";
     79   };
     80 
     81   virtual bool IsHandledURL(const GURL& url) const OVERRIDE {
     82     return url.SchemeIs("http");
     83   }
     84 
     85   virtual bool IsSafeRedirectTarget(const GURL& location) const OVERRIDE {
     86     return false;
     87   }
     88 
     89  private:
     90   mutable net::URLRequestJob* job_;
     91 };
     92 
     93 class AppCacheURLRequestJobTest : public testing::Test {
     94  public:
     95 
     96   // Test Harness -------------------------------------------------------------
     97   // TODO(michaeln): share this test harness with AppCacheResponseTest
     98 
     99   class MockStorageDelegate : public AppCacheStorage::Delegate {
    100    public:
    101     explicit MockStorageDelegate(AppCacheURLRequestJobTest* test)
    102         : loaded_info_id_(0), test_(test) {
    103     }
    104 
    105     virtual void OnResponseInfoLoaded(AppCacheResponseInfo* info,
    106                                       int64 response_id) OVERRIDE {
    107       loaded_info_ = info;
    108       loaded_info_id_ = response_id;
    109       test_->ScheduleNextTask();
    110     }
    111 
    112     scoped_refptr<AppCacheResponseInfo> loaded_info_;
    113     int64 loaded_info_id_;
    114     AppCacheURLRequestJobTest* test_;
    115   };
    116 
    117   class MockURLRequestDelegate : public net::URLRequest::Delegate {
    118    public:
    119     explicit MockURLRequestDelegate(AppCacheURLRequestJobTest* test)
    120         : test_(test),
    121           received_data_(new net::IOBuffer(kNumBlocks * kBlockSize)),
    122           did_receive_headers_(false), amount_received_(0),
    123           kill_after_amount_received_(0), kill_with_io_pending_(false) {
    124     }
    125 
    126     virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE {
    127       amount_received_ = 0;
    128       did_receive_headers_ = false;
    129       if (request->status().is_success()) {
    130         EXPECT_TRUE(request->response_headers());
    131         did_receive_headers_ = true;
    132         received_info_ = request->response_info();
    133         ReadSome(request);
    134       } else {
    135         RequestComplete();
    136       }
    137     }
    138 
    139     virtual void OnReadCompleted(net::URLRequest* request,
    140                                  int bytes_read) OVERRIDE {
    141       if (bytes_read > 0) {
    142         amount_received_ += bytes_read;
    143 
    144         if (kill_after_amount_received_ && !kill_with_io_pending_) {
    145           if (amount_received_ >= kill_after_amount_received_) {
    146             request->Cancel();
    147             return;
    148           }
    149         }
    150 
    151         ReadSome(request);
    152 
    153         if (kill_after_amount_received_ && kill_with_io_pending_) {
    154           if (amount_received_ >= kill_after_amount_received_) {
    155             request->Cancel();
    156             return;
    157           }
    158         }
    159       } else {
    160         RequestComplete();
    161       }
    162     }
    163 
    164     void ReadSome(net::URLRequest* request) {
    165       DCHECK(amount_received_ + kBlockSize <= kNumBlocks * kBlockSize);
    166       scoped_refptr<IOBuffer> wrapped_buffer(
    167           new net::WrappedIOBuffer(received_data_->data() + amount_received_));
    168       int bytes_read = 0;
    169       EXPECT_FALSE(
    170           request->Read(wrapped_buffer.get(), kBlockSize, &bytes_read));
    171       EXPECT_EQ(0, bytes_read);
    172     }
    173 
    174     void RequestComplete() {
    175       test_->ScheduleNextTask();
    176     }
    177 
    178     AppCacheURLRequestJobTest* test_;
    179     net::HttpResponseInfo received_info_;
    180     scoped_refptr<net::IOBuffer> received_data_;
    181     bool did_receive_headers_;
    182     int amount_received_;
    183     int kill_after_amount_received_;
    184     bool kill_with_io_pending_;
    185   };
    186 
    187   // Helper callback to run a test on our io_thread. The io_thread is spun up
    188   // once and reused for all tests.
    189   template <class Method>
    190   void MethodWrapper(Method method) {
    191     SetUpTest();
    192     (this->*method)();
    193   }
    194 
    195   static void SetUpTestCase() {
    196     io_thread_.reset(new base::Thread("AppCacheURLRequestJobTest Thread"));
    197     base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
    198     io_thread_->StartWithOptions(options);
    199   }
    200 
    201   static void TearDownTestCase() {
    202     io_thread_.reset(NULL);
    203   }
    204 
    205   AppCacheURLRequestJobTest() {}
    206 
    207   template <class Method>
    208   void RunTestOnIOThread(Method method) {
    209     test_finished_event_ .reset(new base::WaitableEvent(false, false));
    210     io_thread_->message_loop()->PostTask(
    211         FROM_HERE, base::Bind(&AppCacheURLRequestJobTest::MethodWrapper<Method>,
    212                               base::Unretained(this), method));
    213     test_finished_event_->Wait();
    214   }
    215 
    216   void SetUpTest() {
    217     DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
    218     DCHECK(task_stack_.empty());
    219 
    220     storage_delegate_.reset(new MockStorageDelegate(this));
    221     service_.reset(new MockAppCacheService());
    222     expected_read_result_ = 0;
    223     expected_write_result_ = 0;
    224     written_response_id_ = 0;
    225     reader_deletion_count_down_ = 0;
    226     writer_deletion_count_down_ = 0;
    227 
    228     url_request_delegate_.reset(new MockURLRequestDelegate(this));
    229     job_factory_.reset(new MockURLRequestJobFactory());
    230     empty_context_.reset(new net::URLRequestContext());
    231     empty_context_->set_job_factory(job_factory_.get());
    232   }
    233 
    234   void TearDownTest() {
    235     DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
    236     request_.reset();
    237 
    238     while (!task_stack_.empty())
    239       task_stack_.pop();
    240 
    241     reader_.reset();
    242     read_buffer_ = NULL;
    243     read_info_buffer_ = NULL;
    244     writer_.reset();
    245     write_buffer_ = NULL;
    246     write_info_buffer_ = NULL;
    247     storage_delegate_.reset();
    248     service_.reset();
    249 
    250     DCHECK(!job_factory_->has_job());
    251     empty_context_.reset();
    252     job_factory_.reset();
    253     url_request_delegate_.reset();
    254   }
    255 
    256   void TestFinished() {
    257     // We unwind the stack prior to finishing up to let stack
    258     // based objects get deleted.
    259     DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
    260     base::MessageLoop::current()->PostTask(
    261         FROM_HERE,
    262         base::Bind(&AppCacheURLRequestJobTest::TestFinishedUnwound,
    263                    base::Unretained(this)));
    264   }
    265 
    266   void TestFinishedUnwound() {
    267     TearDownTest();
    268     test_finished_event_->Signal();
    269   }
    270 
    271   void PushNextTask(const base::Closure& task) {
    272     task_stack_.push(std::pair<base::Closure, bool>(task, false));
    273   }
    274 
    275   void PushNextTaskAsImmediate(const base::Closure& task) {
    276     task_stack_.push(std::pair<base::Closure, bool>(task, true));
    277   }
    278 
    279   void ScheduleNextTask() {
    280     DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
    281     if (task_stack_.empty()) {
    282       TestFinished();
    283       return;
    284     }
    285     base::Closure task =task_stack_.top().first;
    286     bool immediate = task_stack_.top().second;
    287     task_stack_.pop();
    288     if (immediate)
    289       task.Run();
    290     else
    291       base::MessageLoop::current()->PostTask(FROM_HERE, task);
    292   }
    293 
    294   // Wrappers to call AppCacheResponseReader/Writer Read and Write methods
    295 
    296   void WriteBasicResponse() {
    297     scoped_refptr<IOBuffer> body(new WrappedIOBuffer(kHttpBasicBody));
    298     std::string raw_headers(kHttpBasicHeaders, arraysize(kHttpBasicHeaders));
    299     WriteResponse(
    300         MakeHttpResponseInfo(raw_headers), body.get(), strlen(kHttpBasicBody));
    301   }
    302 
    303   void WriteResponse(net::HttpResponseInfo* head,
    304                      IOBuffer* body, int body_len) {
    305     DCHECK(body);
    306     scoped_refptr<IOBuffer> body_ref(body);
    307     PushNextTask(base::Bind(&AppCacheURLRequestJobTest::WriteResponseBody,
    308                             base::Unretained(this), body_ref, body_len));
    309     WriteResponseHead(head);
    310   }
    311 
    312   void WriteResponseHead(net::HttpResponseInfo* head) {
    313     EXPECT_FALSE(writer_->IsWritePending());
    314     expected_write_result_ = GetHttpResponseInfoSize(head);
    315     write_info_buffer_ = new HttpResponseInfoIOBuffer(head);
    316     writer_->WriteInfo(
    317         write_info_buffer_.get(),
    318         base::Bind(&AppCacheURLRequestJobTest::OnWriteInfoComplete,
    319                    base::Unretained(this)));
    320   }
    321 
    322   void WriteResponseBody(scoped_refptr<IOBuffer> io_buffer, int buf_len) {
    323     EXPECT_FALSE(writer_->IsWritePending());
    324     write_buffer_ = io_buffer;
    325     expected_write_result_ = buf_len;
    326     writer_->WriteData(write_buffer_.get(),
    327                        buf_len,
    328                        base::Bind(&AppCacheURLRequestJobTest::OnWriteComplete,
    329                                   base::Unretained(this)));
    330   }
    331 
    332   void ReadResponseBody(scoped_refptr<IOBuffer> io_buffer, int buf_len) {
    333     EXPECT_FALSE(reader_->IsReadPending());
    334     read_buffer_ = io_buffer;
    335     expected_read_result_ = buf_len;
    336     reader_->ReadData(read_buffer_.get(),
    337                       buf_len,
    338                       base::Bind(&AppCacheURLRequestJobTest::OnReadComplete,
    339                                  base::Unretained(this)));
    340   }
    341 
    342   // AppCacheResponseReader / Writer completion callbacks
    343 
    344   void OnWriteInfoComplete(int result) {
    345     EXPECT_FALSE(writer_->IsWritePending());
    346     EXPECT_EQ(expected_write_result_, result);
    347     ScheduleNextTask();
    348   }
    349 
    350   void OnWriteComplete(int result) {
    351     EXPECT_FALSE(writer_->IsWritePending());
    352     EXPECT_EQ(expected_write_result_, result);
    353     ScheduleNextTask();
    354   }
    355 
    356   void OnReadInfoComplete(int result) {
    357     EXPECT_FALSE(reader_->IsReadPending());
    358     EXPECT_EQ(expected_read_result_, result);
    359     ScheduleNextTask();
    360   }
    361 
    362   void OnReadComplete(int result) {
    363     EXPECT_FALSE(reader_->IsReadPending());
    364     EXPECT_EQ(expected_read_result_, result);
    365     ScheduleNextTask();
    366   }
    367 
    368   // Helpers to work with HttpResponseInfo objects
    369 
    370   net::HttpResponseInfo* MakeHttpResponseInfo(const std::string& raw_headers) {
    371     net::HttpResponseInfo* info = new net::HttpResponseInfo;
    372     info->request_time = base::Time::Now();
    373     info->response_time = base::Time::Now();
    374     info->was_cached = false;
    375     info->headers = new net::HttpResponseHeaders(raw_headers);
    376     return info;
    377   }
    378 
    379   int GetHttpResponseInfoSize(const net::HttpResponseInfo* info) {
    380     Pickle pickle;
    381     return PickleHttpResonseInfo(&pickle, info);
    382   }
    383 
    384   bool CompareHttpResponseInfos(const net::HttpResponseInfo* info1,
    385                                 const net::HttpResponseInfo* info2) {
    386     Pickle pickle1;
    387     Pickle pickle2;
    388     PickleHttpResonseInfo(&pickle1, info1);
    389     PickleHttpResonseInfo(&pickle2, info2);
    390     return (pickle1.size() == pickle2.size()) &&
    391            (0 == memcmp(pickle1.data(), pickle2.data(), pickle1.size()));
    392   }
    393 
    394   int PickleHttpResonseInfo(Pickle* pickle, const net::HttpResponseInfo* info) {
    395     const bool kSkipTransientHeaders = true;
    396     const bool kTruncated = false;
    397     info->Persist(pickle, kSkipTransientHeaders, kTruncated);
    398     return pickle->size();
    399   }
    400 
    401   // Helpers to fill and verify blocks of memory with a value
    402 
    403   void FillData(char value, char* data, int data_len) {
    404     memset(data, value, data_len);
    405   }
    406 
    407   bool CheckData(char value, const char* data, int data_len) {
    408     for (int i = 0; i < data_len; ++i, ++data) {
    409       if (*data != value)
    410         return false;
    411     }
    412     return true;
    413   }
    414 
    415   // Individual Tests ---------------------------------------------------------
    416   // Some of the individual tests involve multiple async steps. Each test
    417   // is delineated with a section header.
    418 
    419   // Basic -------------------------------------------------------------------
    420   void Basic() {
    421     AppCacheStorage* storage = service_->storage();
    422     scoped_ptr<net::URLRequest> request(empty_context_->CreateRequest(
    423         GURL("http://blah/"), net::DEFAULT_PRIORITY, NULL, NULL));
    424     scoped_refptr<AppCacheURLRequestJob> job;
    425 
    426     // Create an instance and see that it looks as expected.
    427 
    428     job = new AppCacheURLRequestJob(request.get(), NULL, storage, NULL, false);
    429     EXPECT_TRUE(job->is_waiting());
    430     EXPECT_FALSE(job->is_delivering_appcache_response());
    431     EXPECT_FALSE(job->is_delivering_network_response());
    432     EXPECT_FALSE(job->is_delivering_error_response());
    433     EXPECT_FALSE(job->has_been_started());
    434     EXPECT_FALSE(job->has_been_killed());
    435     EXPECT_EQ(GURL(), job->manifest_url());
    436     EXPECT_EQ(kAppCacheNoCacheId, job->cache_id());
    437     EXPECT_FALSE(job->entry().has_response_id());
    438 
    439     TestFinished();
    440   }
    441 
    442   // DeliveryOrders -----------------------------------------------------
    443   void DeliveryOrders() {
    444     AppCacheStorage* storage = service_->storage();
    445     scoped_ptr<net::URLRequest> request(empty_context_->CreateRequest(
    446         GURL("http://blah/"), net::DEFAULT_PRIORITY, NULL, NULL));
    447     scoped_refptr<AppCacheURLRequestJob> job;
    448 
    449     // Create an instance, give it a delivery order and see that
    450     // it looks as expected.
    451 
    452     job = new AppCacheURLRequestJob(request.get(), NULL, storage, NULL, false);
    453     job->DeliverErrorResponse();
    454     EXPECT_TRUE(job->is_delivering_error_response());
    455     EXPECT_FALSE(job->has_been_started());
    456 
    457     job = new AppCacheURLRequestJob(request.get(), NULL, storage, NULL, false);
    458     job->DeliverNetworkResponse();
    459     EXPECT_TRUE(job->is_delivering_network_response());
    460     EXPECT_FALSE(job->has_been_started());
    461 
    462     job = new AppCacheURLRequestJob(request.get(), NULL, storage, NULL, false);
    463     const GURL kManifestUrl("http://blah/");
    464     const int64 kCacheId(1);
    465     const int64 kGroupId(1);
    466     const AppCacheEntry kEntry(AppCacheEntry::EXPLICIT, 1);
    467     job->DeliverAppCachedResponse(kManifestUrl, kCacheId, kGroupId,
    468                                   kEntry, false);
    469     EXPECT_FALSE(job->is_waiting());
    470     EXPECT_TRUE(job->is_delivering_appcache_response());
    471     EXPECT_FALSE(job->has_been_started());
    472     EXPECT_EQ(kManifestUrl, job->manifest_url());
    473     EXPECT_EQ(kCacheId, job->cache_id());
    474     EXPECT_EQ(kGroupId, job->group_id());
    475     EXPECT_EQ(kEntry.types(), job->entry().types());
    476     EXPECT_EQ(kEntry.response_id(), job->entry().response_id());
    477 
    478     TestFinished();
    479   }
    480 
    481   // DeliverNetworkResponse --------------------------------------------------
    482 
    483   void DeliverNetworkResponse() {
    484     // This test has async steps.
    485     PushNextTask(
    486         base::Bind(&AppCacheURLRequestJobTest::VerifyDeliverNetworkResponse,
    487                    base::Unretained(this)));
    488 
    489     AppCacheStorage* storage = service_->storage();
    490     request_ = empty_context_->CreateRequest(GURL("http://blah/"),
    491                                              net::DEFAULT_PRIORITY,
    492                                              url_request_delegate_.get(),
    493                                              NULL);
    494 
    495     // Setup to create an AppCacheURLRequestJob with orders to deliver
    496     // a network response.
    497     AppCacheURLRequestJob* mock_job = new AppCacheURLRequestJob(
    498         request_.get(), NULL, storage, NULL, false);
    499     job_factory_->SetJob(mock_job);
    500     mock_job->DeliverNetworkResponse();
    501     EXPECT_TRUE(mock_job->is_delivering_network_response());
    502     EXPECT_FALSE(mock_job->has_been_started());
    503 
    504     // Start the request.
    505     request_->Start();
    506 
    507     // The job should have been picked up.
    508     EXPECT_FALSE(job_factory_->has_job());
    509     // Completion is async.
    510   }
    511 
    512   void VerifyDeliverNetworkResponse() {
    513     EXPECT_EQ(request_->status().error(),
    514               net::ERR_INTERNET_DISCONNECTED);
    515     TestFinished();
    516   }
    517 
    518   // DeliverErrorResponse --------------------------------------------------
    519 
    520   void DeliverErrorResponse() {
    521     // This test has async steps.
    522     PushNextTask(
    523         base::Bind(&AppCacheURLRequestJobTest::VerifyDeliverErrorResponse,
    524                    base::Unretained(this)));
    525 
    526     AppCacheStorage* storage = service_->storage();
    527     request_ = empty_context_->CreateRequest(GURL("http://blah/"),
    528                                              net::DEFAULT_PRIORITY,
    529                                              url_request_delegate_.get(),
    530                                              NULL);
    531 
    532     // Setup to create an AppCacheURLRequestJob with orders to deliver
    533     // a network response.
    534     AppCacheURLRequestJob* mock_job = new AppCacheURLRequestJob(
    535         request_.get(), NULL, storage, NULL, false);
    536     job_factory_->SetJob(mock_job);
    537     mock_job->DeliverErrorResponse();
    538     EXPECT_TRUE(mock_job->is_delivering_error_response());
    539     EXPECT_FALSE(mock_job->has_been_started());
    540 
    541     // Start the request.
    542     request_->Start();
    543 
    544     // The job should have been picked up.
    545     EXPECT_FALSE(job_factory_->has_job());
    546     // Completion is async.
    547   }
    548 
    549   void VerifyDeliverErrorResponse() {
    550     EXPECT_EQ(request_->status().error(), net::ERR_FAILED);
    551     TestFinished();
    552   }
    553 
    554   // DeliverSmallAppCachedResponse --------------------------------------
    555   // "Small" being small enough to read completely in a single
    556   // request->Read call.
    557 
    558   void DeliverSmallAppCachedResponse() {
    559     // This test has several async steps.
    560     // 1. Write a small response to response storage.
    561     // 2. Use net::URLRequest to retrieve it.
    562     // 3. Verify we received what we expected to receive.
    563 
    564     PushNextTask(base::Bind(
    565         &AppCacheURLRequestJobTest::VerifyDeliverSmallAppCachedResponse,
    566         base::Unretained(this)));
    567     PushNextTask(
    568         base::Bind(&AppCacheURLRequestJobTest::RequestAppCachedResource,
    569                    base::Unretained(this), false));
    570 
    571     writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0));
    572     written_response_id_ = writer_->response_id();
    573     WriteBasicResponse();
    574     // Continues async
    575   }
    576 
    577   void RequestAppCachedResource(bool start_after_delivery_orders) {
    578     AppCacheStorage* storage = service_->storage();
    579     request_ = empty_context_->CreateRequest(GURL("http://blah/"),
    580                                              net::DEFAULT_PRIORITY,
    581                                              url_request_delegate_.get(),
    582                                              NULL);
    583 
    584     // Setup to create an AppCacheURLRequestJob with orders to deliver
    585     // a network response.
    586     scoped_refptr<AppCacheURLRequestJob> job(new AppCacheURLRequestJob(
    587         request_.get(), NULL, storage, NULL, false));
    588 
    589     if (start_after_delivery_orders) {
    590       job->DeliverAppCachedResponse(
    591           GURL(), 0, 111,
    592           AppCacheEntry(AppCacheEntry::EXPLICIT, written_response_id_),
    593           false);
    594       EXPECT_TRUE(job->is_delivering_appcache_response());
    595     }
    596 
    597     // Start the request.
    598     EXPECT_FALSE(job->has_been_started());
    599     job_factory_->SetJob(job.get());
    600     request_->Start();
    601     EXPECT_FALSE(job_factory_->has_job());
    602     EXPECT_TRUE(job->has_been_started());
    603 
    604     if (!start_after_delivery_orders) {
    605       job->DeliverAppCachedResponse(
    606           GURL(), 0, 111,
    607           AppCacheEntry(AppCacheEntry::EXPLICIT, written_response_id_),
    608           false);
    609       EXPECT_TRUE(job->is_delivering_appcache_response());
    610     }
    611 
    612     // Completion is async.
    613   }
    614 
    615   void VerifyDeliverSmallAppCachedResponse() {
    616     EXPECT_TRUE(request_->status().is_success());
    617     EXPECT_TRUE(CompareHttpResponseInfos(
    618         write_info_buffer_->http_info.get(),
    619         &url_request_delegate_->received_info_));
    620     EXPECT_EQ(5, url_request_delegate_->amount_received_);
    621     EXPECT_EQ(0, memcmp(kHttpBasicBody,
    622                         url_request_delegate_->received_data_->data(),
    623                         strlen(kHttpBasicBody)));
    624     TestFinished();
    625   }
    626 
    627   // DeliverLargeAppCachedResponse --------------------------------------
    628   // "Large" enough to require multiple calls to request->Read to complete.
    629 
    630   void DeliverLargeAppCachedResponse() {
    631     // This test has several async steps.
    632     // 1. Write a large response to response storage.
    633     // 2. Use net::URLRequest to retrieve it.
    634     // 3. Verify we received what we expected to receive.
    635 
    636     PushNextTask(base::Bind(
    637        &AppCacheURLRequestJobTest::VerifyDeliverLargeAppCachedResponse,
    638        base::Unretained(this)));
    639     PushNextTask(base::Bind(
    640        &AppCacheURLRequestJobTest::RequestAppCachedResource,
    641        base::Unretained(this), true));
    642 
    643     writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0));
    644     written_response_id_ = writer_->response_id();
    645     WriteLargeResponse();
    646     // Continues async
    647   }
    648 
    649   void WriteLargeResponse() {
    650     // 3, 1k blocks
    651     static const char kHttpHeaders[] =
    652         "HTTP/1.0 200 OK\0Content-Length: 3072\0\0";
    653     scoped_refptr<IOBuffer> body(new IOBuffer(kBlockSize * 3));
    654     char* p = body->data();
    655     for (int i = 0; i < 3; ++i, p += kBlockSize)
    656       FillData(i + 1, p, kBlockSize);
    657     std::string raw_headers(kHttpHeaders, arraysize(kHttpHeaders));
    658     WriteResponse(
    659         MakeHttpResponseInfo(raw_headers), body.get(), kBlockSize * 3);
    660   }
    661 
    662   void VerifyDeliverLargeAppCachedResponse() {
    663     EXPECT_TRUE(request_->status().is_success());
    664     EXPECT_TRUE(CompareHttpResponseInfos(
    665         write_info_buffer_->http_info.get(),
    666         &url_request_delegate_->received_info_));
    667     EXPECT_EQ(3072, url_request_delegate_->amount_received_);
    668     char* p = url_request_delegate_->received_data_->data();
    669     for (int i = 0; i < 3; ++i, p += kBlockSize)
    670       EXPECT_TRUE(CheckData(i + 1, p, kBlockSize));
    671     TestFinished();
    672   }
    673 
    674   // DeliverPartialResponse --------------------------------------
    675 
    676   void DeliverPartialResponse() {
    677     // This test has several async steps.
    678     // 1. Write a small response to response storage.
    679     // 2. Use net::URLRequest to retrieve it a subset using a range request
    680     // 3. Verify we received what we expected to receive.
    681     PushNextTask(base::Bind(
    682        &AppCacheURLRequestJobTest::VerifyDeliverPartialResponse,
    683        base::Unretained(this)));
    684     PushNextTask(base::Bind(
    685        &AppCacheURLRequestJobTest::MakeRangeRequest, base::Unretained(this)));
    686     writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0));
    687     written_response_id_ = writer_->response_id();
    688     WriteBasicResponse();
    689     // Continues async
    690   }
    691 
    692   void MakeRangeRequest() {
    693     AppCacheStorage* storage = service_->storage();
    694     request_ = empty_context_->CreateRequest(GURL("http://blah/"),
    695                                              net::DEFAULT_PRIORITY,
    696                                              url_request_delegate_.get(),
    697                                              NULL);
    698 
    699     // Request a range, the 3 middle chars out of 'Hello'
    700     net::HttpRequestHeaders extra_headers;
    701     extra_headers.SetHeader("Range", "bytes= 1-3");
    702     request_->SetExtraRequestHeaders(extra_headers);
    703 
    704     // Create job with orders to deliver an appcached entry.
    705     scoped_refptr<AppCacheURLRequestJob> job(new AppCacheURLRequestJob(
    706         request_.get(), NULL, storage, NULL, false));
    707     job->DeliverAppCachedResponse(
    708         GURL(), 0, 111,
    709         AppCacheEntry(AppCacheEntry::EXPLICIT, written_response_id_),
    710         false);
    711     EXPECT_TRUE(job->is_delivering_appcache_response());
    712 
    713     // Start the request.
    714     EXPECT_FALSE(job->has_been_started());
    715     job_factory_->SetJob(job.get());
    716     request_->Start();
    717     EXPECT_FALSE(job_factory_->has_job());
    718     EXPECT_TRUE(job->has_been_started());
    719     // Completion is async.
    720   }
    721 
    722   void VerifyDeliverPartialResponse() {
    723     EXPECT_TRUE(request_->status().is_success());
    724     EXPECT_EQ(3, url_request_delegate_->amount_received_);
    725     EXPECT_EQ(0, memcmp(kHttpBasicBody + 1,
    726                         url_request_delegate_->received_data_->data(),
    727                         3));
    728     net::HttpResponseHeaders* headers =
    729         url_request_delegate_->received_info_.headers.get();
    730     EXPECT_EQ(206, headers->response_code());
    731     EXPECT_EQ(3, headers->GetContentLength());
    732     int64 range_start, range_end, object_size;
    733     EXPECT_TRUE(
    734         headers->GetContentRange(&range_start, &range_end, &object_size));
    735     EXPECT_EQ(1, range_start);
    736     EXPECT_EQ(3, range_end);
    737     EXPECT_EQ(5, object_size);
    738     TestFinished();
    739   }
    740 
    741   // CancelRequest --------------------------------------
    742 
    743   void CancelRequest() {
    744     // This test has several async steps.
    745     // 1. Write a large response to response storage.
    746     // 2. Use net::URLRequest to retrieve it.
    747     // 3. Cancel the request after data starts coming in.
    748 
    749     PushNextTask(base::Bind(
    750        &AppCacheURLRequestJobTest::VerifyCancel, base::Unretained(this)));
    751     PushNextTask(base::Bind(
    752        &AppCacheURLRequestJobTest::RequestAppCachedResource,
    753        base::Unretained(this), true));
    754 
    755     writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0));
    756     written_response_id_ = writer_->response_id();
    757     WriteLargeResponse();
    758 
    759     url_request_delegate_->kill_after_amount_received_ = kBlockSize;
    760     url_request_delegate_->kill_with_io_pending_ = false;
    761     // Continues async
    762   }
    763 
    764   void VerifyCancel() {
    765     EXPECT_EQ(net::URLRequestStatus::CANCELED,
    766               request_->status().status());
    767     TestFinished();
    768   }
    769 
    770   // CancelRequestWithIOPending --------------------------------------
    771 
    772   void CancelRequestWithIOPending() {
    773     // This test has several async steps.
    774     // 1. Write a large response to response storage.
    775     // 2. Use net::URLRequest to retrieve it.
    776     // 3. Cancel the request after data starts coming in.
    777 
    778     PushNextTask(base::Bind(
    779        &AppCacheURLRequestJobTest::VerifyCancel, base::Unretained(this)));
    780     PushNextTask(base::Bind(
    781        &AppCacheURLRequestJobTest::RequestAppCachedResource,
    782        base::Unretained(this), true));
    783 
    784     writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0));
    785     written_response_id_ = writer_->response_id();
    786     WriteLargeResponse();
    787 
    788     url_request_delegate_->kill_after_amount_received_ = kBlockSize;
    789     url_request_delegate_->kill_with_io_pending_ = true;
    790     // Continues async
    791   }
    792 
    793 
    794   // Data members --------------------------------------------------------
    795 
    796   scoped_ptr<base::WaitableEvent> test_finished_event_;
    797   scoped_ptr<MockStorageDelegate> storage_delegate_;
    798   scoped_ptr<MockAppCacheService> service_;
    799   std::stack<std::pair<base::Closure, bool> > task_stack_;
    800 
    801   scoped_ptr<AppCacheResponseReader> reader_;
    802   scoped_refptr<HttpResponseInfoIOBuffer> read_info_buffer_;
    803   scoped_refptr<IOBuffer> read_buffer_;
    804   int expected_read_result_;
    805   int reader_deletion_count_down_;
    806 
    807   int64 written_response_id_;
    808   scoped_ptr<AppCacheResponseWriter> writer_;
    809   scoped_refptr<HttpResponseInfoIOBuffer> write_info_buffer_;
    810   scoped_refptr<IOBuffer> write_buffer_;
    811   int expected_write_result_;
    812   int writer_deletion_count_down_;
    813 
    814   scoped_ptr<MockURLRequestJobFactory> job_factory_;
    815   scoped_ptr<net::URLRequestContext> empty_context_;
    816   scoped_ptr<net::URLRequest> request_;
    817   scoped_ptr<MockURLRequestDelegate> url_request_delegate_;
    818 
    819   static scoped_ptr<base::Thread> io_thread_;
    820 };
    821 
    822 // static
    823 scoped_ptr<base::Thread> AppCacheURLRequestJobTest::io_thread_;
    824 
    825 TEST_F(AppCacheURLRequestJobTest, Basic) {
    826   RunTestOnIOThread(&AppCacheURLRequestJobTest::Basic);
    827 }
    828 
    829 TEST_F(AppCacheURLRequestJobTest, DeliveryOrders) {
    830   RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliveryOrders);
    831 }
    832 
    833 TEST_F(AppCacheURLRequestJobTest, DeliverNetworkResponse) {
    834   RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverNetworkResponse);
    835 }
    836 
    837 TEST_F(AppCacheURLRequestJobTest, DeliverErrorResponse) {
    838   RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverErrorResponse);
    839 }
    840 
    841 TEST_F(AppCacheURLRequestJobTest, DeliverSmallAppCachedResponse) {
    842   RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverSmallAppCachedResponse);
    843 }
    844 
    845 TEST_F(AppCacheURLRequestJobTest, DeliverLargeAppCachedResponse) {
    846   RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverLargeAppCachedResponse);
    847 }
    848 
    849 TEST_F(AppCacheURLRequestJobTest, DeliverPartialResponse) {
    850   RunTestOnIOThread(&AppCacheURLRequestJobTest::DeliverPartialResponse);
    851 }
    852 
    853 TEST_F(AppCacheURLRequestJobTest, CancelRequest) {
    854   RunTestOnIOThread(&AppCacheURLRequestJobTest::CancelRequest);
    855 }
    856 
    857 TEST_F(AppCacheURLRequestJobTest, CancelRequestWithIOPending) {
    858   RunTestOnIOThread(&AppCacheURLRequestJobTest::CancelRequestWithIOPending);
    859 }
    860 
    861 }  // namespace
    862 
    863 }  // namespace content
    864