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" | |
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 | |
OLD | NEW |