Fix some interleaving issues in EmbeddedWorkerInstance.
[chromium-blink-merge.git] / content / browser / service_worker / embedded_worker_instance_unittest.cc
blob727fcca1f8208fd03ce9bba3f5d5ba1833509d3f
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"
18 namespace content {
20 namespace {
22 const int kRenderProcessId = 11;
24 void SaveStatus(ServiceWorkerStatusCode* out, ServiceWorkerStatusCode status) {
25 *out = status;
28 void SaveStatusAndCall(ServiceWorkerStatusCode* out,
29 const base::Closure& callback,
30 ServiceWorkerStatusCode status) {
31 *out = status;
32 callback.Run();
35 } // namespace
37 class EmbeddedWorkerInstanceTest : public testing::Test,
38 public EmbeddedWorkerInstance::Listener {
39 protected:
40 EmbeddedWorkerInstanceTest()
41 : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
43 void OnDetached(EmbeddedWorkerInstance::Status old_status) override {
44 detached_ = true;
45 detached_old_status_ = old_status;
48 bool OnMessageReceived(const IPC::Message& message) override { return false; }
50 void SetUp() override {
51 helper_.reset(
52 new EmbeddedWorkerTestHelper(base::FilePath(), kRenderProcessId));
55 void TearDown() override { helper_.reset(); }
57 ServiceWorkerStatusCode StartWorker(EmbeddedWorkerInstance* worker,
58 int id, const GURL& pattern,
59 const GURL& url) {
60 ServiceWorkerStatusCode status;
61 base::RunLoop run_loop;
62 worker->Start(id, pattern, url, base::Bind(&SaveStatusAndCall, &status,
63 run_loop.QuitClosure()));
64 run_loop.Run();
65 return status;
68 ServiceWorkerContextCore* context() { return helper_->context(); }
70 EmbeddedWorkerRegistry* embedded_worker_registry() {
71 DCHECK(context());
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;
83 private:
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;
102 worker->Start(
103 service_worker_version_id,
104 pattern,
105 url,
106 base::Bind(&SaveStatusAndCall, &status, run_loop.QuitClosure()));
107 EXPECT_EQ(EmbeddedWorkerInstance::STARTING, worker->status());
108 run_loop.Run();
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());
116 // Stop the worker.
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.
126 ASSERT_TRUE(
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);
191 // Start worker1.
192 ServiceWorkerStatusCode status;
193 base::RunLoop run_loop;
194 worker1->Start(
195 version_id1, pattern, url,
196 base::Bind(&SaveStatusAndCall, &status, run_loop.QuitClosure()));
197 run_loop.Run();
198 EXPECT_EQ(SERVICE_WORKER_OK, status);
202 // Start worker2.
203 ServiceWorkerStatusCode status;
204 base::RunLoop run_loop;
205 worker2->Start(
206 version_id2, pattern, url,
207 base::Bind(&SaveStatusAndCall, &status, run_loop.QuitClosure()));
208 run_loop.Run();
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();
217 worker1->Stop();
218 worker1.reset();
220 // Only worker1 should be removed from the registry's process_map.
221 EmbeddedWorkerRegistry* registry =
222 helper_->context()->embedded_worker_registry();
223 EXPECT_EQ(0UL,
224 registry->worker_process_map_[kRenderProcessId].count(worker1_id));
225 EXPECT_EQ(1UL, registry->worker_process_map_[kRenderProcessId].count(
226 worker2->embedded_worker_id()));
228 worker2->Stop();
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,
242 -1, false);
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;
257 worker->Stop();
258 EXPECT_EQ(EmbeddedWorkerInstance::STOPPED, worker->status());
259 EXPECT_TRUE(detached_);
260 EXPECT_EQ(EmbeddedWorkerInstance::STARTING, detached_old_status_);
263 } // namespace content