| 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
(AtMost(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->exit
Code()); |
| 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->ex
itCode()); |
| 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->exitCode(); |
| 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 isolate. |
| 141 m_workerThread->terminateAndWait(); |
| 142 WorkerThread::ExitCode exitCode = m_workerThread->exitCode(); |
| 143 EXPECT_TRUE(WorkerThread::ExitCode::TerminatedBeforeStarting == exitCode ||
WorkerThread::ExitCode::SyncForciblyTerminated == exitCode); |
| 144 } |
| 145 |
| 146 TEST_F(WorkerThreadTest, TerminateBeforeStart_AsyncTerminate) |
| 147 { |
| 148 m_workerThread->terminate(); |
| 149 EXPECT_EQ(WorkerThread::ExitCode::TerminatedBeforeStarting, m_workerThread->
exitCode()); |
| 150 EXPECT_FALSE(isForceTerminationTaskScheduled()); |
| 151 |
| 152 // Sync termination requests after the thread is terminated should not block |
| 153 // the main thread. |
| 154 m_workerThread->terminateAndWait(); |
| 155 WorkerThread::terminateAndWaitForAllWorkers(); |
| 156 } |
| 157 |
| 158 TEST_F(WorkerThreadTest, TerminateBeforeStart_SyncTerminate) |
| 159 { |
| 160 // Terminating the worker thread that hasn't started yet does not block the |
| 161 // main thread. |
| 162 m_workerThread->terminateAndWait(); |
| 163 EXPECT_EQ(WorkerThread::ExitCode::TerminatedBeforeStarting, m_workerThread->
exitCode()); |
| 164 |
| 165 // Async termination request after the thread is terminated should not |
| 166 // schedule a delayed task. |
| 167 m_workerThread->terminate(); |
| 168 EXPECT_FALSE(isForceTerminationTaskScheduled()); |
| 169 |
| 170 // Sync termination requests after the thread is terminated should not block |
| 171 // the main thread. |
| 172 m_workerThread->terminateAndWait(); |
| 173 WorkerThread::terminateAndWaitForAllWorkers(); |
| 174 } |
| 175 |
| 176 TEST_F(WorkerThreadTest, StartAndTerminateOnScriptLoaded_SyncForciblyTerminate) |
| 177 { |
| 178 expectReportingCallsForWorkerForciblyTerminated(); |
| 179 startWithSourceCodeNotToFinish(); |
| 180 m_workerThread->waitUntilScriptLoaded(); |
| 181 |
| 182 // terminateAndWait() synchronously terminates the isolate. |
| 183 m_workerThread->terminateAndWait(); |
| 184 EXPECT_EQ(WorkerThread::ExitCode::SyncForciblyTerminated, m_workerThread->ex
itCode()); |
| 185 } |
| 186 |
| 187 TEST_F(WorkerThreadTest, StartAndTerminateOnScriptLoaded_AsyncForciblyTerminate) |
| 188 { |
| 189 const long long kForceTerminationDelayInMs = 10; |
| 190 setForceTerminationDelayInMs(kForceTerminationDelayInMs); |
| 191 |
| 192 expectReportingCallsForWorkerForciblyTerminated(); |
| 193 startWithSourceCodeNotToFinish(); |
| 194 m_workerThread->waitUntilScriptLoaded(); |
| 195 |
| 196 // terminate() schedules a force termination task. |
| 197 m_workerThread->terminate(); |
| 198 EXPECT_TRUE(isForceTerminationTaskScheduled()); |
| 199 EXPECT_EQ(WorkerThread::ExitCode::NotTerminated, m_workerThread->exitCode())
; |
| 200 |
| 201 // Wait until the force termination task runs. |
| 202 testing::runDelayedTasks(kForceTerminationDelayInMs); |
| 203 waitForShutdown(); |
| 204 EXPECT_EQ(WorkerThread::ExitCode::AsyncForciblyTerminated, m_workerThread->e
xitCode()); |
| 205 } |
| 206 |
| 207 TEST_F(WorkerThreadTest, StartAndTerminateOnScriptLoaded_AsyncForciblyTerminate_
MultipleTimes) |
| 208 { |
| 209 const long long kForceTerminationDelayInMs = 10; |
| 210 setForceTerminationDelayInMs(kForceTerminationDelayInMs); |
| 211 |
| 212 expectReportingCallsForWorkerForciblyTerminated(); |
| 213 startWithSourceCodeNotToFinish(); |
| 214 m_workerThread->waitUntilScriptLoaded(); |
| 215 |
| 216 // terminate() schedules a force termination task. |
| 217 m_workerThread->terminate(); |
| 218 EXPECT_TRUE(isForceTerminationTaskScheduled()); |
| 219 EXPECT_EQ(WorkerThread::ExitCode::NotTerminated, m_workerThread->exitCode())
; |
| 220 |
| 221 // Multiple terminate() calls should not take effect. |
| 222 m_workerThread->terminate(); |
| 223 m_workerThread->terminate(); |
| 224 EXPECT_EQ(WorkerThread::ExitCode::NotTerminated, m_workerThread->exitCode())
; |
| 225 |
| 226 // Wait until the force termination task runs. |
| 227 testing::runDelayedTasks(kForceTerminationDelayInMs); |
| 228 waitForShutdown(); |
| 229 EXPECT_EQ(WorkerThread::ExitCode::AsyncForciblyTerminated, m_workerThread->e
xitCode()); |
| 230 } |
| 231 |
| 232 TEST_F(WorkerThreadTest, StartAndTerminateOnScriptLoaded_SyncForciblyTerminateAf
terTerminationTaskIsScheduled) |
| 233 { |
| 234 const long long kForceTerminationDelayInMs = 10; |
| 235 setForceTerminationDelayInMs(kForceTerminationDelayInMs); |
| 236 |
| 237 expectReportingCallsForWorkerForciblyTerminated(); |
| 238 startWithSourceCodeNotToFinish(); |
| 239 m_workerThread->waitUntilScriptLoaded(); |
| 240 |
| 241 // terminate() schedules a force termination task. |
| 242 m_workerThread->terminate(); |
| 243 EXPECT_TRUE(isForceTerminationTaskScheduled()); |
| 244 EXPECT_EQ(WorkerThread::ExitCode::NotTerminated, m_workerThread->exitCode())
; |
| 245 |
| 246 // terminateAndWait() should overtake the scheduled force termination task. |
| 247 m_workerThread->terminateAndWait(); |
| 248 EXPECT_FALSE(isForceTerminationTaskScheduled()); |
| 249 EXPECT_EQ(WorkerThread::ExitCode::SyncForciblyTerminated, m_workerThread->ex
itCode()); |
| 250 } |
| 251 |
| 252 // TODO(nhiroki): Add tests for terminateAndWaitForAllWorkers. |
| 253 |
| 108 } // namespace blink | 254 } // namespace blink |
| OLD | NEW |