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 <string>
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/message_loop/message_loop.h"
     10 #include "base/pickle.h"
     11 #include "net/base/completion_callback.h"
     12 #include "net/base/io_buffer.h"
     13 #include "net/http/http_response_headers.h"
     14 #include "testing/gtest/include/gtest/gtest.h"
     15 #include "webkit/browser/appcache/appcache_response.h"
     16 #include "webkit/browser/appcache/appcache_service.h"
     17 #include "webkit/browser/appcache/mock_appcache_storage.h"
     18 
     19 
     20 namespace appcache {
     21 namespace {
     22 
     23 const int64 kMockGroupId = 1;
     24 const int64 kMockCacheId = 1;
     25 const int64 kMockResponseId = 1;
     26 const int64 kMissingCacheId = 5;
     27 const int64 kMissingResponseId = 5;
     28 const char kMockHeaders[] =
     29     "HTTP/1.0 200 OK\0Content-Length: 5\0\0";
     30 const char kMockBody[] = "Hello";
     31 const int kMockBodySize = 5;
     32 
     33 class MockResponseReader : public AppCacheResponseReader {
     34  public:
     35   MockResponseReader(int64 response_id,
     36                      net::HttpResponseInfo* info, int info_size,
     37                      const char* data, int data_size)
     38       : AppCacheResponseReader(response_id, 0, NULL),
     39         info_(info), info_size_(info_size),
     40         data_(data), data_size_(data_size) {
     41   }
     42   virtual void ReadInfo(HttpResponseInfoIOBuffer* info_buf,
     43                         const net::CompletionCallback& callback) OVERRIDE {
     44     info_buffer_ = info_buf;
     45     callback_ = callback;  // Cleared on completion.
     46 
     47     int rv = info_.get() ? info_size_ : net::ERR_FAILED;
     48     info_buffer_->http_info.reset(info_.release());
     49     info_buffer_->response_data_size = data_size_;
     50     ScheduleUserCallback(rv);
     51   }
     52   virtual void ReadData(net::IOBuffer* buf, int buf_len,
     53                         const net::CompletionCallback& callback) OVERRIDE {
     54     buffer_ = buf;
     55     buffer_len_ = buf_len;
     56     callback_ = callback;  // Cleared on completion.
     57 
     58     if (!data_) {
     59       ScheduleUserCallback(net::ERR_CACHE_READ_FAILURE);
     60       return;
     61     }
     62     DCHECK(buf_len >= data_size_);
     63     memcpy(buf->data(), data_, data_size_);
     64     ScheduleUserCallback(data_size_);
     65     data_size_ = 0;
     66   }
     67 
     68  private:
     69   void ScheduleUserCallback(int result) {
     70     base::MessageLoop::current()->PostTask(FROM_HERE,
     71         base::Bind(&MockResponseReader::InvokeUserCompletionCallback,
     72                    weak_factory_.GetWeakPtr(), result));
     73   }
     74 
     75   scoped_ptr<net::HttpResponseInfo> info_;
     76   int info_size_;
     77   const char* data_;
     78   int data_size_;
     79 };
     80 
     81 }  // namespace
     82 
     83 
     84 class AppCacheServiceTest : public testing::Test {
     85  public:
     86   AppCacheServiceTest()
     87       : kOrigin("http://hello/"),
     88         kManifestUrl(kOrigin.Resolve("manifest")),
     89         service_(new AppCacheService(NULL)),
     90         delete_result_(net::OK), delete_completion_count_(0),
     91         deletion_callback_(
     92             base::Bind(&AppCacheServiceTest::OnDeleteAppCachesComplete,
     93                        base::Unretained(this))) {
     94     // Setup to use mock storage.
     95     service_->storage_.reset(new MockAppCacheStorage(service_.get()));
     96   }
     97 
     98   void OnDeleteAppCachesComplete(int result) {
     99     delete_result_ = result;
    100     ++delete_completion_count_;
    101   }
    102 
    103   MockAppCacheStorage* mock_storage() {
    104     return static_cast<MockAppCacheStorage*>(service_->storage());
    105   }
    106 
    107   void ResetStorage() {
    108     service_->storage_.reset(new MockAppCacheStorage(service_.get()));
    109   }
    110 
    111   bool IsGroupStored(const GURL& manifest_url) {
    112     return mock_storage()->IsGroupForManifestStored(manifest_url);
    113   }
    114 
    115   int CountPendingHelpers() {
    116     return service_->pending_helpers_.size();
    117   }
    118 
    119   void SetupMockGroup() {
    120     scoped_ptr<net::HttpResponseInfo> info(MakeMockResponseInfo());
    121     const int kMockInfoSize = GetResponseInfoSize(info.get());
    122 
    123     // Create a mock group, cache, and entry and stuff them into mock storage.
    124     scoped_refptr<AppCacheGroup> group(
    125         new AppCacheGroup(service_->storage(), kManifestUrl, kMockGroupId));
    126     scoped_refptr<AppCache> cache(
    127         new AppCache(service_->storage(), kMockCacheId));
    128     cache->AddEntry(
    129         kManifestUrl,
    130         AppCacheEntry(AppCacheEntry::MANIFEST, kMockResponseId,
    131                       kMockInfoSize + kMockBodySize));
    132     cache->set_complete(true);
    133     group->AddCache(cache.get());
    134     mock_storage()->AddStoredGroup(group.get());
    135     mock_storage()->AddStoredCache(cache.get());
    136   }
    137 
    138   void SetupMockReader(
    139       bool valid_info, bool valid_data, bool valid_size) {
    140     net::HttpResponseInfo* info = valid_info ? MakeMockResponseInfo() : NULL;
    141     int info_size = info ? GetResponseInfoSize(info) : 0;
    142     const char* data = valid_data ? kMockBody : NULL;
    143     int data_size = valid_size ? kMockBodySize : 3;
    144     mock_storage()->SimulateResponseReader(
    145         new MockResponseReader(kMockResponseId, info, info_size,
    146                                data, data_size));
    147   }
    148 
    149   net::HttpResponseInfo* MakeMockResponseInfo() {
    150     net::HttpResponseInfo* info = new net::HttpResponseInfo;
    151     info->request_time = base::Time::Now();
    152     info->response_time = base::Time::Now();
    153     info->was_cached = false;
    154     info->headers = new net::HttpResponseHeaders(
    155         std::string(kMockHeaders, arraysize(kMockHeaders)));
    156     return info;
    157   }
    158 
    159   int GetResponseInfoSize(const net::HttpResponseInfo* info) {
    160     Pickle pickle;
    161     return PickleResponseInfo(&pickle, info);
    162   }
    163 
    164   int PickleResponseInfo(Pickle* pickle, const net::HttpResponseInfo* info) {
    165     const bool kSkipTransientHeaders = true;
    166     const bool kTruncated = false;
    167     info->Persist(pickle, kSkipTransientHeaders, kTruncated);
    168     return pickle->size();
    169   }
    170 
    171   const GURL kOrigin;
    172   const GURL kManifestUrl;
    173 
    174   scoped_ptr<AppCacheService> service_;
    175   int delete_result_;
    176   int delete_completion_count_;
    177   net::CompletionCallback deletion_callback_;
    178 
    179  private:
    180   base::MessageLoop message_loop_;
    181 };
    182 
    183 TEST_F(AppCacheServiceTest, DeleteAppCachesForOrigin) {
    184   // Without giving mock storage simiulated info, should fail.
    185   service_->DeleteAppCachesForOrigin(kOrigin, deletion_callback_);
    186   EXPECT_EQ(0, delete_completion_count_);
    187   base::MessageLoop::current()->RunUntilIdle();
    188   EXPECT_EQ(1, delete_completion_count_);
    189   EXPECT_EQ(net::ERR_FAILED, delete_result_);
    190   delete_completion_count_ = 0;
    191 
    192   // Should succeed given an empty info collection.
    193   mock_storage()->SimulateGetAllInfo(new AppCacheInfoCollection);
    194   service_->DeleteAppCachesForOrigin(kOrigin, deletion_callback_);
    195   EXPECT_EQ(0, delete_completion_count_);
    196   base::MessageLoop::current()->RunUntilIdle();
    197   EXPECT_EQ(1, delete_completion_count_);
    198   EXPECT_EQ(net::OK, delete_result_);
    199   delete_completion_count_ = 0;
    200 
    201   scoped_refptr<AppCacheInfoCollection> info(new AppCacheInfoCollection);
    202 
    203   // Should succeed given a non-empty info collection.
    204   AppCacheInfo mock_manifest_1;
    205   AppCacheInfo mock_manifest_2;
    206   AppCacheInfo mock_manifest_3;
    207   mock_manifest_1.manifest_url = kOrigin.Resolve("manifest1");
    208   mock_manifest_2.manifest_url = kOrigin.Resolve("manifest2");
    209   mock_manifest_3.manifest_url = kOrigin.Resolve("manifest3");
    210   AppCacheInfoVector info_vector;
    211   info_vector.push_back(mock_manifest_1);
    212   info_vector.push_back(mock_manifest_2);
    213   info_vector.push_back(mock_manifest_3);
    214   info->infos_by_origin[kOrigin] = info_vector;
    215   mock_storage()->SimulateGetAllInfo(info.get());
    216   service_->DeleteAppCachesForOrigin(kOrigin, deletion_callback_);
    217   EXPECT_EQ(0, delete_completion_count_);
    218   base::MessageLoop::current()->RunUntilIdle();
    219   EXPECT_EQ(1, delete_completion_count_);
    220   EXPECT_EQ(net::OK, delete_result_);
    221   delete_completion_count_ = 0;
    222 
    223   // Should fail if storage fails to delete.
    224   info->infos_by_origin[kOrigin] = info_vector;
    225   mock_storage()->SimulateGetAllInfo(info.get());
    226   mock_storage()->SimulateMakeGroupObsoleteFailure();
    227   service_->DeleteAppCachesForOrigin(kOrigin, deletion_callback_);
    228   EXPECT_EQ(0, delete_completion_count_);
    229   base::MessageLoop::current()->RunUntilIdle();
    230   EXPECT_EQ(1, delete_completion_count_);
    231   EXPECT_EQ(net::ERR_FAILED, delete_result_);
    232   delete_completion_count_ = 0;
    233 
    234   // Should complete with abort error if the service is deleted
    235   // prior to a delete completion.
    236   service_->DeleteAppCachesForOrigin(kOrigin, deletion_callback_);
    237   EXPECT_EQ(0, delete_completion_count_);
    238   service_.reset();  // kill it
    239   EXPECT_EQ(1, delete_completion_count_);
    240   EXPECT_EQ(net::ERR_ABORTED, delete_result_);
    241   delete_completion_count_ = 0;
    242 
    243   // Let any tasks lingering from the sudden deletion run and verify
    244   // no other completion calls occur.
    245   base::MessageLoop::current()->RunUntilIdle();
    246   EXPECT_EQ(0, delete_completion_count_);
    247 }
    248 
    249 TEST_F(AppCacheServiceTest, CheckAppCacheResponse) {
    250   // Check a non-existing manifest.
    251   EXPECT_FALSE(IsGroupStored(kManifestUrl));
    252   service_->CheckAppCacheResponse(kManifestUrl, 1, 1);
    253   base::MessageLoop::current()->RunUntilIdle();
    254   EXPECT_EQ(0, CountPendingHelpers());
    255   EXPECT_FALSE(IsGroupStored(kManifestUrl));
    256   ResetStorage();
    257 
    258   // Check a response that looks good.
    259   // Nothing should be deleted.
    260   SetupMockGroup();
    261   EXPECT_TRUE(IsGroupStored(kManifestUrl));
    262   SetupMockReader(true, true, true);
    263   service_->CheckAppCacheResponse(kManifestUrl, kMockCacheId, kMockResponseId);
    264   base::MessageLoop::current()->RunUntilIdle();
    265   EXPECT_EQ(0, CountPendingHelpers());
    266   EXPECT_TRUE(IsGroupStored(kManifestUrl));
    267   ResetStorage();
    268 
    269   // Check a response for which there is no cache entry.
    270   // The group should get deleted.
    271   SetupMockGroup();
    272   service_->CheckAppCacheResponse(kManifestUrl, kMockCacheId,
    273                                   kMissingResponseId);
    274   base::MessageLoop::current()->RunUntilIdle();
    275   EXPECT_EQ(0, CountPendingHelpers());
    276   EXPECT_FALSE(IsGroupStored(kManifestUrl));
    277   ResetStorage();
    278 
    279   // Check a response for which there is no manifest entry in a newer version
    280   // of the cache. Nothing should get deleted in this case.
    281   SetupMockGroup();
    282   service_->CheckAppCacheResponse(kManifestUrl, kMissingCacheId,
    283                                   kMissingResponseId);
    284   base::MessageLoop::current()->RunUntilIdle();
    285   EXPECT_EQ(0, CountPendingHelpers());
    286   EXPECT_TRUE(IsGroupStored(kManifestUrl));
    287   ResetStorage();
    288 
    289   // Check a response with bad headers.
    290   SetupMockGroup();
    291   service_->CheckAppCacheResponse(kManifestUrl, kMockCacheId, kMockResponseId);
    292   SetupMockReader(false, true, true);
    293   base::MessageLoop::current()->RunUntilIdle();
    294   EXPECT_EQ(0, CountPendingHelpers());
    295   EXPECT_FALSE(IsGroupStored(kManifestUrl));
    296   ResetStorage();
    297 
    298   // Check a response with bad data.
    299   SetupMockGroup();
    300   service_->CheckAppCacheResponse(kManifestUrl, kMockCacheId, kMockResponseId);
    301   SetupMockReader(true, false, true);
    302   base::MessageLoop::current()->RunUntilIdle();
    303   EXPECT_EQ(0, CountPendingHelpers());
    304   EXPECT_FALSE(IsGroupStored(kManifestUrl));
    305   ResetStorage();
    306 
    307   // Check a response with truncated data.
    308   SetupMockGroup();
    309   service_->CheckAppCacheResponse(kManifestUrl, kMockCacheId, kMockResponseId);
    310   SetupMockReader(true, true, false);
    311   base::MessageLoop::current()->RunUntilIdle();
    312   EXPECT_EQ(0, CountPendingHelpers());
    313   EXPECT_FALSE(IsGroupStored(kManifestUrl));
    314   ResetStorage();
    315 
    316   service_.reset();  // Clean up.
    317   base::MessageLoop::current()->RunUntilIdle();
    318 }
    319 
    320 }  // namespace appcache
    321