node/test/parallel/test-timers-interval-promisified.js
Antoine du Hamel a973446b91
test: use spawn and spawnPromisified instead of exec
PR-URL: https://github.com/nodejs/node/pull/48991
Reviewed-By: Chemi Atlow <chemi@atlow.co.il>
Reviewed-By: Debadree Chatterjee <debadree333@gmail.com>
2023-08-05 07:29:53 +00:00

259 lines
7.7 KiB
JavaScript

// Flags: --no-warnings --expose-internals
'use strict';
const common = require('../common');
const assert = require('assert');
const timers = require('timers');
const { promisify } = require('util');
const { getEventListeners } = require('events');
const { NodeEventTarget } = require('internal/event_target');
const timerPromises = require('timers/promises');
const setPromiseTimeout = promisify(timers.setTimeout);
const { setInterval } = timerPromises;
process.on('multipleResolves', common.mustNotCall());
{
const iterable = setInterval(1, undefined);
const iterator = iterable[Symbol.asyncIterator]();
const promise = iterator.next();
promise.then(common.mustCall((result) => {
assert.ok(!result.done, 'iterator was wrongly marked as done');
assert.strictEqual(result.value, undefined);
return iterator.return();
})).then(common.mustCall());
}
{
const iterable = setInterval(1, 'foobar');
const iterator = iterable[Symbol.asyncIterator]();
const promise = iterator.next();
promise.then(common.mustCall((result) => {
assert.ok(!result.done, 'iterator was wronly marked as done');
assert.strictEqual(result.value, 'foobar');
return iterator.return();
})).then(common.mustCall());
}
{
const iterable = setInterval(1, 'foobar');
const iterator = iterable[Symbol.asyncIterator]();
const promise = iterator.next();
promise
.then(common.mustCall((result) => {
assert.ok(!result.done, 'iterator was wronly marked as done');
assert.strictEqual(result.value, 'foobar');
return iterator.next();
}))
.then(common.mustCall((result) => {
assert.ok(!result.done, 'iterator was wrongly marked as done');
assert.strictEqual(result.value, 'foobar');
return iterator.return();
}))
.then(common.mustCall());
}
{
const signal = AbortSignal.abort(); // Abort in advance
const iterable = setInterval(1, undefined, { signal });
const iterator = iterable[Symbol.asyncIterator]();
assert.rejects(iterator.next(), /AbortError/).then(common.mustCall());
}
{
const ac = new AbortController();
const { signal } = ac;
const iterable = setInterval(100, undefined, { signal });
const iterator = iterable[Symbol.asyncIterator]();
// This promise should take 100 seconds to resolve, so now aborting it should
// mean we abort early
const promise = iterator.next();
ac.abort(); // Abort in after we have a next promise
assert.rejects(promise, /AbortError/).then(common.mustCall());
}
{
// Check aborting after getting a value.
const ac = new AbortController();
const { signal } = ac;
const iterable = setInterval(100, undefined, { signal });
const iterator = iterable[Symbol.asyncIterator]();
const promise = iterator.next();
const abortPromise = promise.then(common.mustCall(() => ac.abort()))
.then(() => iterator.next());
assert.rejects(abortPromise, /AbortError/).then(common.mustCall());
}
{
[1, '', Infinity, null, {}].forEach((ref) => {
const iterable = setInterval(10, undefined, { ref });
assert.rejects(() => iterable[Symbol.asyncIterator]().next(), /ERR_INVALID_ARG_TYPE/)
.then(common.mustCall());
});
[1, '', Infinity, null, {}].forEach((signal) => {
const iterable = setInterval(10, undefined, { signal });
assert.rejects(() => iterable[Symbol.asyncIterator]().next(), /ERR_INVALID_ARG_TYPE/)
.then(common.mustCall());
});
[1, '', Infinity, null, true, false].forEach((options) => {
const iterable = setInterval(10, undefined, options);
assert.rejects(() => iterable[Symbol.asyncIterator]().next(), /ERR_INVALID_ARG_TYPE/)
.then(common.mustCall());
});
}
{
// Check that timer adding signals does not leak handlers
const signal = new NodeEventTarget();
signal.aborted = false;
const iterator = setInterval(1, undefined, { signal });
iterator.next().then(common.mustCall(() => {
assert.strictEqual(getEventListeners(signal, 'abort').length, 1);
iterator.return();
})).finally(common.mustCall(() => {
assert.strictEqual(getEventListeners(signal, 'abort').length, 0);
}));
}
{
// Check that break removes the signal listener
const signal = new NodeEventTarget();
signal.aborted = false;
async function tryBreak() {
const iterator = setInterval(10, undefined, { signal });
let i = 0;
// eslint-disable-next-line no-unused-vars
for await (const _ of iterator) {
if (i === 0) {
assert.strictEqual(getEventListeners(signal, 'abort').length, 1);
}
i++;
if (i === 2) {
break;
}
}
assert.strictEqual(i, 2);
assert.strictEqual(getEventListeners(signal, 'abort').length, 0);
}
tryBreak().then(common.mustCall());
}
{
common.spawnPromisified(process.execPath, ['-pe', "const assert = require('assert');" +
'const interval = require(\'timers/promises\')' +
'.setInterval(1000, null, { ref: false });' +
'interval[Symbol.asyncIterator]().next()' +
'.then(assert.fail)']).then(common.mustCall(({ stderr }) => {
assert.strictEqual(stderr, '');
}));
}
{
async function runInterval(fn, intervalTime, signal) {
const input = 'foobar';
const interval = setInterval(intervalTime, input, { signal });
let iteration = 0;
for await (const value of interval) {
assert.strictEqual(value, input);
iteration++;
await fn(iteration);
}
}
{
// Check that we call the correct amount of times.
const controller = new AbortController();
const { signal } = controller;
let loopCount = 0;
const delay = 20;
const timeoutLoop = runInterval(() => {
loopCount++;
if (loopCount === 5) controller.abort();
if (loopCount > 5) throw new Error('ran too many times');
}, delay, signal);
assert.rejects(timeoutLoop, /AbortError/).then(common.mustCall(() => {
assert.strictEqual(loopCount, 5);
}));
}
{
// Check that if we abort when we have some unresolved callbacks,
// we actually call them.
const controller = new AbortController();
const { signal } = controller;
const delay = 10;
let totalIterations = 0;
const timeoutLoop = runInterval(async (iterationNumber) => {
await setPromiseTimeout(delay * 4);
if (iterationNumber <= 2) {
assert.strictEqual(signal.aborted, false);
}
if (iterationNumber === 2) {
controller.abort();
}
if (iterationNumber > 2) {
assert.strictEqual(signal.aborted, true);
}
if (iterationNumber > totalIterations) {
totalIterations = iterationNumber;
}
}, delay, signal);
timeoutLoop.catch(common.mustCall(() => {
assert.ok(totalIterations >= 3, `iterations was ${totalIterations} < 3`);
}));
}
}
{
// Check that the timing is correct
let pre = false;
let post = false;
const time_unit = 50;
Promise.all([
setPromiseTimeout(1).then(() => pre = true),
new Promise((res) => {
const iterable = timerPromises.setInterval(time_unit * 2);
const iterator = iterable[Symbol.asyncIterator]();
iterator.next().then(() => {
assert.ok(pre, 'interval ran too early');
assert.ok(!post, 'interval ran too late');
return iterator.next();
}).then(() => {
assert.ok(post, 'second interval ran too early');
return iterator.return();
}).then(res);
}),
setPromiseTimeout(time_unit * 3).then(() => post = true),
]).then(common.mustCall());
}
(async () => {
const signal = AbortSignal.abort('boom');
try {
const iterable = timerPromises.setInterval(2, undefined, { signal });
// eslint-disable-next-line no-unused-vars, no-empty
for await (const _ of iterable) { }
assert.fail('should have failed');
} catch (err) {
assert.strictEqual(err.cause, 'boom');
}
})().then(common.mustCall());