Home | History | Annotate | Download | only in appcache
      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:
     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:
     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 
     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 
     52     virtual void OnStatusChanged(const std::vector<int>& host_ids,
     53                                  appcache::Status status) OVERRIDE {
     54       last_status_changed_ = status;
     55     }
     56 
     57     virtual void OnEventRaised(const std::vector<int>& host_ids,
     58                                appcache::EventID event_id) OVERRIDE {
     59       last_event_id_ = event_id;
     60     }
     61 
     62     virtual void OnErrorEventRaised(const std::vector<int>& host_ids,
     63                                     const std::string& message) OVERRIDE {
     64       last_event_id_ = ERROR_EVENT;
     65     }
     66 
     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 
     74     virtual void OnLogMessage(int host_id,
     75                               appcache::LogLevel log_level,
     76                               const std::string& message) OVERRIDE {
     77     }
     78 
     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:
     94     MockQuotaManagerProxy() : QuotaManagerProxy(NULL, NULL) {}
     95 
     96     // Not needed for our tests.
     97     virtual void RegisterClient(quota::QuotaClient* client) OVERRIDE {}
     98     virtual void NotifyStorageAccessed(quota::QuotaClient::ID client_id,
     99                                        const GURL& origin,
    100                                        quota::StorageType type) OVERRIDE {}
    101     virtual void NotifyStorageModified(quota::QuotaClient::ID client_id,
    102                                        const GURL& origin,
    103                                        quota::StorageType type,
    104                                        int64 delta) OVERRIDE {}
    105     virtual void SetUsageCacheEnabled(quota::QuotaClient::ID client_id,
    106                                       const GURL& origin,
    107                                       quota::StorageType type,
    108                                       bool enabled) OVERRIDE {}
    109     virtual void GetUsageAndQuota(
    110         base::SequencedTaskRunner* original_task_runner,
    111         const GURL& origin,
    112         quota::StorageType type,
    113         const GetUsageAndQuotaCallback& callback) OVERRIDE {}
    114 
    115     virtual void NotifyOriginInUse(const GURL& origin) OVERRIDE {
    116       inuse_[origin] += 1;
    117     }
    118 
    119     virtual void NotifyOriginNoLongerInUse(const GURL& origin) OVERRIDE {
    120       inuse_[origin] -= 1;
    121     }
    122 
    123     int GetInUseCount(const GURL& origin) {
    124       return inuse_[origin];
    125     }
    126 
    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:
    135     virtual ~MockQuotaManagerProxy() {}
    136   };
    137 
    138   void GetStatusCallback(Status status, void* param) {
    139     last_status_result_ = status;
    140     last_callback_param_ = param;
    141   }
    142 
    143   void StartUpdateCallback(bool result, void* param) {
    144     last_start_result_ = result;
    145     last_callback_param_ = param;
    146   }
    147 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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