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