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