| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "core/workers/WorkerThread.h" | 5 #include "core/workers/WorkerThread.h" |
| 6 | 6 |
| 7 #include "core/workers/WorkerThreadTestHelper.h" | 7 #include "core/workers/WorkerThreadTestHelper.h" |
| 8 #include "public/platform/WebScheduler.h" | 8 #include "platform/testing/UnitTestHelpers.h" |
| 9 #include "testing/gmock/include/gmock/gmock.h" | 9 #include "testing/gmock/include/gmock/gmock.h" |
| 10 #include "testing/gtest/include/gtest/gtest.h" | 10 #include "testing/gtest/include/gtest/gtest.h" |
| 11 | 11 |
| 12 using testing::_; | 12 using testing::_; |
| 13 using testing::AtMost; | 13 using testing::AtMost; |
| 14 using testing::Invoke; | |
| 15 using testing::Return; | |
| 16 using testing::Mock; | |
| 17 | 14 |
| 18 namespace blink { | 15 namespace blink { |
| 19 | 16 |
| 20 class WorkerThreadTest : public testing::Test { | 17 class WorkerThreadTest : public ::testing::Test { |
| 21 public: | 18 public: |
| 22 void SetUp() override | 19 void SetUp() override |
| 23 { | 20 { |
| 24 m_mockWorkerLoaderProxyProvider = adoptPtr(new MockWorkerLoaderProxyProv
ider()); | 21 m_mockWorkerLoaderProxyProvider = adoptPtr(new MockWorkerLoaderProxyProv
ider()); |
| 25 m_mockWorkerReportingProxy = adoptPtr(new MockWorkerReportingProxy()); | 22 m_mockWorkerReportingProxy = adoptPtr(new MockWorkerReportingProxy()); |
| 26 m_securityOrigin = SecurityOrigin::create(KURL(ParsedURLString, "http://
fake.url/")); | 23 m_securityOrigin = SecurityOrigin::create(KURL(ParsedURLString, "http://
fake.url/")); |
| 27 m_workerThread = adoptPtr(new WorkerThreadForTest( | 24 m_workerThread = adoptPtr(new WorkerThreadForTest( |
| 28 m_mockWorkerLoaderProxyProvider.get(), | 25 m_mockWorkerLoaderProxyProvider.get(), |
| 29 *m_mockWorkerReportingProxy)); | 26 *m_mockWorkerReportingProxy)); |
| 30 } | 27 } |
| 31 | 28 |
| 32 void TearDown() override | 29 void TearDown() override |
| 33 { | 30 { |
| 34 m_workerThread->workerLoaderProxy()->detachProvider(m_mockWorkerLoaderPr
oxyProvider.get()); | 31 m_workerThread->workerLoaderProxy()->detachProvider(m_mockWorkerLoaderPr
oxyProvider.get()); |
| 35 } | 32 } |
| 36 | 33 |
| 37 void start() | 34 void start() |
| 38 { | 35 { |
| 39 startWithSourceCode("//fake source code"); | 36 m_workerThread->startWithSourceCode(m_securityOrigin.get(), "//fake sour
ce code"); |
| 40 } | 37 } |
| 41 | 38 |
| 42 void startWithSourceCode(const String& source) | 39 void startWithSourceCodeNotToFinish() |
| 43 { | 40 { |
| 44 m_workerThread->startWithSourceCode(m_securityOrigin.get(), source); | 41 // Use a JavaScript source code that makes an infinite loop so that we |
| 42 // can catch some kind of issues as a timeout. |
| 43 m_workerThread->startWithSourceCode(m_securityOrigin.get(), "while(true)
{}"); |
| 45 } | 44 } |
| 46 | 45 |
| 47 void waitForInit() | 46 void waitForShutdown() |
| 48 { | 47 { |
| 49 m_workerThread->waitForInit(); | 48 m_workerThread->m_shutdownEvent->wait(); |
| 49 } |
| 50 |
| 51 void setForceTerminationDelayInMs(long long forceTerminationDelayInMs) |
| 52 { |
| 53 m_workerThread->setForceTerminationDelayInMsForTesting(forceTerminationD
elayInMs); |
| 54 } |
| 55 |
| 56 bool isForceTerminationTaskScheduled() |
| 57 { |
| 58 return m_workerThread->m_scheduledForceTerminationTask.get(); |
| 50 } | 59 } |
| 51 | 60 |
| 52 protected: | 61 protected: |
| 53 void expectWorkerLifetimeReportingCalls() | 62 void expectReportingCalls() |
| 54 { | 63 { |
| 55 EXPECT_CALL(*m_mockWorkerReportingProxy, workerGlobalScopeStarted(_)).Ti
mes(1); | 64 EXPECT_CALL(*m_mockWorkerReportingProxy, workerGlobalScopeStarted(_)).Ti
mes(1); |
| 56 EXPECT_CALL(*m_mockWorkerReportingProxy, didEvaluateWorkerScript(true)).
Times(1); | 65 EXPECT_CALL(*m_mockWorkerReportingProxy, didEvaluateWorkerScript(true)).
Times(1); |
| 57 EXPECT_CALL(*m_mockWorkerReportingProxy, workerThreadTerminated()).Times
(1); | 66 EXPECT_CALL(*m_mockWorkerReportingProxy, workerThreadTerminated()).Times
(1); |
| 58 EXPECT_CALL(*m_mockWorkerReportingProxy, willDestroyWorkerGlobalScope())
.Times(1); | 67 EXPECT_CALL(*m_mockWorkerReportingProxy, willDestroyWorkerGlobalScope())
.Times(1); |
| 59 } | 68 } |
| 60 | 69 |
| 70 void expectReportingCallsForWorkerPossiblyTerminatedBeforeStarting() |
| 71 { |
| 72 EXPECT_CALL(*m_mockWorkerReportingProxy, workerGlobalScopeStarted(_)).Ti
mes(AtMost(1)); |
| 73 EXPECT_CALL(*m_mockWorkerReportingProxy, didEvaluateWorkerScript(_)).Tim
es(AtMost(1)); |
| 74 EXPECT_CALL(*m_mockWorkerReportingProxy, workerThreadTerminated()).Times
(1); |
| 75 EXPECT_CALL(*m_mockWorkerReportingProxy, willDestroyWorkerGlobalScope())
.Times(AtMost(1)); |
| 76 } |
| 77 |
| 78 void expectReportingCallsForWorkerForciblyTerminated() |
| 79 { |
| 80 EXPECT_CALL(*m_mockWorkerReportingProxy, workerGlobalScopeStarted(_)).Ti
mes(1); |
| 81 EXPECT_CALL(*m_mockWorkerReportingProxy, didEvaluateWorkerScript(false))
.Times(1); |
| 82 EXPECT_CALL(*m_mockWorkerReportingProxy, workerThreadTerminated()).Times
(1); |
| 83 EXPECT_CALL(*m_mockWorkerReportingProxy, willDestroyWorkerGlobalScope())
.Times(1); |
| 84 } |
| 85 |
| 61 RefPtr<SecurityOrigin> m_securityOrigin; | 86 RefPtr<SecurityOrigin> m_securityOrigin; |
| 62 OwnPtr<MockWorkerLoaderProxyProvider> m_mockWorkerLoaderProxyProvider; | 87 OwnPtr<MockWorkerLoaderProxyProvider> m_mockWorkerLoaderProxyProvider; |
| 63 OwnPtr<MockWorkerReportingProxy> m_mockWorkerReportingProxy; | 88 OwnPtr<MockWorkerReportingProxy> m_mockWorkerReportingProxy; |
| 64 OwnPtr<WorkerThreadForTest> m_workerThread; | 89 OwnPtr<WorkerThreadForTest> m_workerThread; |
| 65 }; | 90 }; |
| 66 | 91 |
| 67 TEST_F(WorkerThreadTest, StartAndStop) | 92 TEST_F(WorkerThreadTest, StartAndTerminate_AsyncTerminate) |
| 68 { | 93 { |
| 69 expectWorkerLifetimeReportingCalls(); | 94 expectReportingCalls(); |
| 70 start(); | 95 start(); |
| 71 waitForInit(); | 96 m_workerThread->waitForInit(); |
| 72 m_workerThread->terminateAndWait(); | 97 |
| 98 // The worker thread is not being blocked, so the worker thread should be |
| 99 // gracefully shut down. |
| 100 m_workerThread->terminate(); |
| 101 EXPECT_TRUE(isForceTerminationTaskScheduled()); |
| 102 waitForShutdown(); |
| 103 EXPECT_EQ(WorkerThread::ExitCode::GracefullyTerminated, m_workerThread->getE
xitCode()); |
| 73 } | 104 } |
| 74 | 105 |
| 75 TEST_F(WorkerThreadTest, StartAndStopImmediately) | 106 TEST_F(WorkerThreadTest, StartAndTerminate_SyncTerminate) |
| 76 { | 107 { |
| 77 EXPECT_CALL(*m_mockWorkerReportingProxy, workerGlobalScopeStarted(_)) | 108 expectReportingCalls(); |
| 78 .Times(AtMost(1)); | |
| 79 EXPECT_CALL(*m_mockWorkerReportingProxy, didEvaluateWorkerScript(_)) | |
| 80 .Times(AtMost(1)); | |
| 81 EXPECT_CALL(*m_mockWorkerReportingProxy, workerThreadTerminated()) | |
| 82 .Times(AtMost(1)); | |
| 83 EXPECT_CALL(*m_mockWorkerReportingProxy, willDestroyWorkerGlobalScope()) | |
| 84 .Times(AtMost(1)); | |
| 85 start(); | 109 start(); |
| 110 m_workerThread->waitForInit(); |
| 86 m_workerThread->terminateAndWait(); | 111 m_workerThread->terminateAndWait(); |
| 112 EXPECT_EQ(WorkerThread::ExitCode::SyncForciblyTerminated, m_workerThread->ge
tExitCode()); |
| 87 } | 113 } |
| 88 | 114 |
| 89 TEST_F(WorkerThreadTest, StartAndStopOnScriptLoaded) | 115 TEST_F(WorkerThreadTest, StartAndTerminateImmediately_AsyncTerminate) |
| 90 { | 116 { |
| 91 // Use a JavaScript source code that makes an infinite loop so that we can | 117 expectReportingCallsForWorkerPossiblyTerminatedBeforeStarting(); |
| 92 // catch some kind of issues as a timeout. | 118 start(); |
| 93 const String source("while(true) {}"); | |
| 94 | 119 |
| 95 EXPECT_CALL(*m_mockWorkerReportingProxy, workerGlobalScopeStarted(_)) | 120 // There are two possible cases depending on timing: |
| 96 .Times(AtMost(1)); | 121 // (1) If the thread hasn't been initialized on the worker thread yet, |
| 97 EXPECT_CALL(*m_mockWorkerReportingProxy, didEvaluateWorkerScript(_)) | 122 // terminate() should not attempt to shut down the thread. |
| 98 .Times(AtMost(1)); | 123 // (2) If the thread has already been initialized on the worker thread, |
| 99 EXPECT_CALL(*m_mockWorkerReportingProxy, workerThreadTerminated()) | 124 // terminate() should gracefully shut down the thread. |
| 100 .Times(AtMost(1)); | 125 m_workerThread->terminate(); |
| 101 EXPECT_CALL(*m_mockWorkerReportingProxy, willDestroyWorkerGlobalScope()) | 126 waitForShutdown(); |
| 102 .Times(AtMost(1)); | 127 WorkerThread::ExitCode exitCode = m_workerThread->getExitCode(); |
| 103 startWithSourceCode(source); | 128 EXPECT_TRUE(WorkerThread::ExitCode::TerminatedBeforeStarting == exitCode ||
WorkerThread::ExitCode::GracefullyTerminated == exitCode); |
| 104 m_workerThread->waitUntilScriptLoaded(); | |
| 105 m_workerThread->terminateAndWait(); | |
| 106 } | 129 } |
| 107 | 130 |
| 131 TEST_F(WorkerThreadTest, StartAndTerminateImmediately_SyncTerminate) |
| 132 { |
| 133 expectReportingCallsForWorkerPossiblyTerminatedBeforeStarting(); |
| 134 start(); |
| 135 |
| 136 // There are two possible cases depending on timing: |
| 137 // (1) If the thread hasn't been initialized on the worker thread yet, |
| 138 // terminateAndWait() should not attempt to shut down the thread. |
| 139 // (2) If the thread has already been initialized on the worker thread, |
| 140 // terminateAndWait() should synchronously forcibly terminates the worker |
| 141 // execution. |
| 142 m_workerThread->terminateAndWait(); |
| 143 WorkerThread::ExitCode exitCode = m_workerThread->getExitCode(); |
| 144 EXPECT_TRUE(WorkerThread::ExitCode::TerminatedBeforeStarting == exitCode ||
WorkerThread::ExitCode::SyncForciblyTerminated == exitCode); |
| 145 } |
| 146 |
| 147 TEST_F(WorkerThreadTest, TerminateBeforeStart_AsyncTerminate) |
| 148 { |
| 149 expectReportingCallsForWorkerPossiblyTerminatedBeforeStarting(); |
| 150 m_workerThread->terminate(); |
| 151 EXPECT_EQ(WorkerThread::ExitCode::TerminatedBeforeStarting, m_workerThread->
getExitCode()); |
| 152 EXPECT_FALSE(isForceTerminationTaskScheduled()); |
| 153 |
| 154 // Sync termination requests after the thread is terminated should not block |
| 155 // the main thread. |
| 156 m_workerThread->terminateAndWait(); |
| 157 WorkerThread::terminateAndWaitForAllWorkers(); |
| 158 } |
| 159 |
| 160 TEST_F(WorkerThreadTest, TerminateBeforeStart_SyncTerminate) |
| 161 { |
| 162 expectReportingCallsForWorkerPossiblyTerminatedBeforeStarting(); |
| 163 |
| 164 // Terminating the worker thread that hasn't started yet does not block the |
| 165 // main thread. |
| 166 m_workerThread->terminateAndWait(); |
| 167 EXPECT_EQ(WorkerThread::ExitCode::TerminatedBeforeStarting, m_workerThread->
getExitCode()); |
| 168 |
| 169 // Async termination request after the thread is terminated should not |
| 170 // schedule a delayed task. |
| 171 m_workerThread->terminate(); |
| 172 EXPECT_FALSE(isForceTerminationTaskScheduled()); |
| 173 |
| 174 // Sync termination requests after the thread is terminated should not block |
| 175 // the main thread. |
| 176 m_workerThread->terminateAndWait(); |
| 177 WorkerThread::terminateAndWaitForAllWorkers(); |
| 178 } |
| 179 |
| 180 TEST_F(WorkerThreadTest, StartAndTerminateOnScriptLoaded_SyncForciblyTerminate) |
| 181 { |
| 182 expectReportingCallsForWorkerForciblyTerminated(); |
| 183 startWithSourceCodeNotToFinish(); |
| 184 m_workerThread->waitUntilScriptLoaded(); |
| 185 |
| 186 // terminateAndWait() synchronously terminates the worker execution. |
| 187 m_workerThread->terminateAndWait(); |
| 188 EXPECT_EQ(WorkerThread::ExitCode::SyncForciblyTerminated, m_workerThread->ge
tExitCode()); |
| 189 } |
| 190 |
| 191 TEST_F(WorkerThreadTest, StartAndTerminateOnScriptLoaded_AsyncForciblyTerminate) |
| 192 { |
| 193 const long long kForceTerminationDelayInMs = 10; |
| 194 setForceTerminationDelayInMs(kForceTerminationDelayInMs); |
| 195 |
| 196 expectReportingCallsForWorkerForciblyTerminated(); |
| 197 startWithSourceCodeNotToFinish(); |
| 198 m_workerThread->waitUntilScriptLoaded(); |
| 199 |
| 200 // terminate() schedules a force termination task. |
| 201 m_workerThread->terminate(); |
| 202 EXPECT_TRUE(isForceTerminationTaskScheduled()); |
| 203 EXPECT_EQ(WorkerThread::ExitCode::NotTerminated, m_workerThread->getExitCode
()); |
| 204 |
| 205 // Wait until the force termination task runs. |
| 206 testing::runDelayedTasks(kForceTerminationDelayInMs); |
| 207 waitForShutdown(); |
| 208 EXPECT_EQ(WorkerThread::ExitCode::AsyncForciblyTerminated, m_workerThread->g
etExitCode()); |
| 209 } |
| 210 |
| 211 TEST_F(WorkerThreadTest, StartAndTerminateOnScriptLoaded_AsyncForciblyTerminate_
MultipleTimes) |
| 212 { |
| 213 const long long kForceTerminationDelayInMs = 10; |
| 214 setForceTerminationDelayInMs(kForceTerminationDelayInMs); |
| 215 |
| 216 expectReportingCallsForWorkerForciblyTerminated(); |
| 217 startWithSourceCodeNotToFinish(); |
| 218 m_workerThread->waitUntilScriptLoaded(); |
| 219 |
| 220 // terminate() schedules a force termination task. |
| 221 m_workerThread->terminate(); |
| 222 EXPECT_TRUE(isForceTerminationTaskScheduled()); |
| 223 EXPECT_EQ(WorkerThread::ExitCode::NotTerminated, m_workerThread->getExitCode
()); |
| 224 |
| 225 // Multiple terminate() calls should not take effect. |
| 226 m_workerThread->terminate(); |
| 227 m_workerThread->terminate(); |
| 228 EXPECT_EQ(WorkerThread::ExitCode::NotTerminated, m_workerThread->getExitCode
()); |
| 229 |
| 230 // Wait until the force termination task runs. |
| 231 testing::runDelayedTasks(kForceTerminationDelayInMs); |
| 232 waitForShutdown(); |
| 233 EXPECT_EQ(WorkerThread::ExitCode::AsyncForciblyTerminated, m_workerThread->g
etExitCode()); |
| 234 } |
| 235 |
| 236 TEST_F(WorkerThreadTest, StartAndTerminateOnScriptLoaded_SyncForciblyTerminateAf
terTerminationTaskIsScheduled) |
| 237 { |
| 238 const long long kForceTerminationDelayInMs = 10; |
| 239 setForceTerminationDelayInMs(kForceTerminationDelayInMs); |
| 240 |
| 241 expectReportingCallsForWorkerForciblyTerminated(); |
| 242 startWithSourceCodeNotToFinish(); |
| 243 m_workerThread->waitUntilScriptLoaded(); |
| 244 |
| 245 // terminate() schedules a force termination task. |
| 246 m_workerThread->terminate(); |
| 247 EXPECT_TRUE(isForceTerminationTaskScheduled()); |
| 248 EXPECT_EQ(WorkerThread::ExitCode::NotTerminated, m_workerThread->getExitCode
()); |
| 249 |
| 250 // terminateAndWait() should overtake the scheduled force termination task. |
| 251 m_workerThread->terminateAndWait(); |
| 252 EXPECT_FALSE(isForceTerminationTaskScheduled()); |
| 253 EXPECT_EQ(WorkerThread::ExitCode::SyncForciblyTerminated, m_workerThread->ge
tExitCode()); |
| 254 } |
| 255 |
| 256 // TODO(nhiroki): Add tests for terminateAndWaitForAllWorkers. |
| 257 |
| 108 } // namespace blink | 258 } // namespace blink |
| OLD | NEW |