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