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.
6 #include "base/callback.h"
7 #include "base/command_line.h"
8 #include "base/location.h"
9 #include "base/run_loop.h"
10 #include "content/browser/service_worker/embedded_worker_instance.h"
11 #include "content/browser/service_worker/embedded_worker_registry.h"
12 #include "content/browser/service_worker/service_worker_context_core.h"
13 #include "content/browser/service_worker/service_worker_context_wrapper.h"
14 #include "content/browser/service_worker/service_worker_registration.h"
15 #include "content/browser/service_worker/service_worker_version.h"
16 #include "content/common/service_worker/service_worker_messages.h"
17 #include "content/public/browser/browser_context.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/storage_partition.h"
20 #include "content/public/browser/web_contents.h"
21 #include "content/public/common/content_switches.h"
22 #include "content/shell/browser/shell.h"
23 #include "content/test/content_browser_test.h"
24 #include "content/test/content_browser_test_utils.h"
25 #include "net/test/embedded_test_server/embedded_test_server.h"
31 void RunAndQuit(const base::Closure& closure,
32 const base::Closure& quit,
33 base::MessageLoopProxy* original_message_loop) {
35 original_message_loop->PostTask(FROM_HERE, quit);
38 void RunOnIOThread(const base::Closure& closure) {
39 base::RunLoop run_loop;
40 BrowserThread::PostTask(
41 BrowserThread::IO, FROM_HERE,
42 base::Bind(&RunAndQuit, closure, run_loop.QuitClosure(),
43 base::MessageLoopProxy::current()));
47 // TODO(kinuko): Factor out these common test helpers to a separate file.
48 template <typename Arg>
49 void VerifyResult(const tracked_objects::Location& where,
50 const base::Closure& quit,
51 Arg expected, Arg actual) {
52 EXPECT_EQ(expected, actual) << where.ToString();
53 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, quit);
56 template <typename Arg> base::Callback<void(Arg)>
57 CreateVerifier(const tracked_objects::Location& where,
58 const base::Closure& quit, Arg expected) {
59 return base::Bind(&VerifyResult<Arg>, where, quit, expected);
64 class ServiceWorkerBrowserTest : public ContentBrowserTest {
66 typedef ServiceWorkerBrowserTest self;
68 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
69 command_line->AppendSwitch(switches::kEnableServiceWorker);
72 virtual void SetUpOnMainThread() OVERRIDE {
73 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
74 StoragePartition* partition = BrowserContext::GetDefaultStoragePartition(
75 shell()->web_contents()->GetBrowserContext());
76 wrapper_ = partition->GetServiceWorkerContext();
79 virtual void TearDownOnMainThread() OVERRIDE {
80 RunOnIOThread(base::Bind(&self::TearDownOnIOThread, this));
84 virtual void TearDownOnIOThread() {}
86 ServiceWorkerContextWrapper* wrapper() { return wrapper_.get(); }
88 void AssociateProcessToWorker(EmbeddedWorkerInstance* worker) {
89 // TODO(kinuko): this manual wiring should go away when this gets wired
90 // in the actual code path.
91 ServiceWorkerProviderHost* provider_host = GetRegisteredProviderHost();
92 worker->AddProcessReference(provider_host->process_id());
96 ServiceWorkerProviderHost* GetRegisteredProviderHost() {
97 // Assumes only one provider host is registered at this point.
98 std::vector<ServiceWorkerProviderHost*> providers;
99 wrapper_->context()->GetAllProviderHosts(&providers);
100 DCHECK_EQ(1U, providers.size());
104 scoped_refptr<ServiceWorkerContextWrapper> wrapper_;
107 class EmbeddedWorkerBrowserTest : public ServiceWorkerBrowserTest,
108 public EmbeddedWorkerInstance::Observer {
110 typedef EmbeddedWorkerBrowserTest self;
112 EmbeddedWorkerBrowserTest()
113 : last_worker_status_(EmbeddedWorkerInstance::STOPPED) {}
114 virtual ~EmbeddedWorkerBrowserTest() {}
116 virtual void TearDownOnIOThread() OVERRIDE {
118 worker_->RemoveObserver(this);
123 void StartOnIOThread() {
124 ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
125 worker_ = wrapper()->context()->embedded_worker_registry()->CreateWorker();
126 EXPECT_EQ(EmbeddedWorkerInstance::STOPPED, worker_->status());
127 worker_->AddObserver(this);
129 AssociateProcessToWorker(worker_.get());
131 const int64 service_worker_version_id = 33L;
132 const GURL script_url = embedded_test_server()->GetURL(
133 "/service_worker/worker.js");
134 const bool started = worker_->Start(
135 service_worker_version_id, script_url);
137 last_worker_status_ = worker_->status();
138 EXPECT_TRUE(started);
139 EXPECT_EQ(EmbeddedWorkerInstance::STARTING, last_worker_status_);
141 if (!started && !done_closure_.is_null())
145 void StopOnIOThread() {
146 ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
147 EXPECT_EQ(EmbeddedWorkerInstance::RUNNING, worker_->status());
149 const bool stopped = worker_->Stop();
151 last_worker_status_ = worker_->status();
152 EXPECT_TRUE(stopped);
153 EXPECT_EQ(EmbeddedWorkerInstance::STOPPING, last_worker_status_);
155 if (!stopped && !done_closure_.is_null())
160 // EmbeddedWorkerInstance::Observer overrides:
161 virtual void OnStarted() OVERRIDE {
162 ASSERT_TRUE(worker_ != NULL);
163 ASSERT_FALSE(done_closure_.is_null());
164 last_worker_status_ = worker_->status();
165 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, done_closure_);
167 virtual void OnStopped() OVERRIDE {
168 ASSERT_TRUE(worker_ != NULL);
169 ASSERT_FALSE(done_closure_.is_null());
170 last_worker_status_ = worker_->status();
171 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, done_closure_);
173 virtual void OnMessageReceived(const IPC::Message& message) OVERRIDE {
177 scoped_ptr<EmbeddedWorkerInstance> worker_;
178 EmbeddedWorkerInstance::Status last_worker_status_;
180 // Called by EmbeddedWorkerInstance::Observer overrides so that
181 // test code can wait for the worker status notifications.
182 base::Closure done_closure_;
185 class ServiceWorkerVersionBrowserTest : public ServiceWorkerBrowserTest {
187 typedef ServiceWorkerVersionBrowserTest self;
189 ServiceWorkerVersionBrowserTest() {}
190 virtual ~ServiceWorkerVersionBrowserTest() {}
192 virtual void TearDownOnIOThread() OVERRIDE {
194 registration_->Shutdown();
195 registration_ = NULL;
198 version_->Shutdown();
203 void StartOnIOThread(const std::string& worker_url,
204 ServiceWorkerStatusCode expected,
205 const base::Closure& done) {
206 ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
208 const int64 registration_id = 1L;
209 const int64 version_id = 1L;
210 registration_ = new ServiceWorkerRegistration(
211 embedded_test_server()->GetURL("/*"),
212 embedded_test_server()->GetURL(worker_url),
214 version_ = new ServiceWorkerVersion(
216 wrapper()->context()->embedded_worker_registry(),
218 AssociateProcessToWorker(version_->embedded_worker());
219 version_->StartWorker(CreateVerifier(FROM_HERE, done, expected));
222 void StopOnIOThread(const base::Closure& done) {
223 ASSERT_TRUE(version_);
224 version_->StopWorker(
225 CreateVerifier(FROM_HERE, done, SERVICE_WORKER_OK));
229 scoped_refptr<ServiceWorkerRegistration> registration_;
230 scoped_refptr<ServiceWorkerVersion> version_;
233 IN_PROC_BROWSER_TEST_F(EmbeddedWorkerBrowserTest, StartAndStop) {
234 // Navigate to the page to set up a provider.
235 NavigateToURLBlockUntilNavigationsComplete(
236 shell(), embedded_test_server()->GetURL("/service_worker/index.html"), 1);
238 // Start a worker and wait until OnStarted() is called.
239 base::RunLoop start_run_loop;
240 done_closure_ = start_run_loop.QuitClosure();
241 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
242 base::Bind(&self::StartOnIOThread, this));
243 start_run_loop.Run();
245 ASSERT_EQ(EmbeddedWorkerInstance::RUNNING, last_worker_status_);
247 // Stop a worker and wait until OnStopped() is called.
248 base::RunLoop stop_run_loop;
249 done_closure_ = stop_run_loop.QuitClosure();
250 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
251 base::Bind(&self::StopOnIOThread, this));
254 ASSERT_EQ(EmbeddedWorkerInstance::STOPPED, last_worker_status_);
257 IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, StartAndStop) {
258 // Navigate to the page to set up a provider.
259 NavigateToURLBlockUntilNavigationsComplete(
260 shell(), embedded_test_server()->GetURL("/service_worker/index.html"), 1);
263 base::RunLoop start_run_loop;
264 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
265 base::Bind(&self::StartOnIOThread, this,
266 "/service_worker/worker.js",
268 start_run_loop.QuitClosure()));
269 start_run_loop.Run();
272 base::RunLoop stop_run_loop;
273 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
274 base::Bind(&self::StopOnIOThread, this,
275 stop_run_loop.QuitClosure()));
279 IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, StartNotFound) {
280 // Navigate to the page to set up a provider.
281 NavigateToURLBlockUntilNavigationsComplete(
282 shell(), embedded_test_server()->GetURL("/service_worker/index.html"), 1);
284 // Start a worker for nonexistent URL.
285 base::RunLoop start_run_loop;
286 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
287 base::Bind(&self::StartOnIOThread, this,
288 "/service_worker/nonexistent.js",
289 SERVICE_WORKER_ERROR_START_WORKER_FAILED,
290 start_run_loop.QuitClosure()));
291 start_run_loop.Run();
294 } // namespace content