Index: Source/web/tests/ServiceWorkerGlobalScopeProxyTest.cpp |
diff --git a/Source/web/tests/ServiceWorkerGlobalScopeProxyTest.cpp b/Source/web/tests/ServiceWorkerGlobalScopeProxyTest.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..5ee9eaa861634d13030f2c01847f68101bff156d |
--- /dev/null |
+++ b/Source/web/tests/ServiceWorkerGlobalScopeProxyTest.cpp |
@@ -0,0 +1,210 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "config.h" |
+ |
+#include "Source/web/ServiceWorkerGlobalScopeProxy.h" |
+ |
+#include "Source/bindings/core/v8/ScriptSourceCode.h" |
+#include "Source/core/dom/CrossThreadTask.h" |
+#include "platform/weborigin/KURL.h" |
+#include "public/platform/Platform.h" |
+#include "public/platform/WebThread.h" |
+#include "public/platform/WebUnitTestSupport.h" |
+#include "public/web/WebEmbeddedWorker.h" |
+#include "public/web/WebEmbeddedWorkerStartData.h" |
+#include "public/web/WebServiceWorkerContextClient.h" |
+#include "web/WebEmbeddedWorkerImpl.h" |
+#include "web/tests/URLTestHelpers.h" |
+#include <gmock/gmock.h> |
+#include <gtest/gtest.h> |
+#include <v8.h> |
+ |
+using namespace blink; |
+using namespace WebCore; |
+ |
+namespace { |
+ |
+class ServiceWorkerGlobalScopeProxyTest : public testing::Test { |
+protected: |
+ ServiceWorkerGlobalScopeProxyTest() |
+ : m_baseUrl("http://www.test.com/") |
+ { |
+ } |
+ |
+ virtual ~ServiceWorkerGlobalScopeProxyTest() |
+ { |
+ Platform::current()->unitTestSupport()->unregisterAllMockedURLs(); |
+ } |
+ |
+ // Remember to call serveAsynchronousMockedRequests() in order to handle these mocked URLs. |
+ void registerMockedHttpURLLoad(const std::string& fileName, const std::string& mimeType = "text/html") |
+ { |
+ URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseUrl), WebString::fromUTF8(fileName), WebString::fromUTF8(mimeType)); |
+ } |
+ |
+ const std::string m_baseUrl; |
+}; |
+ |
+// Can repeatedly run the current thread's loop, but unlike Chrome's RunLoop, cannot be recursively reentered. |
+// quitWhenIdle() must be called exactly once per run() call. |
+class RunLoop { |
+public: |
+ RunLoop() |
+ : m_thread(blink::Platform::current()->currentThread()) |
+ , m_isRunning(false) |
+ , m_isQuitting(false) |
+ { |
+ } |
+ |
+ // Starts the thread's run loop, and returns when quitWhenIdle() is called. |
+ void run() |
+ { |
+ EXPECT_FALSE(m_isRunning); |
+ m_isRunning = true; |
+ m_thread->enterRunLoop(); |
+ EXPECT_TRUE(m_isQuitting); |
+ m_isRunning = m_isQuitting = false; |
+ } |
+ |
+ // This can be called before run() or concurrently with it from another thread. |
+ // The loop will terminate when it's idle. |
+ void quitWhenIdle() |
+ { |
+ EXPECT_FALSE(m_isQuitting); |
+ m_isQuitting = true; |
+ m_thread->postTask(new QuitTask(this)); |
+ } |
+ |
+ bool isQuitting() |
+ { |
+ return m_isQuitting; |
+ } |
+ |
+private: |
+ class QuitTask : public WebThread::Task { |
+ public: |
+ QuitTask(RunLoop* runLoop) |
+ : m_runLoop(runLoop) |
+ { |
+ } |
+ |
+ virtual void run() |
+ { |
+ EXPECT_TRUE(m_runLoop->m_isRunning); |
+ EXPECT_TRUE(m_runLoop->m_isQuitting); |
+ m_runLoop->m_thread->exitRunLoop(); |
+ } |
+ |
+ RunLoop* m_runLoop; |
+ }; |
+ |
+ WebThread* m_thread; |
+ bool m_isRunning; |
+ bool m_isQuitting; |
+}; |
+ |
+// Continuously pumps the mocked-requests queue during the current execution of runLoop->run(), or next if run() isn't currently running. |
+class ServeAsyncRequestsTask : public WebThread::Task { |
+public: |
+ ServeAsyncRequestsTask(RunLoop* runLoop) |
+ : m_runLoop(runLoop) |
+ { |
+ } |
+ |
+ virtual void run() OVERRIDE |
+ { |
+ Platform::current()->unitTestSupport()->serveAsynchronousMockedRequests(); |
Jeffrey Yasskin
2014/07/17 18:32:09
dcheng says that this call is probably the best we
|
+ // The RunLoop only finishes quitting when it has no tasks left, so this task has to end its chain explicitly. |
+ if (!m_runLoop->isQuitting()) |
+ Platform::current()->currentThread()->postTask(new ServeAsyncRequestsTask(m_runLoop)); |
+ } |
+ |
+private: |
+ RunLoop* m_runLoop; |
+}; |
+ |
+class V8ContextTestContextClient : public WebServiceWorkerContextClient { |
+public: |
+ virtual void workerContextFailedToStart() |
+ { |
+ ADD_FAILURE() << "workerContextFailedToStart"; |
+ m_runLoop->quitWhenIdle(); |
+ } |
+ |
+ virtual void reportException(const WebString& errorMessage, int lineNumber, int columnNumber, const WebString& sourceURL) |
+ { |
+ ADD_FAILURE() << sourceURL.utf8() << "(" << lineNumber << ":" << columnNumber << ") error: " << errorMessage.utf8(); |
+ m_runLoop->quitWhenIdle(); |
+ } |
+ |
+ virtual void workerContextStarted(WebServiceWorkerContextProxy* proxy) |
+ { |
+ m_proxy = proxy; |
+ v8::HandleScope scope(v8::Isolate::GetCurrent()); |
+ EXPECT_TRUE(*m_proxy->v8Context()) << "The v8 context should be valid as soon as the worker is started."; |
+ m_runLoop->quitWhenIdle(); |
+ } |
+ |
+ void checkContextAccessibleAfterClose() |
+ { |
+ v8::HandleScope scope(v8::Isolate::GetCurrent()); |
+ EXPECT_TRUE(*m_proxy->v8Context()) << "The v8 context should still be valid."; |
+ static_cast<ServiceWorkerGlobalScopeProxy*>(m_proxy)->evaluate(String::fromUTF8("self.close();")); |
+ // The context continues to exist until non-cleanup tasks stop running. |
+ checkContextStillExists(); |
+ // close() will eventually terminate the worker, which will quit the |
+ // RunLoop through workerContextDestroyed. |
+ } |
+ |
+ void checkContextStillExists() |
+ { |
+ // The context isn't valid after willDestroyWorkerContext(). |
+ if (!m_proxy) |
+ return; |
+ v8::HandleScope scope(v8::Isolate::GetCurrent()); |
+ EXPECT_TRUE(*m_proxy->v8Context()); |
+ static_cast<WebEmbeddedWorkerImpl*>(m_worker)->postTask(createCrossThreadTask(&V8ContextTestContextClient::checkContextStillExists, this)); |
+ } |
+ |
+ virtual void willDestroyWorkerContext() OVERRIDE |
+ { |
+ m_proxy = 0; |
+ } |
+ |
+ virtual void workerContextDestroyed() OVERRIDE |
+ { |
+ // Now it's safe to finish the main test, which will destroy the WorkerThread. |
+ m_runLoop->quitWhenIdle(); |
+ } |
+ |
+ RunLoop* m_runLoop; |
+ WebEmbeddedWorker* m_worker; |
+ WebServiceWorkerContextProxy* m_proxy; |
+}; |
+ |
+TEST_F(ServiceWorkerGlobalScopeProxyTest, V8ContextTest) |
+{ |
+ V8ContextTestContextClient* contextClient = new V8ContextTestContextClient; |
+ |
+ RunLoop runLoop; |
+ contextClient->m_runLoop = &runLoop; |
+ |
+ OwnPtr<WebEmbeddedWorker> worker = adoptPtr(WebEmbeddedWorker::create(contextClient, 0)); |
+ contextClient->m_worker = worker.get(); |
+ |
+ WebEmbeddedWorkerStartData startData; |
+ registerMockedHttpURLLoad("blank.js", "text/javascript"); |
+ startData.scriptURL = URLTestHelpers::toKURL(m_baseUrl + "blank.js"); |
+ worker->startWorkerContext(startData); |
+ // Serve requests until the message loop quits. |
+ Platform::current()->currentThread()->postTask(new ServeAsyncRequestsTask(&runLoop)); |
+ runLoop.run(); // Wait for workerContextStarted(). |
+ |
+ static_cast<WebEmbeddedWorkerImpl&>(*worker).postTask( |
+ createCrossThreadTask(&V8ContextTestContextClient::checkContextAccessibleAfterClose, contextClient)); |
+ runLoop.run(); // Wait for workerContextDestroyed(). |
+} |
+ |
+} // namespace |