Index: third_party/WebKit/Source/core/workers/DedicatedWorkerTest.cpp |
diff --git a/third_party/WebKit/Source/core/workers/DedicatedWorkerTest.cpp b/third_party/WebKit/Source/core/workers/DedicatedWorkerTest.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..5aa4ac889280d0ec74705c8523a426514196283c |
--- /dev/null |
+++ b/third_party/WebKit/Source/core/workers/DedicatedWorkerTest.cpp |
@@ -0,0 +1,314 @@ |
+// Copyright 2016 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 "core/dom/CrossThreadTask.h" |
+#include "core/events/MessageEvent.h" |
+#include "core/testing/DummyPageHolder.h" |
+#include "core/workers/DedicatedWorkerGlobalScope.h" |
+#include "core/workers/DedicatedWorkerThread.h" |
+#include "core/workers/InProcessWorkerMessagingProxy.h" |
+#include "core/workers/InProcessWorkerObjectProxy.h" |
+#include "core/workers/WorkerThread.h" |
+#include "core/workers/WorkerThreadStartupData.h" |
+#include "core/workers/WorkerThreadTestHelper.h" |
+#include "platform/testing/UnitTestHelpers.h" |
+#include "testing/gmock/include/gmock/gmock.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+#include <memory> |
+ |
+namespace blink { |
+ |
+class DedicatedWorkerThreadForTest final : public DedicatedWorkerThread { |
+public: |
+ DedicatedWorkerThreadForTest( |
+ WorkerLoaderProxyProvider* workerLoaderProxyProvider, |
+ InProcessWorkerObjectProxy& workerObjectProxy) |
+ : DedicatedWorkerThread(WorkerLoaderProxy::create(workerLoaderProxyProvider), workerObjectProxy, monotonicallyIncreasingTime()) |
+ { |
+ m_workerBackingThread = WorkerBackingThread::createForTest("Test thread"); |
+ } |
+ |
+ WorkerOrWorkletGlobalScope* createWorkerGlobalScope(std::unique_ptr<WorkerThreadStartupData> startupData) override |
+ { |
+ return new DedicatedWorkerGlobalScope(startupData->m_scriptURL, startupData->m_userAgent, this, m_timeOrigin, std::move(startupData->m_starterOriginPrivilegeData), std::move(startupData->m_workerClients)); |
+ } |
+}; |
+ |
+class InProcessWorkerMessagingProxyForTest : public InProcessWorkerMessagingProxy { |
+public: |
+ InProcessWorkerMessagingProxyForTest(ExecutionContext* executionContext) |
+ : InProcessWorkerMessagingProxy(executionContext, nullptr /* workerObject */ , nullptr /* workerClients */) |
+ { |
+ workerObjectProxy().m_nextIntervalInSec = 0.1; |
+ workerObjectProxy().m_maxIntervalInSec = 0.2; |
+ |
+ m_mockWorkerLoaderProxyProvider = wrapUnique(new MockWorkerLoaderProxyProvider()); |
+ m_workerThread = wrapUnique(new DedicatedWorkerThreadForTest(m_mockWorkerLoaderProxyProvider.get(), workerObjectProxy())); |
+ workerThreadCreated(); |
+ |
+ m_mockWorkerThreadLifecycleObserver = new MockWorkerThreadLifecycleObserver(m_workerThread->getWorkerThreadLifecycleContext()); |
+ EXPECT_CALL(*m_mockWorkerThreadLifecycleObserver, contextDestroyed()).Times(1); |
+ } |
+ |
+ ~InProcessWorkerMessagingProxyForTest() override |
+ { |
+ EXPECT_EQ(WaitUntilMode::DontWait, m_waitUntilMode); |
+ m_workerThread->workerLoaderProxy()->detachProvider(m_mockWorkerLoaderProxyProvider.get()); |
+ } |
+ |
+ enum class WaitUntilMode { |
+ DontWait, |
+ MessageConfirmed, |
+ PendingActivityReported, |
+ ThreadTerminated, |
+ }; |
+ |
+ // Blocks the main thread until a specified event happens. |
+ void waitUntil(WaitUntilMode mode) |
+ { |
+ EXPECT_TRUE(isMainThread()); |
+ EXPECT_EQ(WaitUntilMode::DontWait, m_waitUntilMode); |
+ m_waitUntilMode = mode; |
+ testing::enterRunLoop(); |
+ } |
+ |
+ void confirmMessageFromWorkerObject() override |
+ { |
+ EXPECT_TRUE(isMainThread()); |
+ InProcessWorkerMessagingProxy::confirmMessageFromWorkerObject(); |
+ if (m_waitUntilMode != WaitUntilMode::MessageConfirmed) |
+ return; |
+ m_waitUntilMode = WaitUntilMode::DontWait; |
+ testing::exitRunLoop(); |
+ } |
+ |
+ void pendingActivityFinished() override |
+ { |
+ EXPECT_TRUE(isMainThread()); |
+ InProcessWorkerMessagingProxy::pendingActivityFinished(); |
+ if (m_waitUntilMode != WaitUntilMode::PendingActivityReported) |
+ return; |
+ m_waitUntilMode = WaitUntilMode::DontWait; |
+ testing::exitRunLoop(); |
+ } |
+ |
+ void workerThreadTerminated() override |
+ { |
+ EXPECT_TRUE(isMainThread()); |
+ if (m_waitUntilMode != WaitUntilMode::ThreadTerminated) |
+ return; |
+ m_waitUntilMode = WaitUntilMode::DontWait; |
+ testing::exitRunLoop(); |
+ } |
+ |
+ std::unique_ptr<WorkerThread> createWorkerThread(double originTime) override |
+ { |
+ NOTREACHED(); |
+ return nullptr; |
+ } |
+ |
+ DedicatedWorkerThreadForTest* workerThread() |
+ { |
+ return static_cast<DedicatedWorkerThreadForTest*>(m_workerThread.get()); |
+ } |
+ |
+ bool workerGlobalScopeMayHavePendingActivity() const { return m_workerGlobalScopeMayHavePendingActivity; } |
+ unsigned unconfirmedMessageCount() const { return m_unconfirmedMessageCount; } |
+ |
+private: |
+ std::unique_ptr<MockWorkerLoaderProxyProvider> m_mockWorkerLoaderProxyProvider; |
+ Persistent<MockWorkerThreadLifecycleObserver> m_mockWorkerThreadLifecycleObserver; |
+ |
+ WaitUntilMode m_waitUntilMode = WaitUntilMode::DontWait; |
+}; |
+ |
+using WaitUntilMode = InProcessWorkerMessagingProxyForTest::WaitUntilMode; |
+ |
+class DedicatedWorkerTest : public ::testing::Test { |
+public: |
+ void SetUp() override |
+ { |
+ m_page = DummyPageHolder::create(); |
+ m_workerMessagingProxy = wrapUnique(new InProcessWorkerMessagingProxyForTest(&m_page->document())); |
+ m_securityOrigin = SecurityOrigin::create(KURL(ParsedURLString, "http://fake.url/")); |
+ } |
+ |
+ void TearDown() override |
+ { |
+ workerThread()->terminate(); |
+ workerMessagingProxy()->waitUntil(WaitUntilMode::ThreadTerminated); |
+ } |
+ |
+ void startWithSourceCode(const String& source) |
+ { |
+ std::unique_ptr<Vector<CSPHeaderAndType>> headers = wrapUnique(new Vector<CSPHeaderAndType>()); |
+ CSPHeaderAndType headerAndType("contentSecurityPolicy", ContentSecurityPolicyHeaderTypeReport); |
+ headers->append(headerAndType); |
+ workerThread()->start(WorkerThreadStartupData::create( |
+ KURL(ParsedURLString, "http://fake.url/"), |
+ "fake user agent", |
+ source, |
+ nullptr /* cachedMetaData */, |
+ DontPauseWorkerGlobalScopeOnStart, |
+ headers.get(), |
+ "" /* referrerPolicy */, |
+ m_securityOrigin.get(), |
+ nullptr /* workerClients */, |
+ WebAddressSpaceLocal, |
+ nullptr /* originTrialTokens */, |
+ nullptr /* workerSettings */, |
+ V8CacheOptionsDefault)); |
+ } |
+ |
+ void dispatchMessageEvent() |
+ { |
+ workerMessagingProxy()->postMessageToWorkerGlobalScope(nullptr /* message */, nullptr /* channels */); |
+ } |
+ |
+ InProcessWorkerMessagingProxyForTest* workerMessagingProxy() |
+ { |
+ return m_workerMessagingProxy.get(); |
+ } |
+ |
+ DedicatedWorkerThreadForTest* workerThread() |
+ { |
+ return m_workerMessagingProxy->workerThread(); |
+ } |
+ |
+private: |
+ RefPtr<SecurityOrigin> m_securityOrigin; |
+ std::unique_ptr<DummyPageHolder> m_page; |
+ std::unique_ptr<InProcessWorkerMessagingProxyForTest> m_workerMessagingProxy; |
+}; |
+ |
+TEST_F(DedicatedWorkerTest, PendingActivity_NoActivity) |
+{ |
+ const String sourceCode = "// Do nothing"; |
+ startWithSourceCode(sourceCode); |
+ |
+ // Worker initialization should be counted as a pending activity. |
+ EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity()); |
+ |
+ // There should be no pending activities after the initialization. |
+ workerMessagingProxy()->waitUntil(WaitUntilMode::PendingActivityReported); |
+ EXPECT_FALSE(workerMessagingProxy()->hasPendingActivity()); |
+} |
+ |
+TEST_F(DedicatedWorkerTest, PendingActivity_SetTimeout) |
+{ |
+ // Start an oneshot timer on initial script evaluation. |
+ const String sourceCode = "setTimeout(function() {}, 50);"; |
+ startWithSourceCode(sourceCode); |
+ |
+ // Worker initialization should be counted as a pending activity. |
+ EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity()); |
+ |
+ // The timer is fired soon and there should be no pending activities after |
+ // that. |
+ workerMessagingProxy()->waitUntil(WaitUntilMode::PendingActivityReported); |
+ EXPECT_FALSE(workerMessagingProxy()->hasPendingActivity()); |
+} |
+ |
+TEST_F(DedicatedWorkerTest, PendingActivity_SetInterval) |
+{ |
+ // Start a repeated timer on initial script evaluation, and stop it when a |
+ // message is received. |
+ const String sourceCode = |
+ "var id = setInterval(function() {}, 50);" |
+ "addEventListener('message', function(event) { clearInterval(id); });"; |
+ startWithSourceCode(sourceCode); |
+ |
+ // Worker initialization should be counted as a pending activity. |
+ EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity()); |
+ |
+ // Stop the timer. |
+ dispatchMessageEvent(); |
+ EXPECT_EQ(1u, workerMessagingProxy()->unconfirmedMessageCount()); |
+ EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity()); |
+ workerMessagingProxy()->waitUntil(WaitUntilMode::MessageConfirmed); |
+ EXPECT_EQ(0u, workerMessagingProxy()->unconfirmedMessageCount()); |
+ EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity()); |
+ |
+ // There should be no pending activities after the timer is stopped. |
+ workerMessagingProxy()->waitUntil(WaitUntilMode::PendingActivityReported); |
+ EXPECT_FALSE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity()); |
+} |
+ |
+TEST_F(DedicatedWorkerTest, PendingActivity_SetTimeoutOnMessageEvent) |
+{ |
+ // Start an oneshot timer on a message event. |
+ const String sourceCode = |
+ "addEventListener('message', function(event) {" |
+ " setTimeout(function() {}, 50);" |
+ "});"; |
+ startWithSourceCode(sourceCode); |
+ |
+ // Worker initialization should be counted as a pending activity. |
+ EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity()); |
+ workerMessagingProxy()->waitUntil(WaitUntilMode::PendingActivityReported); |
+ EXPECT_FALSE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity()); |
+ |
+ // A message starts the oneshot timer that is counted as a pending activity. |
+ dispatchMessageEvent(); |
+ EXPECT_EQ(1u, workerMessagingProxy()->unconfirmedMessageCount()); |
+ EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity()); |
+ workerMessagingProxy()->waitUntil(WaitUntilMode::MessageConfirmed); |
+ EXPECT_EQ(0u, workerMessagingProxy()->unconfirmedMessageCount()); |
+ EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity()); |
+ |
+ // The timer is fired soon and there should be no pending activities after |
+ // that. |
+ workerMessagingProxy()->waitUntil(WaitUntilMode::PendingActivityReported); |
+ EXPECT_FALSE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity()); |
+} |
+ |
+TEST_F(DedicatedWorkerTest, PendingActivity_SetIntervalOnMessageEvent) |
+{ |
+ // Start a repeated timer on a message event, and stop it when another |
+ // message is received. |
+ const String sourceCode = |
+ "var count = 0;" |
+ "var id;" |
+ "addEventListener('message', function(event) {" |
+ " if (count++ == 0) {" |
+ " id = setInterval(function() {}, 50);" |
+ " } else {" |
+ " clearInterval(id);" |
+ " }" |
+ "});"; |
+ startWithSourceCode(sourceCode); |
+ |
+ // Worker initialization should be counted as a pending activity. |
+ EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity()); |
+ workerMessagingProxy()->waitUntil(WaitUntilMode::PendingActivityReported); |
+ EXPECT_FALSE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity()); |
+ |
+ // The first message event sets the active timer that is counted as a |
+ // pending activity. |
+ dispatchMessageEvent(); |
+ EXPECT_EQ(1u, workerMessagingProxy()->unconfirmedMessageCount()); |
+ EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity()); |
+ workerMessagingProxy()->waitUntil(WaitUntilMode::MessageConfirmed); |
+ EXPECT_EQ(0u, workerMessagingProxy()->unconfirmedMessageCount()); |
+ EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity()); |
+ |
+ // Run the message loop for a while to make sure the timer is counted as a |
+ // pending activity until it's stopped. |
+ testing::runDelayedTasks(1000); |
+ EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity()); |
+ |
+ // Stop the timer. |
+ dispatchMessageEvent(); |
+ EXPECT_EQ(1u, workerMessagingProxy()->unconfirmedMessageCount()); |
+ EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity()); |
+ workerMessagingProxy()->waitUntil(WaitUntilMode::MessageConfirmed); |
+ EXPECT_EQ(0u, workerMessagingProxy()->unconfirmedMessageCount()); |
+ EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity()); |
+ |
+ // There should be no pending activities after the timer is stopped. |
+ workerMessagingProxy()->waitUntil(WaitUntilMode::PendingActivityReported); |
+ EXPECT_FALSE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity()); |
+} |
+ |
+} // namespace blink |