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 3670374
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 30 deletions.
58 changes: 42 additions & 16 deletions lib/internal/test_runner/mock/fake_timers.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 2,30 @@

const {
DateNow,
SafeMap,
Symbol,
SafeSet,
globalThis,
} = primordials;

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

function compareTimersLists(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 34,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 @@ -56,18 72,27 @@ class FakeTimers {

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

for (const timer of timers.values()) {
if (!timer) {
alreadyProcessed.clear();
break;
}

if (!(this.now >= timer.runAt)) continue;
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 114,7 @@ class FakeTimers {
globalThis.setInterval = this.fakeTimers.setInterval;
globalThis.clearInterval = this.fakeTimers.clearInterval;

// this.#dispatchPendingTimers()
}

reset() {
Expand Down
28 changes: 14 additions & 14 deletions test/parallel/test-runner-mocking-fake-timers.js
Original file line number Diff line number Diff line change
@@ -1,5 1,5 @@
'use strict';
const common = require('../common');
// const common = require('../common');
process.env.NODE_TEST_KNOWN_GLOBALS = 0;

const assert = require('node:assert');
Expand All @@ -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 3670374

Please sign in to comment.