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 |