Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "config.h" | |
| 6 #include "core/dom/ScriptRunner.h" | |
| 7 | |
| 8 #include "core/dom/Document.h" | |
| 9 #include "core/dom/ScriptLoader.h" | |
| 10 #include "platform/scheduler/Scheduler.h" | |
| 11 #include "public/platform/Platform.h" | |
| 12 #include "wtf/PassOwnPtr.h" | |
| 13 #include <gmock/gmock.h> | |
| 14 #include <gtest/gtest.h> | |
| 15 | |
| 16 using ::testing::Invoke; | |
| 17 using ::testing::ElementsAre; | |
| 18 using ::testing::Return; | |
| 19 | |
| 20 namespace blink { | |
| 21 | |
| 22 class MockScriptLoader: public ScriptLoader { | |
| 23 public: | |
| 24 explicit MockScriptLoader(Element* element) : ScriptLoader(element, false, f alse) { } | |
| 25 | |
| 26 ~MockScriptLoader() override { } | |
| 27 | |
| 28 MOCK_METHOD0(execute, void()); | |
| 29 MOCK_CONST_METHOD0(isReady, bool()); | |
| 30 }; | |
| 31 | |
| 32 class MockPlatform : public Platform, private WebScheduler { | |
| 33 public: | |
| 34 MockPlatform() : m_shouldYield(false) { } | |
| 35 | |
| 36 WebScheduler* scheduler() override | |
| 37 { | |
| 38 return this; | |
| 39 } | |
| 40 | |
| 41 void postLoadingTask(const WebTraceLocation&, WebThread::Task* task) overrid e | |
| 42 { | |
| 43 m_tasks.append(adoptPtr(task)); | |
| 44 } | |
| 45 | |
| 46 void cryptographicallyRandomValues(unsigned char* buffer, size_t length) ove rride { } | |
| 47 | |
| 48 void runSingleTask() | |
| 49 { | |
| 50 if (m_tasks.isEmpty()) | |
| 51 return; | |
| 52 m_tasks.takeFirst()->run(); | |
| 53 } | |
| 54 | |
| 55 void runAllTasks() | |
| 56 { | |
| 57 while (!m_tasks.isEmpty()) | |
| 58 m_tasks.takeFirst()->run(); | |
| 59 } | |
| 60 | |
| 61 bool shouldYieldForHighPriorityWork() override | |
| 62 { | |
| 63 return m_shouldYield; | |
| 64 } | |
| 65 | |
| 66 void setShouldYield(bool shouldYield) | |
| 67 { | |
| 68 m_shouldYield = shouldYield; | |
| 69 } | |
| 70 | |
| 71 private: | |
| 72 Deque<OwnPtr<WebThread::Task>> m_tasks; | |
| 73 bool m_shouldYield; | |
| 74 }; | |
| 75 | |
| 76 class ScriptRunnerTest : public testing::Test { | |
| 77 public: | |
| 78 void SetUp() override | |
| 79 { | |
| 80 m_document = Document::create(); | |
| 81 m_element = m_document->createElement("foo", ASSERT_NO_EXCEPTION); | |
| 82 | |
| 83 m_scriptRunner = ScriptRunner::create(m_document.get()); | |
| 84 m_oldPlatform = Platform::current(); | |
| 85 Platform::initialize(&m_platform); | |
| 86 m_platform.setShouldYield(false); | |
| 87 } | |
| 88 | |
| 89 void TearDown() override | |
| 90 { | |
| 91 Scheduler::shutdown(); | |
| 92 Platform::initialize(m_oldPlatform); | |
| 93 } | |
| 94 | |
| 95 RefPtr<Document> m_document; | |
| 96 RefPtr<Element> m_element; | |
| 97 OwnPtr<ScriptRunner> m_scriptRunner; | |
| 98 std::vector<int> m_order; // gmock matchers don't work nicely with WTF::Vect or | |
| 99 MockPlatform m_platform; | |
| 100 Platform* m_oldPlatform; | |
| 101 }; | |
| 102 | |
| 103 TEST_F(ScriptRunnerTest, QueueSingleScript_Async) | |
| 104 { | |
| 105 MockScriptLoader scriptLoader(m_element.get()); | |
| 106 m_scriptRunner->queueScriptForExecution(&scriptLoader, ScriptRunner::ASYNC_E XECUTION); | |
| 107 m_scriptRunner->notifyScriptReady(&scriptLoader, ScriptRunner::ASYNC_EXECUTI ON); | |
| 108 | |
| 109 EXPECT_CALL(scriptLoader, execute()); | |
| 110 m_platform.runAllTasks(); | |
| 111 } | |
| 112 | |
| 113 TEST_F(ScriptRunnerTest, QueueSingleScript_InOrder) | |
| 114 { | |
| 115 MockScriptLoader scriptLoader(m_element.get()); | |
| 116 m_scriptRunner->queueScriptForExecution(&scriptLoader, ScriptRunner::IN_ORDE R_EXECUTION); | |
| 117 m_scriptRunner->resume(); | |
| 118 | |
| 119 EXPECT_CALL(scriptLoader, isReady()).WillOnce(Return(true)); | |
| 120 EXPECT_CALL(scriptLoader, execute()); | |
| 121 m_platform.runAllTasks(); | |
| 122 } | |
| 123 | |
| 124 TEST_F(ScriptRunnerTest, QueueMultipleScripts_InOrder) | |
| 125 { | |
| 126 MockScriptLoader scriptLoader1(m_element.get()); | |
| 127 MockScriptLoader scriptLoader2(m_element.get()); | |
| 128 MockScriptLoader scriptLoader3(m_element.get()); | |
| 129 | |
| 130 m_scriptRunner->queueScriptForExecution(&scriptLoader1, ScriptRunner::IN_ORD ER_EXECUTION); | |
| 131 m_scriptRunner->queueScriptForExecution(&scriptLoader2, ScriptRunner::IN_ORD ER_EXECUTION); | |
| 132 m_scriptRunner->queueScriptForExecution(&scriptLoader3, ScriptRunner::IN_ORD ER_EXECUTION); | |
| 133 | |
| 134 EXPECT_CALL(scriptLoader1, execute()).WillOnce(Invoke([this] { | |
| 135 m_order.push_back(1); | |
| 136 })); | |
| 137 EXPECT_CALL(scriptLoader2, execute()).WillOnce(Invoke([this] { | |
| 138 m_order.push_back(2); | |
| 139 })); | |
| 140 EXPECT_CALL(scriptLoader3, execute()).WillOnce(Invoke([this] { | |
| 141 m_order.push_back(3); | |
| 142 })); | |
| 143 | |
| 144 // Make the scripts become ready in reverse order. | |
| 145 bool isReady[] = { false, false, false }; | |
| 146 EXPECT_CALL(scriptLoader1, isReady()).WillRepeatedly(Invoke([&isReady] { | |
| 147 return isReady[0]; | |
| 148 })); | |
| 149 EXPECT_CALL(scriptLoader2, isReady()).WillRepeatedly(Invoke([&isReady] { | |
| 150 return isReady[1]; | |
| 151 })); | |
| 152 EXPECT_CALL(scriptLoader3, isReady()).WillRepeatedly(Invoke([&isReady] { | |
| 153 return isReady[2]; | |
| 154 })); | |
| 155 | |
| 156 for (int i = 2; i >= 0; i--) { | |
| 157 isReady[i] = true; | |
| 158 m_scriptRunner->resume(); | |
| 159 m_platform.runAllTasks(); | |
| 160 } | |
| 161 | |
| 162 // But ensure the scripts were run in the expected order. | |
| 163 EXPECT_THAT(m_order, ElementsAre(1, 2, 3)); | |
| 164 } | |
| 165 | |
| 166 TEST_F(ScriptRunnerTest, QueueMixedScripts) | |
| 167 { | |
| 168 MockScriptLoader scriptLoader1(m_element.get()); | |
| 169 MockScriptLoader scriptLoader2(m_element.get()); | |
| 170 MockScriptLoader scriptLoader3(m_element.get()); | |
| 171 MockScriptLoader scriptLoader4(m_element.get()); | |
| 172 MockScriptLoader scriptLoader5(m_element.get()); | |
| 173 | |
| 174 EXPECT_CALL(scriptLoader1, isReady()).WillRepeatedly(Return(true)); | |
| 175 EXPECT_CALL(scriptLoader2, isReady()).WillRepeatedly(Return(true)); | |
| 176 EXPECT_CALL(scriptLoader3, isReady()).WillRepeatedly(Return(true)); | |
| 177 | |
| 178 m_scriptRunner->queueScriptForExecution(&scriptLoader1, ScriptRunner::IN_ORD ER_EXECUTION); | |
| 179 m_scriptRunner->queueScriptForExecution(&scriptLoader2, ScriptRunner::IN_ORD ER_EXECUTION); | |
| 180 m_scriptRunner->queueScriptForExecution(&scriptLoader3, ScriptRunner::IN_ORD ER_EXECUTION); | |
| 181 m_scriptRunner->queueScriptForExecution(&scriptLoader4, ScriptRunner::ASYNC_ EXECUTION); | |
| 182 m_scriptRunner->queueScriptForExecution(&scriptLoader5, ScriptRunner::ASYNC_ EXECUTION); | |
| 183 | |
| 184 m_scriptRunner->notifyScriptReady(&scriptLoader4, ScriptRunner::ASYNC_EXECUT ION); | |
| 185 m_scriptRunner->notifyScriptReady(&scriptLoader5, ScriptRunner::ASYNC_EXECUT ION); | |
| 186 | |
| 187 EXPECT_CALL(scriptLoader1, execute()).WillOnce(Invoke([this] { | |
| 188 m_order.push_back(1); | |
| 189 })); | |
| 190 EXPECT_CALL(scriptLoader2, execute()).WillOnce(Invoke([this] { | |
| 191 m_order.push_back(2); | |
| 192 })); | |
| 193 EXPECT_CALL(scriptLoader3, execute()).WillOnce(Invoke([this] { | |
| 194 m_order.push_back(3); | |
| 195 })); | |
| 196 EXPECT_CALL(scriptLoader4, execute()).WillOnce(Invoke([this] { | |
| 197 m_order.push_back(4); | |
| 198 })); | |
| 199 EXPECT_CALL(scriptLoader5, execute()).WillOnce(Invoke([this] { | |
| 200 m_order.push_back(5); | |
| 201 })); | |
| 202 | |
| 203 m_platform.runAllTasks(); | |
| 204 | |
| 205 // Make sure the async scripts where run before the in-order ones. | |
|
Sami
2015/02/19 12:52:14
typo: were
alex clarke (OOO till 29th)
2015/02/19 15:11:33
Done.
Sami
2015/02/19 16:25:53
Missed this one?
alex clarke (OOO till 29th)
2015/02/19 16:37:06
Done.
| |
| 206 EXPECT_THAT(m_order, ElementsAre(4, 5, 1, 2, 3)); | |
| 207 } | |
| 208 | |
| 209 TEST_F(ScriptRunnerTest, QueueReentrantScript_Async) | |
| 210 { | |
| 211 MockScriptLoader scriptLoader1(m_element.get()); | |
| 212 MockScriptLoader scriptLoader2(m_element.get()); | |
| 213 MockScriptLoader scriptLoader3(m_element.get()); | |
| 214 | |
| 215 m_scriptRunner->queueScriptForExecution(&scriptLoader1, ScriptRunner::ASYNC_ EXECUTION); | |
| 216 m_scriptRunner->queueScriptForExecution(&scriptLoader2, ScriptRunner::ASYNC_ EXECUTION); | |
| 217 m_scriptRunner->queueScriptForExecution(&scriptLoader3, ScriptRunner::ASYNC_ EXECUTION); | |
| 218 m_scriptRunner->notifyScriptReady(&scriptLoader1, ScriptRunner::ASYNC_EXECUT ION); | |
| 219 | |
| 220 EXPECT_CALL(scriptLoader1, execute()).WillOnce(Invoke([&scriptLoader2, this] { | |
| 221 m_order.push_back(1); | |
| 222 m_scriptRunner->notifyScriptReady(&scriptLoader2, ScriptRunner::ASYNC_EX ECUTION); | |
| 223 })); | |
| 224 | |
| 225 EXPECT_CALL(scriptLoader2, execute()).WillOnce(Invoke([&scriptLoader3, this] { | |
| 226 m_order.push_back(2); | |
| 227 m_scriptRunner->notifyScriptReady(&scriptLoader3, ScriptRunner::ASYNC_EX ECUTION); | |
| 228 })); | |
| 229 | |
| 230 EXPECT_CALL(scriptLoader3, execute()).WillOnce(Invoke([this] { | |
| 231 m_order.push_back(3); | |
| 232 })); | |
| 233 | |
| 234 // Make sure that re-entrant calls to notifyScriptReady don't cause ScriptRu nner::execute to do | |
| 235 // more work than expected. | |
| 236 m_platform.runSingleTask(); | |
| 237 EXPECT_THAT(m_order, ElementsAre(1)); | |
| 238 | |
| 239 m_platform.runSingleTask(); | |
| 240 EXPECT_THAT(m_order, ElementsAre(1, 2)); | |
| 241 | |
| 242 m_platform.runSingleTask(); | |
| 243 EXPECT_THAT(m_order, ElementsAre(1, 2, 3)); | |
| 244 } | |
| 245 | |
| 246 TEST_F(ScriptRunnerTest, QueueReentrantScript_InOrder) | |
| 247 { | |
| 248 MockScriptLoader scriptLoader1(m_element.get()); | |
| 249 MockScriptLoader scriptLoader2(m_element.get()); | |
| 250 MockScriptLoader scriptLoader3(m_element.get()); | |
| 251 | |
| 252 EXPECT_CALL(scriptLoader1, isReady()).WillRepeatedly(Return(true)); | |
| 253 EXPECT_CALL(scriptLoader2, isReady()).WillRepeatedly(Return(true)); | |
| 254 EXPECT_CALL(scriptLoader3, isReady()).WillRepeatedly(Return(true)); | |
| 255 | |
| 256 m_scriptRunner->queueScriptForExecution(&scriptLoader1, ScriptRunner::IN_ORD ER_EXECUTION); | |
| 257 m_scriptRunner->resume(); | |
| 258 | |
| 259 EXPECT_CALL(scriptLoader1, execute()).WillOnce(Invoke([&scriptLoader2, this] { | |
| 260 m_order.push_back(1); | |
| 261 m_scriptRunner->queueScriptForExecution(&scriptLoader2, ScriptRunner::IN _ORDER_EXECUTION); | |
| 262 m_scriptRunner->resume(); | |
| 263 })); | |
| 264 | |
| 265 EXPECT_CALL(scriptLoader2, execute()).WillOnce(Invoke([&scriptLoader3, this] { | |
| 266 m_order.push_back(2); | |
| 267 m_scriptRunner->queueScriptForExecution(&scriptLoader3, ScriptRunner::IN _ORDER_EXECUTION); | |
| 268 m_scriptRunner->resume(); | |
| 269 })); | |
| 270 | |
| 271 EXPECT_CALL(scriptLoader3, execute()).WillOnce(Invoke([this] { | |
| 272 m_order.push_back(3); | |
| 273 })); | |
| 274 | |
| 275 // Make sure that re-entrant calls to queueScriptForExecution don't cause Sc riptRunner::execute to do | |
| 276 // more work than expected. | |
| 277 m_platform.runSingleTask(); | |
| 278 EXPECT_THAT(m_order, ElementsAre(1)); | |
| 279 | |
| 280 m_platform.runSingleTask(); | |
| 281 EXPECT_THAT(m_order, ElementsAre(1, 2)); | |
| 282 | |
| 283 m_platform.runSingleTask(); | |
| 284 EXPECT_THAT(m_order, ElementsAre(1, 2, 3)); | |
| 285 } | |
| 286 | |
| 287 TEST_F(ScriptRunnerTest, ShouldYield_AsyncScripts) | |
| 288 { | |
| 289 MockScriptLoader scriptLoader1(m_element.get()); | |
| 290 MockScriptLoader scriptLoader2(m_element.get()); | |
| 291 MockScriptLoader scriptLoader3(m_element.get()); | |
| 292 | |
| 293 m_scriptRunner->queueScriptForExecution(&scriptLoader1, ScriptRunner::ASYNC_ EXECUTION); | |
| 294 m_scriptRunner->queueScriptForExecution(&scriptLoader2, ScriptRunner::ASYNC_ EXECUTION); | |
| 295 m_scriptRunner->queueScriptForExecution(&scriptLoader3, ScriptRunner::ASYNC_ EXECUTION); | |
| 296 m_scriptRunner->notifyScriptReady(&scriptLoader1, ScriptRunner::ASYNC_EXECUT ION); | |
| 297 m_scriptRunner->notifyScriptReady(&scriptLoader2, ScriptRunner::ASYNC_EXECUT ION); | |
| 298 m_scriptRunner->notifyScriptReady(&scriptLoader3, ScriptRunner::ASYNC_EXECUT ION); | |
| 299 | |
| 300 EXPECT_CALL(scriptLoader1, execute()).WillOnce(Invoke([this] { | |
| 301 m_order.push_back(1); | |
| 302 m_platform.setShouldYield(true); | |
| 303 })); | |
| 304 EXPECT_CALL(scriptLoader2, execute()).WillOnce(Invoke([this] { | |
| 305 m_order.push_back(2); | |
| 306 })); | |
| 307 EXPECT_CALL(scriptLoader3, execute()).WillOnce(Invoke([this] { | |
| 308 m_order.push_back(3); | |
| 309 })); | |
| 310 | |
| 311 m_platform.runSingleTask(); // Cancelled task due to calling notifyScriptRea dy 3x, does nothing. | |
| 312 m_platform.runSingleTask(); // Cancelled task due to calling notifyScriptRea dy 3x, does nothing. | |
| 313 m_platform.runSingleTask(); | |
| 314 EXPECT_THAT(m_order, ElementsAre(1)); | |
| 315 | |
| 316 // Make sure the interrupted tasks are executed next 'tick'. | |
| 317 m_platform.setShouldYield(false); | |
| 318 m_platform.runSingleTask(); | |
| 319 EXPECT_THAT(m_order, ElementsAre(1, 2, 3)); | |
| 320 } | |
| 321 | |
| 322 TEST_F(ScriptRunnerTest, ShouldYield_InOrderScripts) | |
| 323 { | |
| 324 MockScriptLoader scriptLoader1(m_element.get()); | |
| 325 MockScriptLoader scriptLoader2(m_element.get()); | |
| 326 MockScriptLoader scriptLoader3(m_element.get()); | |
| 327 | |
| 328 EXPECT_CALL(scriptLoader1, isReady()).WillRepeatedly(Return(true)); | |
| 329 EXPECT_CALL(scriptLoader2, isReady()).WillRepeatedly(Return(true)); | |
| 330 EXPECT_CALL(scriptLoader3, isReady()).WillRepeatedly(Return(true)); | |
| 331 | |
| 332 m_scriptRunner->queueScriptForExecution(&scriptLoader1, ScriptRunner::IN_ORD ER_EXECUTION); | |
| 333 m_scriptRunner->queueScriptForExecution(&scriptLoader2, ScriptRunner::IN_ORD ER_EXECUTION); | |
| 334 m_scriptRunner->queueScriptForExecution(&scriptLoader3, ScriptRunner::IN_ORD ER_EXECUTION); | |
| 335 m_scriptRunner->resume(); | |
| 336 | |
| 337 EXPECT_CALL(scriptLoader1, execute()).WillOnce(Invoke([this] { | |
| 338 m_order.push_back(1); | |
| 339 m_platform.setShouldYield(true); | |
| 340 })); | |
| 341 EXPECT_CALL(scriptLoader2, execute()).WillOnce(Invoke([this] { | |
| 342 m_order.push_back(2); | |
| 343 })); | |
| 344 EXPECT_CALL(scriptLoader3, execute()).WillOnce(Invoke([this] { | |
| 345 m_order.push_back(3); | |
| 346 })); | |
| 347 | |
| 348 m_platform.runSingleTask(); | |
| 349 EXPECT_THAT(m_order, ElementsAre(1)); | |
| 350 | |
| 351 // Make sure the interrupted tasks are executed next 'tick'. | |
| 352 m_platform.setShouldYield(false); | |
| 353 m_platform.runSingleTask(); | |
| 354 EXPECT_THAT(m_order, ElementsAre(1, 2, 3)); | |
| 355 } | |
| 356 | |
| 357 } // namespace blink | |
| OLD | NEW |