OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "core/dom/CrossThreadTask.h" |
| 6 #include "core/events/MessageEvent.h" |
| 7 #include "core/testing/DummyPageHolder.h" |
| 8 #include "core/workers/DedicatedWorkerGlobalScope.h" |
| 9 #include "core/workers/DedicatedWorkerThread.h" |
| 10 #include "core/workers/InProcessWorkerMessagingProxy.h" |
| 11 #include "core/workers/InProcessWorkerObjectProxy.h" |
| 12 #include "core/workers/WorkerThread.h" |
| 13 #include "core/workers/WorkerThreadStartupData.h" |
| 14 #include "core/workers/WorkerThreadTestHelper.h" |
| 15 #include "platform/testing/UnitTestHelpers.h" |
| 16 #include "testing/gmock/include/gmock/gmock.h" |
| 17 #include "testing/gtest/include/gtest/gtest.h" |
| 18 #include <memory> |
| 19 |
| 20 namespace blink { |
| 21 |
| 22 class DedicatedWorkerThreadForTest final : public DedicatedWorkerThread { |
| 23 public: |
| 24 DedicatedWorkerThreadForTest( |
| 25 WorkerLoaderProxyProvider* workerLoaderProxyProvider, |
| 26 InProcessWorkerObjectProxy& workerObjectProxy) |
| 27 : DedicatedWorkerThread(WorkerLoaderProxy::create(workerLoaderProxyProvi
der), workerObjectProxy, monotonicallyIncreasingTime()) |
| 28 { |
| 29 m_workerBackingThread = WorkerBackingThread::createForTest("Test thread"
); |
| 30 } |
| 31 |
| 32 WorkerOrWorkletGlobalScope* createWorkerGlobalScope(std::unique_ptr<WorkerTh
readStartupData> startupData) override |
| 33 { |
| 34 return new DedicatedWorkerGlobalScope(startupData->m_scriptURL, startupD
ata->m_userAgent, this, m_timeOrigin, std::move(startupData->m_starterOriginPriv
ilegeData), std::move(startupData->m_workerClients)); |
| 35 } |
| 36 }; |
| 37 |
| 38 class InProcessWorkerMessagingProxyForTest : public InProcessWorkerMessagingProx
y { |
| 39 public: |
| 40 InProcessWorkerMessagingProxyForTest(ExecutionContext* executionContext) |
| 41 : InProcessWorkerMessagingProxy(executionContext, nullptr /* workerObjec
t */ , nullptr /* workerClients */) |
| 42 { |
| 43 workerObjectProxy().m_nextIntervalInSec = 0.1; |
| 44 workerObjectProxy().m_maxIntervalInSec = 0.2; |
| 45 |
| 46 m_mockWorkerLoaderProxyProvider = wrapUnique(new MockWorkerLoaderProxyPr
ovider()); |
| 47 m_workerThread = wrapUnique(new DedicatedWorkerThreadForTest(m_mockWorke
rLoaderProxyProvider.get(), workerObjectProxy())); |
| 48 workerThreadCreated(); |
| 49 |
| 50 m_mockWorkerThreadLifecycleObserver = new MockWorkerThreadLifecycleObser
ver(m_workerThread->getWorkerThreadLifecycleContext()); |
| 51 EXPECT_CALL(*m_mockWorkerThreadLifecycleObserver, contextDestroyed()).Ti
mes(1); |
| 52 } |
| 53 |
| 54 ~InProcessWorkerMessagingProxyForTest() override |
| 55 { |
| 56 EXPECT_EQ(WaitUntilMode::DontWait, m_waitUntilMode); |
| 57 m_workerThread->workerLoaderProxy()->detachProvider(m_mockWorkerLoaderPr
oxyProvider.get()); |
| 58 } |
| 59 |
| 60 enum class WaitUntilMode { |
| 61 DontWait, |
| 62 MessageConfirmed, |
| 63 PendingActivityReported, |
| 64 ThreadTerminated, |
| 65 }; |
| 66 |
| 67 // Blocks the main thread until a specified event happens. |
| 68 void waitUntil(WaitUntilMode mode) |
| 69 { |
| 70 EXPECT_TRUE(isMainThread()); |
| 71 EXPECT_EQ(WaitUntilMode::DontWait, m_waitUntilMode); |
| 72 m_waitUntilMode = mode; |
| 73 testing::enterRunLoop(); |
| 74 } |
| 75 |
| 76 void confirmMessageFromWorkerObject() override |
| 77 { |
| 78 EXPECT_TRUE(isMainThread()); |
| 79 InProcessWorkerMessagingProxy::confirmMessageFromWorkerObject(); |
| 80 if (m_waitUntilMode != WaitUntilMode::MessageConfirmed) |
| 81 return; |
| 82 m_waitUntilMode = WaitUntilMode::DontWait; |
| 83 testing::exitRunLoop(); |
| 84 } |
| 85 |
| 86 void pendingActivityFinished() override |
| 87 { |
| 88 EXPECT_TRUE(isMainThread()); |
| 89 InProcessWorkerMessagingProxy::pendingActivityFinished(); |
| 90 if (m_waitUntilMode != WaitUntilMode::PendingActivityReported) |
| 91 return; |
| 92 m_waitUntilMode = WaitUntilMode::DontWait; |
| 93 testing::exitRunLoop(); |
| 94 } |
| 95 |
| 96 void workerThreadTerminated() override |
| 97 { |
| 98 EXPECT_TRUE(isMainThread()); |
| 99 if (m_waitUntilMode != WaitUntilMode::ThreadTerminated) |
| 100 return; |
| 101 m_waitUntilMode = WaitUntilMode::DontWait; |
| 102 testing::exitRunLoop(); |
| 103 } |
| 104 |
| 105 std::unique_ptr<WorkerThread> createWorkerThread(double originTime) override |
| 106 { |
| 107 NOTREACHED(); |
| 108 return nullptr; |
| 109 } |
| 110 |
| 111 DedicatedWorkerThreadForTest* workerThread() |
| 112 { |
| 113 return static_cast<DedicatedWorkerThreadForTest*>(m_workerThread.get()); |
| 114 } |
| 115 |
| 116 bool workerGlobalScopeMayHavePendingActivity() const { return m_workerGlobal
ScopeMayHavePendingActivity; } |
| 117 unsigned unconfirmedMessageCount() const { return m_unconfirmedMessageCount;
} |
| 118 |
| 119 private: |
| 120 std::unique_ptr<MockWorkerLoaderProxyProvider> m_mockWorkerLoaderProxyProvid
er; |
| 121 Persistent<MockWorkerThreadLifecycleObserver> m_mockWorkerThreadLifecycleObs
erver; |
| 122 |
| 123 WaitUntilMode m_waitUntilMode = WaitUntilMode::DontWait; |
| 124 }; |
| 125 |
| 126 using WaitUntilMode = InProcessWorkerMessagingProxyForTest::WaitUntilMode; |
| 127 |
| 128 class DedicatedWorkerTest : public ::testing::Test { |
| 129 public: |
| 130 void SetUp() override |
| 131 { |
| 132 m_page = DummyPageHolder::create(); |
| 133 m_workerMessagingProxy = wrapUnique(new InProcessWorkerMessagingProxyFor
Test(&m_page->document())); |
| 134 m_securityOrigin = SecurityOrigin::create(KURL(ParsedURLString, "http://
fake.url/")); |
| 135 } |
| 136 |
| 137 void TearDown() override |
| 138 { |
| 139 workerThread()->terminate(); |
| 140 workerMessagingProxy()->waitUntil(WaitUntilMode::ThreadTerminated); |
| 141 } |
| 142 |
| 143 void startWithSourceCode(const String& source) |
| 144 { |
| 145 std::unique_ptr<Vector<CSPHeaderAndType>> headers = wrapUnique(new Vecto
r<CSPHeaderAndType>()); |
| 146 CSPHeaderAndType headerAndType("contentSecurityPolicy", ContentSecurityP
olicyHeaderTypeReport); |
| 147 headers->append(headerAndType); |
| 148 workerThread()->start(WorkerThreadStartupData::create( |
| 149 KURL(ParsedURLString, "http://fake.url/"), |
| 150 "fake user agent", |
| 151 source, |
| 152 nullptr /* cachedMetaData */, |
| 153 DontPauseWorkerGlobalScopeOnStart, |
| 154 headers.get(), |
| 155 "" /* referrerPolicy */, |
| 156 m_securityOrigin.get(), |
| 157 nullptr /* workerClients */, |
| 158 WebAddressSpaceLocal, |
| 159 nullptr /* originTrialTokens */, |
| 160 nullptr /* workerSettings */, |
| 161 V8CacheOptionsDefault)); |
| 162 } |
| 163 |
| 164 void dispatchMessageEvent() |
| 165 { |
| 166 workerMessagingProxy()->postMessageToWorkerGlobalScope(nullptr /* messag
e */, nullptr /* channels */); |
| 167 } |
| 168 |
| 169 InProcessWorkerMessagingProxyForTest* workerMessagingProxy() |
| 170 { |
| 171 return m_workerMessagingProxy.get(); |
| 172 } |
| 173 |
| 174 DedicatedWorkerThreadForTest* workerThread() |
| 175 { |
| 176 return m_workerMessagingProxy->workerThread(); |
| 177 } |
| 178 |
| 179 private: |
| 180 RefPtr<SecurityOrigin> m_securityOrigin; |
| 181 std::unique_ptr<DummyPageHolder> m_page; |
| 182 std::unique_ptr<InProcessWorkerMessagingProxyForTest> m_workerMessagingProxy
; |
| 183 }; |
| 184 |
| 185 TEST_F(DedicatedWorkerTest, PendingActivity_NoActivity) |
| 186 { |
| 187 const String sourceCode = "// Do nothing"; |
| 188 startWithSourceCode(sourceCode); |
| 189 |
| 190 // Worker initialization should be counted as a pending activity. |
| 191 EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity(
)); |
| 192 |
| 193 // There should be no pending activities after the initialization. |
| 194 workerMessagingProxy()->waitUntil(WaitUntilMode::PendingActivityReported); |
| 195 EXPECT_FALSE(workerMessagingProxy()->hasPendingActivity()); |
| 196 } |
| 197 |
| 198 TEST_F(DedicatedWorkerTest, PendingActivity_SetTimeout) |
| 199 { |
| 200 // Start an oneshot timer on initial script evaluation. |
| 201 const String sourceCode = "setTimeout(function() {}, 50);"; |
| 202 startWithSourceCode(sourceCode); |
| 203 |
| 204 // Worker initialization should be counted as a pending activity. |
| 205 EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity(
)); |
| 206 |
| 207 // The timer is fired soon and there should be no pending activities after |
| 208 // that. |
| 209 workerMessagingProxy()->waitUntil(WaitUntilMode::PendingActivityReported); |
| 210 EXPECT_FALSE(workerMessagingProxy()->hasPendingActivity()); |
| 211 } |
| 212 |
| 213 TEST_F(DedicatedWorkerTest, PendingActivity_SetInterval) |
| 214 { |
| 215 // Start a repeated timer on initial script evaluation, and stop it when a |
| 216 // message is received. |
| 217 const String sourceCode = |
| 218 "var id = setInterval(function() {}, 50);" |
| 219 "addEventListener('message', function(event) { clearInterval(id); });"; |
| 220 startWithSourceCode(sourceCode); |
| 221 |
| 222 // Worker initialization should be counted as a pending activity. |
| 223 EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity(
)); |
| 224 |
| 225 // Stop the timer. |
| 226 dispatchMessageEvent(); |
| 227 EXPECT_EQ(1u, workerMessagingProxy()->unconfirmedMessageCount()); |
| 228 EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity(
)); |
| 229 workerMessagingProxy()->waitUntil(WaitUntilMode::MessageConfirmed); |
| 230 EXPECT_EQ(0u, workerMessagingProxy()->unconfirmedMessageCount()); |
| 231 EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity(
)); |
| 232 |
| 233 // There should be no pending activities after the timer is stopped. |
| 234 workerMessagingProxy()->waitUntil(WaitUntilMode::PendingActivityReported); |
| 235 EXPECT_FALSE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity
()); |
| 236 } |
| 237 |
| 238 TEST_F(DedicatedWorkerTest, PendingActivity_SetTimeoutOnMessageEvent) |
| 239 { |
| 240 // Start an oneshot timer on a message event. |
| 241 const String sourceCode = |
| 242 "addEventListener('message', function(event) {" |
| 243 " setTimeout(function() {}, 50);" |
| 244 "});"; |
| 245 startWithSourceCode(sourceCode); |
| 246 |
| 247 // Worker initialization should be counted as a pending activity. |
| 248 EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity(
)); |
| 249 workerMessagingProxy()->waitUntil(WaitUntilMode::PendingActivityReported); |
| 250 EXPECT_FALSE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity
()); |
| 251 |
| 252 // A message starts the oneshot timer that is counted as a pending activity. |
| 253 dispatchMessageEvent(); |
| 254 EXPECT_EQ(1u, workerMessagingProxy()->unconfirmedMessageCount()); |
| 255 EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity(
)); |
| 256 workerMessagingProxy()->waitUntil(WaitUntilMode::MessageConfirmed); |
| 257 EXPECT_EQ(0u, workerMessagingProxy()->unconfirmedMessageCount()); |
| 258 EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity(
)); |
| 259 |
| 260 // The timer is fired soon and there should be no pending activities after |
| 261 // that. |
| 262 workerMessagingProxy()->waitUntil(WaitUntilMode::PendingActivityReported); |
| 263 EXPECT_FALSE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity
()); |
| 264 } |
| 265 |
| 266 TEST_F(DedicatedWorkerTest, PendingActivity_SetIntervalOnMessageEvent) |
| 267 { |
| 268 // Start a repeated timer on a message event, and stop it when another |
| 269 // message is received. |
| 270 const String sourceCode = |
| 271 "var count = 0;" |
| 272 "var id;" |
| 273 "addEventListener('message', function(event) {" |
| 274 " if (count++ == 0) {" |
| 275 " id = setInterval(function() {}, 50);" |
| 276 " } else {" |
| 277 " clearInterval(id);" |
| 278 " }" |
| 279 "});"; |
| 280 startWithSourceCode(sourceCode); |
| 281 |
| 282 // Worker initialization should be counted as a pending activity. |
| 283 EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity(
)); |
| 284 workerMessagingProxy()->waitUntil(WaitUntilMode::PendingActivityReported); |
| 285 EXPECT_FALSE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity
()); |
| 286 |
| 287 // The first message event sets the active timer that is counted as a |
| 288 // pending activity. |
| 289 dispatchMessageEvent(); |
| 290 EXPECT_EQ(1u, workerMessagingProxy()->unconfirmedMessageCount()); |
| 291 EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity(
)); |
| 292 workerMessagingProxy()->waitUntil(WaitUntilMode::MessageConfirmed); |
| 293 EXPECT_EQ(0u, workerMessagingProxy()->unconfirmedMessageCount()); |
| 294 EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity(
)); |
| 295 |
| 296 // Run the message loop for a while to make sure the timer is counted as a |
| 297 // pending activity until it's stopped. |
| 298 testing::runDelayedTasks(1000); |
| 299 EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity(
)); |
| 300 |
| 301 // Stop the timer. |
| 302 dispatchMessageEvent(); |
| 303 EXPECT_EQ(1u, workerMessagingProxy()->unconfirmedMessageCount()); |
| 304 EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity(
)); |
| 305 workerMessagingProxy()->waitUntil(WaitUntilMode::MessageConfirmed); |
| 306 EXPECT_EQ(0u, workerMessagingProxy()->unconfirmedMessageCount()); |
| 307 EXPECT_TRUE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity(
)); |
| 308 |
| 309 // There should be no pending activities after the timer is stopped. |
| 310 workerMessagingProxy()->waitUntil(WaitUntilMode::PendingActivityReported); |
| 311 EXPECT_FALSE(workerMessagingProxy()->workerGlobalScopeMayHavePendingActivity
()); |
| 312 } |
| 313 |
| 314 } // namespace blink |
OLD | NEW |