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 19namespace content { 20namespace { 21 22const int64 kMockGroupId = 1; 23const int64 kMockCacheId = 1; 24const int64 kMockResponseId = 1; 25const int64 kMissingCacheId = 5; 26const int64 kMissingResponseId = 5; 27const char kMockHeaders[] = 28 "HTTP/1.0 200 OK\0Content-Length: 5\0\0"; 29const char kMockBody[] = "Hello"; 30const int kMockBodySize = 5; 31 32class 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 83class 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 182TEST_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 248TEST_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. 320TEST_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