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.
5 #include "content/browser/service_worker/service_worker_cache.h"
7 #include "base/files/file_path.h"
8 #include "base/files/scoped_temp_dir.h"
9 #include "base/message_loop/message_loop_proxy.h"
10 #include "base/run_loop.h"
11 #include "content/browser/fileapi/chrome_blob_storage_context.h"
12 #include "content/browser/fileapi/mock_url_request_delegate.h"
13 #include "content/common/service_worker/service_worker_types.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "content/public/test/test_browser_context.h"
16 #include "content/public/test/test_browser_thread_bundle.h"
17 #include "net/url_request/url_request_context.h"
18 #include "net/url_request/url_request_context_getter.h"
19 #include "net/url_request/url_request_job_factory_impl.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21 #include "webkit/browser/blob/blob_data_handle.h"
22 #include "webkit/browser/blob/blob_storage_context.h"
23 #include "webkit/browser/blob/blob_url_request_job_factory.h"
24 #include "webkit/common/blob/blob_data.h"
29 const char kTestData
[] = "Hello World";
31 // Returns a BlobProtocolHandler that uses |blob_storage_context|. Caller owns
33 storage::BlobProtocolHandler
* CreateMockBlobProtocolHandler(
34 storage::BlobStorageContext
* blob_storage_context
) {
35 // The FileSystemContext and MessageLoopProxy are not actually used but a
36 // MessageLoopProxy is needed to avoid a DCHECK in BlobURLRequestJob ctor.
37 return new storage::BlobProtocolHandler(
38 blob_storage_context
, NULL
, base::MessageLoopProxy::current().get());
43 class ServiceWorkerCacheTest
: public testing::Test
{
45 ServiceWorkerCacheTest()
46 : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP
),
47 callback_error_(ServiceWorkerCache::ErrorTypeOK
) {}
49 virtual void SetUp() OVERRIDE
{
50 ChromeBlobStorageContext
* blob_storage_context
=
51 ChromeBlobStorageContext::GetFor(&browser_context_
);
52 // Wait for chrome_blob_storage_context to finish initializing.
53 base::RunLoop().RunUntilIdle();
54 blob_storage_context_
= blob_storage_context
->context();
56 url_request_job_factory_
.reset(new net::URLRequestJobFactoryImpl
);
57 url_request_job_factory_
->SetProtocolHandler(
58 "blob", CreateMockBlobProtocolHandler(blob_storage_context
->context()));
60 net::URLRequestContext
* url_request_context
=
61 browser_context_
.GetRequestContext()->GetURLRequestContext();
63 url_request_context
->set_job_factory(url_request_job_factory_
.get());
65 CreateRequests(blob_storage_context
);
68 cache_
= ServiceWorkerCache::CreateMemoryCache(
70 blob_storage_context
->context()->AsWeakPtr());
72 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
73 cache_
= ServiceWorkerCache::CreatePersistentCache(
76 blob_storage_context
->context()->AsWeakPtr());
81 void CreateRequests(ChromeBlobStorageContext
* blob_storage_context
) {
82 std::map
<std::string
, std::string
> headers
;
83 headers
.insert(std::make_pair("a", "a"));
84 headers
.insert(std::make_pair("b", "b"));
85 body_request_
.reset(new ServiceWorkerFetchRequest(
86 GURL("http://example.com/body.html"), "GET", headers
, GURL(""), false));
87 no_body_request_
.reset(
88 new ServiceWorkerFetchRequest(GURL("http://example.com/no_body.html"),
94 std::string expected_response
;
95 for (int i
= 0; i
< 100; ++i
)
96 expected_blob_data_
+= kTestData
;
98 scoped_refptr
<storage::BlobData
> blob_data(
99 new storage::BlobData("blob-id:myblob"));
100 blob_data
->AppendData(expected_blob_data_
);
102 blob_handle_
= blob_storage_context
->context()->AddFinishedBlob(blob_data
);
104 body_response_
.reset(
105 new ServiceWorkerResponse(GURL("http://example.com/body.html"),
109 blob_handle_
->uuid()));
111 no_body_response_
.reset(new ServiceWorkerResponse(
112 GURL("http://example.com/no_body.html"), 200, "OK", headers
, ""));
115 void CreateBackend() {
116 scoped_ptr
<base::RunLoop
> loop(new base::RunLoop());
117 cache_
->CreateBackend(base::Bind(&ServiceWorkerCacheTest::ErrorTypeCallback
,
118 base::Unretained(this),
119 base::Unretained(loop
.get())));
121 EXPECT_EQ(ServiceWorkerCache::ErrorTypeOK
, callback_error_
);
124 bool Put(ServiceWorkerFetchRequest
* request
,
125 ServiceWorkerResponse
* response
) {
126 scoped_ptr
<base::RunLoop
> loop(new base::RunLoop());
130 base::Bind(&ServiceWorkerCacheTest::ErrorTypeCallback
,
131 base::Unretained(this),
132 base::Unretained(loop
.get())));
133 // TODO(jkarlin): These functions should use base::RunLoop().RunUntilIdle()
134 // once the cache uses a passed in MessageLoopProxy instead of the CACHE
138 return callback_error_
== ServiceWorkerCache::ErrorTypeOK
;
141 bool Match(ServiceWorkerFetchRequest
* request
) {
142 scoped_ptr
<base::RunLoop
> loop(new base::RunLoop());
144 cache_
->Match(request
,
145 base::Bind(&ServiceWorkerCacheTest::ResponseAndErrorCallback
,
146 base::Unretained(this),
147 base::Unretained(loop
.get())));
150 return callback_error_
== ServiceWorkerCache::ErrorTypeOK
;
153 bool Delete(ServiceWorkerFetchRequest
* request
) {
154 scoped_ptr
<base::RunLoop
> loop(new base::RunLoop());
156 cache_
->Delete(request
,
157 base::Bind(&ServiceWorkerCacheTest::ErrorTypeCallback
,
158 base::Unretained(this),
159 base::Unretained(loop
.get())));
162 return callback_error_
== ServiceWorkerCache::ErrorTypeOK
;
165 void ErrorTypeCallback(base::RunLoop
* run_loop
,
166 ServiceWorkerCache::ErrorType error
) {
167 callback_error_
= error
;
171 void ResponseAndErrorCallback(
172 base::RunLoop
* run_loop
,
173 ServiceWorkerCache::ErrorType error
,
174 scoped_ptr
<ServiceWorkerResponse
> response
,
175 scoped_ptr
<storage::BlobDataHandle
> body_handle
) {
176 callback_error_
= error
;
177 callback_response_
= response
.Pass();
179 if (error
== ServiceWorkerCache::ErrorTypeOK
&&
180 !callback_response_
->blob_uuid
.empty()) {
181 callback_response_data_
= body_handle
.Pass();
187 void CopyBody(storage::BlobDataHandle
* blob_handle
, std::string
* output
) {
188 storage::BlobData
* data
= blob_handle
->data();
189 std::vector
<storage::BlobData::Item
> items
= data
->items();
190 for (size_t i
= 0, max
= items
.size(); i
< max
; ++i
)
191 output
->append(items
[i
].bytes(), items
[i
].length());
194 virtual bool MemoryOnly() { return false; }
197 TestBrowserContext browser_context_
;
198 TestBrowserThreadBundle browser_thread_bundle_
;
199 scoped_ptr
<net::URLRequestJobFactoryImpl
> url_request_job_factory_
;
200 storage::BlobStorageContext
* blob_storage_context_
;
202 base::ScopedTempDir temp_dir_
;
203 scoped_ptr
<ServiceWorkerCache
> cache_
;
205 scoped_ptr
<ServiceWorkerFetchRequest
> body_request_
;
206 scoped_ptr
<ServiceWorkerResponse
> body_response_
;
207 scoped_ptr
<ServiceWorkerFetchRequest
> no_body_request_
;
208 scoped_ptr
<ServiceWorkerResponse
> no_body_response_
;
209 scoped_ptr
<storage::BlobDataHandle
> blob_handle_
;
210 std::string expected_blob_data_
;
212 ServiceWorkerCache::ErrorType callback_error_
;
213 scoped_ptr
<ServiceWorkerResponse
> callback_response_
;
214 scoped_ptr
<storage::BlobDataHandle
> callback_response_data_
;
217 class ServiceWorkerCacheTestP
: public ServiceWorkerCacheTest
,
218 public testing::WithParamInterface
<bool> {
219 virtual bool MemoryOnly() OVERRIDE
{ return !GetParam(); }
222 TEST_P(ServiceWorkerCacheTestP
, PutNoBody
) {
223 EXPECT_TRUE(Put(no_body_request_
.get(), no_body_response_
.get()));
226 TEST_P(ServiceWorkerCacheTestP
, PutBody
) {
227 EXPECT_TRUE(Put(body_request_
.get(), body_response_
.get()));
230 TEST_P(ServiceWorkerCacheTestP
, PutBodyDropBlobRef
) {
231 scoped_ptr
<base::RunLoop
> loop(new base::RunLoop());
232 cache_
->Put(body_request_
.get(),
233 body_response_
.get(),
234 base::Bind(&ServiceWorkerCacheTestP::ErrorTypeCallback
,
235 base::Unretained(this),
236 base::Unretained(loop
.get())));
237 // The handle should be held by the cache now so the deref here should be
239 blob_handle_
.reset();
242 EXPECT_EQ(ServiceWorkerCache::ErrorTypeOK
, callback_error_
);
245 TEST_P(ServiceWorkerCacheTestP
, DeleteNoBody
) {
246 EXPECT_TRUE(Put(no_body_request_
.get(), no_body_response_
.get()));
247 EXPECT_TRUE(Match(no_body_request_
.get()));
248 EXPECT_TRUE(Delete(no_body_request_
.get()));
249 EXPECT_FALSE(Match(no_body_request_
.get()));
250 EXPECT_FALSE(Delete(no_body_request_
.get()));
251 EXPECT_TRUE(Put(no_body_request_
.get(), no_body_response_
.get()));
252 EXPECT_TRUE(Match(no_body_request_
.get()));
253 EXPECT_TRUE(Delete(no_body_request_
.get()));
256 TEST_P(ServiceWorkerCacheTestP
, DeleteBody
) {
257 EXPECT_TRUE(Put(body_request_
.get(), body_response_
.get()));
258 EXPECT_TRUE(Match(body_request_
.get()));
259 EXPECT_TRUE(Delete(body_request_
.get()));
260 EXPECT_FALSE(Match(body_request_
.get()));
261 EXPECT_FALSE(Delete(body_request_
.get()));
262 EXPECT_TRUE(Put(body_request_
.get(), body_response_
.get()));
263 EXPECT_TRUE(Match(body_request_
.get()));
264 EXPECT_TRUE(Delete(body_request_
.get()));
267 TEST_P(ServiceWorkerCacheTestP
, MatchNoBody
) {
268 EXPECT_TRUE(Put(no_body_request_
.get(), no_body_response_
.get()));
269 EXPECT_TRUE(Match(no_body_request_
.get()));
270 EXPECT_EQ(200, callback_response_
->status_code
);
271 EXPECT_STREQ("OK", callback_response_
->status_text
.c_str());
272 EXPECT_STREQ("http://example.com/no_body.html",
273 callback_response_
->url
.spec().c_str());
276 TEST_P(ServiceWorkerCacheTestP
, MatchBody
) {
277 EXPECT_TRUE(Put(body_request_
.get(), body_response_
.get()));
278 EXPECT_TRUE(Match(body_request_
.get()));
279 EXPECT_EQ(200, callback_response_
->status_code
);
280 EXPECT_STREQ("OK", callback_response_
->status_text
.c_str());
281 EXPECT_STREQ("http://example.com/body.html",
282 callback_response_
->url
.spec().c_str());
283 std::string response_body
;
284 CopyBody(callback_response_data_
.get(), &response_body
);
285 EXPECT_STREQ(expected_blob_data_
.c_str(), response_body
.c_str());
288 TEST_P(ServiceWorkerCacheTestP
, QuickStressNoBody
) {
289 for (int i
= 0; i
< 100; ++i
) {
290 EXPECT_FALSE(Match(no_body_request_
.get()));
291 EXPECT_TRUE(Put(no_body_request_
.get(), no_body_response_
.get()));
292 EXPECT_TRUE(Match(no_body_request_
.get()));
293 EXPECT_TRUE(Delete(no_body_request_
.get()));
297 TEST_P(ServiceWorkerCacheTestP
, QuickStressBody
) {
298 for (int i
= 0; i
< 100; ++i
) {
299 ASSERT_FALSE(Match(body_request_
.get()));
300 ASSERT_TRUE(Put(body_request_
.get(), body_response_
.get()));
301 ASSERT_TRUE(Match(body_request_
.get()));
302 ASSERT_TRUE(Delete(body_request_
.get()));
306 INSTANTIATE_TEST_CASE_P(ServiceWorkerCacheTest
,
307 ServiceWorkerCacheTestP
,
308 ::testing::Values(false, true));
310 } // namespace content