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/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"
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;
32 class MockResponseReader
: public AppCacheResponseReader
{
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
) {
41 virtual void ReadInfo(HttpResponseInfoIOBuffer
* info_buf
,
42 const net::CompletionCallback
& callback
) OVERRIDE
{
43 info_buffer_
= info_buf
;
44 callback_
= callback
; // Cleared on completion.
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
);
51 virtual void ReadData(net::IOBuffer
* buf
, int buf_len
,
52 const net::CompletionCallback
& callback
) OVERRIDE
{
54 buffer_len_
= buf_len
;
55 callback_
= callback
; // Cleared on completion.
58 ScheduleUserCallback(net::ERR_CACHE_READ_FAILURE
);
61 DCHECK(buf_len
>= data_size_
);
62 memcpy(buf
->data(), data_
, data_size_
);
63 ScheduleUserCallback(data_size_
);
68 void ScheduleUserCallback(int result
) {
69 base::MessageLoop::current()->PostTask(FROM_HERE
,
70 base::Bind(&MockResponseReader::InvokeUserCompletionCallback
,
71 weak_factory_
.GetWeakPtr(), result
));
74 scoped_ptr
<net::HttpResponseInfo
> info_
;
83 class AppCacheServiceImplTest
: public testing::Test
{
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),
91 base::Bind(&AppCacheServiceImplTest::OnDeleteAppCachesComplete
,
92 base::Unretained(this))) {
93 // Setup to use mock storage.
94 service_
->storage_
.reset(new MockAppCacheStorage(service_
.get()));
97 void OnDeleteAppCachesComplete(int result
) {
98 delete_result_
= result
;
99 ++delete_completion_count_
;
102 MockAppCacheStorage
* mock_storage() {
103 return static_cast<MockAppCacheStorage
*>(service_
->storage());
106 void ResetStorage() {
107 service_
->storage_
.reset(new MockAppCacheStorage(service_
.get()));
110 bool IsGroupStored(const GURL
& manifest_url
) {
111 return mock_storage()->IsGroupForManifestStored(manifest_url
);
114 int CountPendingHelpers() {
115 return service_
->pending_helpers_
.size();
118 void SetupMockGroup() {
119 scoped_ptr
<net::HttpResponseInfo
> info(MakeMockResponseInfo());
120 const int kMockInfoSize
= GetResponseInfoSize(info
.get());
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
));
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());
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
,
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
)));
158 int GetResponseInfoSize(const net::HttpResponseInfo
* info
) {
160 return PickleResponseInfo(&pickle
, info
);
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();
171 const GURL kManifestUrl
;
173 scoped_ptr
<AppCacheServiceImpl
> service_
;
175 int delete_completion_count_
;
176 net::CompletionCallback deletion_callback_
;
179 base::MessageLoop message_loop_
;
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;
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;
200 scoped_refptr
<AppCacheInfoCollection
> info(new AppCacheInfoCollection
);
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;
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;
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;
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_
);
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
));
257 // Check a response that looks good.
258 // Nothing should be deleted.
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
));
268 // Check a response for which there is no cache entry.
269 // The group should get deleted.
271 service_
->CheckAppCacheResponse(kManifestUrl
, kMockCacheId
,
273 base::RunLoop().RunUntilIdle();
274 EXPECT_EQ(0, CountPendingHelpers());
275 EXPECT_FALSE(IsGroupStored(kManifestUrl
));
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.
281 service_
->CheckAppCacheResponse(kManifestUrl
, kMissingCacheId
,
283 base::RunLoop().RunUntilIdle();
284 EXPECT_EQ(0, CountPendingHelpers());
285 EXPECT_TRUE(IsGroupStored(kManifestUrl
));
288 // Check a response with bad headers.
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
));
297 // Check a response with bad data.
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
));
306 // Check a response with truncated data.
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
));
315 service_
.reset(); // Clean up.
316 base::RunLoop().RunUntilIdle();
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));
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_
);
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_
);
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_
);
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_
);
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_
);
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_
);
370 // Fine to delete while pending.
376 } // namespace content