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