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 |