1 // Copyright 2013 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/basictypes.h"
6 #include "base/run_loop.h"
7 #include "base/stl_util.h"
8 #include "content/browser/service_worker/embedded_worker_instance.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_context_wrapper.h"
13 #include "content/common/service_worker/embedded_worker_messages.h"
14 #include "content/public/test/test_browser_thread_bundle.h"
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
22 const int kRenderProcessId
= 11;
24 void SaveStatus(ServiceWorkerStatusCode
* out
, ServiceWorkerStatusCode status
) {
28 void SaveStatusAndCall(ServiceWorkerStatusCode
* out
,
29 const base::Closure
& callback
,
30 ServiceWorkerStatusCode status
) {
37 class EmbeddedWorkerInstanceTest
: public testing::Test
,
38 public EmbeddedWorkerInstance::Listener
{
40 EmbeddedWorkerInstanceTest()
41 : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP
) {}
43 void OnDetached(EmbeddedWorkerInstance::Status old_status
) override
{
45 detached_old_status_
= old_status
;
48 bool OnMessageReceived(const IPC::Message
& message
) override
{ return false; }
50 void SetUp() override
{
52 new EmbeddedWorkerTestHelper(base::FilePath(), kRenderProcessId
));
55 void TearDown() override
{ helper_
.reset(); }
57 ServiceWorkerStatusCode
StartWorker(EmbeddedWorkerInstance
* worker
,
58 int id
, const GURL
& pattern
,
60 ServiceWorkerStatusCode status
;
61 base::RunLoop run_loop
;
62 worker
->Start(id
, pattern
, url
, base::Bind(&SaveStatusAndCall
, &status
,
63 run_loop
.QuitClosure()));
68 ServiceWorkerContextCore
* context() { return helper_
->context(); }
70 EmbeddedWorkerRegistry
* embedded_worker_registry() {
72 return context()->embedded_worker_registry();
75 IPC::TestSink
* ipc_sink() { return helper_
->ipc_sink(); }
77 TestBrowserThreadBundle thread_bundle_
;
78 scoped_ptr
<EmbeddedWorkerTestHelper
> helper_
;
79 bool detached_
= false;
80 EmbeddedWorkerInstance::Status detached_old_status_
=
81 EmbeddedWorkerInstance::STOPPED
;
84 DISALLOW_COPY_AND_ASSIGN(EmbeddedWorkerInstanceTest
);
87 TEST_F(EmbeddedWorkerInstanceTest
, StartAndStop
) {
88 scoped_ptr
<EmbeddedWorkerInstance
> worker
=
89 embedded_worker_registry()->CreateWorker();
90 EXPECT_EQ(EmbeddedWorkerInstance::STOPPED
, worker
->status());
92 const int64 service_worker_version_id
= 55L;
93 const GURL
pattern("http://example.com/");
94 const GURL
url("http://example.com/worker.js");
96 // Simulate adding one process to the pattern.
97 helper_
->SimulateAddProcessToPattern(pattern
, kRenderProcessId
);
99 // Start should succeed.
100 ServiceWorkerStatusCode status
;
101 base::RunLoop run_loop
;
103 service_worker_version_id
,
106 base::Bind(&SaveStatusAndCall
, &status
, run_loop
.QuitClosure()));
107 EXPECT_EQ(EmbeddedWorkerInstance::STARTING
, worker
->status());
109 EXPECT_EQ(SERVICE_WORKER_OK
, status
);
111 // The 'WorkerStarted' message should have been sent by
112 // EmbeddedWorkerTestHelper.
113 EXPECT_EQ(EmbeddedWorkerInstance::RUNNING
, worker
->status());
114 EXPECT_EQ(kRenderProcessId
, worker
->process_id());
117 EXPECT_EQ(SERVICE_WORKER_OK
, worker
->Stop());
118 EXPECT_EQ(EmbeddedWorkerInstance::STOPPING
, worker
->status());
119 base::RunLoop().RunUntilIdle();
121 // The 'WorkerStopped' message should have been sent by
122 // EmbeddedWorkerTestHelper.
123 EXPECT_EQ(EmbeddedWorkerInstance::STOPPED
, worker
->status());
125 // Verify that we've sent two messages to start and terminate the worker.
127 ipc_sink()->GetUniqueMessageMatching(EmbeddedWorkerMsg_StartWorker::ID
));
128 ASSERT_TRUE(ipc_sink()->GetUniqueMessageMatching(
129 EmbeddedWorkerMsg_StopWorker::ID
));
132 TEST_F(EmbeddedWorkerInstanceTest
, StopWhenDevToolsAttached
) {
133 scoped_ptr
<EmbeddedWorkerInstance
> worker
=
134 embedded_worker_registry()->CreateWorker();
135 EXPECT_EQ(EmbeddedWorkerInstance::STOPPED
, worker
->status());
137 const int64 service_worker_version_id
= 55L;
138 const GURL
pattern("http://example.com/");
139 const GURL
url("http://example.com/worker.js");
141 // Simulate adding one process to the pattern.
142 helper_
->SimulateAddProcessToPattern(pattern
, kRenderProcessId
);
144 // Start the worker and then call StopIfIdle().
145 EXPECT_EQ(SERVICE_WORKER_OK
,
146 StartWorker(worker
.get(), service_worker_version_id
, pattern
, url
));
147 EXPECT_EQ(EmbeddedWorkerInstance::RUNNING
, worker
->status());
148 EXPECT_EQ(kRenderProcessId
, worker
->process_id());
149 worker
->StopIfIdle();
150 EXPECT_EQ(EmbeddedWorkerInstance::STOPPING
, worker
->status());
151 base::RunLoop().RunUntilIdle();
153 // The worker must be stopped now.
154 EXPECT_EQ(EmbeddedWorkerInstance::STOPPED
, worker
->status());
156 // Set devtools_attached to true, and do the same.
157 worker
->set_devtools_attached(true);
159 EXPECT_EQ(SERVICE_WORKER_OK
,
160 StartWorker(worker
.get(), service_worker_version_id
, pattern
, url
));
161 EXPECT_EQ(EmbeddedWorkerInstance::RUNNING
, worker
->status());
162 EXPECT_EQ(kRenderProcessId
, worker
->process_id());
163 worker
->StopIfIdle();
164 base::RunLoop().RunUntilIdle();
166 // The worker must not be stopped this time.
167 EXPECT_EQ(EmbeddedWorkerInstance::RUNNING
, worker
->status());
169 // Calling Stop() actually stops the worker regardless of whether devtools
170 // is attached or not.
171 EXPECT_EQ(SERVICE_WORKER_OK
, worker
->Stop());
172 base::RunLoop().RunUntilIdle();
173 EXPECT_EQ(EmbeddedWorkerInstance::STOPPED
, worker
->status());
176 // Test that the removal of a worker from the registry doesn't remove
177 // other workers in the same process.
178 TEST_F(EmbeddedWorkerInstanceTest
, RemoveWorkerInSharedProcess
) {
179 scoped_ptr
<EmbeddedWorkerInstance
> worker1
=
180 embedded_worker_registry()->CreateWorker();
181 scoped_ptr
<EmbeddedWorkerInstance
> worker2
=
182 embedded_worker_registry()->CreateWorker();
184 const int64 version_id1
= 55L;
185 const int64 version_id2
= 56L;
186 const GURL
pattern("http://example.com/");
187 const GURL
url("http://example.com/worker.js");
189 helper_
->SimulateAddProcessToPattern(pattern
, kRenderProcessId
);
192 ServiceWorkerStatusCode status
;
193 base::RunLoop run_loop
;
195 version_id1
, pattern
, url
,
196 base::Bind(&SaveStatusAndCall
, &status
, run_loop
.QuitClosure()));
198 EXPECT_EQ(SERVICE_WORKER_OK
, status
);
203 ServiceWorkerStatusCode status
;
204 base::RunLoop run_loop
;
206 version_id2
, pattern
, url
,
207 base::Bind(&SaveStatusAndCall
, &status
, run_loop
.QuitClosure()));
209 EXPECT_EQ(SERVICE_WORKER_OK
, status
);
212 // The two workers share the same process.
213 EXPECT_EQ(worker1
->process_id(), worker2
->process_id());
215 // Destroy worker1. It removes itself from the registry.
216 int worker1_id
= worker1
->embedded_worker_id();
220 // Only worker1 should be removed from the registry's process_map.
221 EmbeddedWorkerRegistry
* registry
=
222 helper_
->context()->embedded_worker_registry();
224 registry
->worker_process_map_
[kRenderProcessId
].count(worker1_id
));
225 EXPECT_EQ(1UL, registry
->worker_process_map_
[kRenderProcessId
].count(
226 worker2
->embedded_worker_id()));
231 // Test detaching in the middle of the start worker sequence.
232 TEST_F(EmbeddedWorkerInstanceTest
, DetachDuringStart
) {
233 scoped_ptr
<EmbeddedWorkerInstance
> worker
=
234 embedded_worker_registry()->CreateWorker();
235 scoped_ptr
<EmbeddedWorkerMsg_StartWorker_Params
> params(
236 new EmbeddedWorkerMsg_StartWorker_Params());
237 ServiceWorkerStatusCode status
= SERVICE_WORKER_ERROR_FAILED
;
238 // Pretend we had a process allocated but then got detached before
239 // the start sequence reached SendStartWorker.
240 worker
->process_id_
= -1;
241 worker
->SendStartWorker(params
.Pass(), base::Bind(&SaveStatus
, &status
), true,
243 EXPECT_EQ(SERVICE_WORKER_ERROR_ABORT
, status
);
246 // Test stopping in the middle of the start worker sequence, before
247 // a process is allocated.
248 TEST_F(EmbeddedWorkerInstanceTest
, StopDuringStart
) {
249 scoped_ptr
<EmbeddedWorkerInstance
> worker
=
250 embedded_worker_registry()->CreateWorker();
251 worker
->AddListener(this);
252 scoped_ptr
<EmbeddedWorkerMsg_StartWorker_Params
> params(
253 new EmbeddedWorkerMsg_StartWorker_Params());
254 // Pretend we stop during starting before we got a process allocated.
255 worker
->status_
= EmbeddedWorkerInstance::STARTING
;
256 worker
->process_id_
= -1;
258 EXPECT_EQ(EmbeddedWorkerInstance::STOPPED
, worker
->status());
259 EXPECT_TRUE(detached_
);
260 EXPECT_EQ(EmbeddedWorkerInstance::STARTING
, detached_old_status_
);
263 } // namespace content