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