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