Skip to content

Commit

Permalink
test_runner: implement PriorityQueue for timers
Browse files Browse the repository at this point in the history
Signed-off-by: Erick Wendel <[email protected]>
  • Loading branch information
ErickWendel committed May 1, 2023
1 parent e54a162 commit ed3a873
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 30 deletions.
63 changes: 46 additions & 17 deletions lib/internal/test_runner/mock/fake_timers.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,32 @@
const {
DateNow,
SafeMap,
Symbol,
SafeSet,
globalThis,
} = primordials;

const console = require('console')
const PriorityQueue = require('internal/priority_queue');

function compareTimersLists(a, b) {
// console.log({ a, b})
const expiryDiff = a.runAt - b.runAt;
if (expiryDiff === 0) {
if (a.id < b.id)
return -1;
if (a.id > b.id)
return 1;
}
return expiryDiff;
}

function setPosition(node, pos) {
node.priorityQueuePosition = pos;
}
class Timers {
#currentTimer = 1;
constructor() {
this.timers = new SafeMap();
this.timers = new PriorityQueue(compareTimersLists, setPosition);

this.setTimeout = this.#createTimer.bind(this, false);
this.clearTimeout = this.#clearTimer.bind(this);
Expand All @@ -18,20 +37,20 @@ class Timers {
}

#createTimer(isInterval, callback, delay, ...args) {
const timerId = Symbol('kTimerId');
const timer = {
const timerId = this.#currentTimer ++;
this.timers.insert({
id: timerId,
callback,
runAt: DateNow() + delay,
interval: isInterval,
args,
};
this.timers.set(timerId, timer);
});

return timerId;
}

#clearTimer(timerId) {
this.timers.delete(timerId);
#clearTimer(position) {
this.timers.removeAt(position);
}

}
Expand All @@ -49,25 +68,34 @@ class FakeTimers {
}

tick(time = 0) {

// if (!this.isEnabled) {
// throw new Error('you should enable fakeTimers first by calling the .enable function');
// }

this.now += time;
const timers = this.fakeTimers.timers;
const alreadyProcessed = new SafeSet();
while (true) {
const timer = timers.peek();

if(!timer) {
alreadyProcessed.clear();
break;
}

for (const timer of timers.values()) {
if(alreadyProcessed.has(timer)) break;
alreadyProcessed.add(timer);

if (!(this.now >= timer.runAt)) continue;

timer.callback(...timer.args);
if (timer.interval) {
timer.runAt = this.now + (timer.runAt - this.now) % timer.args[0];
continue;
}

timers.delete(timer.id);

// if (timer.interval) {
// timer.runAt = this.now + (timer.runAt - this.now) % timer.args[0];
// continue;
// }

timers.removeAt(alreadyProcessed.size - 1);
}
}

Expand All @@ -89,6 +117,7 @@ class FakeTimers {
globalThis.setInterval = this.fakeTimers.setInterval;
globalThis.clearInterval = this.fakeTimers.clearInterval;

// this.#dispatchPendingTimers()
}

reset() {
Expand Down
26 changes: 13 additions & 13 deletions test/parallel/test-runner-mocking-fake-timers.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,26 @@ describe('Faketimers Test Suite', () => {
});

it('should advance in time and trigger timers when calling the .tick function multiple times', (t) => {
fakeTimers.enable();
const fn = mock.fn();
t.mock.fakeTimers.enable();
const fn = t.mock.fn();

global.setTimeout(fn, 2000);

fakeTimers.tick(1000);
fakeTimers.tick(1000);
t.mock.fakeTimers.tick(1000);
t.mock.fakeTimers.tick(1000);

assert.strictEqual(fn.mock.callCount(), 1);
});

it('should keep setTimeout working if fakeTimers are disabled', (t, done) => {
const now = Date.now();
const timeout = 2;
const expected = () => now - timeout;
global.setTimeout(common.mustCall(() => {
assert.strictEqual(now - timeout, expected());
done();
}), timeout);
});
// it('should keep setTimeout working if fakeTimers are disabled', (t, done) => {
// const now = Date.now();
// const timeout = 2;
// const expected = () => now - timeout;
// global.setTimeout(common.mustCall(() => {
// assert.strictEqual(now - timeout, expected());
// done();
// }), timeout);
// });

});
});

0 comments on commit ed3a873

Please sign in to comment.