Chromium Code Reviews| 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..ff476eb5047536013e38a1d7ca2b700e73a0ee91 |
| --- /dev/null |
| +++ b/Source/web/tests/ServiceWorkerGlobalScopeProxyTest.cpp |
| @@ -0,0 +1,209 @@ |
| +// 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.cpp" |
| +#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 { |
|
Jeffrey Yasskin
2014/07/16 23:14:58
I was kind of shocked not to find anything like th
|
| +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/16 23:14:58
This gets flagged by https://code.google.com/p/chr
|
| + // 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 V8ContextTestSWContextClient : 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(&V8ContextTestSWContextClient::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, V8ContextText) |
| +{ |
| + V8ContextTestSWContextClient* contextClient = new V8ContextTestSWContextClient; |
| + |
| + 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(&V8ContextTestSWContextClient::checkContextAccessibleAfterClose, contextClient)); |
| + runLoop.Run(); // Wait for workerContextDestroyed(). |
| +} |
| + |
| +} // namespace |