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.
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"
19 using appcache::AppCache;
20 using appcache::AppCacheEntry;
21 using appcache::AppCacheGroup;
22 using appcache::AppCacheInfo;
23 using appcache::AppCacheInfoCollection;
24 using appcache::AppCacheInfoVector;
25 using appcache::AppCacheResponseReader;
26 using appcache::AppCacheService;
27 using appcache::HttpResponseInfoIOBuffer;
32 const int64 kMockGroupId = 1;
33 const int64 kMockCacheId = 1;
34 const int64 kMockResponseId = 1;
35 const int64 kMissingCacheId = 5;
36 const int64 kMissingResponseId = 5;
37 const char kMockHeaders[] =
38 "HTTP/1.0 200 OK\0Content-Length: 5\0\0";
39 const char kMockBody[] = "Hello";
40 const int kMockBodySize = 5;
42 class MockResponseReader : public AppCacheResponseReader {
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) {
51 virtual void ReadInfo(HttpResponseInfoIOBuffer* info_buf,
52 const net::CompletionCallback& callback) OVERRIDE {
53 info_buffer_ = info_buf;
54 callback_ = callback; // Cleared on completion.
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);
61 virtual void ReadData(net::IOBuffer* buf, int buf_len,
62 const net::CompletionCallback& callback) OVERRIDE {
64 buffer_len_ = buf_len;
65 callback_ = callback; // Cleared on completion.
68 ScheduleUserCallback(net::ERR_CACHE_READ_FAILURE);
71 DCHECK(buf_len >= data_size_);
72 memcpy(buf->data(), data_, data_size_);
73 ScheduleUserCallback(data_size_);
78 void ScheduleUserCallback(int result) {
79 base::MessageLoop::current()->PostTask(FROM_HERE,
80 base::Bind(&MockResponseReader::InvokeUserCompletionCallback,
81 weak_factory_.GetWeakPtr(), result));
84 scoped_ptr<net::HttpResponseInfo> info_;
93 class AppCacheServiceTest : public testing::Test {
96 : kOrigin("http://hello/"),
97 kManifestUrl(kOrigin.Resolve("manifest")),
98 service_(new AppCacheService(NULL)),
99 delete_result_(net::OK), delete_completion_count_(0),
101 base::Bind(&AppCacheServiceTest::OnDeleteAppCachesComplete,
102 base::Unretained(this))) {
103 // Setup to use mock storage.
104 service_->storage_.reset(new MockAppCacheStorage(service_.get()));
107 void OnDeleteAppCachesComplete(int result) {
108 delete_result_ = result;
109 ++delete_completion_count_;
112 MockAppCacheStorage* mock_storage() {
113 return static_cast<MockAppCacheStorage*>(service_->storage());
116 void ResetStorage() {
117 service_->storage_.reset(new MockAppCacheStorage(service_.get()));
120 bool IsGroupStored(const GURL& manifest_url) {
121 return mock_storage()->IsGroupForManifestStored(manifest_url);
124 int CountPendingHelpers() {
125 return service_->pending_helpers_.size();
128 void SetupMockGroup() {
129 scoped_ptr<net::HttpResponseInfo> info(MakeMockResponseInfo());
130 const int kMockInfoSize = GetResponseInfoSize(info.get());
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));
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());
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,
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)));
168 int GetResponseInfoSize(const net::HttpResponseInfo* info) {
170 return PickleResponseInfo(&pickle, info);
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();
181 const GURL kManifestUrl;
183 scoped_ptr<AppCacheService> service_;
185 int delete_completion_count_;
186 net::CompletionCallback deletion_callback_;
189 base::MessageLoop message_loop_;
192 TEST_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;
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;
210 scoped_refptr<AppCacheInfoCollection> info(new AppCacheInfoCollection);
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;
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;
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;
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_);
258 TEST_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));
267 // Check a response that looks good.
268 // Nothing should be deleted.
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));
278 // Check a response for which there is no cache entry.
279 // The group should get deleted.
281 service_->CheckAppCacheResponse(kManifestUrl, kMockCacheId,
283 base::RunLoop().RunUntilIdle();
284 EXPECT_EQ(0, CountPendingHelpers());
285 EXPECT_FALSE(IsGroupStored(kManifestUrl));
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.
291 service_->CheckAppCacheResponse(kManifestUrl, kMissingCacheId,
293 base::RunLoop().RunUntilIdle();
294 EXPECT_EQ(0, CountPendingHelpers());
295 EXPECT_TRUE(IsGroupStored(kManifestUrl));
298 // Check a response with bad headers.
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));
307 // Check a response with bad data.
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));
316 // Check a response with truncated data.
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));
325 service_.reset(); // Clean up.
326 base::RunLoop().RunUntilIdle();
329 // Just tests the backoff scheduling function, not the actual reinit function.
330 TEST_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));
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_);
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_);
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_);
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_);
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_);
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_);
380 // Fine to delete while pending.
386 } // namespace content