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/pickle.h" 10 #include "base/run_loop.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::RunLoop().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::RunLoop().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::RunLoop().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::RunLoop().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::RunLoop().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::RunLoop().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::RunLoop().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::RunLoop().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::RunLoop().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::RunLoop().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::RunLoop().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::RunLoop().RunUntilIdle(); 312 EXPECT_EQ(0, CountPendingHelpers()); 313 EXPECT_FALSE(IsGroupStored(kManifestUrl)); 314 ResetStorage(); 315 316 service_.reset(); // Clean up. 317 base::RunLoop().RunUntilIdle(); 318 } 319 320 } // namespace appcache 321