Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(138)

Side by Side Diff: Source/core/dom/ScriptRunnerTest.cpp

Issue 866273005: Teach ScriptRunner how to yield and post on loading task queue (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Responding to feedback re task cancellation Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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/heap/Handle.h"
11 #include "platform/scheduler/Scheduler.h"
12 #include "public/platform/Platform.h"
13 #include "wtf/PassOwnPtr.h"
14 #include <gmock/gmock.h>
15 #include <gtest/gtest.h>
16
17 using ::testing::Invoke;
18 using ::testing::ElementsAre;
19 using ::testing::Return;
20
21 namespace blink {
22
23 class MockScriptLoader: public ScriptLoader {
24 public:
25 explicit MockScriptLoader(Element* element) : ScriptLoader(element, false, f alse) { }
26
27 ~MockScriptLoader() override { }
28
29 MOCK_METHOD0(execute, void());
30 MOCK_CONST_METHOD0(isReady, bool());
31 };
32
33 class MockPlatform : public Platform, private WebScheduler {
34 public:
35 MockPlatform() : m_shouldYield(false), m_shouldYieldEveryOtherTime(false) { }
36
37 WebScheduler* scheduler() override
38 {
39 return this;
40 }
41
42 void postLoadingTask(const WebTraceLocation&, WebThread::Task* task) overrid e
43 {
44 m_tasks.append(adoptPtr(task));
45 }
46
47 void cryptographicallyRandomValues(unsigned char* buffer, size_t length) ove rride { }
48
49 void runSingleTask()
50 {
51 if (m_tasks.isEmpty())
52 return;
53 m_tasks.takeFirst()->run();
54 }
55
56 void runAllTasks()
57 {
58 while (!m_tasks.isEmpty())
59 m_tasks.takeFirst()->run();
60 }
61
62 bool shouldYieldForHighPriorityWork() override
63 {
64 if (m_shouldYieldEveryOtherTime)
65 m_shouldYield = !m_shouldYield;
66 return m_shouldYield;
67 }
68
69 void setShouldYield(bool shouldYield)
70 {
71 m_shouldYield = shouldYield;
72 }
73
74 // NOTE if we yield 100% of the time, nothing will get run.
75 void setShouldYieldEveryOtherTime(bool shouldYieldEveryOtherTime)
76 {
77 m_shouldYieldEveryOtherTime = shouldYieldEveryOtherTime;
78 }
79
80 private:
81 Deque<OwnPtr<WebThread::Task>> m_tasks;
82 bool m_shouldYield;
83 bool m_shouldYieldEveryOtherTime;
84 };
85
86 class ScriptRunnerTest : public testing::Test {
87 public:
88 void SetUp() override
89 {
90 m_document = Document::create();
91 m_element = m_document->createElement("foo", ASSERT_NO_EXCEPTION);
92
93 m_scriptRunner = ScriptRunner::create(m_document.get());
94 m_oldPlatform = Platform::current();
95 Platform::initialize(&m_platform);
96 m_platform.setShouldYield(false);
97 m_platform.setShouldYieldEveryOtherTime(false);
98 }
99
100 void TearDown() override
101 {
102 Scheduler::shutdown();
103 Platform::initialize(m_oldPlatform);
104 }
105
106 RefPtrWillBePersistent<Document> m_document;
107 RefPtrWillBePersistent<Element> m_element;
108 OwnPtrWillBePersistent<ScriptRunner> m_scriptRunner;
109 std::vector<int> m_order; // gmock matchers don't work nicely with WTF::Vect or
110 MockPlatform m_platform;
111 Platform* m_oldPlatform;
112 };
113
114 TEST_F(ScriptRunnerTest, QueueSingleScript_Async)
115 {
116 MockScriptLoader scriptLoader(m_element.get());
117 m_scriptRunner->queueScriptForExecution(&scriptLoader, ScriptRunner::ASYNC_E XECUTION);
118 m_scriptRunner->notifyScriptReady(&scriptLoader, ScriptRunner::ASYNC_EXECUTI ON);
119
120 EXPECT_CALL(scriptLoader, execute());
121 m_platform.runAllTasks();
122 }
123
124 TEST_F(ScriptRunnerTest, QueueSingleScript_InOrder)
125 {
126 MockScriptLoader scriptLoader(m_element.get());
127 m_scriptRunner->queueScriptForExecution(&scriptLoader, ScriptRunner::IN_ORDE R_EXECUTION);
128 m_scriptRunner->resume();
129
130 EXPECT_CALL(scriptLoader, isReady()).WillOnce(Return(true));
131 EXPECT_CALL(scriptLoader, execute());
132 m_platform.runAllTasks();
133 }
134
135 TEST_F(ScriptRunnerTest, QueueMultipleScripts_InOrder)
136 {
137 MockScriptLoader scriptLoader1(m_element.get());
138 MockScriptLoader scriptLoader2(m_element.get());
139 MockScriptLoader scriptLoader3(m_element.get());
140
141 m_scriptRunner->queueScriptForExecution(&scriptLoader1, ScriptRunner::IN_ORD ER_EXECUTION);
142 m_scriptRunner->queueScriptForExecution(&scriptLoader2, ScriptRunner::IN_ORD ER_EXECUTION);
143 m_scriptRunner->queueScriptForExecution(&scriptLoader3, ScriptRunner::IN_ORD ER_EXECUTION);
144
145 EXPECT_CALL(scriptLoader1, execute()).WillOnce(Invoke([this] {
146 m_order.push_back(1);
147 }));
148 EXPECT_CALL(scriptLoader2, execute()).WillOnce(Invoke([this] {
149 m_order.push_back(2);
150 }));
151 EXPECT_CALL(scriptLoader3, execute()).WillOnce(Invoke([this] {
152 m_order.push_back(3);
153 }));
154
155 // Make the scripts become ready in reverse order.
156 bool isReady[] = { false, false, false };
157 EXPECT_CALL(scriptLoader1, isReady()).WillRepeatedly(Invoke([&isReady] {
158 return isReady[0];
159 }));
160 EXPECT_CALL(scriptLoader2, isReady()).WillRepeatedly(Invoke([&isReady] {
161 return isReady[1];
162 }));
163 EXPECT_CALL(scriptLoader3, isReady()).WillRepeatedly(Invoke([&isReady] {
164 return isReady[2];
165 }));
166
167 for (int i = 2; i >= 0; i--) {
168 isReady[i] = true;
169 m_scriptRunner->resume();
170 m_platform.runAllTasks();
171 }
172
173 // But ensure the scripts were run in the expected order.
174 EXPECT_THAT(m_order, ElementsAre(1, 2, 3));
175 }
176
177 TEST_F(ScriptRunnerTest, QueueMixedScripts)
178 {
179 MockScriptLoader scriptLoader1(m_element.get());
180 MockScriptLoader scriptLoader2(m_element.get());
181 MockScriptLoader scriptLoader3(m_element.get());
182 MockScriptLoader scriptLoader4(m_element.get());
183 MockScriptLoader scriptLoader5(m_element.get());
184
185 EXPECT_CALL(scriptLoader1, isReady()).WillRepeatedly(Return(true));
186 EXPECT_CALL(scriptLoader2, isReady()).WillRepeatedly(Return(true));
187 EXPECT_CALL(scriptLoader3, isReady()).WillRepeatedly(Return(true));
188
189 m_scriptRunner->queueScriptForExecution(&scriptLoader1, ScriptRunner::IN_ORD ER_EXECUTION);
190 m_scriptRunner->queueScriptForExecution(&scriptLoader2, ScriptRunner::IN_ORD ER_EXECUTION);
191 m_scriptRunner->queueScriptForExecution(&scriptLoader3, ScriptRunner::IN_ORD ER_EXECUTION);
192 m_scriptRunner->queueScriptForExecution(&scriptLoader4, ScriptRunner::ASYNC_ EXECUTION);
193 m_scriptRunner->queueScriptForExecution(&scriptLoader5, ScriptRunner::ASYNC_ EXECUTION);
194
195 m_scriptRunner->notifyScriptReady(&scriptLoader4, ScriptRunner::ASYNC_EXECUT ION);
196 m_scriptRunner->notifyScriptReady(&scriptLoader5, ScriptRunner::ASYNC_EXECUT ION);
197
198 EXPECT_CALL(scriptLoader1, execute()).WillOnce(Invoke([this] {
199 m_order.push_back(1);
200 }));
201 EXPECT_CALL(scriptLoader2, execute()).WillOnce(Invoke([this] {
202 m_order.push_back(2);
203 }));
204 EXPECT_CALL(scriptLoader3, execute()).WillOnce(Invoke([this] {
205 m_order.push_back(3);
206 }));
207 EXPECT_CALL(scriptLoader4, execute()).WillOnce(Invoke([this] {
208 m_order.push_back(4);
209 }));
210 EXPECT_CALL(scriptLoader5, execute()).WillOnce(Invoke([this] {
211 m_order.push_back(5);
212 }));
213
214 m_platform.runAllTasks();
215
216 // Make sure the async scripts were run before the in-order ones.
217 EXPECT_THAT(m_order, ElementsAre(4, 5, 1, 2, 3));
218 }
219
220 TEST_F(ScriptRunnerTest, QueueMixedScripts_YieldAfterEveryExecution)
221 {
222 MockScriptLoader scriptLoader1(m_element.get());
223 MockScriptLoader scriptLoader2(m_element.get());
224 MockScriptLoader scriptLoader3(m_element.get());
225 MockScriptLoader scriptLoader4(m_element.get());
226 MockScriptLoader scriptLoader5(m_element.get());
227
228 m_platform.setShouldYieldEveryOtherTime(true);
229
230 EXPECT_CALL(scriptLoader1, isReady()).WillRepeatedly(Return(true));
231 EXPECT_CALL(scriptLoader2, isReady()).WillRepeatedly(Return(true));
232 EXPECT_CALL(scriptLoader3, isReady()).WillRepeatedly(Return(true));
233
234 m_scriptRunner->queueScriptForExecution(&scriptLoader1, ScriptRunner::IN_ORD ER_EXECUTION);
235 m_scriptRunner->queueScriptForExecution(&scriptLoader2, ScriptRunner::IN_ORD ER_EXECUTION);
236 m_scriptRunner->queueScriptForExecution(&scriptLoader3, ScriptRunner::IN_ORD ER_EXECUTION);
237 m_scriptRunner->queueScriptForExecution(&scriptLoader4, ScriptRunner::ASYNC_ EXECUTION);
238 m_scriptRunner->queueScriptForExecution(&scriptLoader5, ScriptRunner::ASYNC_ EXECUTION);
239
240 m_scriptRunner->notifyScriptReady(&scriptLoader4, ScriptRunner::ASYNC_EXECUT ION);
241 m_scriptRunner->notifyScriptReady(&scriptLoader5, ScriptRunner::ASYNC_EXECUT ION);
242
243 EXPECT_CALL(scriptLoader1, execute()).WillOnce(Invoke([this] {
244 m_order.push_back(1);
245 }));
246 EXPECT_CALL(scriptLoader2, execute()).WillOnce(Invoke([this] {
247 m_order.push_back(2);
248 }));
249 EXPECT_CALL(scriptLoader3, execute()).WillOnce(Invoke([this] {
250 m_order.push_back(3);
251 }));
252 EXPECT_CALL(scriptLoader4, execute()).WillOnce(Invoke([this] {
253 m_order.push_back(4);
254 }));
255 EXPECT_CALL(scriptLoader5, execute()).WillOnce(Invoke([this] {
256 m_order.push_back(5);
257 }));
258
259 m_platform.runAllTasks();
260
261 // Make sure the async scripts were run before the in-order ones.
262 EXPECT_THAT(m_order, ElementsAre(4, 5, 1, 2, 3));
263 }
264
265 TEST_F(ScriptRunnerTest, QueueReentrantScript_Async)
266 {
267 MockScriptLoader scriptLoader1(m_element.get());
268 MockScriptLoader scriptLoader2(m_element.get());
269 MockScriptLoader scriptLoader3(m_element.get());
270
271 m_scriptRunner->queueScriptForExecution(&scriptLoader1, ScriptRunner::ASYNC_ EXECUTION);
272 m_scriptRunner->queueScriptForExecution(&scriptLoader2, ScriptRunner::ASYNC_ EXECUTION);
273 m_scriptRunner->queueScriptForExecution(&scriptLoader3, ScriptRunner::ASYNC_ EXECUTION);
274 m_scriptRunner->notifyScriptReady(&scriptLoader1, ScriptRunner::ASYNC_EXECUT ION);
275
276 EXPECT_CALL(scriptLoader1, execute()).WillOnce(Invoke([&scriptLoader2, this] {
277 m_order.push_back(1);
278 m_scriptRunner->notifyScriptReady(&scriptLoader2, ScriptRunner::ASYNC_EX ECUTION);
279 }));
280
281 EXPECT_CALL(scriptLoader2, execute()).WillOnce(Invoke([&scriptLoader3, this] {
282 m_order.push_back(2);
283 m_scriptRunner->notifyScriptReady(&scriptLoader3, ScriptRunner::ASYNC_EX ECUTION);
284 }));
285
286 EXPECT_CALL(scriptLoader3, execute()).WillOnce(Invoke([this] {
287 m_order.push_back(3);
288 }));
289
290 // Make sure that re-entrant calls to notifyScriptReady don't cause ScriptRu nner::execute to do
291 // more work than expected.
292 m_platform.runSingleTask();
293 EXPECT_THAT(m_order, ElementsAre(1));
294
295 m_platform.runSingleTask();
296 EXPECT_THAT(m_order, ElementsAre(1, 2));
297
298 m_platform.runSingleTask();
299 EXPECT_THAT(m_order, ElementsAre(1, 2, 3));
300 }
301
302 TEST_F(ScriptRunnerTest, QueueReentrantScript_InOrder)
303 {
304 MockScriptLoader scriptLoader1(m_element.get());
305 MockScriptLoader scriptLoader2(m_element.get());
306 MockScriptLoader scriptLoader3(m_element.get());
307
308 EXPECT_CALL(scriptLoader1, isReady()).WillRepeatedly(Return(true));
309 EXPECT_CALL(scriptLoader2, isReady()).WillRepeatedly(Return(true));
310 EXPECT_CALL(scriptLoader3, isReady()).WillRepeatedly(Return(true));
311
312 m_scriptRunner->queueScriptForExecution(&scriptLoader1, ScriptRunner::IN_ORD ER_EXECUTION);
313 m_scriptRunner->resume();
314
315 EXPECT_CALL(scriptLoader1, execute()).WillOnce(Invoke([&scriptLoader2, this] {
316 m_order.push_back(1);
317 m_scriptRunner->queueScriptForExecution(&scriptLoader2, ScriptRunner::IN _ORDER_EXECUTION);
318 m_scriptRunner->resume();
319 }));
320
321 EXPECT_CALL(scriptLoader2, execute()).WillOnce(Invoke([&scriptLoader3, this] {
322 m_order.push_back(2);
323 m_scriptRunner->queueScriptForExecution(&scriptLoader3, ScriptRunner::IN _ORDER_EXECUTION);
324 m_scriptRunner->resume();
325 }));
326
327 EXPECT_CALL(scriptLoader3, execute()).WillOnce(Invoke([this] {
328 m_order.push_back(3);
329 }));
330
331 // Make sure that re-entrant calls to queueScriptForExecution don't cause Sc riptRunner::execute to do
332 // more work than expected.
333 m_platform.runSingleTask();
334 EXPECT_THAT(m_order, ElementsAre(1));
335
336 m_platform.runSingleTask();
337 EXPECT_THAT(m_order, ElementsAre(1, 2));
338
339 m_platform.runSingleTask();
340 EXPECT_THAT(m_order, ElementsAre(1, 2, 3));
341 }
342
343 TEST_F(ScriptRunnerTest, ShouldYield_AsyncScripts)
344 {
345 MockScriptLoader scriptLoader1(m_element.get());
346 MockScriptLoader scriptLoader2(m_element.get());
347 MockScriptLoader scriptLoader3(m_element.get());
348
349 m_scriptRunner->queueScriptForExecution(&scriptLoader1, ScriptRunner::ASYNC_ EXECUTION);
350 m_scriptRunner->queueScriptForExecution(&scriptLoader2, ScriptRunner::ASYNC_ EXECUTION);
351 m_scriptRunner->queueScriptForExecution(&scriptLoader3, ScriptRunner::ASYNC_ EXECUTION);
352 m_scriptRunner->notifyScriptReady(&scriptLoader1, ScriptRunner::ASYNC_EXECUT ION);
353 m_scriptRunner->notifyScriptReady(&scriptLoader2, ScriptRunner::ASYNC_EXECUT ION);
354 m_scriptRunner->notifyScriptReady(&scriptLoader3, ScriptRunner::ASYNC_EXECUT ION);
355
356 EXPECT_CALL(scriptLoader1, execute()).WillOnce(Invoke([this] {
357 m_order.push_back(1);
358 m_platform.setShouldYield(true);
359 }));
360 EXPECT_CALL(scriptLoader2, execute()).WillOnce(Invoke([this] {
361 m_order.push_back(2);
362 }));
363 EXPECT_CALL(scriptLoader3, execute()).WillOnce(Invoke([this] {
364 m_order.push_back(3);
365 }));
366
367 m_platform.runSingleTask();
368 EXPECT_THAT(m_order, ElementsAre(1));
369
370 // Make sure the interrupted tasks are executed next 'tick'.
371 m_platform.setShouldYield(false);
372 m_platform.runSingleTask();
373 EXPECT_THAT(m_order, ElementsAre(1, 2, 3));
374 }
375
376 TEST_F(ScriptRunnerTest, ShouldYield_InOrderScripts)
377 {
378 MockScriptLoader scriptLoader1(m_element.get());
379 MockScriptLoader scriptLoader2(m_element.get());
380 MockScriptLoader scriptLoader3(m_element.get());
381
382 EXPECT_CALL(scriptLoader1, isReady()).WillRepeatedly(Return(true));
383 EXPECT_CALL(scriptLoader2, isReady()).WillRepeatedly(Return(true));
384 EXPECT_CALL(scriptLoader3, isReady()).WillRepeatedly(Return(true));
385
386 m_scriptRunner->queueScriptForExecution(&scriptLoader1, ScriptRunner::IN_ORD ER_EXECUTION);
387 m_scriptRunner->queueScriptForExecution(&scriptLoader2, ScriptRunner::IN_ORD ER_EXECUTION);
388 m_scriptRunner->queueScriptForExecution(&scriptLoader3, ScriptRunner::IN_ORD ER_EXECUTION);
389 m_scriptRunner->resume();
390
391 EXPECT_CALL(scriptLoader1, execute()).WillOnce(Invoke([this] {
392 m_order.push_back(1);
393 m_platform.setShouldYield(true);
394 }));
395 EXPECT_CALL(scriptLoader2, execute()).WillOnce(Invoke([this] {
396 m_order.push_back(2);
397 }));
398 EXPECT_CALL(scriptLoader3, execute()).WillOnce(Invoke([this] {
399 m_order.push_back(3);
400 }));
401
402 m_platform.runSingleTask();
403 EXPECT_THAT(m_order, ElementsAre(1));
404
405 // Make sure the interrupted tasks are executed next 'tick'.
406 m_platform.setShouldYield(false);
407 m_platform.runSingleTask();
408 EXPECT_THAT(m_order, ElementsAre(1, 2, 3));
409 }
410
411 } // namespace blink
OLDNEW
« Source/core/dom/ScriptRunner.cpp ('K') | « Source/core/dom/ScriptRunner.cpp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698