Chromium Code Reviews| 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..499c37396dccd8e9d36ab391309b5f402585aa76 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(); |
| + m_workerThread->terminateAndWaitForAllWorkers(); |
|
yhirano
2016/05/31 04:48:07
[optional] I prefer calling static member function
nhiroki
2016/05/31 06:08:56
Done.
|
| +} |
| + |
| +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(); |
| + m_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 |