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 "base/bind.h"
6 #include "base/bind_helpers.h"
7 #include "base/memory/scoped_ptr.h"
8 #include "base/message_loop/message_loop.h"
9 #include "net/url_request/url_request.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "webkit/browser/appcache/appcache.h"
12 #include "webkit/browser/appcache/appcache_backend_impl.h"
13 #include "webkit/browser/appcache/appcache_group.h"
14 #include "webkit/browser/appcache/appcache_host.h"
15 #include "webkit/browser/appcache/mock_appcache_policy.h"
16 #include "webkit/browser/appcache/mock_appcache_service.h"
17 #include "webkit/browser/quota/quota_manager.h"
18
19 namespace appcache {
20
21 class AppCacheHostTest : public testing::Test {
22 public:
AppCacheHostTest()23 AppCacheHostTest() {
24 get_status_callback_ =
25 base::Bind(&AppCacheHostTest::GetStatusCallback,
26 base::Unretained(this));
27 start_update_callback_ =
28 base::Bind(&AppCacheHostTest::StartUpdateCallback,
29 base::Unretained(this));
30 swap_cache_callback_ =
31 base::Bind(&AppCacheHostTest::SwapCacheCallback,
32 base::Unretained(this));
33 }
34
35 class MockFrontend : public AppCacheFrontend {
36 public:
MockFrontend()37 MockFrontend()
38 : last_host_id_(-222), last_cache_id_(-222),
39 last_status_(appcache::OBSOLETE),
40 last_status_changed_(appcache::OBSOLETE),
41 last_event_id_(appcache::OBSOLETE_EVENT),
42 content_blocked_(false) {
43 }
44
OnCacheSelected(int host_id,const appcache::AppCacheInfo & info)45 virtual void OnCacheSelected(
46 int host_id, const appcache::AppCacheInfo& info) OVERRIDE {
47 last_host_id_ = host_id;
48 last_cache_id_ = info.cache_id;
49 last_status_ = info.status;
50 }
51
OnStatusChanged(const std::vector<int> & host_ids,appcache::Status status)52 virtual void OnStatusChanged(const std::vector<int>& host_ids,
53 appcache::Status status) OVERRIDE {
54 last_status_changed_ = status;
55 }
56
OnEventRaised(const std::vector<int> & host_ids,appcache::EventID event_id)57 virtual void OnEventRaised(const std::vector<int>& host_ids,
58 appcache::EventID event_id) OVERRIDE {
59 last_event_id_ = event_id;
60 }
61
OnErrorEventRaised(const std::vector<int> & host_ids,const std::string & message)62 virtual void OnErrorEventRaised(const std::vector<int>& host_ids,
63 const std::string& message) OVERRIDE {
64 last_event_id_ = ERROR_EVENT;
65 }
66
OnProgressEventRaised(const std::vector<int> & host_ids,const GURL & url,int num_total,int num_complete)67 virtual void OnProgressEventRaised(const std::vector<int>& host_ids,
68 const GURL& url,
69 int num_total,
70 int num_complete) OVERRIDE {
71 last_event_id_ = PROGRESS_EVENT;
72 }
73
OnLogMessage(int host_id,appcache::LogLevel log_level,const std::string & message)74 virtual void OnLogMessage(int host_id,
75 appcache::LogLevel log_level,
76 const std::string& message) OVERRIDE {
77 }
78
OnContentBlocked(int host_id,const GURL & manifest_url)79 virtual void OnContentBlocked(int host_id,
80 const GURL& manifest_url) OVERRIDE {
81 content_blocked_ = true;
82 }
83
84 int last_host_id_;
85 int64 last_cache_id_;
86 appcache::Status last_status_;
87 appcache::Status last_status_changed_;
88 appcache::EventID last_event_id_;
89 bool content_blocked_;
90 };
91
92 class MockQuotaManagerProxy : public quota::QuotaManagerProxy {
93 public:
MockQuotaManagerProxy()94 MockQuotaManagerProxy() : QuotaManagerProxy(NULL, NULL) {}
95
96 // Not needed for our tests.
RegisterClient(quota::QuotaClient * client)97 virtual void RegisterClient(quota::QuotaClient* client) OVERRIDE {}
NotifyStorageAccessed(quota::QuotaClient::ID client_id,const GURL & origin,quota::StorageType type)98 virtual void NotifyStorageAccessed(quota::QuotaClient::ID client_id,
99 const GURL& origin,
100 quota::StorageType type) OVERRIDE {}
NotifyStorageModified(quota::QuotaClient::ID client_id,const GURL & origin,quota::StorageType type,int64 delta)101 virtual void NotifyStorageModified(quota::QuotaClient::ID client_id,
102 const GURL& origin,
103 quota::StorageType type,
104 int64 delta) OVERRIDE {}
SetUsageCacheEnabled(quota::QuotaClient::ID client_id,const GURL & origin,quota::StorageType type,bool enabled)105 virtual void SetUsageCacheEnabled(quota::QuotaClient::ID client_id,
106 const GURL& origin,
107 quota::StorageType type,
108 bool enabled) OVERRIDE {}
GetUsageAndQuota(base::SequencedTaskRunner * original_task_runner,const GURL & origin,quota::StorageType type,const GetUsageAndQuotaCallback & callback)109 virtual void GetUsageAndQuota(
110 base::SequencedTaskRunner* original_task_runner,
111 const GURL& origin,
112 quota::StorageType type,
113 const GetUsageAndQuotaCallback& callback) OVERRIDE {}
114
NotifyOriginInUse(const GURL & origin)115 virtual void NotifyOriginInUse(const GURL& origin) OVERRIDE {
116 inuse_[origin] += 1;
117 }
118
NotifyOriginNoLongerInUse(const GURL & origin)119 virtual void NotifyOriginNoLongerInUse(const GURL& origin) OVERRIDE {
120 inuse_[origin] -= 1;
121 }
122
GetInUseCount(const GURL & origin)123 int GetInUseCount(const GURL& origin) {
124 return inuse_[origin];
125 }
126
reset()127 void reset() {
128 inuse_.clear();
129 }
130
131 // Map from origin to count of inuse notifications.
132 std::map<GURL, int> inuse_;
133
134 protected:
~MockQuotaManagerProxy()135 virtual ~MockQuotaManagerProxy() {}
136 };
137
GetStatusCallback(Status status,void * param)138 void GetStatusCallback(Status status, void* param) {
139 last_status_result_ = status;
140 last_callback_param_ = param;
141 }
142
StartUpdateCallback(bool result,void * param)143 void StartUpdateCallback(bool result, void* param) {
144 last_start_result_ = result;
145 last_callback_param_ = param;
146 }
147
SwapCacheCallback(bool result,void * param)148 void SwapCacheCallback(bool result, void* param) {
149 last_swap_result_ = result;
150 last_callback_param_ = param;
151 }
152
153 base::MessageLoop message_loop_;
154
155 // Mock classes for the 'host' to work with
156 MockAppCacheService service_;
157 MockFrontend mock_frontend_;
158
159 // Mock callbacks we expect to receive from the 'host'
160 appcache::GetStatusCallback get_status_callback_;
161 appcache::StartUpdateCallback start_update_callback_;
162 appcache::SwapCacheCallback swap_cache_callback_;
163
164 Status last_status_result_;
165 bool last_swap_result_;
166 bool last_start_result_;
167 void* last_callback_param_;
168 };
169
TEST_F(AppCacheHostTest,Basic)170 TEST_F(AppCacheHostTest, Basic) {
171 // Construct a host and test what state it appears to be in.
172 AppCacheHost host(1, &mock_frontend_, &service_);
173 EXPECT_EQ(1, host.host_id());
174 EXPECT_EQ(&service_, host.service());
175 EXPECT_EQ(&mock_frontend_, host.frontend());
176 EXPECT_EQ(NULL, host.associated_cache());
177 EXPECT_FALSE(host.is_selection_pending());
178
179 // See that the callbacks are delivered immediately
180 // and respond as if there is no cache selected.
181 last_status_result_ = OBSOLETE;
182 host.GetStatusWithCallback(get_status_callback_, reinterpret_cast<void*>(1));
183 EXPECT_EQ(UNCACHED, last_status_result_);
184 EXPECT_EQ(reinterpret_cast<void*>(1), last_callback_param_);
185
186 last_start_result_ = true;
187 host.StartUpdateWithCallback(start_update_callback_,
188 reinterpret_cast<void*>(2));
189 EXPECT_FALSE(last_start_result_);
190 EXPECT_EQ(reinterpret_cast<void*>(2), last_callback_param_);
191
192 last_swap_result_ = true;
193 host.SwapCacheWithCallback(swap_cache_callback_, reinterpret_cast<void*>(3));
194 EXPECT_FALSE(last_swap_result_);
195 EXPECT_EQ(reinterpret_cast<void*>(3), last_callback_param_);
196 }
197
TEST_F(AppCacheHostTest,SelectNoCache)198 TEST_F(AppCacheHostTest, SelectNoCache) {
199 scoped_refptr<MockQuotaManagerProxy> mock_quota_proxy(
200 new MockQuotaManagerProxy);
201 service_.set_quota_manager_proxy(mock_quota_proxy.get());
202
203 // Reset our mock frontend
204 mock_frontend_.last_cache_id_ = -333;
205 mock_frontend_.last_host_id_ = -333;
206 mock_frontend_.last_status_ = OBSOLETE;
207
208 const GURL kDocAndOriginUrl(GURL("http://whatever/").GetOrigin());
209 {
210 AppCacheHost host(1, &mock_frontend_, &service_);
211 host.SelectCache(kDocAndOriginUrl, kNoCacheId, GURL());
212 EXPECT_EQ(1, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl));
213
214 // We should have received an OnCacheSelected msg
215 EXPECT_EQ(1, mock_frontend_.last_host_id_);
216 EXPECT_EQ(kNoCacheId, mock_frontend_.last_cache_id_);
217 EXPECT_EQ(UNCACHED, mock_frontend_.last_status_);
218
219 // Otherwise, see that it respond as if there is no cache selected.
220 EXPECT_EQ(1, host.host_id());
221 EXPECT_EQ(&service_, host.service());
222 EXPECT_EQ(&mock_frontend_, host.frontend());
223 EXPECT_EQ(NULL, host.associated_cache());
224 EXPECT_FALSE(host.is_selection_pending());
225 EXPECT_TRUE(host.preferred_manifest_url().is_empty());
226 }
227 EXPECT_EQ(0, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl));
228 service_.set_quota_manager_proxy(NULL);
229 }
230
TEST_F(AppCacheHostTest,ForeignEntry)231 TEST_F(AppCacheHostTest, ForeignEntry) {
232 // Reset our mock frontend
233 mock_frontend_.last_cache_id_ = -333;
234 mock_frontend_.last_host_id_ = -333;
235 mock_frontend_.last_status_ = OBSOLETE;
236
237 // Precondition, a cache with an entry that is not marked as foreign.
238 const int kCacheId = 22;
239 const GURL kDocumentURL("http://origin/document");
240 scoped_refptr<AppCache> cache = new AppCache(service_.storage(), kCacheId);
241 cache->AddEntry(kDocumentURL, AppCacheEntry(AppCacheEntry::EXPLICIT));
242
243 AppCacheHost host(1, &mock_frontend_, &service_);
244 host.MarkAsForeignEntry(kDocumentURL, kCacheId);
245
246 // We should have received an OnCacheSelected msg for kNoCacheId.
247 EXPECT_EQ(1, mock_frontend_.last_host_id_);
248 EXPECT_EQ(kNoCacheId, mock_frontend_.last_cache_id_);
249 EXPECT_EQ(UNCACHED, mock_frontend_.last_status_);
250
251 // See that it respond as if there is no cache selected.
252 EXPECT_EQ(1, host.host_id());
253 EXPECT_EQ(&service_, host.service());
254 EXPECT_EQ(&mock_frontend_, host.frontend());
255 EXPECT_EQ(NULL, host.associated_cache());
256 EXPECT_FALSE(host.is_selection_pending());
257
258 // See that the entry was marked as foreign.
259 EXPECT_TRUE(cache->GetEntry(kDocumentURL)->IsForeign());
260 }
261
TEST_F(AppCacheHostTest,ForeignFallbackEntry)262 TEST_F(AppCacheHostTest, ForeignFallbackEntry) {
263 // Reset our mock frontend
264 mock_frontend_.last_cache_id_ = -333;
265 mock_frontend_.last_host_id_ = -333;
266 mock_frontend_.last_status_ = OBSOLETE;
267
268 // Precondition, a cache with a fallback entry that is not marked as foreign.
269 const int kCacheId = 22;
270 const GURL kFallbackURL("http://origin/fallback_resource");
271 scoped_refptr<AppCache> cache = new AppCache(service_.storage(), kCacheId);
272 cache->AddEntry(kFallbackURL, AppCacheEntry(AppCacheEntry::FALLBACK));
273
274 AppCacheHost host(1, &mock_frontend_, &service_);
275 host.NotifyMainResourceIsNamespaceEntry(kFallbackURL);
276 host.MarkAsForeignEntry(GURL("http://origin/missing_document"), kCacheId);
277
278 // We should have received an OnCacheSelected msg for kNoCacheId.
279 EXPECT_EQ(1, mock_frontend_.last_host_id_);
280 EXPECT_EQ(kNoCacheId, mock_frontend_.last_cache_id_);
281 EXPECT_EQ(UNCACHED, mock_frontend_.last_status_);
282
283 // See that the fallback entry was marked as foreign.
284 EXPECT_TRUE(cache->GetEntry(kFallbackURL)->IsForeign());
285 }
286
TEST_F(AppCacheHostTest,FailedCacheLoad)287 TEST_F(AppCacheHostTest, FailedCacheLoad) {
288 // Reset our mock frontend
289 mock_frontend_.last_cache_id_ = -333;
290 mock_frontend_.last_host_id_ = -333;
291 mock_frontend_.last_status_ = OBSOLETE;
292
293 AppCacheHost host(1, &mock_frontend_, &service_);
294 EXPECT_FALSE(host.is_selection_pending());
295
296 const int kMockCacheId = 333;
297
298 // Put it in a state where we're waiting on a cache
299 // load prior to finishing cache selection.
300 host.pending_selected_cache_id_ = kMockCacheId;
301 EXPECT_TRUE(host.is_selection_pending());
302
303 // The callback should not occur until we finish cache selection.
304 last_status_result_ = OBSOLETE;
305 last_callback_param_ = reinterpret_cast<void*>(-1);
306 host.GetStatusWithCallback(get_status_callback_, reinterpret_cast<void*>(1));
307 EXPECT_EQ(OBSOLETE, last_status_result_);
308 EXPECT_EQ(reinterpret_cast<void*>(-1), last_callback_param_);
309
310 // Satisfy the load with NULL, a failure.
311 host.OnCacheLoaded(NULL, kMockCacheId);
312
313 // Cache selection should have finished
314 EXPECT_FALSE(host.is_selection_pending());
315 EXPECT_EQ(1, mock_frontend_.last_host_id_);
316 EXPECT_EQ(kNoCacheId, mock_frontend_.last_cache_id_);
317 EXPECT_EQ(UNCACHED, mock_frontend_.last_status_);
318
319 // Callback should have fired upon completing the cache load too.
320 EXPECT_EQ(UNCACHED, last_status_result_);
321 EXPECT_EQ(reinterpret_cast<void*>(1), last_callback_param_);
322 }
323
TEST_F(AppCacheHostTest,FailedGroupLoad)324 TEST_F(AppCacheHostTest, FailedGroupLoad) {
325 AppCacheHost host(1, &mock_frontend_, &service_);
326
327 const GURL kMockManifestUrl("http://foo.bar/baz");
328
329 // Put it in a state where we're waiting on a cache
330 // load prior to finishing cache selection.
331 host.pending_selected_manifest_url_ = kMockManifestUrl;
332 EXPECT_TRUE(host.is_selection_pending());
333
334 // The callback should not occur until we finish cache selection.
335 last_status_result_ = OBSOLETE;
336 last_callback_param_ = reinterpret_cast<void*>(-1);
337 host.GetStatusWithCallback(get_status_callback_, reinterpret_cast<void*>(1));
338 EXPECT_EQ(OBSOLETE, last_status_result_);
339 EXPECT_EQ(reinterpret_cast<void*>(-1), last_callback_param_);
340
341 // Satisfy the load will NULL, a failure.
342 host.OnGroupLoaded(NULL, kMockManifestUrl);
343
344 // Cache selection should have finished
345 EXPECT_FALSE(host.is_selection_pending());
346 EXPECT_EQ(1, mock_frontend_.last_host_id_);
347 EXPECT_EQ(kNoCacheId, mock_frontend_.last_cache_id_);
348 EXPECT_EQ(UNCACHED, mock_frontend_.last_status_);
349
350 // Callback should have fired upon completing the group load.
351 EXPECT_EQ(UNCACHED, last_status_result_);
352 EXPECT_EQ(reinterpret_cast<void*>(1), last_callback_param_);
353 }
354
TEST_F(AppCacheHostTest,SetSwappableCache)355 TEST_F(AppCacheHostTest, SetSwappableCache) {
356 AppCacheHost host(1, &mock_frontend_, &service_);
357 host.SetSwappableCache(NULL);
358 EXPECT_FALSE(host.swappable_cache_.get());
359
360 scoped_refptr<AppCacheGroup> group1(new AppCacheGroup(
361 service_.storage(), GURL(), service_.storage()->NewGroupId()));
362 host.SetSwappableCache(group1.get());
363 EXPECT_FALSE(host.swappable_cache_.get());
364
365 AppCache* cache1 = new AppCache(service_.storage(), 111);
366 cache1->set_complete(true);
367 group1->AddCache(cache1);
368 host.SetSwappableCache(group1.get());
369 EXPECT_EQ(cache1, host.swappable_cache_.get());
370
371 mock_frontend_.last_host_id_ = -222; // to verify we received OnCacheSelected
372
373 host.AssociateCompleteCache(cache1);
374 EXPECT_FALSE(host.swappable_cache_.get()); // was same as associated cache
375 EXPECT_EQ(appcache::IDLE, host.GetStatus());
376 // verify OnCacheSelected was called
377 EXPECT_EQ(host.host_id(), mock_frontend_.last_host_id_);
378 EXPECT_EQ(cache1->cache_id(), mock_frontend_.last_cache_id_);
379 EXPECT_EQ(appcache::IDLE, mock_frontend_.last_status_);
380
381 AppCache* cache2 = new AppCache(service_.storage(), 222);
382 cache2->set_complete(true);
383 group1->AddCache(cache2);
384 EXPECT_EQ(cache2, host.swappable_cache_.get()); // updated to newest
385
386 scoped_refptr<AppCacheGroup> group2(
387 new AppCacheGroup(service_.storage(), GURL("http://foo.com"),
388 service_.storage()->NewGroupId()));
389 AppCache* cache3 = new AppCache(service_.storage(), 333);
390 cache3->set_complete(true);
391 group2->AddCache(cache3);
392
393 AppCache* cache4 = new AppCache(service_.storage(), 444);
394 cache4->set_complete(true);
395 group2->AddCache(cache4);
396 EXPECT_EQ(cache2, host.swappable_cache_.get()); // unchanged
397
398 host.AssociateCompleteCache(cache3);
399 EXPECT_EQ(cache4, host.swappable_cache_.get()); // newest cache in group2
400 EXPECT_FALSE(group1->HasCache()); // both caches in group1 have refcount 0
401
402 host.AssociateNoCache(GURL());
403 EXPECT_FALSE(host.swappable_cache_.get());
404 EXPECT_FALSE(group2->HasCache()); // both caches in group2 have refcount 0
405
406 // Host adds reference to newest cache when an update is complete.
407 AppCache* cache5 = new AppCache(service_.storage(), 555);
408 cache5->set_complete(true);
409 group2->AddCache(cache5);
410 host.group_being_updated_ = group2;
411 host.OnUpdateComplete(group2.get());
412 EXPECT_FALSE(host.group_being_updated_.get());
413 EXPECT_EQ(cache5, host.swappable_cache_.get());
414
415 group2->RemoveCache(cache5);
416 EXPECT_FALSE(group2->HasCache());
417 host.group_being_updated_ = group2;
418 host.OnUpdateComplete(group2.get());
419 EXPECT_FALSE(host.group_being_updated_.get());
420 EXPECT_FALSE(host.swappable_cache_.get()); // group2 had no newest cache
421 }
422
TEST_F(AppCacheHostTest,ForDedicatedWorker)423 TEST_F(AppCacheHostTest, ForDedicatedWorker) {
424 const int kMockProcessId = 1;
425 const int kParentHostId = 1;
426 const int kWorkerHostId = 2;
427
428 AppCacheBackendImpl backend_impl;
429 backend_impl.Initialize(&service_, &mock_frontend_, kMockProcessId);
430 backend_impl.RegisterHost(kParentHostId);
431 backend_impl.RegisterHost(kWorkerHostId);
432
433 AppCacheHost* parent_host = backend_impl.GetHost(kParentHostId);
434 EXPECT_FALSE(parent_host->is_for_dedicated_worker());
435
436 AppCacheHost* worker_host = backend_impl.GetHost(kWorkerHostId);
437 worker_host->SelectCacheForWorker(kParentHostId, kMockProcessId);
438 EXPECT_TRUE(worker_host->is_for_dedicated_worker());
439 EXPECT_EQ(parent_host, worker_host->GetParentAppCacheHost());
440
441 // We should have received an OnCacheSelected msg for the worker_host.
442 // The host for workers always indicates 'no cache selected' regardless
443 // of its parent's state. This is OK because the worker cannot access
444 // the scriptable interface, the only function available is resource
445 // loading (see appcache_request_handler_unittests those tests).
446 EXPECT_EQ(kWorkerHostId, mock_frontend_.last_host_id_);
447 EXPECT_EQ(kNoCacheId, mock_frontend_.last_cache_id_);
448 EXPECT_EQ(UNCACHED, mock_frontend_.last_status_);
449
450 // Simulate the parent being torn down.
451 backend_impl.UnregisterHost(kParentHostId);
452 parent_host = NULL;
453 EXPECT_EQ(NULL, backend_impl.GetHost(kParentHostId));
454 EXPECT_EQ(NULL, worker_host->GetParentAppCacheHost());
455 }
456
TEST_F(AppCacheHostTest,SelectCacheAllowed)457 TEST_F(AppCacheHostTest, SelectCacheAllowed) {
458 scoped_refptr<MockQuotaManagerProxy> mock_quota_proxy(
459 new MockQuotaManagerProxy);
460 MockAppCachePolicy mock_appcache_policy;
461 mock_appcache_policy.can_create_return_value_ = true;
462 service_.set_quota_manager_proxy(mock_quota_proxy.get());
463 service_.set_appcache_policy(&mock_appcache_policy);
464
465 // Reset our mock frontend
466 mock_frontend_.last_cache_id_ = -333;
467 mock_frontend_.last_host_id_ = -333;
468 mock_frontend_.last_status_ = OBSOLETE;
469 mock_frontend_.last_event_id_ = OBSOLETE_EVENT;
470 mock_frontend_.content_blocked_ = false;
471
472 const GURL kDocAndOriginUrl(GURL("http://whatever/").GetOrigin());
473 const GURL kManifestUrl(GURL("http://whatever/cache.manifest"));
474 {
475 AppCacheHost host(1, &mock_frontend_, &service_);
476 host.first_party_url_ = kDocAndOriginUrl;
477 host.SelectCache(kDocAndOriginUrl, kNoCacheId, kManifestUrl);
478 EXPECT_EQ(1, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl));
479
480 // MockAppCacheService::LoadOrCreateGroup is asynchronous, so we shouldn't
481 // have received an OnCacheSelected msg yet.
482 EXPECT_EQ(-333, mock_frontend_.last_host_id_);
483 EXPECT_EQ(-333, mock_frontend_.last_cache_id_);
484 EXPECT_EQ(OBSOLETE, mock_frontend_.last_status_);
485 // No error events either
486 EXPECT_EQ(OBSOLETE_EVENT, mock_frontend_.last_event_id_);
487 EXPECT_FALSE(mock_frontend_.content_blocked_);
488
489 EXPECT_TRUE(host.is_selection_pending());
490 }
491 EXPECT_EQ(0, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl));
492 service_.set_quota_manager_proxy(NULL);
493 }
494
TEST_F(AppCacheHostTest,SelectCacheBlocked)495 TEST_F(AppCacheHostTest, SelectCacheBlocked) {
496 scoped_refptr<MockQuotaManagerProxy> mock_quota_proxy(
497 new MockQuotaManagerProxy);
498 MockAppCachePolicy mock_appcache_policy;
499 mock_appcache_policy.can_create_return_value_ = false;
500 service_.set_quota_manager_proxy(mock_quota_proxy.get());
501 service_.set_appcache_policy(&mock_appcache_policy);
502
503 // Reset our mock frontend
504 mock_frontend_.last_cache_id_ = -333;
505 mock_frontend_.last_host_id_ = -333;
506 mock_frontend_.last_status_ = OBSOLETE;
507 mock_frontend_.last_event_id_ = OBSOLETE_EVENT;
508 mock_frontend_.content_blocked_ = false;
509
510 const GURL kDocAndOriginUrl(GURL("http://whatever/").GetOrigin());
511 const GURL kManifestUrl(GURL("http://whatever/cache.manifest"));
512 {
513 AppCacheHost host(1, &mock_frontend_, &service_);
514 host.first_party_url_ = kDocAndOriginUrl;
515 host.SelectCache(kDocAndOriginUrl, kNoCacheId, kManifestUrl);
516 EXPECT_EQ(1, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl));
517
518 // We should have received an OnCacheSelected msg
519 EXPECT_EQ(1, mock_frontend_.last_host_id_);
520 EXPECT_EQ(kNoCacheId, mock_frontend_.last_cache_id_);
521 EXPECT_EQ(UNCACHED, mock_frontend_.last_status_);
522
523 // Also, an error event was raised
524 EXPECT_EQ(ERROR_EVENT, mock_frontend_.last_event_id_);
525 EXPECT_TRUE(mock_frontend_.content_blocked_);
526
527 // Otherwise, see that it respond as if there is no cache selected.
528 EXPECT_EQ(1, host.host_id());
529 EXPECT_EQ(&service_, host.service());
530 EXPECT_EQ(&mock_frontend_, host.frontend());
531 EXPECT_EQ(NULL, host.associated_cache());
532 EXPECT_FALSE(host.is_selection_pending());
533 EXPECT_TRUE(host.preferred_manifest_url().is_empty());
534 }
535 EXPECT_EQ(0, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl));
536 service_.set_quota_manager_proxy(NULL);
537 }
538
539 } // namespace appcache
540