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 "base/files/scoped_temp_dir.h"
6 #include "base/logging.h"
7 #include "base/run_loop.h"
8 #include "content/browser/browser_thread_impl.h"
9 #include "content/browser/service_worker/embedded_worker_registry.h"
10 #include "content/browser/service_worker/embedded_worker_test_helper.h"
11 #include "content/browser/service_worker/service_worker_context_core.h"
12 #include "content/browser/service_worker/service_worker_job_coordinator.h"
13 #include "content/browser/service_worker/service_worker_registration.h"
14 #include "content/browser/service_worker/service_worker_registration_status.h"
15 #include "content/public/test/test_browser_thread_bundle.h"
16 #include "ipc/ipc_test_sink.h"
17 #include "testing/gtest/include/gtest/gtest.h"
19 // Unit tests for testing all job registration tasks.
24 void SaveRegistrationCallback(
25 ServiceWorkerStatusCode expected_status,
27 scoped_refptr<ServiceWorkerRegistration>* registration_out,
28 ServiceWorkerStatusCode status,
29 ServiceWorkerRegistration* registration,
30 ServiceWorkerVersion* version) {
31 ASSERT_TRUE(!version || version->registration_id() == registration->id())
32 << version << " " << registration;
33 EXPECT_EQ(expected_status, status);
35 *registration_out = registration;
38 void SaveFoundRegistrationCallback(
39 ServiceWorkerStatusCode expected_status,
41 scoped_refptr<ServiceWorkerRegistration>* registration,
42 ServiceWorkerStatusCode status,
43 const scoped_refptr<ServiceWorkerRegistration>& result) {
44 EXPECT_EQ(expected_status, status);
46 *registration = result;
49 // Creates a callback which both keeps track of if it's been called,
50 // as well as the resulting registration. Whent the callback is fired,
51 // it ensures that the resulting status matches the expectation.
52 // 'called' is useful for making sure a sychronous callback is or
54 ServiceWorkerRegisterJob::RegistrationCallback SaveRegistration(
55 ServiceWorkerStatusCode expected_status,
57 scoped_refptr<ServiceWorkerRegistration>* registration) {
60 &SaveRegistrationCallback, expected_status, called, registration);
63 ServiceWorkerStorage::FindRegistrationCallback SaveFoundRegistration(
64 ServiceWorkerStatusCode expected_status,
66 scoped_refptr<ServiceWorkerRegistration>* registration) {
68 return base::Bind(&SaveFoundRegistrationCallback,
74 void SaveUnregistrationCallback(ServiceWorkerStatusCode expected_status,
76 ServiceWorkerStatusCode status) {
77 EXPECT_EQ(expected_status, status);
81 ServiceWorkerUnregisterJob::UnregistrationCallback SaveUnregistration(
82 ServiceWorkerStatusCode expected_status,
85 return base::Bind(&SaveUnregistrationCallback, expected_status, called);
90 class ServiceWorkerJobTest : public testing::Test {
92 ServiceWorkerJobTest()
93 : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
94 render_process_id_(88) {}
96 virtual void SetUp() OVERRIDE {
97 helper_.reset(new EmbeddedWorkerTestHelper(render_process_id_));
100 virtual void TearDown() OVERRIDE {
104 ServiceWorkerContextCore* context() const { return helper_->context(); }
106 ServiceWorkerJobCoordinator* job_coordinator() const {
107 return context()->job_coordinator();
109 ServiceWorkerStorage* storage() const { return context()->storage(); }
112 TestBrowserThreadBundle browser_thread_bundle_;
113 scoped_ptr<EmbeddedWorkerTestHelper> helper_;
115 int render_process_id_;
118 TEST_F(ServiceWorkerJobTest, SameDocumentSameRegistration) {
119 scoped_refptr<ServiceWorkerRegistration> original_registration;
121 job_coordinator()->Register(
122 GURL("http://www.example.com/*"),
123 GURL("http://www.example.com/service_worker.js"),
125 SaveRegistration(SERVICE_WORKER_OK, &called, &original_registration));
126 EXPECT_FALSE(called);
127 base::RunLoop().RunUntilIdle();
130 scoped_refptr<ServiceWorkerRegistration> registration1;
131 storage()->FindRegistrationForDocument(
132 GURL("http://www.example.com/"),
133 SaveFoundRegistration(SERVICE_WORKER_OK, &called, ®istration1));
134 scoped_refptr<ServiceWorkerRegistration> registration2;
135 storage()->FindRegistrationForDocument(
136 GURL("http://www.example.com/"),
137 SaveFoundRegistration(SERVICE_WORKER_OK, &called, ®istration2));
138 base::RunLoop().RunUntilIdle();
140 ASSERT_TRUE(registration1);
141 ASSERT_EQ(registration1, original_registration);
142 ASSERT_EQ(registration1, registration2);
145 TEST_F(ServiceWorkerJobTest, SameMatchSameRegistration) {
147 scoped_refptr<ServiceWorkerRegistration> original_registration;
148 job_coordinator()->Register(
149 GURL("http://www.example.com/*"),
150 GURL("http://www.example.com/service_worker.js"),
152 SaveRegistration(SERVICE_WORKER_OK, &called, &original_registration));
153 EXPECT_FALSE(called);
154 base::RunLoop().RunUntilIdle();
156 ASSERT_NE(static_cast<ServiceWorkerRegistration*>(NULL),
157 original_registration.get());
159 scoped_refptr<ServiceWorkerRegistration> registration1;
160 storage()->FindRegistrationForDocument(
161 GURL("http://www.example.com/one"),
162 SaveFoundRegistration(SERVICE_WORKER_OK, &called, ®istration1));
163 base::RunLoop().RunUntilIdle();
166 scoped_refptr<ServiceWorkerRegistration> registration2;
167 storage()->FindRegistrationForDocument(
168 GURL("http://www.example.com/two"),
169 SaveFoundRegistration(SERVICE_WORKER_OK, &called, ®istration2));
170 base::RunLoop().RunUntilIdle();
172 ASSERT_EQ(registration1, original_registration);
173 ASSERT_EQ(registration1, registration2);
176 TEST_F(ServiceWorkerJobTest, DifferentMatchDifferentRegistration) {
178 scoped_refptr<ServiceWorkerRegistration> original_registration1;
179 job_coordinator()->Register(
180 GURL("http://www.example.com/one/*"),
181 GURL("http://www.example.com/service_worker.js"),
183 SaveRegistration(SERVICE_WORKER_OK, &called1, &original_registration1));
186 scoped_refptr<ServiceWorkerRegistration> original_registration2;
187 job_coordinator()->Register(
188 GURL("http://www.example.com/two/*"),
189 GURL("http://www.example.com/service_worker.js"),
191 SaveRegistration(SERVICE_WORKER_OK, &called2, &original_registration2));
193 EXPECT_FALSE(called1);
194 EXPECT_FALSE(called2);
195 base::RunLoop().RunUntilIdle();
196 EXPECT_TRUE(called2);
197 EXPECT_TRUE(called1);
199 scoped_refptr<ServiceWorkerRegistration> registration1;
200 storage()->FindRegistrationForDocument(
201 GURL("http://www.example.com/one/"),
202 SaveFoundRegistration(SERVICE_WORKER_OK, &called1, ®istration1));
203 scoped_refptr<ServiceWorkerRegistration> registration2;
204 storage()->FindRegistrationForDocument(
205 GURL("http://www.example.com/two/"),
206 SaveFoundRegistration(SERVICE_WORKER_OK, &called2, ®istration2));
208 base::RunLoop().RunUntilIdle();
209 EXPECT_TRUE(called2);
210 EXPECT_TRUE(called1);
211 ASSERT_NE(registration1, registration2);
214 // Make sure basic registration is working.
215 TEST_F(ServiceWorkerJobTest, Register) {
217 scoped_refptr<ServiceWorkerRegistration> registration;
218 job_coordinator()->Register(
219 GURL("http://www.example.com/*"),
220 GURL("http://www.example.com/service_worker.js"),
222 SaveRegistration(SERVICE_WORKER_OK, &called, ®istration));
224 ASSERT_FALSE(called);
225 base::RunLoop().RunUntilIdle();
228 ASSERT_NE(scoped_refptr<ServiceWorkerRegistration>(NULL), registration);
231 // Make sure registrations are cleaned up when they are unregistered.
232 TEST_F(ServiceWorkerJobTest, Unregister) {
233 GURL pattern("http://www.example.com/*");
236 scoped_refptr<ServiceWorkerRegistration> registration;
237 job_coordinator()->Register(
239 GURL("http://www.example.com/service_worker.js"),
241 SaveRegistration(SERVICE_WORKER_OK, &called, ®istration));
243 ASSERT_FALSE(called);
244 base::RunLoop().RunUntilIdle();
247 job_coordinator()->Unregister(pattern,
248 SaveUnregistration(SERVICE_WORKER_OK, &called));
250 ASSERT_FALSE(called);
251 base::RunLoop().RunUntilIdle();
254 ASSERT_TRUE(registration->HasOneRef());
256 storage()->FindRegistrationForPattern(
258 SaveFoundRegistration(SERVICE_WORKER_ERROR_NOT_FOUND,
259 &called, ®istration));
261 ASSERT_FALSE(called);
262 base::RunLoop().RunUntilIdle();
265 ASSERT_EQ(scoped_refptr<ServiceWorkerRegistration>(NULL), registration);
268 TEST_F(ServiceWorkerJobTest, Unregister_NothingRegistered) {
269 GURL pattern("http://www.example.com/*");
272 job_coordinator()->Unregister(pattern,
273 SaveUnregistration(SERVICE_WORKER_OK, &called));
275 ASSERT_FALSE(called);
276 base::RunLoop().RunUntilIdle();
280 // Make sure that when a new registration replaces an existing
281 // registration, that the old one is cleaned up.
282 TEST_F(ServiceWorkerJobTest, RegisterNewScript) {
283 GURL pattern("http://www.example.com/*");
286 scoped_refptr<ServiceWorkerRegistration> old_registration;
287 job_coordinator()->Register(
289 GURL("http://www.example.com/service_worker.js"),
291 SaveRegistration(SERVICE_WORKER_OK, &called, &old_registration));
293 ASSERT_FALSE(called);
294 base::RunLoop().RunUntilIdle();
297 scoped_refptr<ServiceWorkerRegistration> old_registration_by_pattern;
298 storage()->FindRegistrationForPattern(
300 SaveFoundRegistration(
301 SERVICE_WORKER_OK, &called, &old_registration_by_pattern));
303 ASSERT_FALSE(called);
304 base::RunLoop().RunUntilIdle();
307 ASSERT_EQ(old_registration, old_registration_by_pattern);
308 old_registration_by_pattern = NULL;
310 scoped_refptr<ServiceWorkerRegistration> new_registration;
311 job_coordinator()->Register(
313 GURL("http://www.example.com/service_worker_new.js"),
315 SaveRegistration(SERVICE_WORKER_OK, &called, &new_registration));
317 ASSERT_FALSE(called);
318 base::RunLoop().RunUntilIdle();
321 ASSERT_TRUE(old_registration->HasOneRef());
323 ASSERT_NE(old_registration, new_registration);
325 scoped_refptr<ServiceWorkerRegistration> new_registration_by_pattern;
326 storage()->FindRegistrationForPattern(
328 SaveFoundRegistration(
329 SERVICE_WORKER_OK, &called, &new_registration));
331 ASSERT_FALSE(called);
332 base::RunLoop().RunUntilIdle();
335 ASSERT_NE(new_registration_by_pattern, old_registration);
338 // Make sure that when registering a duplicate pattern+script_url
339 // combination, that the same registration is used.
340 TEST_F(ServiceWorkerJobTest, RegisterDuplicateScript) {
341 GURL pattern("http://www.example.com/*");
342 GURL script_url("http://www.example.com/service_worker.js");
345 scoped_refptr<ServiceWorkerRegistration> old_registration;
346 job_coordinator()->Register(
350 SaveRegistration(SERVICE_WORKER_OK, &called, &old_registration));
352 ASSERT_FALSE(called);
353 base::RunLoop().RunUntilIdle();
356 scoped_refptr<ServiceWorkerRegistration> old_registration_by_pattern;
357 storage()->FindRegistrationForPattern(
359 SaveFoundRegistration(
360 SERVICE_WORKER_OK, &called, &old_registration_by_pattern));
361 ASSERT_FALSE(called);
362 base::RunLoop().RunUntilIdle();
365 ASSERT_TRUE(old_registration_by_pattern);
367 scoped_refptr<ServiceWorkerRegistration> new_registration;
368 job_coordinator()->Register(
372 SaveRegistration(SERVICE_WORKER_OK, &called, &new_registration));
374 ASSERT_FALSE(called);
375 base::RunLoop().RunUntilIdle();
378 ASSERT_EQ(old_registration, new_registration);
380 ASSERT_FALSE(old_registration->HasOneRef());
382 scoped_refptr<ServiceWorkerRegistration> new_registration_by_pattern;
383 storage()->FindRegistrationForPattern(
385 SaveFoundRegistration(
386 SERVICE_WORKER_OK, &called, &new_registration_by_pattern));
388 ASSERT_FALSE(called);
389 base::RunLoop().RunUntilIdle();
392 ASSERT_EQ(new_registration, old_registration);
395 class FailToStartWorkerTestHelper : public EmbeddedWorkerTestHelper {
397 FailToStartWorkerTestHelper(int mock_render_process_id)
398 : EmbeddedWorkerTestHelper(mock_render_process_id) {}
400 virtual void OnStartWorker(int embedded_worker_id,
401 int64 service_worker_version_id,
403 const GURL& script_url) OVERRIDE {
404 // Simulate failure by sending worker stopped instead of started.
405 EmbeddedWorkerInstance* worker = registry()->GetWorker(embedded_worker_id);
406 registry()->OnWorkerStopped(worker->process_id(), embedded_worker_id);
410 TEST_F(ServiceWorkerJobTest, Register_FailToStartWorker) {
411 helper_.reset(new FailToStartWorkerTestHelper(render_process_id_));
414 scoped_refptr<ServiceWorkerRegistration> registration;
415 job_coordinator()->Register(
416 GURL("http://www.example.com/*"),
417 GURL("http://www.example.com/service_worker.js"),
420 SERVICE_WORKER_ERROR_START_WORKER_FAILED, &called, ®istration));
422 ASSERT_FALSE(called);
423 base::RunLoop().RunUntilIdle();
426 ASSERT_EQ(scoped_refptr<ServiceWorkerRegistration>(NULL), registration);
429 // Register and then unregister the pattern, in parallel. Job coordinator should
430 // process jobs until the last job.
431 TEST_F(ServiceWorkerJobTest, ParallelRegUnreg) {
432 GURL pattern("http://www.example.com/*");
433 GURL script_url("http://www.example.com/service_worker.js");
435 bool registration_called = false;
436 scoped_refptr<ServiceWorkerRegistration> registration;
437 job_coordinator()->Register(
441 SaveRegistration(SERVICE_WORKER_OK, ®istration_called, ®istration));
443 bool unregistration_called = false;
444 job_coordinator()->Unregister(
446 SaveUnregistration(SERVICE_WORKER_OK, &unregistration_called));
448 ASSERT_FALSE(registration_called);
449 ASSERT_FALSE(unregistration_called);
450 base::RunLoop().RunUntilIdle();
451 ASSERT_TRUE(registration_called);
452 ASSERT_TRUE(unregistration_called);
454 bool find_called = false;
455 storage()->FindRegistrationForPattern(
457 SaveFoundRegistration(
458 SERVICE_WORKER_ERROR_NOT_FOUND, &find_called, ®istration));
460 base::RunLoop().RunUntilIdle();
462 ASSERT_EQ(scoped_refptr<ServiceWorkerRegistration>(), registration);
465 // Register conflicting scripts for the same pattern. The most recent
466 // registration should win, and the old registration should have been
468 TEST_F(ServiceWorkerJobTest, ParallelRegNewScript) {
469 GURL pattern("http://www.example.com/*");
471 GURL script_url1("http://www.example.com/service_worker1.js");
472 bool registration1_called = false;
473 scoped_refptr<ServiceWorkerRegistration> registration1;
474 job_coordinator()->Register(
479 SERVICE_WORKER_OK, ®istration1_called, ®istration1));
481 GURL script_url2("http://www.example.com/service_worker2.js");
482 bool registration2_called = false;
483 scoped_refptr<ServiceWorkerRegistration> registration2;
484 job_coordinator()->Register(
489 SERVICE_WORKER_OK, ®istration2_called, ®istration2));
491 ASSERT_FALSE(registration1_called);
492 ASSERT_FALSE(registration2_called);
493 base::RunLoop().RunUntilIdle();
494 ASSERT_TRUE(registration1_called);
495 ASSERT_TRUE(registration2_called);
497 scoped_refptr<ServiceWorkerRegistration> registration;
498 bool find_called = false;
499 storage()->FindRegistrationForPattern(
501 SaveFoundRegistration(
502 SERVICE_WORKER_OK, &find_called, ®istration));
504 base::RunLoop().RunUntilIdle();
506 ASSERT_EQ(registration2, registration);
509 // Register the exact same pattern + script. Requests should be
510 // coalesced such that both callers get the exact same registration
512 TEST_F(ServiceWorkerJobTest, ParallelRegSameScript) {
513 GURL pattern("http://www.example.com/*");
515 GURL script_url("http://www.example.com/service_worker1.js");
516 bool registration1_called = false;
517 scoped_refptr<ServiceWorkerRegistration> registration1;
518 job_coordinator()->Register(
523 SERVICE_WORKER_OK, ®istration1_called, ®istration1));
525 bool registration2_called = false;
526 scoped_refptr<ServiceWorkerRegistration> registration2;
527 job_coordinator()->Register(
532 SERVICE_WORKER_OK, ®istration2_called, ®istration2));
534 ASSERT_FALSE(registration1_called);
535 ASSERT_FALSE(registration2_called);
536 base::RunLoop().RunUntilIdle();
537 ASSERT_TRUE(registration1_called);
538 ASSERT_TRUE(registration2_called);
540 ASSERT_EQ(registration1, registration2);
542 scoped_refptr<ServiceWorkerRegistration> registration;
543 bool find_called = false;
544 storage()->FindRegistrationForPattern(
546 SaveFoundRegistration(
547 SERVICE_WORKER_OK, &find_called, ®istration));
549 base::RunLoop().RunUntilIdle();
550 ASSERT_EQ(registration, registration1);
553 // Call simulataneous unregister calls.
554 TEST_F(ServiceWorkerJobTest, ParallelUnreg) {
555 GURL pattern("http://www.example.com/*");
557 GURL script_url("http://www.example.com/service_worker.js");
558 bool unregistration1_called = false;
559 job_coordinator()->Unregister(
561 SaveUnregistration(SERVICE_WORKER_OK, &unregistration1_called));
563 bool unregistration2_called = false;
564 job_coordinator()->Unregister(
565 pattern, SaveUnregistration(SERVICE_WORKER_OK, &unregistration2_called));
567 ASSERT_FALSE(unregistration1_called);
568 ASSERT_FALSE(unregistration2_called);
569 base::RunLoop().RunUntilIdle();
570 ASSERT_TRUE(unregistration1_called);
571 ASSERT_TRUE(unregistration2_called);
573 // There isn't really a way to test that they are being coalesced,
574 // but we can make sure they can exist simultaneously without
576 scoped_refptr<ServiceWorkerRegistration> registration;
577 bool find_called = false;
578 storage()->FindRegistrationForPattern(
580 SaveFoundRegistration(
581 SERVICE_WORKER_ERROR_NOT_FOUND, &find_called, ®istration));
583 base::RunLoop().RunUntilIdle();
584 ASSERT_EQ(scoped_refptr<ServiceWorkerRegistration>(), registration);
587 } // namespace content