OLD | NEW |
---|---|
(Empty) | |
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. | |
4 | |
5 #include "config.h" | |
6 | |
7 #include "Source/web/ServiceWorkerGlobalScopeProxy.h" | |
8 | |
9 #include "Source/bindings/core/v8/ScriptSourceCode.h" | |
10 #include "Source/core/dom/CrossThreadTask.h" | |
11 #include "platform/weborigin/KURL.h" | |
12 #include "public/platform/Platform.h" | |
13 #include "public/platform/WebThread.h" | |
14 #include "public/platform/WebUnitTestSupport.h" | |
15 #include "public/web/WebEmbeddedWorker.h" | |
16 #include "public/web/WebEmbeddedWorkerStartData.h" | |
17 #include "public/web/WebServiceWorkerContextClient.h" | |
18 #include "web/WebEmbeddedWorkerImpl.h" | |
19 #include "web/tests/URLTestHelpers.h" | |
20 #include <gmock/gmock.h> | |
21 #include <gtest/gtest.h> | |
22 #include <v8.h> | |
23 | |
24 using namespace blink; | |
25 using namespace WebCore; | |
26 | |
27 namespace { | |
28 | |
29 class ServiceWorkerGlobalScopeProxyTest : public testing::Test { | |
30 protected: | |
31 ServiceWorkerGlobalScopeProxyTest() | |
32 : m_baseUrl("http://www.test.com/") | |
33 { | |
34 } | |
35 | |
36 virtual ~ServiceWorkerGlobalScopeProxyTest() | |
37 { | |
38 Platform::current()->unitTestSupport()->unregisterAllMockedURLs(); | |
39 } | |
40 | |
41 // Remember to call serveAsynchronousMockedRequests() in order to handle the se mocked URLs. | |
42 void registerMockedHttpURLLoad(const std::string& fileName, const std::strin g& mimeType = "text/html") | |
43 { | |
44 URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseU rl), WebString::fromUTF8(fileName), WebString::fromUTF8(mimeType)); | |
45 } | |
46 | |
47 const std::string m_baseUrl; | |
48 }; | |
49 | |
50 // Can repeatedly run the current thread's loop, but unlike Chrome's RunLoop, ca nnot be recursively reentered. | |
51 // quitWhenIdle() must be called exactly once per run() call. | |
52 class RunLoop { | |
53 public: | |
54 RunLoop() | |
55 : m_thread(blink::Platform::current()->currentThread()) | |
56 , m_isRunning(false) | |
57 , m_isQuitting(false) | |
58 { | |
59 } | |
60 | |
61 // Starts the thread's run loop, and returns when quitWhenIdle() is called. | |
62 void run() | |
63 { | |
64 EXPECT_FALSE(m_isRunning); | |
65 m_isRunning = true; | |
66 m_thread->enterRunLoop(); | |
67 EXPECT_TRUE(m_isQuitting); | |
68 m_isRunning = m_isQuitting = false; | |
69 } | |
70 | |
71 // This can be called before run() or concurrently with it from another thre ad. | |
72 // The loop will terminate when it's idle. | |
73 void quitWhenIdle() | |
74 { | |
75 EXPECT_FALSE(m_isQuitting); | |
76 m_isQuitting = true; | |
77 m_thread->postTask(new QuitTask(this)); | |
78 } | |
79 | |
80 bool isQuitting() | |
81 { | |
82 return m_isQuitting; | |
83 } | |
84 | |
85 private: | |
86 class QuitTask : public WebThread::Task { | |
87 public: | |
88 QuitTask(RunLoop* runLoop) | |
89 : m_runLoop(runLoop) | |
90 { | |
91 } | |
92 | |
93 virtual void run() | |
94 { | |
95 EXPECT_TRUE(m_runLoop->m_isRunning); | |
96 EXPECT_TRUE(m_runLoop->m_isQuitting); | |
97 m_runLoop->m_thread->exitRunLoop(); | |
98 } | |
99 | |
100 RunLoop* m_runLoop; | |
101 }; | |
102 | |
103 WebThread* m_thread; | |
104 bool m_isRunning; | |
105 bool m_isQuitting; | |
106 }; | |
107 | |
108 // Continuously pumps the mocked-requests queue during the current execution of runLoop->run(), or next if run() isn't currently running. | |
109 class ServeAsyncRequestsTask : public WebThread::Task { | |
110 public: | |
111 ServeAsyncRequestsTask(RunLoop* runLoop) | |
112 : m_runLoop(runLoop) | |
113 { | |
114 } | |
115 | |
116 virtual void run() OVERRIDE | |
117 { | |
118 Platform::current()->unitTestSupport()->serveAsynchronousMockedRequests( ); | |
Jeffrey Yasskin
2014/07/17 18:32:09
dcheng says that this call is probably the best we
| |
119 // The RunLoop only finishes quitting when it has no tasks left, so this task has to end its chain explicitly. | |
120 if (!m_runLoop->isQuitting()) | |
121 Platform::current()->currentThread()->postTask(new ServeAsyncRequest sTask(m_runLoop)); | |
122 } | |
123 | |
124 private: | |
125 RunLoop* m_runLoop; | |
126 }; | |
127 | |
128 class V8ContextTestContextClient : public WebServiceWorkerContextClient { | |
129 public: | |
130 virtual void workerContextFailedToStart() | |
131 { | |
132 ADD_FAILURE() << "workerContextFailedToStart"; | |
133 m_runLoop->quitWhenIdle(); | |
134 } | |
135 | |
136 virtual void reportException(const WebString& errorMessage, int lineNumber, int columnNumber, const WebString& sourceURL) | |
137 { | |
138 ADD_FAILURE() << sourceURL.utf8() << "(" << lineNumber << ":" << columnN umber << ") error: " << errorMessage.utf8(); | |
139 m_runLoop->quitWhenIdle(); | |
140 } | |
141 | |
142 virtual void workerContextStarted(WebServiceWorkerContextProxy* proxy) | |
143 { | |
144 m_proxy = proxy; | |
145 v8::HandleScope scope(v8::Isolate::GetCurrent()); | |
146 EXPECT_TRUE(*m_proxy->v8Context()) << "The v8 context should be valid as soon as the worker is started."; | |
147 m_runLoop->quitWhenIdle(); | |
148 } | |
149 | |
150 void checkContextAccessibleAfterClose() | |
151 { | |
152 v8::HandleScope scope(v8::Isolate::GetCurrent()); | |
153 EXPECT_TRUE(*m_proxy->v8Context()) << "The v8 context should still be va lid."; | |
154 static_cast<ServiceWorkerGlobalScopeProxy*>(m_proxy)->evaluate(String::f romUTF8("self.close();")); | |
155 // The context continues to exist until non-cleanup tasks stop running. | |
156 checkContextStillExists(); | |
157 // close() will eventually terminate the worker, which will quit the | |
158 // RunLoop through workerContextDestroyed. | |
159 } | |
160 | |
161 void checkContextStillExists() | |
162 { | |
163 // The context isn't valid after willDestroyWorkerContext(). | |
164 if (!m_proxy) | |
165 return; | |
166 v8::HandleScope scope(v8::Isolate::GetCurrent()); | |
167 EXPECT_TRUE(*m_proxy->v8Context()); | |
168 static_cast<WebEmbeddedWorkerImpl*>(m_worker)->postTask(createCrossThrea dTask(&V8ContextTestContextClient::checkContextStillExists, this)); | |
169 } | |
170 | |
171 virtual void willDestroyWorkerContext() OVERRIDE | |
172 { | |
173 m_proxy = 0; | |
174 } | |
175 | |
176 virtual void workerContextDestroyed() OVERRIDE | |
177 { | |
178 // Now it's safe to finish the main test, which will destroy the WorkerT hread. | |
179 m_runLoop->quitWhenIdle(); | |
180 } | |
181 | |
182 RunLoop* m_runLoop; | |
183 WebEmbeddedWorker* m_worker; | |
184 WebServiceWorkerContextProxy* m_proxy; | |
185 }; | |
186 | |
187 TEST_F(ServiceWorkerGlobalScopeProxyTest, V8ContextTest) | |
188 { | |
189 V8ContextTestContextClient* contextClient = new V8ContextTestContextClient; | |
190 | |
191 RunLoop runLoop; | |
192 contextClient->m_runLoop = &runLoop; | |
193 | |
194 OwnPtr<WebEmbeddedWorker> worker = adoptPtr(WebEmbeddedWorker::create(contex tClient, 0)); | |
195 contextClient->m_worker = worker.get(); | |
196 | |
197 WebEmbeddedWorkerStartData startData; | |
198 registerMockedHttpURLLoad("blank.js", "text/javascript"); | |
199 startData.scriptURL = URLTestHelpers::toKURL(m_baseUrl + "blank.js"); | |
200 worker->startWorkerContext(startData); | |
201 // Serve requests until the message loop quits. | |
202 Platform::current()->currentThread()->postTask(new ServeAsyncRequestsTask(&r unLoop)); | |
203 runLoop.run(); // Wait for workerContextStarted(). | |
204 | |
205 static_cast<WebEmbeddedWorkerImpl&>(*worker).postTask( | |
206 createCrossThreadTask(&V8ContextTestContextClient::checkContextAccessibl eAfterClose, contextClient)); | |
207 runLoop.run(); // Wait for workerContextDestroyed(). | |
208 } | |
209 | |
210 } // namespace | |
OLD | NEW |