Home | History | Annotate | Download | only in appcache
      1 // Copyright (c) 2012 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 <string>
      7 #include <utility>
      8 
      9 #include "base/bind.h"
     10 #include "base/bind_helpers.h"
     11 #include "base/callback.h"
     12 #include "base/compiler_specific.h"
     13 #include "base/pickle.h"
     14 #include "base/synchronization/waitable_event.h"
     15 #include "base/threading/thread.h"
     16 #include "net/base/io_buffer.h"
     17 #include "net/base/net_errors.h"
     18 #include "net/http/http_response_headers.h"
     19 #include "testing/gtest/include/gtest/gtest.h"
     20 #include "webkit/browser/appcache/appcache_response.h"
     21 #include "webkit/browser/appcache/mock_appcache_service.h"
     22 
     23 using net::IOBuffer;
     24 using net::WrappedIOBuffer;
     25 
     26 namespace appcache {
     27 
     28 static const int kNumBlocks = 4;
     29 static const int kBlockSize = 1024;
     30 static const int kNoSuchResponseId = 123;
     31 
     32 class AppCacheResponseTest : public testing::Test {
     33  public:
     34 
     35   // Test Harness -------------------------------------------------------------
     36 
     37   // Helper class used to verify test results
     38   class MockStorageDelegate : public AppCacheStorage::Delegate {
     39    public:
     40     explicit MockStorageDelegate(AppCacheResponseTest* test)
     41         : loaded_info_id_(0), test_(test) {
     42     }
     43 
     44     virtual void OnResponseInfoLoaded(AppCacheResponseInfo* info,
     45                                       int64 response_id) OVERRIDE {
     46       loaded_info_ = info;
     47       loaded_info_id_ = response_id;
     48       test_->ScheduleNextTask();
     49     }
     50 
     51     scoped_refptr<AppCacheResponseInfo> loaded_info_;
     52     int64 loaded_info_id_;
     53     AppCacheResponseTest* test_;
     54   };
     55 
     56   // Helper callback to run a test on our io_thread. The io_thread is spun up
     57   // once and reused for all tests.
     58   template <class Method>
     59   void MethodWrapper(Method method) {
     60     SetUpTest();
     61     (this->*method)();
     62   }
     63 
     64   static void SetUpTestCase() {
     65     io_thread_.reset(new base::Thread("AppCacheResponseTest Thread"));
     66     base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
     67     io_thread_->StartWithOptions(options);
     68   }
     69 
     70   static void TearDownTestCase() {
     71     io_thread_.reset(NULL);
     72   }
     73 
     74   AppCacheResponseTest() {}
     75 
     76   template <class Method>
     77   void RunTestOnIOThread(Method method) {
     78     test_finished_event_ .reset(new base::WaitableEvent(false, false));
     79     io_thread_->message_loop()->PostTask(
     80         FROM_HERE, base::Bind(&AppCacheResponseTest::MethodWrapper<Method>,
     81                               base::Unretained(this), method));
     82     test_finished_event_->Wait();
     83   }
     84 
     85   void SetUpTest() {
     86     DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
     87     DCHECK(task_stack_.empty());
     88     storage_delegate_.reset(new MockStorageDelegate(this));
     89     service_.reset(new MockAppCacheService());
     90     expected_read_result_ = 0;
     91     expected_write_result_ = 0;
     92     written_response_id_ = 0;
     93     should_delete_reader_in_completion_callback_ = false;
     94     should_delete_writer_in_completion_callback_ = false;
     95     reader_deletion_count_down_ = 0;
     96     writer_deletion_count_down_ = 0;
     97     read_callback_was_called_ = false;
     98     write_callback_was_called_ = false;
     99   }
    100 
    101   void TearDownTest() {
    102     DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
    103     while (!task_stack_.empty())
    104       task_stack_.pop();
    105 
    106     reader_.reset();
    107     read_buffer_ = NULL;
    108     read_info_buffer_ = NULL;
    109     writer_.reset();
    110     write_buffer_ = NULL;
    111     write_info_buffer_ = NULL;
    112     storage_delegate_.reset();
    113     service_.reset();
    114   }
    115 
    116   void TestFinished() {
    117     // We unwind the stack prior to finishing up to let stack
    118     // based objects get deleted.
    119     DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
    120     base::MessageLoop::current()->PostTask(
    121         FROM_HERE, base::Bind(&AppCacheResponseTest::TestFinishedUnwound,
    122                               base::Unretained(this)));
    123   }
    124 
    125   void TestFinishedUnwound() {
    126     TearDownTest();
    127     test_finished_event_->Signal();
    128   }
    129 
    130   void PushNextTask(const base::Closure& task) {
    131     task_stack_.push(std::pair<base::Closure, bool>(task, false));
    132   }
    133 
    134   void PushNextTaskAsImmediate(const base::Closure& task) {
    135     task_stack_.push(std::pair<base::Closure, bool>(task, true));
    136   }
    137 
    138   void ScheduleNextTask() {
    139     DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
    140     if (task_stack_.empty()) {
    141       TestFinished();
    142       return;
    143     }
    144     base::Closure task = task_stack_.top().first;
    145     bool immediate = task_stack_.top().second;
    146     task_stack_.pop();
    147     if (immediate)
    148       task.Run();
    149     else
    150       base::MessageLoop::current()->PostTask(FROM_HERE, task);
    151   }
    152 
    153   // Wrappers to call AppCacheResponseReader/Writer Read and Write methods
    154 
    155   void WriteBasicResponse() {
    156     static const char kHttpHeaders[] =
    157         "HTTP/1.0 200 OK\0Content-Length: 5\0\0";
    158     static const char* kHttpBody = "Hello";
    159     scoped_refptr<IOBuffer> body(new WrappedIOBuffer(kHttpBody));
    160     std::string raw_headers(kHttpHeaders, arraysize(kHttpHeaders));
    161     WriteResponse(
    162         MakeHttpResponseInfo(raw_headers), body.get(), strlen(kHttpBody));
    163   }
    164 
    165   int basic_response_size() { return 5; }  // should match kHttpBody above
    166 
    167   void WriteResponse(net::HttpResponseInfo* head,
    168                      IOBuffer* body, int body_len) {
    169     DCHECK(body);
    170     scoped_refptr<IOBuffer> body_ref(body);
    171     PushNextTask(base::Bind(&AppCacheResponseTest::WriteResponseBody,
    172                             base::Unretained(this), body_ref, body_len));
    173     WriteResponseHead(head);
    174   }
    175 
    176   void WriteResponseHead(net::HttpResponseInfo* head) {
    177     EXPECT_FALSE(writer_->IsWritePending());
    178     expected_write_result_ = GetHttpResponseInfoSize(head);
    179     write_info_buffer_ = new HttpResponseInfoIOBuffer(head);
    180     writer_->WriteInfo(write_info_buffer_.get(),
    181                        base::Bind(&AppCacheResponseTest::OnWriteInfoComplete,
    182                                   base::Unretained(this)));
    183   }
    184 
    185   void WriteResponseBody(scoped_refptr<IOBuffer> io_buffer, int buf_len) {
    186     EXPECT_FALSE(writer_->IsWritePending());
    187     write_buffer_ = io_buffer;
    188     expected_write_result_ = buf_len;
    189     writer_->WriteData(write_buffer_.get(),
    190                        buf_len,
    191                        base::Bind(&AppCacheResponseTest::OnWriteComplete,
    192                                   base::Unretained(this)));
    193   }
    194 
    195   void ReadResponseBody(scoped_refptr<IOBuffer> io_buffer, int buf_len) {
    196     EXPECT_FALSE(reader_->IsReadPending());
    197     read_buffer_ = io_buffer;
    198     expected_read_result_ = buf_len;
    199     reader_->ReadData(read_buffer_.get(),
    200                       buf_len,
    201                       base::Bind(&AppCacheResponseTest::OnReadComplete,
    202                                  base::Unretained(this)));
    203   }
    204 
    205   // AppCacheResponseReader / Writer completion callbacks
    206 
    207   void OnWriteInfoComplete(int result) {
    208     EXPECT_FALSE(writer_->IsWritePending());
    209     EXPECT_EQ(expected_write_result_, result);
    210     ScheduleNextTask();
    211   }
    212 
    213   void OnWriteComplete(int result) {
    214     EXPECT_FALSE(writer_->IsWritePending());
    215     write_callback_was_called_ = true;
    216     EXPECT_EQ(expected_write_result_, result);
    217     if (should_delete_writer_in_completion_callback_ &&
    218         --writer_deletion_count_down_ == 0) {
    219       writer_.reset();
    220     }
    221     ScheduleNextTask();
    222   }
    223 
    224   void OnReadInfoComplete(int result) {
    225     EXPECT_FALSE(reader_->IsReadPending());
    226     EXPECT_EQ(expected_read_result_, result);
    227     ScheduleNextTask();
    228   }
    229 
    230   void OnReadComplete(int result) {
    231     EXPECT_FALSE(reader_->IsReadPending());
    232     read_callback_was_called_ = true;
    233     EXPECT_EQ(expected_read_result_, result);
    234     if (should_delete_reader_in_completion_callback_ &&
    235         --reader_deletion_count_down_ == 0) {
    236       reader_.reset();
    237     }
    238     ScheduleNextTask();
    239   }
    240 
    241   // Helpers to work with HttpResponseInfo objects
    242 
    243   net::HttpResponseInfo* MakeHttpResponseInfo(const std::string& raw_headers) {
    244     net::HttpResponseInfo* info = new net::HttpResponseInfo;
    245     info->request_time = base::Time::Now();
    246     info->response_time = base::Time::Now();
    247     info->was_cached = false;
    248     info->headers = new net::HttpResponseHeaders(raw_headers);
    249     return info;
    250   }
    251 
    252   int GetHttpResponseInfoSize(const net::HttpResponseInfo* info) {
    253     Pickle pickle;
    254     return PickleHttpResonseInfo(&pickle, info);
    255   }
    256 
    257   bool CompareHttpResponseInfos(const net::HttpResponseInfo* info1,
    258                                 const net::HttpResponseInfo* info2) {
    259     Pickle pickle1;
    260     Pickle pickle2;
    261     PickleHttpResonseInfo(&pickle1, info1);
    262     PickleHttpResonseInfo(&pickle2, info2);
    263     return (pickle1.size() == pickle2.size()) &&
    264            (0 == memcmp(pickle1.data(), pickle2.data(), pickle1.size()));
    265   }
    266 
    267   int PickleHttpResonseInfo(Pickle* pickle, const net::HttpResponseInfo* info) {
    268     const bool kSkipTransientHeaders = true;
    269     const bool kTruncated = false;
    270     info->Persist(pickle, kSkipTransientHeaders, kTruncated);
    271     return pickle->size();
    272   }
    273 
    274   // Helpers to fill and verify blocks of memory with a value
    275 
    276   void FillData(char value, char* data, int data_len) {
    277     memset(data, value, data_len);
    278   }
    279 
    280   bool CheckData(char value, const char* data, int data_len) {
    281     for (int i = 0; i < data_len; ++i, ++data) {
    282       if (*data != value)
    283         return false;
    284     }
    285     return true;
    286   }
    287 
    288   // Individual Tests ---------------------------------------------------------
    289   // Most of the individual tests involve multiple async steps. Each test
    290   // is delineated with a section header.
    291 
    292 
    293   // ReadNonExistentResponse -------------------------------------------
    294   void ReadNonExistentResponse() {
    295     // 1. Attempt to ReadInfo
    296     // 2. Attempt to ReadData
    297 
    298     reader_.reset(service_->storage()->CreateResponseReader(
    299         GURL(), 0, kNoSuchResponseId));
    300 
    301     // Push tasks in reverse order
    302     PushNextTask(base::Bind(&AppCacheResponseTest::ReadNonExistentData,
    303                             base::Unretained(this)));
    304     PushNextTask(base::Bind(&AppCacheResponseTest::ReadNonExistentInfo,
    305                             base::Unretained(this)));
    306     ScheduleNextTask();
    307   }
    308 
    309   void ReadNonExistentInfo() {
    310     EXPECT_FALSE(reader_->IsReadPending());
    311     read_info_buffer_ = new HttpResponseInfoIOBuffer();
    312     reader_->ReadInfo(read_info_buffer_.get(),
    313                       base::Bind(&AppCacheResponseTest::OnReadInfoComplete,
    314                                  base::Unretained(this)));
    315     EXPECT_TRUE(reader_->IsReadPending());
    316     expected_read_result_ = net::ERR_CACHE_MISS;
    317   }
    318 
    319   void ReadNonExistentData() {
    320     EXPECT_FALSE(reader_->IsReadPending());
    321     read_buffer_ = new IOBuffer(kBlockSize);
    322     reader_->ReadData(read_buffer_.get(),
    323                       kBlockSize,
    324                       base::Bind(&AppCacheResponseTest::OnReadComplete,
    325                                  base::Unretained(this)));
    326     EXPECT_TRUE(reader_->IsReadPending());
    327     expected_read_result_ = net::ERR_CACHE_MISS;
    328   }
    329 
    330   // LoadResponseInfo_Miss ----------------------------------------------------
    331   void LoadResponseInfo_Miss() {
    332     PushNextTask(base::Bind(&AppCacheResponseTest::LoadResponseInfo_Miss_Verify,
    333                             base::Unretained(this)));
    334     service_->storage()->LoadResponseInfo(GURL(), 0, kNoSuchResponseId,
    335                                           storage_delegate_.get());
    336   }
    337 
    338   void LoadResponseInfo_Miss_Verify() {
    339     EXPECT_EQ(kNoSuchResponseId, storage_delegate_->loaded_info_id_);
    340     EXPECT_TRUE(!storage_delegate_->loaded_info_.get());
    341     TestFinished();
    342   }
    343 
    344   // LoadResponseInfo_Hit ----------------------------------------------------
    345   void LoadResponseInfo_Hit() {
    346     // This tests involves multiple async steps.
    347     // 1. Write a response headers and body to storage
    348     //   a. headers
    349     //   b. body
    350     // 2. Use LoadResponseInfo to read the response headers back out
    351     PushNextTask(base::Bind(&AppCacheResponseTest::LoadResponseInfo_Hit_Step2,
    352                             base::Unretained(this)));
    353     writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0));
    354     written_response_id_ = writer_->response_id();
    355     WriteBasicResponse();
    356   }
    357 
    358   void LoadResponseInfo_Hit_Step2() {
    359     writer_.reset();
    360     PushNextTask(base::Bind(&AppCacheResponseTest::LoadResponseInfo_Hit_Verify,
    361                             base::Unretained(this)));
    362     service_->storage()->LoadResponseInfo(GURL(), 0, written_response_id_,
    363                                           storage_delegate_.get());
    364   }
    365 
    366   void LoadResponseInfo_Hit_Verify() {
    367     EXPECT_EQ(written_response_id_, storage_delegate_->loaded_info_id_);
    368     EXPECT_TRUE(storage_delegate_->loaded_info_.get());
    369     EXPECT_TRUE(CompareHttpResponseInfos(
    370         write_info_buffer_->http_info.get(),
    371         storage_delegate_->loaded_info_->http_response_info()));
    372     EXPECT_EQ(basic_response_size(),
    373               storage_delegate_->loaded_info_->response_data_size());
    374     TestFinished();
    375   }
    376 
    377   // AmountWritten ----------------------------------------------------
    378 
    379   void AmountWritten() {
    380     static const char kHttpHeaders[] =
    381         "HTTP/1.0 200 OK\0\0";
    382     std::string raw_headers(kHttpHeaders, arraysize(kHttpHeaders));
    383     net::HttpResponseInfo* head = MakeHttpResponseInfo(raw_headers);
    384     int expected_amount_written =
    385         GetHttpResponseInfoSize(head) + kNumBlocks * kBlockSize;
    386 
    387     // Push tasks in reverse order.
    388     PushNextTask(base::Bind(&AppCacheResponseTest::Verify_AmountWritten,
    389                             base::Unretained(this), expected_amount_written));
    390     for (int i = 0; i < kNumBlocks; ++i) {
    391       PushNextTask(base::Bind(&AppCacheResponseTest::WriteOneBlock,
    392                               base::Unretained(this), kNumBlocks - i));
    393     }
    394     PushNextTask(base::Bind(&AppCacheResponseTest::WriteResponseHead,
    395                             base::Unretained(this), head));
    396 
    397     writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0));
    398     written_response_id_ = writer_->response_id();
    399     ScheduleNextTask();
    400   }
    401 
    402   void Verify_AmountWritten(int expected_amount_written) {
    403     EXPECT_EQ(expected_amount_written, writer_->amount_written());
    404     TestFinished();
    405   }
    406 
    407 
    408   // WriteThenVariouslyReadResponse -------------------------------------------
    409 
    410   void WriteThenVariouslyReadResponse() {
    411     // This tests involves multiple async steps.
    412     // 1. First, write a large body using multiple writes, we don't bother
    413     //    with a response head for this test.
    414     // 2. Read the entire body, using multiple reads
    415     // 3. Read the entire body, using one read.
    416     // 4. Attempt to read beyond the EOF.
    417     // 5. Read just a range.
    418     // 6. Attempt to read beyond EOF of a range.
    419 
    420     // Push tasks in reverse order
    421     PushNextTask(base::Bind(&AppCacheResponseTest::ReadRangeFullyBeyondEOF,
    422                             base::Unretained(this)));
    423     PushNextTask(base::Bind(&AppCacheResponseTest::ReadRangePartiallyBeyondEOF,
    424                             base::Unretained(this)));
    425     PushNextTask(base::Bind(&AppCacheResponseTest::ReadPastEOF,
    426                             base::Unretained(this)));
    427     PushNextTask(base::Bind(&AppCacheResponseTest::ReadRange,
    428                             base::Unretained(this)));
    429     PushNextTask(base::Bind(&AppCacheResponseTest::ReadPastEOF,
    430                             base::Unretained(this)));
    431     PushNextTask(base::Bind(&AppCacheResponseTest::ReadAllAtOnce,
    432                             base::Unretained(this)));
    433     PushNextTask(base::Bind(&AppCacheResponseTest::ReadInBlocks,
    434                             base::Unretained(this)));
    435     PushNextTask(base::Bind(&AppCacheResponseTest::WriteOutBlocks,
    436                             base::Unretained(this)));
    437 
    438     // Get them going.
    439     ScheduleNextTask();
    440   }
    441 
    442   void WriteOutBlocks() {
    443     writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0));
    444     written_response_id_ = writer_->response_id();
    445     for (int i = 0; i < kNumBlocks; ++i) {
    446       PushNextTask(base::Bind(&AppCacheResponseTest::WriteOneBlock,
    447                               base::Unretained(this), kNumBlocks - i));
    448     }
    449     ScheduleNextTask();
    450   }
    451 
    452   void WriteOneBlock(int block_number) {
    453     scoped_refptr<IOBuffer> io_buffer(
    454         new IOBuffer(kBlockSize));
    455     FillData(block_number, io_buffer->data(), kBlockSize);
    456     WriteResponseBody(io_buffer, kBlockSize);
    457   }
    458 
    459   void ReadInBlocks() {
    460     writer_.reset();
    461     reader_.reset(service_->storage()->CreateResponseReader(
    462         GURL(), 0, written_response_id_));
    463     for (int i = 0; i < kNumBlocks; ++i) {
    464       PushNextTask(base::Bind(&AppCacheResponseTest::ReadOneBlock,
    465                               base::Unretained(this), kNumBlocks - i));
    466     }
    467     ScheduleNextTask();
    468   }
    469 
    470   void ReadOneBlock(int block_number) {
    471     PushNextTask(base::Bind(&AppCacheResponseTest::VerifyOneBlock,
    472                             base::Unretained(this), block_number));
    473     ReadResponseBody(new IOBuffer(kBlockSize), kBlockSize);
    474   }
    475 
    476   void VerifyOneBlock(int block_number) {
    477     EXPECT_TRUE(CheckData(block_number, read_buffer_->data(), kBlockSize));
    478     ScheduleNextTask();
    479   }
    480 
    481   void ReadAllAtOnce() {
    482     PushNextTask(base::Bind(&AppCacheResponseTest::VerifyAllAtOnce,
    483                             base::Unretained(this)));
    484     reader_.reset(service_->storage()->CreateResponseReader(
    485         GURL(), 0, written_response_id_));
    486     int big_size = kNumBlocks * kBlockSize;
    487     ReadResponseBody(new IOBuffer(big_size), big_size);
    488   }
    489 
    490   void VerifyAllAtOnce() {
    491     char* p = read_buffer_->data();
    492     for (int i = 0; i < kNumBlocks; ++i, p += kBlockSize)
    493       EXPECT_TRUE(CheckData(i + 1, p, kBlockSize));
    494     ScheduleNextTask();
    495   }
    496 
    497   void ReadPastEOF() {
    498     EXPECT_FALSE(reader_->IsReadPending());
    499     read_buffer_ = new IOBuffer(kBlockSize);
    500     expected_read_result_ = 0;
    501     reader_->ReadData(read_buffer_.get(),
    502                       kBlockSize,
    503                       base::Bind(&AppCacheResponseTest::OnReadComplete,
    504                                  base::Unretained(this)));
    505   }
    506 
    507   void ReadRange() {
    508     PushNextTask(base::Bind(&AppCacheResponseTest::VerifyRange,
    509                             base::Unretained(this)));
    510     reader_.reset(service_->storage()->CreateResponseReader(
    511         GURL(), 0, written_response_id_));
    512     reader_->SetReadRange(kBlockSize, kBlockSize);
    513     ReadResponseBody(new IOBuffer(kBlockSize), kBlockSize);
    514   }
    515 
    516   void VerifyRange() {
    517     EXPECT_TRUE(CheckData(2, read_buffer_->data(), kBlockSize));
    518     ScheduleNextTask();  // ReadPastEOF is scheduled next
    519   }
    520 
    521   void ReadRangePartiallyBeyondEOF() {
    522     PushNextTask(base::Bind(&AppCacheResponseTest::VerifyRangeBeyondEOF,
    523                             base::Unretained(this)));
    524     reader_.reset(service_->storage()->CreateResponseReader(
    525         GURL(), 0, written_response_id_));
    526     reader_->SetReadRange(kBlockSize, kNumBlocks * kBlockSize);
    527     ReadResponseBody(new IOBuffer(kNumBlocks * kBlockSize),
    528                      kNumBlocks * kBlockSize);
    529     expected_read_result_ = (kNumBlocks - 1) * kBlockSize;
    530   }
    531 
    532   void VerifyRangeBeyondEOF() {
    533     // Just verify the first 1k
    534     VerifyRange();
    535   }
    536 
    537   void ReadRangeFullyBeyondEOF() {
    538     reader_.reset(service_->storage()->CreateResponseReader(
    539         GURL(), 0, written_response_id_));
    540     reader_->SetReadRange((kNumBlocks * kBlockSize) + 1, kBlockSize);
    541     ReadResponseBody(new IOBuffer(kBlockSize), kBlockSize);
    542     expected_read_result_ = 0;
    543   }
    544 
    545   // IOChaining -------------------------------------------
    546   void IOChaining() {
    547     // 1. Write several blocks out initiating the subsequent write
    548     //    from within the completion callback of the previous write.
    549     // 2. Read and verify several blocks in similarly chaining reads.
    550 
    551     // Push tasks in reverse order
    552     PushNextTaskAsImmediate(
    553         base::Bind(&AppCacheResponseTest::ReadInBlocksImmediately,
    554                    base::Unretained(this)));
    555     PushNextTaskAsImmediate(
    556         base::Bind(&AppCacheResponseTest::WriteOutBlocksImmediately,
    557                    base::Unretained(this)));
    558 
    559     // Get them going.
    560     ScheduleNextTask();
    561   }
    562 
    563   void WriteOutBlocksImmediately() {
    564     writer_.reset(service_->storage()->CreateResponseWriter(GURL(), 0));
    565     written_response_id_ = writer_->response_id();
    566     for (int i = 0; i < kNumBlocks; ++i) {
    567       PushNextTaskAsImmediate(
    568           base::Bind(&AppCacheResponseTest::WriteOneBlock,
    569                      base::Unretained(this), kNumBlocks - i));
    570     }
    571     ScheduleNextTask();
    572   }
    573 
    574   void ReadInBlocksImmediately() {
    575     writer_.reset();
    576     reader_.reset(service_->storage()->CreateResponseReader(
    577         GURL(), 0, written_response_id_));
    578     for (int i = 0; i < kNumBlocks; ++i) {
    579       PushNextTaskAsImmediate(
    580           base::Bind(&AppCacheResponseTest::ReadOneBlockImmediately,
    581                      base::Unretained(this),
    582           kNumBlocks - i));
    583     }
    584     ScheduleNextTask();
    585   }
    586 
    587   void ReadOneBlockImmediately(int block_number) {
    588     PushNextTaskAsImmediate(base::Bind(&AppCacheResponseTest::VerifyOneBlock,
    589                                        base::Unretained(this), block_number));
    590     ReadResponseBody(new IOBuffer(kBlockSize), kBlockSize);
    591   }
    592 
    593   // DeleteWithinCallbacks -------------------------------------------
    594   void DeleteWithinCallbacks() {
    595     // 1. Write out a few blocks normally, and upon
    596     //    completion of the last write, delete the writer.
    597     // 2. Read in a few blocks normally, and upon completion
    598     //    of the last read, delete the reader.
    599 
    600     should_delete_reader_in_completion_callback_ = true;
    601     reader_deletion_count_down_ = kNumBlocks;
    602     should_delete_writer_in_completion_callback_ = true;
    603     writer_deletion_count_down_ = kNumBlocks;
    604 
    605     PushNextTask(base::Bind(&AppCacheResponseTest::ReadInBlocks,
    606                             base::Unretained(this)));
    607     PushNextTask(base::Bind(&AppCacheResponseTest::WriteOutBlocks,
    608                             base::Unretained(this)));
    609     ScheduleNextTask();
    610   }
    611 
    612   // DeleteWithIOPending -------------------------------------------
    613   void DeleteWithIOPending() {
    614     // 1. Write a few blocks normally.
    615     // 2. Start a write, delete with it pending.
    616     // 3. Start a read, delete with it pending.
    617     PushNextTask(base::Bind(&AppCacheResponseTest::ReadThenDelete,
    618                             base::Unretained(this)));
    619     PushNextTask(base::Bind(&AppCacheResponseTest::WriteThenDelete,
    620                             base::Unretained(this)));
    621     PushNextTask(base::Bind(&AppCacheResponseTest::WriteOutBlocks,
    622                             base::Unretained(this)));
    623     ScheduleNextTask();
    624   }
    625 
    626   void WriteThenDelete() {
    627     write_callback_was_called_ = false;
    628     WriteOneBlock(5);
    629     EXPECT_TRUE(writer_->IsWritePending());
    630     writer_.reset();
    631     ScheduleNextTask();
    632   }
    633 
    634   void ReadThenDelete() {
    635     read_callback_was_called_ = false;
    636     reader_.reset(service_->storage()->CreateResponseReader(
    637         GURL(), 0, written_response_id_));
    638     ReadResponseBody(new IOBuffer(kBlockSize), kBlockSize);
    639     EXPECT_TRUE(reader_->IsReadPending());
    640     reader_.reset();
    641 
    642     // Wait a moment to verify no callbacks.
    643     base::MessageLoop::current()->PostDelayedTask(
    644         FROM_HERE, base::Bind(&AppCacheResponseTest::VerifyNoCallbacks,
    645                               base::Unretained(this)),
    646         base::TimeDelta::FromMilliseconds(10));
    647   }
    648 
    649   void VerifyNoCallbacks() {
    650     EXPECT_TRUE(!write_callback_was_called_);
    651     EXPECT_TRUE(!read_callback_was_called_);
    652     TestFinished();
    653   }
    654 
    655   // Data members
    656 
    657   scoped_ptr<base::WaitableEvent> test_finished_event_;
    658   scoped_ptr<MockStorageDelegate> storage_delegate_;
    659   scoped_ptr<MockAppCacheService> service_;
    660   std::stack<std::pair<base::Closure, bool> > task_stack_;
    661 
    662   scoped_ptr<AppCacheResponseReader> reader_;
    663   scoped_refptr<HttpResponseInfoIOBuffer> read_info_buffer_;
    664   scoped_refptr<IOBuffer> read_buffer_;
    665   int expected_read_result_;
    666   bool should_delete_reader_in_completion_callback_;
    667   int reader_deletion_count_down_;
    668   bool read_callback_was_called_;
    669 
    670   int64 written_response_id_;
    671   scoped_ptr<AppCacheResponseWriter> writer_;
    672   scoped_refptr<HttpResponseInfoIOBuffer> write_info_buffer_;
    673   scoped_refptr<IOBuffer> write_buffer_;
    674   int expected_write_result_;
    675   bool should_delete_writer_in_completion_callback_;
    676   int writer_deletion_count_down_;
    677   bool write_callback_was_called_;
    678 
    679   static scoped_ptr<base::Thread> io_thread_;
    680 };
    681 
    682 // static
    683 scoped_ptr<base::Thread> AppCacheResponseTest::io_thread_;
    684 
    685 TEST_F(AppCacheResponseTest, ReadNonExistentResponse) {
    686   RunTestOnIOThread(&AppCacheResponseTest::ReadNonExistentResponse);
    687 }
    688 
    689 TEST_F(AppCacheResponseTest, LoadResponseInfo_Miss) {
    690   RunTestOnIOThread(&AppCacheResponseTest::LoadResponseInfo_Miss);
    691 }
    692 
    693 TEST_F(AppCacheResponseTest, LoadResponseInfo_Hit) {
    694   RunTestOnIOThread(&AppCacheResponseTest::LoadResponseInfo_Hit);
    695 }
    696 
    697 TEST_F(AppCacheResponseTest, AmountWritten) {
    698   RunTestOnIOThread(&AppCacheResponseTest::AmountWritten);
    699 }
    700 
    701 TEST_F(AppCacheResponseTest, WriteThenVariouslyReadResponse) {
    702   RunTestOnIOThread(&AppCacheResponseTest::WriteThenVariouslyReadResponse);
    703 }
    704 
    705 TEST_F(AppCacheResponseTest, IOChaining) {
    706   RunTestOnIOThread(&AppCacheResponseTest::IOChaining);
    707 }
    708 
    709 TEST_F(AppCacheResponseTest, DeleteWithinCallbacks) {
    710   RunTestOnIOThread(&AppCacheResponseTest::DeleteWithinCallbacks);
    711 }
    712 
    713 TEST_F(AppCacheResponseTest, DeleteWithIOPending) {
    714   RunTestOnIOThread(&AppCacheResponseTest::DeleteWithIOPending);
    715 }
    716 
    717 }  // namespace appcache
    718