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

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: Fix spelling mistake 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/scheduler/Scheduler.h"
sof 2015/02/20 07:48:50 Add #include "platform/heap/Handle.h" round about
alex clarke (OOO till 29th) 2015/02/20 11:59:34 Done.
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), m_shouldYieldEveryOtherTime(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 if (m_shouldYieldEveryOtherTime)
64 m_shouldYield = !m_shouldYield;
65 return m_shouldYield;
66 }
67
68 void setShouldYield(bool shouldYield)
69 {
70 m_shouldYield = shouldYield;
71 }
72
73 // NOTE if we yield 100% of the time, nothing will get run.
74 void setShouldYieldEveryOtherTime(bool shouldYieldEveryOtherTime)
75 {
76 m_shouldYieldEveryOtherTime = shouldYieldEveryOtherTime;
77 }
78
79 private:
80 Deque<OwnPtr<WebThread::Task>> m_tasks;
81 bool m_shouldYield;
82 bool m_shouldYieldEveryOtherTime;
83 };
84
85 class ScriptRunnerTest : public testing::Test {
86 public:
87 void SetUp() override
88 {
89 m_document = Document::create();
90 m_element = m_document->createElement("foo", ASSERT_NO_EXCEPTION);
91
92 m_scriptRunner = ScriptRunner::create(m_document.get());
93 m_oldPlatform = Platform::current();
94 Platform::initialize(&m_platform);
95 m_platform.setShouldYield(false);
96 m_platform.setShouldYieldEveryOtherTime(false);
97 }
98
99 void TearDown() override
100 {
101 Scheduler::shutdown();
102 Platform::initialize(m_oldPlatform);
103 }
104
105 RefPtr<Document> m_document;
sof 2015/02/20 07:48:50 Could you make this RefPtrWillBePersistent<> inste
alex clarke (OOO till 29th) 2015/02/20 11:59:34 Done.
106 RefPtr<Element> m_element;
sof 2015/02/20 07:48:50 RefPtrWillBePersistent<>
alex clarke (OOO till 29th) 2015/02/20 11:59:34 Done.
107 OwnPtr<ScriptRunner> m_scriptRunner;
sof 2015/02/20 07:48:50 OwnPtrWillBePersistent<>
alex clarke (OOO till 29th) 2015/02/20 11:59:34 Done.
alex clarke (OOO till 29th) 2015/02/20 11:59:34 Done.
108 std::vector<int> m_order; // gmock matchers don't work nicely with WTF::Vect or
109 MockPlatform m_platform;
110 Platform* m_oldPlatform;
111 };
112
113 TEST_F(ScriptRunnerTest, QueueSingleScript_Async)
114 {
115 MockScriptLoader scriptLoader(m_element.get());
116 m_scriptRunner->queueScriptForExecution(&scriptLoader, ScriptRunner::ASYNC_E XECUTION);
117 m_scriptRunner->notifyScriptReady(&scriptLoader, ScriptRunner::ASYNC_EXECUTI ON);
118
119 EXPECT_CALL(scriptLoader, execute());
120 m_platform.runAllTasks();
121 }
122
123 TEST_F(ScriptRunnerTest, QueueSingleScript_InOrder)
124 {
125 MockScriptLoader scriptLoader(m_element.get());
126 m_scriptRunner->queueScriptForExecution(&scriptLoader, ScriptRunner::IN_ORDE R_EXECUTION);
127 m_scriptRunner->resume();
128
129 EXPECT_CALL(scriptLoader, isReady()).WillOnce(Return(true));
130 EXPECT_CALL(scriptLoader, execute());
131 m_platform.runAllTasks();
132 }
133
134 TEST_F(ScriptRunnerTest, QueueMultipleScripts_InOrder)
135 {
136 MockScriptLoader scriptLoader1(m_element.get());
137 MockScriptLoader scriptLoader2(m_element.get());
138 MockScriptLoader scriptLoader3(m_element.get());
139
140 m_scriptRunner->queueScriptForExecution(&scriptLoader1, ScriptRunner::IN_ORD ER_EXECUTION);
141 m_scriptRunner->queueScriptForExecution(&scriptLoader2, ScriptRunner::IN_ORD ER_EXECUTION);
142 m_scriptRunner->queueScriptForExecution(&scriptLoader3, ScriptRunner::IN_ORD ER_EXECUTION);
143
144 EXPECT_CALL(scriptLoader1, execute()).WillOnce(Invoke([this] {
145 m_order.push_back(1);
146 }));
147 EXPECT_CALL(scriptLoader2, execute()).WillOnce(Invoke([this] {
148 m_order.push_back(2);
149 }));
150 EXPECT_CALL(scriptLoader3, execute()).WillOnce(Invoke([this] {
151 m_order.push_back(3);
152 }));
153
154 // Make the scripts become ready in reverse order.
155 bool isReady[] = { false, false, false };
156 EXPECT_CALL(scriptLoader1, isReady()).WillRepeatedly(Invoke([&isReady] {
157 return isReady[0];
158 }));
159 EXPECT_CALL(scriptLoader2, isReady()).WillRepeatedly(Invoke([&isReady] {
160 return isReady[1];
161 }));
162 EXPECT_CALL(scriptLoader3, isReady()).WillRepeatedly(Invoke([&isReady] {
163 return isReady[2];
164 }));
165
166 for (int i = 2; i >= 0; i--) {
167 isReady[i] = true;
168 m_scriptRunner->resume();
169 m_platform.runAllTasks();
170 }
171
172 // But ensure the scripts were run in the expected order.
173 EXPECT_THAT(m_order, ElementsAre(1, 2, 3));
174 }
175
176 TEST_F(ScriptRunnerTest, QueueMixedScripts)
177 {
178 MockScriptLoader scriptLoader1(m_element.get());
179 MockScriptLoader scriptLoader2(m_element.get());
180 MockScriptLoader scriptLoader3(m_element.get());
181 MockScriptLoader scriptLoader4(m_element.get());
182 MockScriptLoader scriptLoader5(m_element.get());
183
184 EXPECT_CALL(scriptLoader1, isReady()).WillRepeatedly(Return(true));
185 EXPECT_CALL(scriptLoader2, isReady()).WillRepeatedly(Return(true));
186 EXPECT_CALL(scriptLoader3, isReady()).WillRepeatedly(Return(true));
187
188 m_scriptRunner->queueScriptForExecution(&scriptLoader1, ScriptRunner::IN_ORD ER_EXECUTION);
189 m_scriptRunner->queueScriptForExecution(&scriptLoader2, ScriptRunner::IN_ORD ER_EXECUTION);
190 m_scriptRunner->queueScriptForExecution(&scriptLoader3, ScriptRunner::IN_ORD ER_EXECUTION);
191 m_scriptRunner->queueScriptForExecution(&scriptLoader4, ScriptRunner::ASYNC_ EXECUTION);
192 m_scriptRunner->queueScriptForExecution(&scriptLoader5, ScriptRunner::ASYNC_ EXECUTION);
193
194 m_scriptRunner->notifyScriptReady(&scriptLoader4, ScriptRunner::ASYNC_EXECUT ION);
195 m_scriptRunner->notifyScriptReady(&scriptLoader5, ScriptRunner::ASYNC_EXECUT ION);
196
197 EXPECT_CALL(scriptLoader1, execute()).WillOnce(Invoke([this] {
198 m_order.push_back(1);
199 }));
200 EXPECT_CALL(scriptLoader2, execute()).WillOnce(Invoke([this] {
201 m_order.push_back(2);
202 }));
203 EXPECT_CALL(scriptLoader3, execute()).WillOnce(Invoke([this] {
204 m_order.push_back(3);
205 }));
206 EXPECT_CALL(scriptLoader4, execute()).WillOnce(Invoke([this] {
207 m_order.push_back(4);
208 }));
209 EXPECT_CALL(scriptLoader5, execute()).WillOnce(Invoke([this] {
210 m_order.push_back(5);
211 }));
212
213 m_platform.runAllTasks();
214
215 // Make sure the async scripts were run before the in-order ones.
216 EXPECT_THAT(m_order, ElementsAre(4, 5, 1, 2, 3));
217 }
218
219 TEST_F(ScriptRunnerTest, QueueMixedScripts_YieldAfterEveryExecution)
220 {
221 MockScriptLoader scriptLoader1(m_element.get());
222 MockScriptLoader scriptLoader2(m_element.get());
223 MockScriptLoader scriptLoader3(m_element.get());
224 MockScriptLoader scriptLoader4(m_element.get());
225 MockScriptLoader scriptLoader5(m_element.get());
226
227 m_platform.setShouldYieldEveryOtherTime(true);
228
229 EXPECT_CALL(scriptLoader1, isReady()).WillRepeatedly(Return(true));
230 EXPECT_CALL(scriptLoader2, isReady()).WillRepeatedly(Return(true));
231 EXPECT_CALL(scriptLoader3, isReady()).WillRepeatedly(Return(true));
232
233 m_scriptRunner->queueScriptForExecution(&scriptLoader1, ScriptRunner::IN_ORD ER_EXECUTION);
234 m_scriptRunner->queueScriptForExecution(&scriptLoader2, ScriptRunner::IN_ORD ER_EXECUTION);
235 m_scriptRunner->queueScriptForExecution(&scriptLoader3, ScriptRunner::IN_ORD ER_EXECUTION);
236 m_scriptRunner->queueScriptForExecution(&scriptLoader4, ScriptRunner::ASYNC_ EXECUTION);
237 m_scriptRunner->queueScriptForExecution(&scriptLoader5, ScriptRunner::ASYNC_ EXECUTION);
238
239 m_scriptRunner->notifyScriptReady(&scriptLoader4, ScriptRunner::ASYNC_EXECUT ION);
240 m_scriptRunner->notifyScriptReady(&scriptLoader5, ScriptRunner::ASYNC_EXECUT ION);
241
242 EXPECT_CALL(scriptLoader1, execute()).WillOnce(Invoke([this] {
243 m_order.push_back(1);
244 }));
245 EXPECT_CALL(scriptLoader2, execute()).WillOnce(Invoke([this] {
246 m_order.push_back(2);
247 }));
248 EXPECT_CALL(scriptLoader3, execute()).WillOnce(Invoke([this] {
249 m_order.push_back(3);
250 }));
251 EXPECT_CALL(scriptLoader4, execute()).WillOnce(Invoke([this] {
252 m_order.push_back(4);
253 }));
254 EXPECT_CALL(scriptLoader5, execute()).WillOnce(Invoke([this] {
255 m_order.push_back(5);
256 }));
257
258 m_platform.runAllTasks();
259
260 // Make sure the async scripts were run before the in-order ones.
261 EXPECT_THAT(m_order, ElementsAre(4, 5, 1, 2, 3));
262 }
263
264 TEST_F(ScriptRunnerTest, QueueReentrantScript_Async)
265 {
266 MockScriptLoader scriptLoader1(m_element.get());
267 MockScriptLoader scriptLoader2(m_element.get());
268 MockScriptLoader scriptLoader3(m_element.get());
269
270 m_scriptRunner->queueScriptForExecution(&scriptLoader1, ScriptRunner::ASYNC_ EXECUTION);
271 m_scriptRunner->queueScriptForExecution(&scriptLoader2, ScriptRunner::ASYNC_ EXECUTION);
272 m_scriptRunner->queueScriptForExecution(&scriptLoader3, ScriptRunner::ASYNC_ EXECUTION);
273 m_scriptRunner->notifyScriptReady(&scriptLoader1, ScriptRunner::ASYNC_EXECUT ION);
274
275 EXPECT_CALL(scriptLoader1, execute()).WillOnce(Invoke([&scriptLoader2, this] {
276 m_order.push_back(1);
277 m_scriptRunner->notifyScriptReady(&scriptLoader2, ScriptRunner::ASYNC_EX ECUTION);
278 }));
279
280 EXPECT_CALL(scriptLoader2, execute()).WillOnce(Invoke([&scriptLoader3, this] {
281 m_order.push_back(2);
282 m_scriptRunner->notifyScriptReady(&scriptLoader3, ScriptRunner::ASYNC_EX ECUTION);
283 }));
284
285 EXPECT_CALL(scriptLoader3, execute()).WillOnce(Invoke([this] {
286 m_order.push_back(3);
287 }));
288
289 // Make sure that re-entrant calls to notifyScriptReady don't cause ScriptRu nner::execute to do
290 // more work than expected.
291 m_platform.runSingleTask();
292 EXPECT_THAT(m_order, ElementsAre(1));
293
294 m_platform.runSingleTask();
295 EXPECT_THAT(m_order, ElementsAre(1, 2));
296
297 m_platform.runSingleTask();
298 EXPECT_THAT(m_order, ElementsAre(1, 2, 3));
299 }
300
301 TEST_F(ScriptRunnerTest, QueueReentrantScript_InOrder)
302 {
303 MockScriptLoader scriptLoader1(m_element.get());
304 MockScriptLoader scriptLoader2(m_element.get());
305 MockScriptLoader scriptLoader3(m_element.get());
306
307 EXPECT_CALL(scriptLoader1, isReady()).WillRepeatedly(Return(true));
308 EXPECT_CALL(scriptLoader2, isReady()).WillRepeatedly(Return(true));
309 EXPECT_CALL(scriptLoader3, isReady()).WillRepeatedly(Return(true));
310
311 m_scriptRunner->queueScriptForExecution(&scriptLoader1, ScriptRunner::IN_ORD ER_EXECUTION);
312 m_scriptRunner->resume();
313
314 EXPECT_CALL(scriptLoader1, execute()).WillOnce(Invoke([&scriptLoader2, this] {
315 m_order.push_back(1);
316 m_scriptRunner->queueScriptForExecution(&scriptLoader2, ScriptRunner::IN _ORDER_EXECUTION);
317 m_scriptRunner->resume();
318 }));
319
320 EXPECT_CALL(scriptLoader2, execute()).WillOnce(Invoke([&scriptLoader3, this] {
321 m_order.push_back(2);
322 m_scriptRunner->queueScriptForExecution(&scriptLoader3, ScriptRunner::IN _ORDER_EXECUTION);
323 m_scriptRunner->resume();
324 }));
325
326 EXPECT_CALL(scriptLoader3, execute()).WillOnce(Invoke([this] {
327 m_order.push_back(3);
328 }));
329
330 // Make sure that re-entrant calls to queueScriptForExecution don't cause Sc riptRunner::execute to do
331 // more work than expected.
332 m_platform.runSingleTask();
333 EXPECT_THAT(m_order, ElementsAre(1));
334
335 m_platform.runSingleTask();
336 EXPECT_THAT(m_order, ElementsAre(1, 2));
337
338 m_platform.runSingleTask();
339 EXPECT_THAT(m_order, ElementsAre(1, 2, 3));
340 }
341
342 TEST_F(ScriptRunnerTest, ShouldYield_AsyncScripts)
marja 2015/02/20 09:34:37 Nice!
alex clarke (OOO till 29th) 2015/02/20 11:59:34 :)
343 {
344 MockScriptLoader scriptLoader1(m_element.get());
345 MockScriptLoader scriptLoader2(m_element.get());
346 MockScriptLoader scriptLoader3(m_element.get());
347
348 m_scriptRunner->queueScriptForExecution(&scriptLoader1, ScriptRunner::ASYNC_ EXECUTION);
349 m_scriptRunner->queueScriptForExecution(&scriptLoader2, ScriptRunner::ASYNC_ EXECUTION);
350 m_scriptRunner->queueScriptForExecution(&scriptLoader3, ScriptRunner::ASYNC_ EXECUTION);
351 m_scriptRunner->notifyScriptReady(&scriptLoader1, ScriptRunner::ASYNC_EXECUT ION);
352 m_scriptRunner->notifyScriptReady(&scriptLoader2, ScriptRunner::ASYNC_EXECUT ION);
353 m_scriptRunner->notifyScriptReady(&scriptLoader3, ScriptRunner::ASYNC_EXECUT ION);
354
355 EXPECT_CALL(scriptLoader1, execute()).WillOnce(Invoke([this] {
356 m_order.push_back(1);
357 m_platform.setShouldYield(true);
358 }));
359 EXPECT_CALL(scriptLoader2, execute()).WillOnce(Invoke([this] {
360 m_order.push_back(2);
361 }));
362 EXPECT_CALL(scriptLoader3, execute()).WillOnce(Invoke([this] {
363 m_order.push_back(3);
364 }));
365
366 m_platform.runSingleTask(); // Cancelled task due to calling notifyScriptRea dy 3x, does nothing.
367 m_platform.runSingleTask(); // Cancelled task due to calling notifyScriptRea dy 3x, does nothing.
368 m_platform.runSingleTask();
369 EXPECT_THAT(m_order, ElementsAre(1));
370
371 // Make sure the interrupted tasks are executed next 'tick'.
372 m_platform.setShouldYield(false);
373 m_platform.runSingleTask();
374 EXPECT_THAT(m_order, ElementsAre(1, 2, 3));
375 }
376
377 TEST_F(ScriptRunnerTest, ShouldYield_InOrderScripts)
378 {
379 MockScriptLoader scriptLoader1(m_element.get());
380 MockScriptLoader scriptLoader2(m_element.get());
381 MockScriptLoader scriptLoader3(m_element.get());
382
383 EXPECT_CALL(scriptLoader1, isReady()).WillRepeatedly(Return(true));
384 EXPECT_CALL(scriptLoader2, isReady()).WillRepeatedly(Return(true));
385 EXPECT_CALL(scriptLoader3, isReady()).WillRepeatedly(Return(true));
386
387 m_scriptRunner->queueScriptForExecution(&scriptLoader1, ScriptRunner::IN_ORD ER_EXECUTION);
388 m_scriptRunner->queueScriptForExecution(&scriptLoader2, ScriptRunner::IN_ORD ER_EXECUTION);
389 m_scriptRunner->queueScriptForExecution(&scriptLoader3, ScriptRunner::IN_ORD ER_EXECUTION);
390 m_scriptRunner->resume();
391
392 EXPECT_CALL(scriptLoader1, execute()).WillOnce(Invoke([this] {
393 m_order.push_back(1);
394 m_platform.setShouldYield(true);
395 }));
396 EXPECT_CALL(scriptLoader2, execute()).WillOnce(Invoke([this] {
397 m_order.push_back(2);
398 }));
399 EXPECT_CALL(scriptLoader3, execute()).WillOnce(Invoke([this] {
400 m_order.push_back(3);
401 }));
402
403 m_platform.runSingleTask();
404 EXPECT_THAT(m_order, ElementsAre(1));
405
406 // Make sure the interrupted tasks are executed next 'tick'.
407 m_platform.setShouldYield(false);
408 m_platform.runSingleTask();
409 EXPECT_THAT(m_order, ElementsAre(1, 2, 3));
410 }
411
412 } // 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