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