Skip to content

Commit

Permalink
Improve tests for 'schedule' module (facebook#12880)
Browse files Browse the repository at this point in the history
**what is the change?:**
Renamed some methods, and made a method to advance a frame in the test
environment.

**why make this change?:**
We often need to simulate a frame passing with some amount of idle time
or lack of idle time, and the new method makes it easier to write that
out.

**test plan:**
Run the updated tests.
Also temporarily tried breaking the scheduler and verified that the
tests will fail.

**issue:**
See internal task T29442940
  • Loading branch information
flarnie authored May 24, 2018
1 parent fa7fa81 commit 345e0a7
Showing 1 changed file with 33 additions and 29 deletions.
62 changes: 33 additions & 29 deletions packages/react-scheduler/src/__tests__/ReactScheduler-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,25 @@
'use strict';

let ReactScheduler;
type FrameTimeoutConfigType = {
// should only specify one or the other
timeLeftInFrame: ?number,
timePastFrameDeadline: ?number,
};

describe('ReactScheduler', () => {
let rAFCallbacks = [];
let postMessageCallback;
let postMessageEvents = [];

function drainPostMessageQueue() {
// default to this falling about 15 ms before next frame
currentTime = startOfLatestFrame + frameSize - 15;
function runPostMessageCallbacks(config: FrameTimeoutConfigType) {
let timeLeftInFrame = 0;
if (typeof config.timeLeftInFrame === 'number') {
timeLeftInFrame = config.timeLeftInFrame;
} else if (typeof config.timePastFrameDeadline === 'number') {
timeLeftInFrame = -1 * config.timePastFrameDeadline;
}
currentTime = startOfLatestFrame + frameSize - timeLeftInFrame;
if (postMessageCallback) {
while (postMessageEvents.length) {
postMessageCallback(postMessageEvents.shift());
Expand All @@ -31,9 +41,9 @@ describe('ReactScheduler', () => {
rAFCallbacks.forEach(cb => cb());
rAFCallbacks = [];
}
function advanceAll() {
function advanceOneFrame(config: FrameTimeoutConfigType = {}) {
runRAFCallbacks();
drainPostMessageQueue();
runPostMessageCallbacks(config);
}

let frameSize = 33;
Expand Down Expand Up @@ -77,7 +87,7 @@ describe('ReactScheduler', () => {
const {scheduleWork} = ReactScheduler;
const cb = jest.fn();
scheduleWork(cb);
advanceAll();
advanceOneFrame({timeLeftInFrame: 15});
expect(cb.mock.calls.length).toBe(1);
// should not have timed out and should include a timeRemaining method
expect(cb.mock.calls[0][0].didTimeout).toBe(false);
Expand All @@ -97,7 +107,7 @@ describe('ReactScheduler', () => {
scheduleWork(callbackB);
expect(callbackLog).toEqual([]);
// after a delay, calls as many callbacks as it has time for
advanceAll();
advanceOneFrame({timeLeftInFrame: 15});
expect(callbackLog).toEqual(['A', 'B']);
// callbackA should not have timed out and should include a timeRemaining method
expect(callbackA.mock.calls[0][0].didTimeout).toBe(false);
Expand Down Expand Up @@ -125,16 +135,16 @@ describe('ReactScheduler', () => {
scheduleWork(callbackB);
expect(callbackLog).toEqual([]);
// now it should drain the message queue and do all scheduled work
drainPostMessageQueue();
runPostMessageCallbacks({timeLeftInFrame: 15});
expect(callbackLog).toEqual(['A', 'B']);

// advances timers, now with an empty queue of work (to ensure they don't deadlock)
advanceAll();
advanceOneFrame({timeLeftInFrame: 15});

// see if more work can be done now.
scheduleWork(callbackC);
expect(callbackLog).toEqual(['A', 'B']);
advanceAll();
advanceOneFrame({timeLeftInFrame: 15});
expect(callbackLog).toEqual(['A', 'B', 'C']);
});

Expand Down Expand Up @@ -163,7 +173,7 @@ describe('ReactScheduler', () => {
expect(callbackLog).toEqual([]);
// after a delay, calls the scheduled callbacks,
// and also calls new callbacks scheduled by current callbacks
advanceAll();
advanceOneFrame({timeLeftInFrame: 15});
expect(callbackLog).toEqual(['A', 'B', 'C']);
},
);
Expand Down Expand Up @@ -199,7 +209,7 @@ describe('ReactScheduler', () => {
// initially waits to call the callback
expect(callbackLog).toEqual([]);
// while flushing callbacks, calls as many as it has time for
advanceAll();
advanceOneFrame({timeLeftInFrame: 15});
expect(callbackLog).toEqual(['A', 'B', 'C', 'D', 'E', 'F']);
});

Expand All @@ -222,7 +232,7 @@ describe('ReactScheduler', () => {
scheduleWork(callbackB);
expect(callbackLog).toEqual([]);
// after a delay, calls the latest callback passed
advanceAll();
advanceOneFrame({timeLeftInFrame: 15});
expect(callbackLog).toEqual(['A0', 'B', 'A1']);
});
});
Expand All @@ -248,24 +258,20 @@ describe('ReactScheduler', () => {
scheduleWork(callbackB, {timeout: 100}); // times out later
scheduleWork(callbackC, {timeout: 2}); // will time out fast

runRAFCallbacks(); // runs rAF callback
// push time ahead a bit so that we have no idle time
startOfLatestFrame += 16;
drainPostMessageQueue(); // runs postMessage callback, idleTick
advanceOneFrame({timePastFrameDeadline: 16});

// callbackC should have timed out
expect(callbackLog).toEqual(['C']);

runRAFCallbacks(); // runs rAF callback
// push time ahead a bit so that we have no idle time
startOfLatestFrame += 16;
drainPostMessageQueue(); // runs postMessage callback, idleTick
advanceOneFrame({timePastFrameDeadline: 16});

// callbackB should have timed out
expect(callbackLog).toEqual(['C', 'B']);

runRAFCallbacks(); // runs rAF callback
drainPostMessageQueue(); // runs postMessage callback, idleTick
// let's give ourselves some idle time now
advanceOneFrame({timeLeftInFrame: 16});

// we should have run callbackA in the idle time
expect(callbackLog).toEqual(['C', 'B', 'A']);
Expand Down Expand Up @@ -295,27 +301,25 @@ describe('ReactScheduler', () => {
scheduleWork(callbackC, {timeout: 2}); // will time out fast
scheduleWork(callbackD); // won't time out

advanceAll(); // runs rAF and postMessage callbacks
advanceOneFrame({timeLeftInFrame: 15}); // runs rAF and postMessage callbacks

// callbackC should have timed out
// we should have had time to call A also, then we run out of time
expect(callbackLog).toEqual(['C', 'A']);

runRAFCallbacks(); // runs rAF callback
// push time ahead a bit so that we have no idle time
startOfLatestFrame += 16;
drainPostMessageQueue(); // runs postMessage callback, idleTick
advanceOneFrame({timePastFrameDeadline: 16});

// callbackB should have timed out
// but we should not run callbackD because we have no idle time
expect(callbackLog).toEqual(['C', 'A', 'B']);

advanceAll(); // runs rAF and postMessage callbacks
advanceOneFrame({timeLeftInFrame: 15}); // runs rAF and postMessage callbacks

// we should have run callbackD in the idle time
expect(callbackLog).toEqual(['C', 'A', 'B', 'D']);

advanceAll(); // runs rAF and postMessage callbacks
advanceOneFrame({timeLeftInFrame: 15}); // runs rAF and postMessage callbacks

// we should not have run anything again, nothing is scheduled
expect(callbackLog).toEqual(['C', 'A', 'B', 'D']);
Expand All @@ -331,7 +335,7 @@ describe('ReactScheduler', () => {
const callbackId = scheduleWork(cb);
expect(cb.mock.calls.length).toBe(0);
cancelScheduledWork(callbackId);
advanceAll();
advanceOneFrame({timeLeftInFrame: 15});
expect(cb.mock.calls.length).toBe(0);
});

Expand All @@ -349,7 +353,7 @@ describe('ReactScheduler', () => {
callbackBId = scheduleWork(callbackB);
// Initially doesn't call anything
expect(callbackLog).toEqual([]);
advanceAll();
advanceOneFrame({timeLeftInFrame: 15});
// B should not get called because A cancelled B
expect(callbackLog).toEqual(['A']);
expect(callbackB.mock.calls.length).toBe(0);
Expand Down

0 comments on commit 345e0a7

Please sign in to comment.