@@ -36,29 +36,32 @@ let NormalPriority;
3636// It also includes Scheduler-specific invariants, e.g. only one rAF callback
3737// can be scheduled at a time.
3838describe ( 'SchedulerBrowser' , ( ) => {
39- beforeEach ( ( ) => {
40- jest . resetModules ( ) ;
41-
42- // Un-mock scheduler
43- jest . mock ( 'scheduler' , ( ) => require . requireActual ( 'scheduler' ) ) ;
44- jest . mock ( 'scheduler/src/SchedulerHostConfig' , ( ) =>
45- require . requireActual (
46- 'scheduler/src/forks/SchedulerHostConfig.default.js' ,
47- ) ,
48- ) ;
49-
50- runtime = installMockBrowserRuntime ( ) ;
51- performance = window . performance ;
52- Scheduler = require ( 'scheduler' ) ;
53- scheduleCallback = Scheduler . unstable_scheduleCallback ;
54- NormalPriority = Scheduler . unstable_NormalPriority ;
55- } ) ;
39+ function beforeAndAfterHooks ( enableMessageLoopImplementation ) {
40+ beforeEach ( ( ) => {
41+ jest . resetModules ( ) ;
42+
43+ // Un-mock scheduler
44+ jest . mock ( 'scheduler' , ( ) => require . requireActual ( 'scheduler' ) ) ;
45+ jest . mock ( 'scheduler/src/SchedulerHostConfig' , ( ) =>
46+ require . requireActual (
47+ 'scheduler/src/forks/SchedulerHostConfig.default.js' ,
48+ ) ,
49+ ) ;
50+
51+ runtime = installMockBrowserRuntime ( ) ;
52+ performance = window . performance ;
53+ require ( 'scheduler/src/SchedulerFeatureFlags' ) . enableMessageLoopImplementation = enableMessageLoopImplementation ;
54+ Scheduler = require ( 'scheduler' ) ;
55+ scheduleCallback = Scheduler . unstable_scheduleCallback ;
56+ NormalPriority = Scheduler . unstable_NormalPriority ;
57+ } ) ;
5658
57- afterEach ( ( ) => {
58- if ( ! runtime . isLogEmpty ( ) ) {
59- throw Error ( 'Test exited without clearing log.' ) ;
60- }
61- } ) ;
59+ afterEach ( ( ) => {
60+ if ( ! runtime . isLogEmpty ( ) ) {
61+ throw Error ( 'Test exited without clearing log.' ) ;
62+ }
63+ } ) ;
64+ }
6265
6366 function installMockBrowserRuntime ( ) {
6467 let VSYNC_INTERVAL = 33.33 ;
@@ -228,89 +231,115 @@ describe('SchedulerBrowser', () => {
228231 } ;
229232 }
230233
231- it ( 'callback with continuation' , ( ) => {
232- scheduleCallback ( NormalPriority , ( ) => {
233- runtime . log ( 'Task' ) ;
234- while ( ! Scheduler . unstable_shouldYield ( ) ) {
235- runtime . advanceTime ( 1 ) ;
236- }
237- runtime . log ( `Yield at ${ performance . now ( ) } ms` ) ;
238- return ( ) => {
239- runtime . log ( 'Continuation' ) ;
240- } ;
234+ describe ( 'rAF aligned frame boundaries' , ( ) => {
235+ const enableMessageLoopImplementation = false ;
236+ beforeAndAfterHooks ( enableMessageLoopImplementation ) ;
237+
238+ it ( 'callback with continuation' , ( ) => {
239+ scheduleCallback ( NormalPriority , ( ) => {
240+ runtime . log ( 'Task' ) ;
241+ while ( ! Scheduler . unstable_shouldYield ( ) ) {
242+ runtime . advanceTime ( 1 ) ;
243+ }
244+ runtime . log ( `Yield at ${ performance . now ( ) } ms` ) ;
245+ return ( ) => {
246+ runtime . log ( 'Continuation' ) ;
247+ } ;
248+ } ) ;
249+ runtime . assertLog ( [ 'Request Animation Frame' ] ) ;
250+
251+ runtime . fireAnimationFrame ( ) ;
252+ runtime . assertLog ( [
253+ 'Animation Frame [0]' ,
254+ 'Request Animation Frame [Reposted]' ,
255+ 'Set Timer' ,
256+ 'Post Message' ,
257+ ] ) ;
258+ runtime . fireMessageEvent ( ) ;
259+ runtime . assertLog ( [ 'Message Event' , 'Task' , 'Yield at 34ms' ] ) ;
260+
261+ runtime . fireAnimationFrame ( ) ;
262+ runtime . assertLog ( [
263+ 'Animation Frame [1]' ,
264+ 'Request Animation Frame [Reposted]' ,
265+ 'Set Timer' ,
266+ 'Post Message' ,
267+ ] ) ;
268+
269+ runtime . fireMessageEvent ( ) ;
270+ runtime . assertLog ( [ 'Message Event' , 'Continuation' ] ) ;
271+
272+ runtime . advanceTimeToNextFrame ( ) ;
273+ runtime . fireAnimationFrame ( ) ;
274+ runtime . assertLog ( [ 'Animation Frame [2]' ] ) ;
241275 } ) ;
242- runtime . assertLog ( [ 'Request Animation Frame' ] ) ;
243-
244- runtime . fireAnimationFrame ( ) ;
245- runtime . assertLog ( [
246- 'Animation Frame [0]' ,
247- 'Request Animation Frame [Reposted]' ,
248- 'Set Timer' ,
249- 'Post Message' ,
250- ] ) ;
251- runtime . fireMessageEvent ( ) ;
252- runtime . assertLog ( [ 'Message Event' , 'Task' , 'Yield at 34ms' ] ) ;
253-
254- runtime . fireAnimationFrame ( ) ;
255- runtime . assertLog ( [
256- 'Animation Frame [1]' ,
257- 'Request Animation Frame [Reposted]' ,
258- 'Set Timer' ,
259- 'Post Message' ,
260- ] ) ;
261-
262- runtime . fireMessageEvent ( ) ;
263- runtime . assertLog ( [ 'Message Event' , 'Continuation' ] ) ;
264-
265- runtime . advanceTimeToNextFrame ( ) ;
266- runtime . fireAnimationFrame ( ) ;
267- runtime . assertLog ( [ 'Animation Frame [2]' ] ) ;
268- } ) ;
269276
270- it ( 'two rAF calls in the same frame' , ( ) => {
271- scheduleCallback ( NormalPriority , ( ) => runtime . log ( 'A' ) ) ;
272- runtime . assertLog ( [ 'Request Animation Frame' ] ) ;
273- runtime . fireAnimationFrame ( ) ;
274- runtime . assertLog ( [
275- 'Animation Frame [0]' ,
276- 'Request Animation Frame [Reposted]' ,
277- 'Set Timer' ,
278- 'Post Message' ,
279- ] ) ;
280- runtime . fireMessageEvent ( ) ;
281- runtime . assertLog ( [ 'Message Event' , 'A' ] ) ;
282-
283- // The Scheduler queue is now empty. We're still in frame 0.
284- expect ( runtime . getMostRecentFrameNumber ( ) ) . toBe ( 0 ) ;
285-
286- // Post a task to Scheduler.
287- scheduleCallback ( NormalPriority , ( ) => runtime . log ( 'B' ) ) ;
288-
289- // Did not request another animation frame, since one was already scheduled
290- // during the previous rAF.
291- runtime . assertLog ( [ ] ) ;
292-
293- // Fire the animation frame.
294- runtime . fireAnimationFrame ( ) ;
295- runtime . assertLog ( [
296- 'Animation Frame [0]' ,
297- 'Request Animation Frame [Reposted]' ,
298- 'Set Timer' ,
299- 'Post Message' ,
300- ] ) ;
301-
302- runtime . fireMessageEvent ( ) ;
303- runtime . assertLog ( [ 'Message Event' , 'B' ] ) ;
304- } ) ;
277+ it ( 'two rAF calls in the same frame' , ( ) => {
278+ scheduleCallback ( NormalPriority , ( ) => runtime . log ( 'A' ) ) ;
279+ runtime . assertLog ( [ 'Request Animation Frame' ] ) ;
280+ runtime . fireAnimationFrame ( ) ;
281+ runtime . assertLog ( [
282+ 'Animation Frame [0]' ,
283+ 'Request Animation Frame [Reposted]' ,
284+ 'Set Timer' ,
285+ 'Post Message' ,
286+ ] ) ;
287+ runtime . fireMessageEvent ( ) ;
288+ runtime . assertLog ( [ 'Message Event' , 'A' ] ) ;
289+
290+ // The Scheduler queue is now empty. We're still in frame 0.
291+ expect ( runtime . getMostRecentFrameNumber ( ) ) . toBe ( 0 ) ;
305292
306- it ( 'adjusts frame rate by measuring inteval between rAF events' , ( ) => {
307- runtime . setHardwareFrameRate ( 60 ) ;
293+ // Post a task to Scheduler.
294+ scheduleCallback ( NormalPriority , ( ) => runtime . log ( 'B' ) ) ;
308295
309- scheduleCallback ( NormalPriority , ( ) => runtime . log ( 'Tick' ) ) ;
310- runtime . assertLog ( [ 'Request Animation Frame' ] ) ;
296+ // Did not request another animation frame, since one was already scheduled
297+ // during the previous rAF.
298+ runtime . assertLog ( [ ] ) ;
311299
312- // Need to measure two consecutive intervals between frames.
313- for ( let i = 0 ; i < 2 ; i ++ ) {
300+ // Fire the animation frame.
301+ runtime . fireAnimationFrame ( ) ;
302+ runtime . assertLog ( [
303+ 'Animation Frame [0]' ,
304+ 'Request Animation Frame [Reposted]' ,
305+ 'Set Timer' ,
306+ 'Post Message' ,
307+ ] ) ;
308+
309+ runtime . fireMessageEvent ( ) ;
310+ runtime . assertLog ( [ 'Message Event' , 'B' ] ) ;
311+ } ) ;
312+
313+ it ( 'adjusts frame rate by measuring inteval between rAF events' , ( ) => {
314+ runtime . setHardwareFrameRate ( 60 ) ;
315+
316+ scheduleCallback ( NormalPriority , ( ) => runtime . log ( 'Tick' ) ) ;
317+ runtime . assertLog ( [ 'Request Animation Frame' ] ) ;
318+
319+ // Need to measure two consecutive intervals between frames.
320+ for ( let i = 0 ; i < 2 ; i ++ ) {
321+ runtime . fireAnimationFrame ( ) ;
322+ runtime . assertLog ( [
323+ `Animation Frame [${ runtime . getMostRecentFrameNumber ( ) } ]` ,
324+ 'Request Animation Frame [Reposted]' ,
325+ 'Set Timer' ,
326+ 'Post Message' ,
327+ ] ) ;
328+ runtime . fireMessageEvent ( ) ;
329+ runtime . assertLog ( [ 'Message Event' , 'Tick' ] ) ;
330+ scheduleCallback ( NormalPriority , ( ) => runtime . log ( 'Tick' ) ) ;
331+ runtime . advanceTimeToNextFrame ( ) ;
332+ }
333+
334+ // Scheduler should observe that it's receiving rAFs every 16.6 ms and
335+ // adjust its frame rate accordingly. Test by blocking the thread until
336+ // Scheduler tells us to yield. Then measure how much time has elapsed.
337+ const start = performance . now ( ) ;
338+ scheduleCallback ( NormalPriority , ( ) => {
339+ while ( ! Scheduler . unstable_shouldYield ( ) ) {
340+ runtime . advanceTime ( 1 ) ;
341+ }
342+ } ) ;
314343 runtime . fireAnimationFrame ( ) ;
315344 runtime . assertLog ( [
316345 `Animation Frame [${ runtime . getMostRecentFrameNumber ( ) } ]` ,
@@ -320,31 +349,57 @@ describe('SchedulerBrowser', () => {
320349 ] ) ;
321350 runtime . fireMessageEvent ( ) ;
322351 runtime . assertLog ( [ 'Message Event' , 'Tick' ] ) ;
323- scheduleCallback ( NormalPriority , ( ) => runtime . log ( 'Tick' ) ) ;
324- runtime . advanceTimeToNextFrame ( ) ;
325- }
352+ const end = performance . now ( ) ;
326353
327- // Scheduler should observe that it's receiving rAFs every 16.6 ms and
328- // adjust its frame rate accordingly. Test by blocking the thread until
329- // Scheduler tells us to yield. Then measure how much time has elapsed.
330- const start = performance . now ( ) ;
331- scheduleCallback ( NormalPriority , ( ) => {
332- while ( ! Scheduler . unstable_shouldYield ( ) ) {
333- runtime . advanceTime ( 1 ) ;
334- }
354+ // Check how much time elapsed in the frame.
355+ expect ( end - start ) . toEqual ( 17 ) ;
356+ } ) ;
357+ } ) ;
358+
359+ describe ( 'message event loop' , ( ) => {
360+ const enableMessageLoopImplementation = true ;
361+ beforeAndAfterHooks ( enableMessageLoopImplementation ) ;
362+
363+ it ( 'callback with continutation' , ( ) => {
364+ scheduleCallback ( NormalPriority , ( ) => {
365+ runtime . log ( 'Task' ) ;
366+ while ( ! Scheduler . unstable_shouldYield ( ) ) {
367+ runtime . advanceTime ( 1 ) ;
368+ }
369+ runtime . log ( `Yield at ${ performance . now ( ) } ms` ) ;
370+ return ( ) => {
371+ runtime . log ( 'Continuation' ) ;
372+ } ;
373+ } ) ;
374+ runtime . assertLog ( [ 'Post Message' ] ) ;
375+
376+ runtime . fireMessageEvent ( ) ;
377+ runtime . assertLog ( [
378+ 'Message Event' ,
379+ 'Task' ,
380+ 'Yield at 5ms' ,
381+ 'Post Message' ,
382+ ] ) ;
383+
384+ runtime . fireMessageEvent ( ) ;
385+ runtime . assertLog ( [ 'Message Event' , 'Continuation' ] ) ;
386+ } ) ;
387+
388+ it ( 'task that throws' , ( ) => {
389+ scheduleCallback ( NormalPriority , ( ) => {
390+ runtime . log ( 'Oops!' ) ;
391+ throw Error ( 'Oops!' ) ;
392+ } ) ;
393+ scheduleCallback ( NormalPriority , ( ) => {
394+ runtime . log ( 'Yay' ) ;
395+ } ) ;
396+ runtime . assertLog ( [ 'Post Message' ] ) ;
397+
398+ expect ( ( ) => runtime . fireMessageEvent ( ) ) . toThrow ( 'Oops!' ) ;
399+ runtime . assertLog ( [ 'Message Event' , 'Oops!' , 'Post Message' ] ) ;
400+
401+ runtime . fireMessageEvent ( ) ;
402+ runtime . assertLog ( [ 'Message Event' , 'Yay' ] ) ;
335403 } ) ;
336- runtime . fireAnimationFrame ( ) ;
337- runtime . assertLog ( [
338- `Animation Frame [${ runtime . getMostRecentFrameNumber ( ) } ]` ,
339- 'Request Animation Frame [Reposted]' ,
340- 'Set Timer' ,
341- 'Post Message' ,
342- ] ) ;
343- runtime . fireMessageEvent ( ) ;
344- runtime . assertLog ( [ 'Message Event' , 'Tick' ] ) ;
345- const end = performance . now ( ) ;
346-
347- // Check how much time elapsed in the frame.
348- expect ( end - start ) . toEqual ( 17 ) ;
349404 } ) ;
350405} ) ;
0 commit comments