Index: third_party/WebKit/Source/core/workers/WorkerThreadTest.cpp |
diff --git a/third_party/WebKit/Source/core/workers/WorkerThreadTest.cpp b/third_party/WebKit/Source/core/workers/WorkerThreadTest.cpp |
index 8fb299c9d7693bf4a41fbf1664d1646ac85b57d8..6e11f3b36c539da3f853da174c516960ebe1a370 100644 |
--- a/third_party/WebKit/Source/core/workers/WorkerThreadTest.cpp |
+++ b/third_party/WebKit/Source/core/workers/WorkerThreadTest.cpp |
@@ -5,19 +5,16 @@ |
#include "core/workers/WorkerThread.h" |
#include "core/workers/WorkerThreadTestHelper.h" |
-#include "public/platform/WebScheduler.h" |
+#include "platform/testing/UnitTestHelpers.h" |
#include "testing/gmock/include/gmock/gmock.h" |
#include "testing/gtest/include/gtest/gtest.h" |
using testing::_; |
using testing::AtMost; |
-using testing::Invoke; |
-using testing::Return; |
-using testing::Mock; |
namespace blink { |
-class WorkerThreadTest : public testing::Test { |
+class WorkerThreadTest : public ::testing::Test { |
public: |
void SetUp() override |
{ |
@@ -36,21 +33,33 @@ public: |
void start() |
{ |
- startWithSourceCode("//fake source code"); |
+ m_workerThread->startWithSourceCode(m_securityOrigin.get(), "//fake source code"); |
} |
- void startWithSourceCode(const String& source) |
+ void startWithSourceCodeNotToFinish() |
{ |
- m_workerThread->startWithSourceCode(m_securityOrigin.get(), source); |
+ // Use a JavaScript source code that makes an infinite loop so that we |
+ // can catch some kind of issues as a timeout. |
+ m_workerThread->startWithSourceCode(m_securityOrigin.get(), "while(true) {}"); |
} |
- void waitForInit() |
+ void waitForShutdown() |
{ |
- m_workerThread->waitForInit(); |
+ m_workerThread->m_shutdownEvent->wait(); |
+ } |
+ |
+ void setForceTerminationDelayInMs(long long forceTerminationDelayInMs) |
+ { |
+ m_workerThread->setForceTerminationDelayInMsForTesting(forceTerminationDelayInMs); |
+ } |
+ |
+ bool isForceTerminationTaskScheduled() |
+ { |
+ return m_workerThread->m_scheduledForceTerminationTask.get(); |
} |
protected: |
- void expectWorkerLifetimeReportingCalls() |
+ void expectReportingCalls() |
{ |
EXPECT_CALL(*m_mockWorkerReportingProxy, workerGlobalScopeStarted(_)).Times(1); |
EXPECT_CALL(*m_mockWorkerReportingProxy, didEvaluateWorkerScript(true)).Times(1); |
@@ -58,51 +67,188 @@ protected: |
EXPECT_CALL(*m_mockWorkerReportingProxy, willDestroyWorkerGlobalScope()).Times(1); |
} |
+ void expectReportingCallsForWorkerPossiblyTerminatedBeforeStarting() |
+ { |
+ EXPECT_CALL(*m_mockWorkerReportingProxy, workerGlobalScopeStarted(_)).Times(AtMost(1)); |
+ EXPECT_CALL(*m_mockWorkerReportingProxy, didEvaluateWorkerScript(_)).Times(AtMost(1)); |
+ EXPECT_CALL(*m_mockWorkerReportingProxy, workerThreadTerminated()).Times(AtMost(1)); |
+ EXPECT_CALL(*m_mockWorkerReportingProxy, willDestroyWorkerGlobalScope()).Times(AtMost(1)); |
+ } |
+ |
+ void expectReportingCallsForWorkerForciblyTerminated() |
+ { |
+ EXPECT_CALL(*m_mockWorkerReportingProxy, workerGlobalScopeStarted(_)).Times(1); |
+ EXPECT_CALL(*m_mockWorkerReportingProxy, didEvaluateWorkerScript(false)).Times(1); |
+ EXPECT_CALL(*m_mockWorkerReportingProxy, workerThreadTerminated()).Times(1); |
+ EXPECT_CALL(*m_mockWorkerReportingProxy, willDestroyWorkerGlobalScope()).Times(1); |
+ } |
+ |
RefPtr<SecurityOrigin> m_securityOrigin; |
OwnPtr<MockWorkerLoaderProxyProvider> m_mockWorkerLoaderProxyProvider; |
OwnPtr<MockWorkerReportingProxy> m_mockWorkerReportingProxy; |
OwnPtr<WorkerThreadForTest> m_workerThread; |
}; |
-TEST_F(WorkerThreadTest, StartAndStop) |
+TEST_F(WorkerThreadTest, StartAndTerminate_AsyncTerminate) |
{ |
- expectWorkerLifetimeReportingCalls(); |
+ expectReportingCalls(); |
start(); |
- waitForInit(); |
+ m_workerThread->waitForInit(); |
+ |
+ // The worker thread is not being blocked, so the worker thread should be |
+ // gracefully shut down. |
+ m_workerThread->terminate(); |
+ EXPECT_TRUE(isForceTerminationTaskScheduled()); |
+ waitForShutdown(); |
+ EXPECT_EQ(WorkerThread::ExitCode::GracefullyTerminated, m_workerThread->exitCode()); |
+} |
+ |
+TEST_F(WorkerThreadTest, StartAndTerminate_SyncTerminate) |
+{ |
+ expectReportingCalls(); |
+ start(); |
+ m_workerThread->waitForInit(); |
m_workerThread->terminateAndWait(); |
+ EXPECT_EQ(WorkerThread::ExitCode::SyncForciblyTerminated, m_workerThread->exitCode()); |
+} |
+ |
+TEST_F(WorkerThreadTest, StartAndTerminateImmediately_AsyncTerminate) |
+{ |
+ expectReportingCallsForWorkerPossiblyTerminatedBeforeStarting(); |
+ start(); |
+ |
+ // There are two possible cases depending on timing: |
+ // (1) If the thread hasn't been initialized on the worker thread yet, |
+ // terminate() should not attempt to shut down the thread. |
+ // (2) If the thread has already been initialized on the worker thread, |
+ // terminate() should gracefully shut down the thread. |
+ m_workerThread->terminate(); |
+ waitForShutdown(); |
+ WorkerThread::ExitCode exitCode = m_workerThread->exitCode(); |
+ EXPECT_TRUE(WorkerThread::ExitCode::TerminatedBeforeStarting == exitCode || WorkerThread::ExitCode::GracefullyTerminated == exitCode); |
} |
-TEST_F(WorkerThreadTest, StartAndStopImmediately) |
+TEST_F(WorkerThreadTest, StartAndTerminateImmediately_SyncTerminate) |
{ |
- EXPECT_CALL(*m_mockWorkerReportingProxy, workerGlobalScopeStarted(_)) |
- .Times(AtMost(1)); |
- EXPECT_CALL(*m_mockWorkerReportingProxy, didEvaluateWorkerScript(_)) |
- .Times(AtMost(1)); |
- EXPECT_CALL(*m_mockWorkerReportingProxy, workerThreadTerminated()) |
- .Times(AtMost(1)); |
- EXPECT_CALL(*m_mockWorkerReportingProxy, willDestroyWorkerGlobalScope()) |
- .Times(AtMost(1)); |
+ expectReportingCallsForWorkerPossiblyTerminatedBeforeStarting(); |
start(); |
+ |
+ // There are two possible cases depending on timing: |
+ // (1) If the thread hasn't been initialized on the worker thread yet, |
+ // terminateAndWait() should not attempt to shut down the thread. |
+ // (2) If the thread has already been initialized on the worker thread, |
+ // terminateAndWait() should synchronously forcibly terminates the isolate. |
+ m_workerThread->terminateAndWait(); |
+ WorkerThread::ExitCode exitCode = m_workerThread->exitCode(); |
+ EXPECT_TRUE(WorkerThread::ExitCode::TerminatedBeforeStarting == exitCode || WorkerThread::ExitCode::SyncForciblyTerminated == exitCode); |
+} |
+ |
+TEST_F(WorkerThreadTest, TerminateBeforeStart_AsyncTerminate) |
+{ |
+ m_workerThread->terminate(); |
+ EXPECT_EQ(WorkerThread::ExitCode::TerminatedBeforeStarting, m_workerThread->exitCode()); |
+ EXPECT_FALSE(isForceTerminationTaskScheduled()); |
+ |
+ // Sync termination requests after the thread is terminated should not block |
+ // the main thread. |
+ m_workerThread->terminateAndWait(); |
+ WorkerThread::terminateAndWaitForAllWorkers(); |
+} |
+ |
+TEST_F(WorkerThreadTest, TerminateBeforeStart_SyncTerminate) |
+{ |
+ // Terminating the worker thread that hasn't started yet does not block the |
+ // main thread. |
+ m_workerThread->terminateAndWait(); |
+ EXPECT_EQ(WorkerThread::ExitCode::TerminatedBeforeStarting, m_workerThread->exitCode()); |
+ |
+ // Async termination request after the thread is terminated should not |
+ // schedule a delayed task. |
+ m_workerThread->terminate(); |
+ EXPECT_FALSE(isForceTerminationTaskScheduled()); |
+ |
+ // Sync termination requests after the thread is terminated should not block |
+ // the main thread. |
m_workerThread->terminateAndWait(); |
+ WorkerThread::terminateAndWaitForAllWorkers(); |
} |
-TEST_F(WorkerThreadTest, StartAndStopOnScriptLoaded) |
+TEST_F(WorkerThreadTest, StartAndTerminateOnScriptLoaded_SyncForciblyTerminate) |
{ |
- // Use a JavaScript source code that makes an infinite loop so that we can |
- // catch some kind of issues as a timeout. |
- const String source("while(true) {}"); |
- |
- EXPECT_CALL(*m_mockWorkerReportingProxy, workerGlobalScopeStarted(_)) |
- .Times(AtMost(1)); |
- EXPECT_CALL(*m_mockWorkerReportingProxy, didEvaluateWorkerScript(_)) |
- .Times(AtMost(1)); |
- EXPECT_CALL(*m_mockWorkerReportingProxy, workerThreadTerminated()) |
- .Times(AtMost(1)); |
- EXPECT_CALL(*m_mockWorkerReportingProxy, willDestroyWorkerGlobalScope()) |
- .Times(AtMost(1)); |
- startWithSourceCode(source); |
+ expectReportingCallsForWorkerForciblyTerminated(); |
+ startWithSourceCodeNotToFinish(); |
m_workerThread->waitUntilScriptLoaded(); |
+ |
+ // terminateAndWait() synchronously terminates the isolate. |
m_workerThread->terminateAndWait(); |
+ EXPECT_EQ(WorkerThread::ExitCode::SyncForciblyTerminated, m_workerThread->exitCode()); |
+} |
+ |
+TEST_F(WorkerThreadTest, StartAndTerminateOnScriptLoaded_AsyncForciblyTerminate) |
+{ |
+ const long long kForceTerminationDelayInMs = 10; |
+ setForceTerminationDelayInMs(kForceTerminationDelayInMs); |
+ |
+ expectReportingCallsForWorkerForciblyTerminated(); |
+ startWithSourceCodeNotToFinish(); |
+ m_workerThread->waitUntilScriptLoaded(); |
+ |
+ // terminate() schedules a force termination task. |
+ m_workerThread->terminate(); |
+ EXPECT_TRUE(isForceTerminationTaskScheduled()); |
+ EXPECT_EQ(WorkerThread::ExitCode::NotTerminated, m_workerThread->exitCode()); |
+ |
+ // Wait until the force termination task runs. |
+ testing::runDelayedTasks(kForceTerminationDelayInMs); |
+ waitForShutdown(); |
+ EXPECT_EQ(WorkerThread::ExitCode::AsyncForciblyTerminated, m_workerThread->exitCode()); |
} |
+TEST_F(WorkerThreadTest, StartAndTerminateOnScriptLoaded_AsyncForciblyTerminate_MultipleTimes) |
+{ |
+ const long long kForceTerminationDelayInMs = 10; |
+ setForceTerminationDelayInMs(kForceTerminationDelayInMs); |
+ |
+ expectReportingCallsForWorkerForciblyTerminated(); |
+ startWithSourceCodeNotToFinish(); |
+ m_workerThread->waitUntilScriptLoaded(); |
+ |
+ // terminate() schedules a force termination task. |
+ m_workerThread->terminate(); |
+ EXPECT_TRUE(isForceTerminationTaskScheduled()); |
+ EXPECT_EQ(WorkerThread::ExitCode::NotTerminated, m_workerThread->exitCode()); |
+ |
+ // Multiple terminate() calls should not take effect. |
+ m_workerThread->terminate(); |
+ m_workerThread->terminate(); |
+ EXPECT_EQ(WorkerThread::ExitCode::NotTerminated, m_workerThread->exitCode()); |
+ |
+ // Wait until the force termination task runs. |
+ testing::runDelayedTasks(kForceTerminationDelayInMs); |
+ waitForShutdown(); |
+ EXPECT_EQ(WorkerThread::ExitCode::AsyncForciblyTerminated, m_workerThread->exitCode()); |
+} |
+ |
+TEST_F(WorkerThreadTest, StartAndTerminateOnScriptLoaded_SyncForciblyTerminateAfterTerminationTaskIsScheduled) |
+{ |
+ const long long kForceTerminationDelayInMs = 10; |
+ setForceTerminationDelayInMs(kForceTerminationDelayInMs); |
+ |
+ expectReportingCallsForWorkerForciblyTerminated(); |
+ startWithSourceCodeNotToFinish(); |
+ m_workerThread->waitUntilScriptLoaded(); |
+ |
+ // terminate() schedules a force termination task. |
+ m_workerThread->terminate(); |
+ EXPECT_TRUE(isForceTerminationTaskScheduled()); |
+ EXPECT_EQ(WorkerThread::ExitCode::NotTerminated, m_workerThread->exitCode()); |
+ |
+ // terminateAndWait() should overtake the scheduled force termination task. |
+ m_workerThread->terminateAndWait(); |
+ EXPECT_FALSE(isForceTerminationTaskScheduled()); |
+ EXPECT_EQ(WorkerThread::ExitCode::SyncForciblyTerminated, m_workerThread->exitCode()); |
+} |
+ |
+// TODO(nhiroki): Add tests for terminateAndWaitForAllWorkers. |
+ |
} // namespace blink |