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