| 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
|
|
|