- add sources.
[platform/framework/web/crosswalk.git] / src / webkit / browser / appcache / appcache_request_handler_unittest.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <stack>
6 #include <string>
7 #include <vector>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/callback.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/synchronization/waitable_event.h"
14 #include "base/threading/thread.h"
15 #include "net/base/net_errors.h"
16 #include "net/base/request_priority.h"
17 #include "net/http/http_response_headers.h"
18 #include "net/url_request/url_request.h"
19 #include "net/url_request/url_request_context.h"
20 #include "net/url_request/url_request_error_job.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 #include "webkit/browser/appcache/appcache.h"
23 #include "webkit/browser/appcache/appcache_backend_impl.h"
24 #include "webkit/browser/appcache/appcache_request_handler.h"
25 #include "webkit/browser/appcache/appcache_url_request_job.h"
26 #include "webkit/browser/appcache/mock_appcache_policy.h"
27 #include "webkit/browser/appcache/mock_appcache_service.h"
28
29 namespace appcache {
30
31 static const int kMockProcessId = 1;
32
33 class AppCacheRequestHandlerTest : public testing::Test {
34  public:
35   class MockFrontend : public AppCacheFrontend {
36    public:
37     virtual void OnCacheSelected(
38         int host_id, const appcache::AppCacheInfo& info) OVERRIDE {}
39
40     virtual void OnStatusChanged(const std::vector<int>& host_ids,
41                                  appcache::Status status) OVERRIDE {}
42
43     virtual void OnEventRaised(const std::vector<int>& host_ids,
44                                appcache::EventID event_id) OVERRIDE {}
45
46     virtual void OnErrorEventRaised(const std::vector<int>& host_ids,
47                                     const std::string& message) OVERRIDE {}
48
49     virtual void OnProgressEventRaised(const std::vector<int>& host_ids,
50                                        const GURL& url,
51                                        int num_total,
52                                        int num_complete) OVERRIDE {
53     }
54
55     virtual void OnLogMessage(int host_id,
56                               appcache::LogLevel log_level,
57                               const std::string& message) OVERRIDE {
58     }
59
60     virtual void OnContentBlocked(int host_id,
61                                   const GURL& manifest_url) OVERRIDE {}
62   };
63
64   // Helper callback to run a test on our io_thread. The io_thread is spun up
65   // once and reused for all tests.
66   template <class Method>
67   void MethodWrapper(Method method) {
68     SetUpTest();
69     (this->*method)();
70   }
71
72   // Subclasses to simulate particular responses so test cases can
73   // exercise fallback code paths.
74
75   class MockURLRequestDelegate : public net::URLRequest::Delegate {
76     virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE {}
77     virtual void OnReadCompleted(net::URLRequest* request,
78                                  int bytes_read) OVERRIDE {
79     }
80   };
81
82   class MockURLRequestJob : public net::URLRequestJob {
83    public:
84     MockURLRequestJob(net::URLRequest* request,
85                       net::NetworkDelegate* network_delegate,
86                       int response_code)
87         : net::URLRequestJob(request, network_delegate),
88           response_code_(response_code),
89           has_response_info_(false) {}
90     MockURLRequestJob(net::URLRequest* request,
91                       net::NetworkDelegate* network_delegate,
92                       const net::HttpResponseInfo& info)
93         : net::URLRequestJob(request, network_delegate),
94           response_code_(info.headers->response_code()),
95           has_response_info_(true),
96           response_info_(info) {}
97
98   protected:
99     virtual ~MockURLRequestJob() {}
100     virtual void Start() OVERRIDE {
101       NotifyHeadersComplete();
102     }
103     virtual int GetResponseCode() const OVERRIDE {
104       return response_code_;
105     }
106     virtual void GetResponseInfo(
107         net::HttpResponseInfo* info) OVERRIDE {
108       if (!has_response_info_)
109         return;
110       *info = response_info_;
111     }
112
113   private:
114     int response_code_;
115     bool has_response_info_;
116     net::HttpResponseInfo response_info_;
117   };
118
119   class MockURLRequest : public net::URLRequest {
120    public:
121     MockURLRequest(const GURL& url, net::URLRequestContext* context)
122         : net::URLRequest(url, net::DEFAULT_PRIORITY, NULL, context) {}
123
124     void SimulateResponseCode(int http_response_code) {
125       mock_factory_job_ = new MockURLRequestJob(
126           this, context()->network_delegate(), http_response_code);
127       Start();
128       DCHECK(!mock_factory_job_);
129       // All our simulation needs  to satisfy are the following two DCHECKs
130       DCHECK(status().is_success());
131       DCHECK_EQ(http_response_code, GetResponseCode());
132     }
133
134     void SimulateResponseInfo(const net::HttpResponseInfo& info) {
135       mock_factory_job_ =
136           new MockURLRequestJob(this, context()->network_delegate(), info);
137       set_delegate(&delegate_);  // needed to get the info back out
138       Start();
139       DCHECK(!mock_factory_job_);
140     }
141
142     MockURLRequestDelegate delegate_;
143   };
144
145   static net::URLRequestJob* MockHttpJobFactory(
146       net::URLRequest* request,
147       net::NetworkDelegate* network_delegate,
148       const std::string& scheme) {
149     if (mock_factory_job_) {
150       net::URLRequestJob* temp = mock_factory_job_;
151       mock_factory_job_ = NULL;
152       return temp;
153     } else {
154       // Some of these tests trigger UpdateJobs which start URLRequests.
155       // We short circuit those be returning error jobs.
156       return new net::URLRequestErrorJob(request,
157                                          network_delegate,
158                                          net::ERR_INTERNET_DISCONNECTED);
159     }
160   }
161
162   static void SetUpTestCase() {
163     io_thread_.reset(new base::Thread("AppCacheRequestHandlerTest Thread"));
164     base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
165     io_thread_->StartWithOptions(options);
166   }
167
168   static void TearDownTestCase() {
169     io_thread_.reset(NULL);
170   }
171
172   // Test harness --------------------------------------------------
173
174   AppCacheRequestHandlerTest() : host_(NULL), orig_http_factory_(NULL) {}
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,
181         base::Bind(&AppCacheRequestHandlerTest::MethodWrapper<Method>,
182                    base::Unretained(this), method));
183     test_finished_event_->Wait();
184   }
185
186   void SetUpTest() {
187     DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
188     orig_http_factory_ = net::URLRequest::Deprecated::RegisterProtocolFactory(
189         "http", MockHttpJobFactory);
190     mock_service_.reset(new MockAppCacheService);
191     mock_service_->set_request_context(&empty_context_);
192     mock_policy_.reset(new MockAppCachePolicy);
193     mock_service_->set_appcache_policy(mock_policy_.get());
194     mock_frontend_.reset(new MockFrontend);
195     backend_impl_.reset(new AppCacheBackendImpl);
196     backend_impl_->Initialize(mock_service_.get(), mock_frontend_.get(),
197                               kMockProcessId);
198     const int kHostId = 1;
199     backend_impl_->RegisterHost(kHostId);
200     host_ = backend_impl_->GetHost(kHostId);
201   }
202
203   void TearDownTest() {
204     DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
205     DCHECK(!mock_factory_job_);
206     net::URLRequest::Deprecated::RegisterProtocolFactory(
207         "http", orig_http_factory_);
208     orig_http_factory_ = NULL;
209     job_ = NULL;
210     handler_.reset();
211     request_.reset();
212     backend_impl_.reset();
213     mock_frontend_.reset();
214     mock_service_.reset();
215     mock_policy_.reset();
216     host_ = NULL;
217   }
218
219   void TestFinished() {
220     // We unwind the stack prior to finishing up to let stack
221     // based objects get deleted.
222     DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
223     base::MessageLoop::current()->PostTask(
224         FROM_HERE,
225         base::Bind(&AppCacheRequestHandlerTest::TestFinishedUnwound,
226                    base::Unretained(this)));
227   }
228
229   void TestFinishedUnwound() {
230     TearDownTest();
231     test_finished_event_->Signal();
232   }
233
234   void PushNextTask(const base::Closure& task) {
235     task_stack_.push(task);
236   }
237
238   void ScheduleNextTask() {
239     DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
240     if (task_stack_.empty()) {
241       TestFinished();
242       return;
243     }
244     base::MessageLoop::current()->PostTask(FROM_HERE, task_stack_.top());
245     task_stack_.pop();
246   }
247
248   // MainResource_Miss --------------------------------------------------
249
250   void MainResource_Miss() {
251     PushNextTask(
252         base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Miss,
253                    base::Unretained(this)));
254
255     request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
256     handler_.reset(host_->CreateRequestHandler(request_.get(),
257                                                ResourceType::MAIN_FRAME));
258     EXPECT_TRUE(handler_.get());
259
260     job_ = handler_->MaybeLoadResource(request_.get(),
261                                        request_->context()->network_delegate());
262     EXPECT_TRUE(job_.get());
263     EXPECT_TRUE(job_->is_waiting());
264
265     // We have to wait for completion of storage->FindResponseForMainRequest.
266     ScheduleNextTask();
267   }
268
269   void Verify_MainResource_Miss() {
270     EXPECT_FALSE(job_->is_waiting());
271     EXPECT_TRUE(job_->is_delivering_network_response());
272
273     int64 cache_id = kNoCacheId;
274     GURL manifest_url;
275     handler_->GetExtraResponseInfo(&cache_id, &manifest_url);
276     EXPECT_EQ(kNoCacheId, cache_id);
277     EXPECT_EQ(GURL(), manifest_url);
278     EXPECT_EQ(0, handler_->found_group_id_);
279
280     AppCacheURLRequestJob* fallback_job;
281     fallback_job = handler_->MaybeLoadFallbackForRedirect(
282         request_.get(),
283         request_->context()->network_delegate(),
284         GURL("http://blah/redirect"));
285     EXPECT_FALSE(fallback_job);
286     fallback_job = handler_->MaybeLoadFallbackForResponse(
287         request_.get(), request_->context()->network_delegate());
288     EXPECT_FALSE(fallback_job);
289
290     EXPECT_TRUE(host_->preferred_manifest_url().is_empty());
291
292     TestFinished();
293   }
294
295   // MainResource_Hit --------------------------------------------------
296
297   void MainResource_Hit() {
298     PushNextTask(
299         base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Hit,
300                    base::Unretained(this)));
301
302     request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
303     handler_.reset(host_->CreateRequestHandler(request_.get(),
304                                                ResourceType::MAIN_FRAME));
305     EXPECT_TRUE(handler_.get());
306
307     mock_storage()->SimulateFindMainResource(
308         AppCacheEntry(AppCacheEntry::EXPLICIT, 1),
309         GURL(), AppCacheEntry(),
310         1, 2, GURL("http://blah/manifest/"));
311
312     job_ = handler_->MaybeLoadResource(request_.get(),
313                                        request_->context()->network_delegate());
314     EXPECT_TRUE(job_.get());
315     EXPECT_TRUE(job_->is_waiting());
316
317     // We have to wait for completion of storage->FindResponseForMainRequest.
318     ScheduleNextTask();
319   }
320
321   void Verify_MainResource_Hit() {
322     EXPECT_FALSE(job_->is_waiting());
323     EXPECT_TRUE(job_->is_delivering_appcache_response());
324
325     int64 cache_id = kNoCacheId;
326     GURL manifest_url;
327     handler_->GetExtraResponseInfo(&cache_id, &manifest_url);
328     EXPECT_EQ(1, cache_id);
329     EXPECT_EQ(GURL("http://blah/manifest/"), manifest_url);
330     EXPECT_EQ(2, handler_->found_group_id_);
331
332     AppCacheURLRequestJob* fallback_job;
333     fallback_job = handler_->MaybeLoadFallbackForResponse(
334         request_.get(), request_->context()->network_delegate());
335     EXPECT_FALSE(fallback_job);
336
337     EXPECT_EQ(GURL("http://blah/manifest/"),
338               host_->preferred_manifest_url());
339
340     TestFinished();
341   }
342
343   // MainResource_Fallback --------------------------------------------------
344
345   void MainResource_Fallback() {
346     PushNextTask(
347         base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Fallback,
348                    base::Unretained(this)));
349
350     request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
351     handler_.reset(host_->CreateRequestHandler(request_.get(),
352                                                ResourceType::MAIN_FRAME));
353     EXPECT_TRUE(handler_.get());
354
355     mock_storage()->SimulateFindMainResource(
356         AppCacheEntry(),
357         GURL("http://blah/fallbackurl"),
358         AppCacheEntry(AppCacheEntry::EXPLICIT, 1),
359         1, 2, GURL("http://blah/manifest/"));
360
361     job_ = handler_->MaybeLoadResource(request_.get(),
362                                        request_->context()->network_delegate());
363     EXPECT_TRUE(job_.get());
364     EXPECT_TRUE(job_->is_waiting());
365
366     // We have to wait for completion of storage->FindResponseForMainRequest.
367     ScheduleNextTask();
368   }
369
370   void Verify_MainResource_Fallback() {
371     EXPECT_FALSE(job_->is_waiting());
372     EXPECT_TRUE(job_->is_delivering_network_response());
373
374     // When the request is restarted, the existing job is dropped so a
375     // real network job gets created. We expect NULL here which will cause
376     // the net library to create a real job.
377     job_ = handler_->MaybeLoadResource(request_.get(),
378                                        request_->context()->network_delegate());
379     EXPECT_FALSE(job_.get());
380
381     // Simulate an http error of the real network job.
382     request_->SimulateResponseCode(500);
383
384     job_ = handler_->MaybeLoadFallbackForResponse(
385         request_.get(), request_->context()->network_delegate());
386     EXPECT_TRUE(job_.get());
387     EXPECT_TRUE(job_->is_delivering_appcache_response());
388
389     int64 cache_id = kNoCacheId;
390     GURL manifest_url;
391     handler_->GetExtraResponseInfo(&cache_id, &manifest_url);
392     EXPECT_EQ(1, cache_id);
393     EXPECT_EQ(GURL("http://blah/manifest/"), manifest_url);
394     EXPECT_TRUE(host_->main_resource_was_namespace_entry_);
395     EXPECT_EQ(GURL("http://blah/fallbackurl"), host_->namespace_entry_url_);
396
397     EXPECT_EQ(GURL("http://blah/manifest/"),
398               host_->preferred_manifest_url());
399
400     TestFinished();
401   }
402
403   // MainResource_FallbackOverride --------------------------------------------
404
405   void MainResource_FallbackOverride() {
406     PushNextTask(base::Bind(
407         &AppCacheRequestHandlerTest::Verify_MainResource_FallbackOverride,
408         base::Unretained(this)));
409
410     request_.reset(new MockURLRequest(GURL("http://blah/fallback-override"),
411                                       &empty_context_));
412     handler_.reset(host_->CreateRequestHandler(request_.get(),
413                                                ResourceType::MAIN_FRAME));
414     EXPECT_TRUE(handler_.get());
415
416     mock_storage()->SimulateFindMainResource(
417         AppCacheEntry(),
418         GURL("http://blah/fallbackurl"),
419         AppCacheEntry(AppCacheEntry::EXPLICIT, 1),
420         1, 2, GURL("http://blah/manifest/"));
421
422     job_ = handler_->MaybeLoadResource(request_.get(),
423                                        request_->context()->network_delegate());
424     EXPECT_TRUE(job_.get());
425     EXPECT_TRUE(job_->is_waiting());
426
427     // We have to wait for completion of storage->FindResponseForMainRequest.
428     ScheduleNextTask();
429   }
430
431   void Verify_MainResource_FallbackOverride() {
432     EXPECT_FALSE(job_->is_waiting());
433     EXPECT_TRUE(job_->is_delivering_network_response());
434
435     // When the request is restarted, the existing job is dropped so a
436     // real network job gets created. We expect NULL here which will cause
437     // the net library to create a real job.
438     job_ = handler_->MaybeLoadResource(request_.get(),
439                                        request_->context()->network_delegate());
440     EXPECT_FALSE(job_.get());
441
442     // Simulate an http error of the real network job, but with custom
443     // headers that override the fallback behavior.
444     const char kOverrideHeaders[] =
445         "HTTP/1.1 404 BOO HOO\0"
446         "x-chromium-appcache-fallback-override: disallow-fallback\0"
447         "\0";
448     net::HttpResponseInfo info;
449     info.headers = new net::HttpResponseHeaders(
450         std::string(kOverrideHeaders, arraysize(kOverrideHeaders)));
451     request_->SimulateResponseInfo(info);
452
453     job_ = handler_->MaybeLoadFallbackForResponse(
454         request_.get(), request_->context()->network_delegate());
455     EXPECT_FALSE(job_.get());
456
457     TestFinished();
458   }
459
460   // SubResource_Miss_WithNoCacheSelected ----------------------------------
461
462   void SubResource_Miss_WithNoCacheSelected() {
463     request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
464     handler_.reset(host_->CreateRequestHandler(request_.get(),
465                                                ResourceType::SUB_RESOURCE));
466
467     // We avoid creating handler when possible, sub-resource requests are not
468     // subject to retrieval from an appcache when there's no associated cache.
469     EXPECT_FALSE(handler_.get());
470
471     TestFinished();
472   }
473
474   // SubResource_Miss_WithCacheSelected ----------------------------------
475
476   void SubResource_Miss_WithCacheSelected() {
477     // A sub-resource load where the resource is not in an appcache, or
478     // in a network or fallback namespace, should result in a failed request.
479     host_->AssociateCompleteCache(MakeNewCache());
480
481     request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
482     handler_.reset(host_->CreateRequestHandler(request_.get(),
483                                                ResourceType::SUB_RESOURCE));
484     EXPECT_TRUE(handler_.get());
485
486     job_ = handler_->MaybeLoadResource(request_.get(),
487                                        request_->context()->network_delegate());
488     EXPECT_TRUE(job_.get());
489     EXPECT_TRUE(job_->is_delivering_error_response());
490
491     AppCacheURLRequestJob* fallback_job;
492     fallback_job = handler_->MaybeLoadFallbackForRedirect(
493         request_.get(),
494         request_->context()->network_delegate(),
495         GURL("http://blah/redirect"));
496     EXPECT_FALSE(fallback_job);
497     fallback_job = handler_->MaybeLoadFallbackForResponse(
498         request_.get(), request_->context()->network_delegate());
499     EXPECT_FALSE(fallback_job);
500
501     TestFinished();
502   }
503
504   // SubResource_Miss_WithWaitForCacheSelection -----------------------------
505
506   void SubResource_Miss_WithWaitForCacheSelection() {
507     // Precondition, the host is waiting on cache selection.
508     scoped_refptr<AppCache> cache(MakeNewCache());
509     host_->pending_selected_cache_id_ = cache->cache_id();
510     host_->set_preferred_manifest_url(cache->owning_group()->manifest_url());
511
512     request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
513     handler_.reset(host_->CreateRequestHandler(request_.get(),
514                                                ResourceType::SUB_RESOURCE));
515     EXPECT_TRUE(handler_.get());
516     job_ = handler_->MaybeLoadResource(request_.get(),
517                                        request_->context()->network_delegate());
518     EXPECT_TRUE(job_.get());
519     EXPECT_TRUE(job_->is_waiting());
520
521     host_->FinishCacheSelection(cache.get(), NULL);
522     EXPECT_FALSE(job_->is_waiting());
523     EXPECT_TRUE(job_->is_delivering_error_response());
524
525     AppCacheURLRequestJob* fallback_job;
526     fallback_job = handler_->MaybeLoadFallbackForRedirect(
527         request_.get(),
528         request_->context()->network_delegate(),
529         GURL("http://blah/redirect"));
530     EXPECT_FALSE(fallback_job);
531     fallback_job = handler_->MaybeLoadFallbackForResponse(
532         request_.get(), request_->context()->network_delegate());
533     EXPECT_FALSE(fallback_job);
534
535     TestFinished();
536   }
537
538   // SubResource_Hit -----------------------------
539
540   void SubResource_Hit() {
541     host_->AssociateCompleteCache(MakeNewCache());
542
543     mock_storage()->SimulateFindSubResource(
544         AppCacheEntry(AppCacheEntry::EXPLICIT, 1), AppCacheEntry(), false);
545
546     request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
547     handler_.reset(host_->CreateRequestHandler(request_.get(),
548                                                ResourceType::SUB_RESOURCE));
549     EXPECT_TRUE(handler_.get());
550     job_ = handler_->MaybeLoadResource(request_.get(),
551                                        request_->context()->network_delegate());
552     EXPECT_TRUE(job_.get());
553     EXPECT_TRUE(job_->is_delivering_appcache_response());
554
555     AppCacheURLRequestJob* fallback_job;
556     fallback_job = handler_->MaybeLoadFallbackForRedirect(
557         request_.get(),
558         request_->context()->network_delegate(),
559         GURL("http://blah/redirect"));
560     EXPECT_FALSE(fallback_job);
561     fallback_job = handler_->MaybeLoadFallbackForResponse(
562         request_.get(), request_->context()->network_delegate());
563     EXPECT_FALSE(fallback_job);
564
565     TestFinished();
566   }
567
568   // SubResource_RedirectFallback -----------------------------
569
570   void SubResource_RedirectFallback() {
571     // Redirects to resources in the a different origin are subject to
572     // fallback namespaces.
573     host_->AssociateCompleteCache(MakeNewCache());
574
575     mock_storage()->SimulateFindSubResource(
576         AppCacheEntry(), AppCacheEntry(AppCacheEntry::EXPLICIT, 1), false);
577
578     request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
579     handler_.reset(host_->CreateRequestHandler(request_.get(),
580                                                ResourceType::SUB_RESOURCE));
581     EXPECT_TRUE(handler_.get());
582     job_ = handler_->MaybeLoadResource(request_.get(),
583                                        request_->context()->network_delegate());
584     EXPECT_FALSE(job_.get());
585
586     job_ = handler_->MaybeLoadFallbackForRedirect(
587         request_.get(),
588         request_->context()->network_delegate(),
589         GURL("http://not_blah/redirect"));
590     EXPECT_TRUE(job_.get());
591     EXPECT_TRUE(job_->is_delivering_appcache_response());
592
593     AppCacheURLRequestJob* fallback_job;
594     fallback_job = handler_->MaybeLoadFallbackForResponse(
595         request_.get(), request_->context()->network_delegate());
596     EXPECT_FALSE(fallback_job);
597
598     TestFinished();
599   }
600
601   // SubResource_NoRedirectFallback -----------------------------
602
603   void SubResource_NoRedirectFallback() {
604     // Redirects to resources in the same-origin are not subject to
605     // fallback namespaces.
606     host_->AssociateCompleteCache(MakeNewCache());
607
608     mock_storage()->SimulateFindSubResource(
609         AppCacheEntry(), AppCacheEntry(AppCacheEntry::EXPLICIT, 1), false);
610
611     request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
612     handler_.reset(host_->CreateRequestHandler(request_.get(),
613                                                ResourceType::SUB_RESOURCE));
614     EXPECT_TRUE(handler_.get());
615     job_ = handler_->MaybeLoadResource(request_.get(),
616                                        request_->context()->network_delegate());
617     EXPECT_FALSE(job_.get());
618
619     AppCacheURLRequestJob* fallback_job;
620     fallback_job = handler_->MaybeLoadFallbackForRedirect(
621         request_.get(),
622         request_->context()->network_delegate(),
623         GURL("http://blah/redirect"));
624     EXPECT_FALSE(fallback_job);
625
626     request_->SimulateResponseCode(200);
627     fallback_job = handler_->MaybeLoadFallbackForResponse(
628         request_.get(), request_->context()->network_delegate());
629     EXPECT_FALSE(fallback_job);
630
631     TestFinished();
632   }
633
634   // SubResource_Network -----------------------------
635
636   void SubResource_Network() {
637     // A sub-resource load where the resource is in a network namespace,
638     // should result in the system using a 'real' job to do the network
639     // retrieval.
640     host_->AssociateCompleteCache(MakeNewCache());
641
642     mock_storage()->SimulateFindSubResource(
643         AppCacheEntry(), AppCacheEntry(), true);
644
645     request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
646     handler_.reset(host_->CreateRequestHandler(request_.get(),
647                                                ResourceType::SUB_RESOURCE));
648     EXPECT_TRUE(handler_.get());
649     job_ = handler_->MaybeLoadResource(request_.get(),
650                                        request_->context()->network_delegate());
651     EXPECT_FALSE(job_.get());
652
653     AppCacheURLRequestJob* fallback_job;
654     fallback_job = handler_->MaybeLoadFallbackForRedirect(
655         request_.get(),
656         request_->context()->network_delegate(),
657         GURL("http://blah/redirect"));
658     EXPECT_FALSE(fallback_job);
659     fallback_job = handler_->MaybeLoadFallbackForResponse(
660         request_.get(), request_->context()->network_delegate());
661     EXPECT_FALSE(fallback_job);
662
663     TestFinished();
664   }
665
666   // DestroyedHost -----------------------------
667
668   void DestroyedHost() {
669     host_->AssociateCompleteCache(MakeNewCache());
670
671     mock_storage()->SimulateFindSubResource(
672         AppCacheEntry(AppCacheEntry::EXPLICIT, 1), AppCacheEntry(), false);
673
674     request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
675     handler_.reset(host_->CreateRequestHandler(request_.get(),
676                                                ResourceType::SUB_RESOURCE));
677     EXPECT_TRUE(handler_.get());
678
679     backend_impl_->UnregisterHost(1);
680     host_ = NULL;
681
682     EXPECT_FALSE(handler_->MaybeLoadResource(
683         request_.get(), request_->context()->network_delegate()));
684     EXPECT_FALSE(handler_->MaybeLoadFallbackForRedirect(
685         request_.get(),
686         request_->context()->network_delegate(),
687         GURL("http://blah/redirect")));
688     EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse(
689         request_.get(), request_->context()->network_delegate()));
690
691     TestFinished();
692   }
693
694   // DestroyedHostWithWaitingJob -----------------------------
695
696   void DestroyedHostWithWaitingJob() {
697     // Precondition, the host is waiting on cache selection.
698     host_->pending_selected_cache_id_ = 1;
699
700     request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
701     handler_.reset(host_->CreateRequestHandler(request_.get(),
702                                                ResourceType::SUB_RESOURCE));
703     EXPECT_TRUE(handler_.get());
704
705     job_ = handler_->MaybeLoadResource(request_.get(),
706                                        request_->context()->network_delegate());
707     EXPECT_TRUE(job_.get());
708     EXPECT_TRUE(job_->is_waiting());
709
710     backend_impl_->UnregisterHost(1);
711     host_ = NULL;
712     EXPECT_TRUE(job_->has_been_killed());
713
714     EXPECT_FALSE(handler_->MaybeLoadResource(
715         request_.get(), request_->context()->network_delegate()));
716     EXPECT_FALSE(handler_->MaybeLoadFallbackForRedirect(
717         request_.get(),
718         request_->context()->network_delegate(),
719         GURL("http://blah/redirect")));
720     EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse(
721         request_.get(), request_->context()->network_delegate()));
722
723     TestFinished();
724   }
725
726   // UnsupportedScheme -----------------------------
727
728   void UnsupportedScheme() {
729     // Precondition, the host is waiting on cache selection.
730     host_->pending_selected_cache_id_ = 1;
731
732     request_.reset(new MockURLRequest(GURL("ftp://blah/"), &empty_context_));
733     handler_.reset(host_->CreateRequestHandler(request_.get(),
734                                                ResourceType::SUB_RESOURCE));
735     EXPECT_TRUE(handler_.get());  // we could redirect to http (conceivably)
736
737     EXPECT_FALSE(handler_->MaybeLoadResource(
738         request_.get(), request_->context()->network_delegate()));
739     EXPECT_FALSE(handler_->MaybeLoadFallbackForRedirect(
740         request_.get(),
741         request_->context()->network_delegate(),
742         GURL("ftp://blah/redirect")));
743     EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse(
744         request_.get(), request_->context()->network_delegate()));
745
746     TestFinished();
747   }
748
749   // CanceledRequest -----------------------------
750
751   void CanceledRequest() {
752     request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
753     handler_.reset(host_->CreateRequestHandler(request_.get(),
754                                                ResourceType::MAIN_FRAME));
755     EXPECT_TRUE(handler_.get());
756
757     job_ = handler_->MaybeLoadResource(request_.get(),
758                                        request_->context()->network_delegate());
759     EXPECT_TRUE(job_.get());
760     EXPECT_TRUE(job_->is_waiting());
761     EXPECT_FALSE(job_->has_been_started());
762
763     mock_factory_job_ = job_.get();
764     request_->Start();
765     EXPECT_TRUE(job_->has_been_started());
766
767     request_->Cancel();
768     EXPECT_TRUE(job_->has_been_killed());
769
770     EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse(
771         request_.get(), request_->context()->network_delegate()));
772
773     TestFinished();
774   }
775
776   // WorkerRequest -----------------------------
777
778   void WorkerRequest() {
779     EXPECT_TRUE(AppCacheRequestHandler::IsMainResourceType(
780         ResourceType::MAIN_FRAME));
781     EXPECT_TRUE(AppCacheRequestHandler::IsMainResourceType(
782         ResourceType::SUB_FRAME));
783     EXPECT_TRUE(AppCacheRequestHandler::IsMainResourceType(
784         ResourceType::SHARED_WORKER));
785     EXPECT_FALSE(AppCacheRequestHandler::IsMainResourceType(
786         ResourceType::WORKER));
787
788     request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
789
790     const int kParentHostId = host_->host_id();
791     const int kWorkerHostId = 2;
792     const int kAbandonedWorkerHostId = 3;
793     const int kNonExsitingHostId = 700;
794
795     backend_impl_->RegisterHost(kWorkerHostId);
796     AppCacheHost* worker_host = backend_impl_->GetHost(kWorkerHostId);
797     worker_host->SelectCacheForWorker(kParentHostId, kMockProcessId);
798     handler_.reset(worker_host->CreateRequestHandler(
799         request_.get(), ResourceType::SHARED_WORKER));
800     EXPECT_TRUE(handler_.get());
801     // Verify that the handler is associated with the parent host.
802     EXPECT_EQ(host_, handler_->host_);
803
804     // Create a new worker host, but associate it with a parent host that
805     // does not exists to simulate the host having been torn down.
806     backend_impl_->UnregisterHost(kWorkerHostId);
807     backend_impl_->RegisterHost(kAbandonedWorkerHostId);
808     worker_host = backend_impl_->GetHost(kAbandonedWorkerHostId);
809     EXPECT_EQ(NULL, backend_impl_->GetHost(kNonExsitingHostId));
810     worker_host->SelectCacheForWorker(kNonExsitingHostId, kMockProcessId);
811     handler_.reset(worker_host->CreateRequestHandler(
812         request_.get(), ResourceType::SHARED_WORKER));
813     EXPECT_FALSE(handler_.get());
814
815     TestFinished();
816   }
817
818   // MainResource_Blocked --------------------------------------------------
819
820   void MainResource_Blocked() {
821     PushNextTask(
822         base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Blocked,
823                    base::Unretained(this)));
824
825     request_.reset(new MockURLRequest(GURL("http://blah/"), &empty_context_));
826     handler_.reset(host_->CreateRequestHandler(request_.get(),
827                                                ResourceType::MAIN_FRAME));
828     EXPECT_TRUE(handler_.get());
829
830     mock_policy_->can_load_return_value_ = false;
831     mock_storage()->SimulateFindMainResource(
832         AppCacheEntry(AppCacheEntry::EXPLICIT, 1),
833         GURL(), AppCacheEntry(),
834         1, 2, GURL("http://blah/manifest/"));
835
836     job_ = handler_->MaybeLoadResource(request_.get(),
837                                        request_->context()->network_delegate());
838     EXPECT_TRUE(job_.get());
839     EXPECT_TRUE(job_->is_waiting());
840
841     // We have to wait for completion of storage->FindResponseForMainRequest.
842     ScheduleNextTask();
843   }
844
845   void Verify_MainResource_Blocked() {
846     EXPECT_FALSE(job_->is_waiting());
847     EXPECT_FALSE(job_->is_delivering_appcache_response());
848
849     EXPECT_EQ(0, handler_->found_cache_id_);
850     EXPECT_EQ(0, handler_->found_group_id_);
851     EXPECT_TRUE(handler_->found_manifest_url_.is_empty());
852     EXPECT_TRUE(host_->preferred_manifest_url().is_empty());
853     EXPECT_TRUE(host_->main_resource_blocked_);
854     EXPECT_TRUE(host_->blocked_manifest_url_ == GURL("http://blah/manifest/"));
855
856     TestFinished();
857   }
858
859   // Test case helpers --------------------------------------------------
860
861   AppCache* MakeNewCache() {
862     AppCache* cache = new AppCache(
863         mock_storage(), mock_storage()->NewCacheId());
864     cache->set_complete(true);
865     AppCacheGroup* group = new AppCacheGroup(
866         mock_storage(), GURL("http://blah/manifest"),
867         mock_storage()->NewGroupId());
868     group->AddCache(cache);
869     return cache;
870   }
871
872   MockAppCacheStorage* mock_storage() {
873     return reinterpret_cast<MockAppCacheStorage*>(mock_service_->storage());
874   }
875
876   // Data members --------------------------------------------------
877
878   scoped_ptr<base::WaitableEvent> test_finished_event_;
879   std::stack<base::Closure> task_stack_;
880   scoped_ptr<MockAppCacheService> mock_service_;
881   scoped_ptr<AppCacheBackendImpl> backend_impl_;
882   scoped_ptr<MockFrontend> mock_frontend_;
883   scoped_ptr<MockAppCachePolicy> mock_policy_;
884   AppCacheHost* host_;
885   net::URLRequestContext empty_context_;
886   scoped_ptr<MockURLRequest> request_;
887   scoped_ptr<AppCacheRequestHandler> handler_;
888   scoped_refptr<AppCacheURLRequestJob> job_;
889   net::URLRequest::ProtocolFactory* orig_http_factory_;
890
891   static scoped_ptr<base::Thread> io_thread_;
892   static net::URLRequestJob* mock_factory_job_;
893 };
894
895 // static
896 scoped_ptr<base::Thread> AppCacheRequestHandlerTest::io_thread_;
897 net::URLRequestJob* AppCacheRequestHandlerTest::mock_factory_job_ = NULL;
898
899 TEST_F(AppCacheRequestHandlerTest, MainResource_Miss) {
900   RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Miss);
901 }
902
903 TEST_F(AppCacheRequestHandlerTest, MainResource_Hit) {
904   RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Hit);
905 }
906
907 TEST_F(AppCacheRequestHandlerTest, MainResource_Fallback) {
908   RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Fallback);
909 }
910
911 TEST_F(AppCacheRequestHandlerTest, MainResource_FallbackOverride) {
912   RunTestOnIOThread(
913       &AppCacheRequestHandlerTest::MainResource_FallbackOverride);
914 }
915
916 TEST_F(AppCacheRequestHandlerTest, SubResource_Miss_WithNoCacheSelected) {
917   RunTestOnIOThread(
918       &AppCacheRequestHandlerTest::SubResource_Miss_WithNoCacheSelected);
919 }
920
921 TEST_F(AppCacheRequestHandlerTest, SubResource_Miss_WithCacheSelected) {
922   RunTestOnIOThread(
923       &AppCacheRequestHandlerTest::SubResource_Miss_WithCacheSelected);
924 }
925
926 TEST_F(AppCacheRequestHandlerTest,
927        SubResource_Miss_WithWaitForCacheSelection) {
928   RunTestOnIOThread(
929       &AppCacheRequestHandlerTest::SubResource_Miss_WithWaitForCacheSelection);
930 }
931
932 TEST_F(AppCacheRequestHandlerTest, SubResource_Hit) {
933   RunTestOnIOThread(&AppCacheRequestHandlerTest::SubResource_Hit);
934 }
935
936 TEST_F(AppCacheRequestHandlerTest, SubResource_RedirectFallback) {
937   RunTestOnIOThread(&AppCacheRequestHandlerTest::SubResource_RedirectFallback);
938 }
939
940 TEST_F(AppCacheRequestHandlerTest, SubResource_NoRedirectFallback) {
941   RunTestOnIOThread(
942     &AppCacheRequestHandlerTest::SubResource_NoRedirectFallback);
943 }
944
945 TEST_F(AppCacheRequestHandlerTest, SubResource_Network) {
946   RunTestOnIOThread(&AppCacheRequestHandlerTest::SubResource_Network);
947 }
948
949 TEST_F(AppCacheRequestHandlerTest, DestroyedHost) {
950   RunTestOnIOThread(&AppCacheRequestHandlerTest::DestroyedHost);
951 }
952
953 TEST_F(AppCacheRequestHandlerTest, DestroyedHostWithWaitingJob) {
954   RunTestOnIOThread(&AppCacheRequestHandlerTest::DestroyedHostWithWaitingJob);
955 }
956
957 TEST_F(AppCacheRequestHandlerTest, UnsupportedScheme) {
958   RunTestOnIOThread(&AppCacheRequestHandlerTest::UnsupportedScheme);
959 }
960
961 TEST_F(AppCacheRequestHandlerTest, CanceledRequest) {
962   RunTestOnIOThread(&AppCacheRequestHandlerTest::CanceledRequest);
963 }
964
965 TEST_F(AppCacheRequestHandlerTest, WorkerRequest) {
966   RunTestOnIOThread(&AppCacheRequestHandlerTest::WorkerRequest);
967 }
968
969 TEST_F(AppCacheRequestHandlerTest, MainResource_Blocked) {
970   RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Blocked);
971 }
972
973 }  // namespace appcache