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