Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

View File

@ -0,0 +1,141 @@
prefix parallel
# To mark a test as flaky, list the test name in the appropriate section
# below, without ".js", followed by ": PASS,FLAKY". Example:
# sample-test : PASS,FLAKY
[true] # This section applies to all platforms
# https://github.com/nodejs/node/issues/52273
test-shadow-realm-gc: SKIP
test-shadow-realm-gc-module: SKIP
# https://github.com/nodejs/node/issues/51862
test-fs-read-stream-concurrent-reads: PASS, FLAKY
# Until V8 provides a better way to check for flag mismatch without
# making the code cache/snapshot unreproducible, disable the test
# for a preemptive check now. It should ideally fail more gracefully
# with a better checking mechanism.
# https://github.com/nodejs/build/issues/3043
test-snapshot-incompatible: SKIP
[$system==win32]
# https://github.com/nodejs/node/issues/54808
test-async-context-frame: PASS, FLAKY
# https://github.com/nodejs/node/issues/54534
test-runner-run-watch: PASS, FLAKY
# https://github.com/nodejs/node/issues/56794
test-fs-cp: PASS, FLAKY
# https://github.com/nodejs/node/issues/56751
test-without-async-context-frame: PASS, FLAKY
# Windows on ARM
[$system==win32 && $arch==arm64]
[$system==linux]
# https://github.com/nodejs/node/issues/54803
test-performance-function: PASS, FLAKY
# https://github.com/nodejs/node/issues/54346
test-esm-loader-hooks-inspect-wait: PASS, FLAKY
# https://github.com/nodejs/node/issues/54534
test-runner-run-watch: PASS, FLAKY
[$system==linux && $arch==s390x]
# https://github.com/nodejs/node/issues/58353
test-http2-debug: PASS, FLAKY
[$system==linux || $system==win32]
# https://github.com/nodejs/node/issues/49605
test-runner-watch-mode: PASS,FLAKY
[$system==macos]
# https://github.com/nodejs/node/issues/42741
test-http-server-headers-timeout-keepalive: PASS,FLAKY
test-http-server-request-timeout-keepalive: PASS,FLAKY
[$arch==arm || $arch==arm64]
# https://github.com/nodejs/node/pull/31178
test-crypto-dh-stateless: SKIP
test-crypto-keygen: SKIP
# https://github.com/nodejs/node/issues/54801
test-debugger-heap-profiler: PASS, FLAKY
[$system==solaris] # Also applies to SmartOS
# https://github.com/nodejs/node/issues/43457
test-domain-no-error-handler-abort-on-uncaught-0: PASS, FLAKY
test-domain-no-error-handler-abort-on-uncaught-1: PASS,FLAKY
test-domain-no-error-handler-abort-on-uncaught-2: PASS,FLAKY
test-domain-no-error-handler-abort-on-uncaught-3: PASS,FLAKY
test-domain-no-error-handler-abort-on-uncaught-4: PASS,FLAKY
test-domain-no-error-handler-abort-on-uncaught-5: PASS, FLAKY
test-domain-no-error-handler-abort-on-uncaught-6: PASS, FLAKY
test-domain-no-error-handler-abort-on-uncaught-7: PASS, FLAKY
test-domain-no-error-handler-abort-on-uncaught-8: PASS, FLAKY
test-domain-no-error-handler-abort-on-uncaught-9: PASS, FLAKY
test-domain-throw-error-then-throw-from-uncaught-exception-handler: PASS, FLAKY
test-domain-with-abort-on-uncaught-exception: PASS, FLAKY
# https://github.com/nodejs/node/issues/54346
test-esm-loader-hooks-inspect-wait: PASS, FLAKY
# https://github.com/nodejs/node/issues/50050
test-tick-processor-arguments: SKIP
# https://github.com/nodejs/node/issues/54534
test-runner-run-watch: PASS, FLAKY
[$system==freebsd]
# https://github.com/nodejs/node/issues/54346
test-esm-loader-hooks-inspect-wait: PASS, FLAKY
[$system==aix]
# https://github.com/nodejs/node/issues/54346
test-esm-loader-hooks-inspect-wait: PASS, FLAKY
[$system==ibmi]
# https://github.com/nodejs/node/pull/30819
test-child-process-fork-net-server: SKIP
test-cli-node-options: SKIP
test-cluster-shared-leak: SKIP
test-http-writable-true-after-close: SKIP
test-http2-connect-method: SKIP
test-net-error-twice: SKIP
# https://github.com/libuv/libuv/pull/2782
test-net-allow-half-open: SKIP
test-net-keepalive: SKIP
test-net-persistent-keepalive: SKIP
test-net-socket-close-after-end: SKIP
test-net-socket-connect-without-cb: SKIP
test-net-socket-connecting: SKIP
test-net-socket-ready-without-cb: SKIP
test-net-write-after-end-nt: SKIP
test-tls-env-extra-ca: SKIP
# https://github.com/nodejs/node/pull/34209
test-dgram-error-message-address: SKIP
# https://github.com/nodejs/node/issues/36929
test-crypto-secure-heap: SKIP
# https://github.com/nodejs/node/issues/39683
test-dgram-connect: PASS, FLAKY
test-http-client-parse-error: PASS, FLAKY
test-http-multi-line-headers: PASS, FLAKY
test-http-pipeline-requests-connection-leak: PASS, FLAKY
test-http-server-unconsume: PASS, FLAKY
test-http-upgrade-advertise: PASS, FLAKY
test-tls-client-mindhsize: PASS, FLAKY
test-tls-write-error: PASS, FLAKY
# https://github.com/nodejs/node/issues/48047
test-http-pipeline-flood: SKIP
# https://github.com/nodejs/node/issues/58582
test-http-proxy-fetch: PASS, FLAKY
test-https-proxy-fetch: PASS, FLAKY
test-inspector-network-fetch: PASS, FLAKY
test-fetch: PASS, FLAKY
test-without-async-context-frame: PASS, FLAKY
test-process-cpuUsage: PASS, FLAKY
[$asan==on]
# https://github.com/nodejs/node/issues/39655
test-cluster-primary-error: PASS, FLAKY
[$arch==loong64]
# https://github.com/nodejs/node/issues/51662
test-http-correct-hostname: SKIP

View File

@ -0,0 +1,28 @@
'use strict';
require('../common');
const assert = require('assert');
const { once } = require('node:events');
const { describe, it } = require('node:test');
describe('AbortSignal.any() with timeout signals', () => {
it('should abort when the first timeout signal fires', async () => {
const signal = AbortSignal.any([AbortSignal.timeout(9000), AbortSignal.timeout(110000)]);
const abortPromise = Promise.race([
once(signal, 'abort').then(() => {
throw signal.reason;
}),
new Promise((resolve) => setTimeout(resolve, 10000)),
]);
// The promise should be aborted by the 9000ms timeout
await assert.rejects(
() => abortPromise,
{
name: 'TimeoutError',
message: 'The operation was aborted due to timeout'
}
);
});
});

View File

@ -0,0 +1,34 @@
// Flags: --no-warnings --expose-gc --expose-internals
'use strict';
require('../common');
const {
strictEqual,
} = require('assert');
const {
test,
} = require('node:test');
const {
kWeakHandler,
} = require('internal/event_target');
const { setTimeout: sleep } = require('timers/promises');
// The tests in this file depend on Node.js internal APIs. These are not necessarily
// portable to other runtimes
test('A weak event listener should not prevent gc', async () => {
// If the event listener is weak, however, it should not prevent gc
let ref;
function handler() {}
{
ref = new globalThis.WeakRef(AbortSignal.timeout(1_200_000));
ref.deref().addEventListener('abort', handler, { [kWeakHandler]: {} });
}
await sleep(10);
globalThis.gc();
strictEqual(ref.deref(), undefined);
});

View File

@ -0,0 +1,276 @@
// Flags: --expose-gc
'use strict';
require('../common');
const { inspect } = require('util');
const {
ok,
notStrictEqual,
strictEqual,
throws,
} = require('assert');
const {
test,
mock,
} = require('node:test');
const { setTimeout: sleep } = require('timers/promises');
// All of the the tests in this file depend on public-facing Node.js APIs.
// For tests that depend on Node.js internal APIs, please add them to
// test-abortcontroller-internal.js instead.
test('Abort is fired with the correct event type on AbortControllers', () => {
// Tests that abort is fired with the correct event type on AbortControllers
const ac = new AbortController();
ok(ac.signal);
const fn = mock.fn((event) => {
ok(event);
strictEqual(event.type, 'abort');
});
ac.signal.onabort = fn;
ac.signal.addEventListener('abort', fn);
ac.abort();
ac.abort();
ok(ac.signal.aborted);
strictEqual(fn.mock.calls.length, 2);
});
test('Abort events are trusted', () => {
// Tests that abort events are trusted
const ac = new AbortController();
const fn = mock.fn((event) => {
ok(event.isTrusted);
});
ac.signal.onabort = fn;
ac.abort();
strictEqual(fn.mock.calls.length, 1);
});
test('Abort events have the same isTrusted reference', () => {
// Tests that abort events have the same `isTrusted` reference
const first = new AbortController();
const second = new AbortController();
let ev1, ev2;
const ev3 = new Event('abort');
first.signal.addEventListener('abort', (event) => {
ev1 = event;
});
second.signal.addEventListener('abort', (event) => {
ev2 = event;
});
first.abort();
second.abort();
const firstTrusted = Reflect.getOwnPropertyDescriptor(Object.getPrototypeOf(ev1), 'isTrusted').get;
const secondTrusted = Reflect.getOwnPropertyDescriptor(Object.getPrototypeOf(ev2), 'isTrusted').get;
const untrusted = Reflect.getOwnPropertyDescriptor(Object.getPrototypeOf(ev3), 'isTrusted').get;
strictEqual(firstTrusted, secondTrusted);
strictEqual(untrusted, firstTrusted);
});
test('AbortSignal is impossible to construct manually', () => {
// Tests that AbortSignal is impossible to construct manually
const ac = new AbortController();
throws(() => new ac.signal.constructor(), {
code: 'ERR_ILLEGAL_CONSTRUCTOR',
});
});
test('Symbol.toStringTag is correct', () => {
// Symbol.toStringTag
const toString = (o) => Object.prototype.toString.call(o);
const ac = new AbortController();
strictEqual(toString(ac), '[object AbortController]');
strictEqual(toString(ac.signal), '[object AbortSignal]');
});
test('AbortSignal.abort() creates an already aborted signal', () => {
const signal = AbortSignal.abort();
ok(signal.aborted);
});
test('AbortController properties and methods valiate the receiver', () => {
const acSignalGet = Object.getOwnPropertyDescriptor(
AbortController.prototype,
'signal'
).get;
const acAbort = AbortController.prototype.abort;
const goodController = new AbortController();
ok(acSignalGet.call(goodController));
acAbort.call(goodController);
const badAbortControllers = [
null,
undefined,
0,
NaN,
true,
'AbortController',
{ __proto__: AbortController.prototype },
];
for (const badController of badAbortControllers) {
throws(
() => acSignalGet.call(badController),
{ name: 'TypeError' }
);
throws(
() => acAbort.call(badController),
{ name: 'TypeError' }
);
}
});
test('AbortSignal properties validate the receiver', () => {
const signalAbortedGet = Object.getOwnPropertyDescriptor(
AbortSignal.prototype,
'aborted'
).get;
const goodSignal = new AbortController().signal;
strictEqual(signalAbortedGet.call(goodSignal), false);
const badAbortSignals = [
null,
undefined,
0,
NaN,
true,
'AbortSignal',
{ __proto__: AbortSignal.prototype },
];
for (const badSignal of badAbortSignals) {
throws(
() => signalAbortedGet.call(badSignal),
{ name: 'TypeError' }
);
}
});
test('AbortController inspection depth 1 or null works', () => {
const ac = new AbortController();
strictEqual(inspect(ac, { depth: 1 }),
'AbortController { signal: [AbortSignal] }');
strictEqual(inspect(ac, { depth: null }),
'AbortController { signal: AbortSignal { aborted: false } }');
});
test('AbortSignal reason is set correctly', () => {
// Test AbortSignal.reason
const ac = new AbortController();
ac.abort('reason');
strictEqual(ac.signal.reason, 'reason');
});
test('AbortSignal reasonable is set correctly with AbortSignal.abort()', () => {
// Test AbortSignal.reason
const signal = AbortSignal.abort('reason');
strictEqual(signal.reason, 'reason');
});
test('AbortSignal.timeout() works as expected', async () => {
// Test AbortSignal timeout
const signal = AbortSignal.timeout(10);
ok(!signal.aborted);
const { promise, resolve } = Promise.withResolvers();
const fn = mock.fn(() => {
ok(signal.aborted);
strictEqual(signal.reason.name, 'TimeoutError');
strictEqual(signal.reason.code, 23);
resolve();
});
setTimeout(fn, 20);
await promise;
});
test('AbortSignal.timeout() does not prevent the signal from being collected', async () => {
// Test AbortSignal timeout doesn't prevent the signal
// from being garbage collected.
let ref;
{
ref = new globalThis.WeakRef(AbortSignal.timeout(1_200_000));
}
await sleep(10);
globalThis.gc();
strictEqual(ref.deref(), undefined);
});
test('AbortSignal with a timeout is not collected while there is an active listener', async () => {
// Test that an AbortSignal with a timeout is not gc'd while
// there is an active listener on it.
let ref;
function handler() {}
{
ref = new globalThis.WeakRef(AbortSignal.timeout(1_200_000));
ref.deref().addEventListener('abort', handler);
}
await sleep(10);
globalThis.gc();
notStrictEqual(ref.deref(), undefined);
ok(ref.deref() instanceof AbortSignal);
ref.deref().removeEventListener('abort', handler);
await sleep(10);
globalThis.gc();
strictEqual(ref.deref(), undefined);
});
test('Setting a long timeout should not keep the process open', () => {
AbortSignal.timeout(1_200_000);
});
test('AbortSignal.reason should default', () => {
// Test AbortSignal.reason default
const signal = AbortSignal.abort();
ok(signal.reason instanceof DOMException);
strictEqual(signal.reason.code, 20);
const ac = new AbortController();
ac.abort();
ok(ac.signal.reason instanceof DOMException);
strictEqual(ac.signal.reason.code, 20);
});
test('abortSignal.throwIfAborted() works as expected', () => {
// Test abortSignal.throwIfAborted()
throws(() => AbortSignal.abort().throwIfAborted(), {
code: 20,
name: 'AbortError',
});
// Does not throw because it's not aborted.
const ac = new AbortController();
ac.signal.throwIfAborted();
});
test('abortSignal.throwIfAobrted() works as expected (2)', () => {
const originalDesc = Reflect.getOwnPropertyDescriptor(AbortSignal.prototype, 'aborted');
const actualReason = new Error();
Reflect.defineProperty(AbortSignal.prototype, 'aborted', { value: false });
throws(() => AbortSignal.abort(actualReason).throwIfAborted(), actualReason);
Reflect.defineProperty(AbortSignal.prototype, 'aborted', originalDesc);
});
test('abortSignal.throwIfAobrted() works as expected (3)', () => {
const originalDesc = Reflect.getOwnPropertyDescriptor(AbortSignal.prototype, 'reason');
const actualReason = new Error();
const fakeExcuse = new Error();
Reflect.defineProperty(AbortSignal.prototype, 'reason', { value: fakeExcuse });
throws(() => AbortSignal.abort(actualReason).throwIfAborted(), actualReason);
Reflect.defineProperty(AbortSignal.prototype, 'reason', originalDesc);
});

View File

@ -0,0 +1,94 @@
// Flags: --expose-gc
'use strict';
require('../common');
const { aborted } = require('util');
const {
match,
rejects,
strictEqual,
} = require('assert');
const { getEventListeners } = require('events');
const { inspect } = require('util');
const {
test,
} = require('node:test');
test('Aborted works when provided a resource', async () => {
const ac = new AbortController();
const promise = aborted(ac.signal, {});
ac.abort();
await promise;
strictEqual(ac.signal.aborted, true);
strictEqual(getEventListeners(ac.signal, 'abort').length, 0);
});
test('Aborted with gc cleanup', async () => {
// Test aborted with gc cleanup
const ac = new AbortController();
const abortedPromise = aborted(ac.signal, {});
const { promise, resolve } = Promise.withResolvers();
setImmediate(() => {
globalThis.gc();
ac.abort();
strictEqual(ac.signal.aborted, true);
strictEqual(getEventListeners(ac.signal, 'abort').length, 0);
resolve();
});
await promise;
// Ensure that the promise is still pending
match(inspect(abortedPromise), /<pending>/);
});
test('Fails with error if not provided AbortSignal', async () => {
await Promise.all([{}, null, undefined, Symbol(), [], 1, 0, 1n, true, false, 'a', () => {}].map((sig) =>
rejects(aborted(sig, {}), {
code: 'ERR_INVALID_ARG_TYPE',
})
));
});
test('Fails if not provided a resource', async () => {
// Fails if not provided a resource
const ac = new AbortController();
await Promise.all([null, undefined, 0, 1, 0n, 1n, Symbol(), '', 'a'].map((resource) =>
rejects(aborted(ac.signal, resource), {
code: 'ERR_INVALID_ARG_TYPE',
})
));
});
// To allow this case to be more flexibly tested on runtimes that do not have
// child_process.spawn, we lazily require it and skip the test if it is not
// present.
let spawn;
function lazySpawn() {
if (spawn === undefined) {
try {
spawn = require('child_process').spawn;
} catch {
// Ignore if spawn does not exist.
}
}
return spawn;
}
test('Does not hang forever', { skip: !lazySpawn() }, async () => {
const { promise, resolve } = Promise.withResolvers();
const childProcess = spawn(process.execPath, ['--input-type=module']);
childProcess.on('exit', (code) => {
strictEqual(code, 13);
resolve();
});
childProcess.stdin.end(`
import { aborted } from 'node:util';
await aborted(new AbortController().signal, {});
`);
await promise;
});

View File

@ -0,0 +1,134 @@
import * as common from '../common/index.mjs';
import { describe, it } from 'node:test';
import { once } from 'node:events';
import assert from 'node:assert';
describe('AbortSignal.any()', { concurrency: !process.env.TEST_PARALLEL }, () => {
it('should throw when not receiving an array', () => {
const expectedError = { code: 'ERR_INVALID_ARG_TYPE' };
assert.throws(() => AbortSignal.any(), expectedError);
assert.throws(() => AbortSignal.any(null), expectedError);
assert.throws(() => AbortSignal.any(undefined), expectedError);
});
it('should throw when input contains non-signal values', () => {
assert.throws(
() => AbortSignal.any([AbortSignal.abort(), undefined]),
{
code: 'ERR_INVALID_ARG_TYPE',
message: 'The "signals[1]" argument must be an instance of AbortSignal. Received undefined'
},
);
});
it('creates a non-aborted signal for an empty input', () => {
const signal = AbortSignal.any([]);
assert.strictEqual(signal.aborted, false);
signal.addEventListener('abort', common.mustNotCall());
});
it('returns a new signal', () => {
const originalSignal = new AbortController().signal;
const signalAny = AbortSignal.any([originalSignal]);
assert.notStrictEqual(originalSignal, signalAny);
});
it('returns an aborted signal if input has an aborted signal', () => {
const signal = AbortSignal.any([AbortSignal.abort('some reason')]);
assert.strictEqual(signal.aborted, true);
assert.strictEqual(signal.reason, 'some reason');
signal.addEventListener('abort', common.mustNotCall());
});
it('returns an aborted signal with the reason of first aborted signal input', () => {
const signal = AbortSignal.any([AbortSignal.abort('some reason'), AbortSignal.abort('another reason')]);
assert.strictEqual(signal.aborted, true);
assert.strictEqual(signal.reason, 'some reason');
signal.addEventListener('abort', common.mustNotCall());
});
it('returns the correct signal in the event target', async () => {
const signal = AbortSignal.any([AbortSignal.timeout(5)]);
const interval = setInterval(() => {}, 100000); // Keep event loop alive
const [{ target }] = await once(signal, 'abort');
clearInterval(interval);
assert.strictEqual(target, signal);
assert.ok(signal.aborted);
assert.strictEqual(signal.reason.name, 'TimeoutError');
});
it('aborts with reason of first aborted signal', () => {
const controllers = Array.from({ length: 3 }, () => new AbortController());
const combinedSignal = AbortSignal.any(controllers.map((c) => c.signal));
controllers[1].abort(1);
controllers[2].abort(2);
assert.ok(combinedSignal.aborted);
assert.strictEqual(combinedSignal.reason, 1);
});
it('can accept the same signal more than once', () => {
const controller = new AbortController();
const signal = AbortSignal.any([controller.signal, controller.signal]);
assert.strictEqual(signal.aborted, false);
controller.abort('reason');
assert.ok(signal.aborted);
assert.strictEqual(signal.reason, 'reason');
});
it('handles deeply aborted signals', async () => {
const controllers = Array.from({ length: 2 }, () => new AbortController());
const composedSignal1 = AbortSignal.any([controllers[0].signal]);
const composedSignal2 = AbortSignal.any([composedSignal1, controllers[1].signal]);
composedSignal2.onabort = common.mustCall();
controllers[0].abort();
assert.ok(composedSignal2.aborted);
assert.ok(composedSignal2.reason instanceof DOMException);
assert.strictEqual(composedSignal2.reason.name, 'AbortError');
});
it('executes abort handlers in correct order', () => {
const controller = new AbortController();
const signals = [];
signals.push(controller.signal);
signals.push(AbortSignal.any([controller.signal]));
signals.push(AbortSignal.any([controller.signal]));
signals.push(AbortSignal.any([signals[0]]));
signals.push(AbortSignal.any([signals[1]]));
let result = '';
signals.forEach((signal, i) => signal.addEventListener('abort', () => result += i));
controller.abort();
assert.strictEqual(result, '01234');
});
it('must accept WebIDL sequence', () => {
const controller = new AbortController();
const iterable = {
*[Symbol.iterator]() {
yield controller.signal;
yield new AbortController().signal;
yield new AbortController().signal;
yield new AbortController().signal;
},
};
const signal = AbortSignal.any(iterable);
let result = 0;
signal.addEventListener('abort', () => result += 1);
controller.abort();
assert.strictEqual(result, 1);
});
it('throws TypeError if any value does not implement AbortSignal', () => {
const expectedError = { code: 'ERR_INVALID_ARG_TYPE' };
assert.throws(() => AbortSignal.any([ null ]), expectedError);
assert.throws(() => AbortSignal.any([ undefined ]), expectedError);
assert.throws(() => AbortSignal.any([ '123' ]), expectedError);
assert.throws(() => AbortSignal.any([ 123 ]), expectedError);
assert.throws(() => AbortSignal.any([{}]), expectedError);
assert.throws(() => AbortSignal.any([{ aborted: true }]), expectedError);
assert.throws(() => AbortSignal.any([{
aborted: true, reason: '', throwIfAborted: null,
}]), expectedError);
});
});

View File

@ -0,0 +1,91 @@
'use strict';
require('../common');
const { ok, strictEqual } = require('assert');
const { setImmediate: sleep } = require('timers/promises');
const {
transferableAbortSignal,
transferableAbortController,
} = require('util');
const {
test,
mock,
} = require('node:test');
test('Can create a transferable abort controller', async () => {
const ac = transferableAbortController();
const mc = new MessageChannel();
const setup1 = Promise.withResolvers();
const setup2 = Promise.withResolvers();
const setupResolvers = [setup1, setup2];
const abort1 = Promise.withResolvers();
const abort2 = Promise.withResolvers();
const abort3 = Promise.withResolvers();
const abortResolvers = [abort1, abort2, abort3];
mc.port1.onmessage = ({ data }) => {
data.addEventListener('abort', () => {
strictEqual(data.reason, 'boom');
abortResolvers.shift().resolve();
});
setupResolvers.shift().resolve();
};
mc.port2.postMessage(ac.signal, [ac.signal]);
// Can be cloned/transferd multiple times and they all still work
mc.port2.postMessage(ac.signal, [ac.signal]);
// Although we're using transfer semantics, the local AbortSignal
// is still usable locally.
ac.signal.addEventListener('abort', () => {
strictEqual(ac.signal.reason, 'boom');
abortResolvers.shift().resolve();
});
await Promise.all([ setup1.promise, setup2.promise ]);
ac.abort('boom');
await Promise.all([ abort1.promise, abort2.promise, abort3.promise ]);
mc.port2.close();
});
test('Can create a transferable abort signal', async () => {
const signal = transferableAbortSignal(AbortSignal.abort('boom'));
ok(signal.aborted);
strictEqual(signal.reason, 'boom');
const mc = new MessageChannel();
const { promise, resolve } = Promise.withResolvers();
mc.port1.onmessage = ({ data }) => {
ok(data instanceof AbortSignal);
ok(data.aborted);
strictEqual(data.reason, 'boom');
resolve();
};
mc.port2.postMessage(signal, [signal]);
await promise;
mc.port1.close();
});
test('A cloned AbortSignal does not keep the event loop open', async () => {
const ac = transferableAbortController();
const mc = new MessageChannel();
const fn = mock.fn();
mc.port1.onmessage = fn;
mc.port2.postMessage(ac.signal, [ac.signal]);
// Because the postMessage used by the underlying AbortSignal
// takes at least one turn of the event loop to be processed,
// and because it is unref'd, it won't, by itself, keep the
// event loop open long enough for the test to complete, so
// we schedule two back to back turns of the event to ensure
// the loop runs long enough for the test to complete.
await sleep();
await sleep();
strictEqual(fn.mock.calls.length, 1);
mc.port2.close();
});

View File

@ -0,0 +1,175 @@
// Flags: --expose_gc
//
import '../common/index.mjs';
import { gcUntil } from '../common/gc.js';
import { describe, it } from 'node:test';
function makeSubsequentCalls(limit, done, holdReferences = false) {
let dependantSymbol;
let signalRef;
const ac = new AbortController();
const retainedSignals = [];
const handler = () => { };
function run(iteration) {
if (iteration > limit) {
// This setImmediate is necessary to ensure that in the last iteration the remaining signal is GCed (if not
// retained)
setImmediate(() => {
globalThis.gc();
done(ac.signal, dependantSymbol);
});
return;
}
if (holdReferences) {
retainedSignals.push(AbortSignal.any([ac.signal]));
} else {
// Using a WeakRef to avoid retaining information that will interfere with the test
signalRef = new WeakRef(AbortSignal.any([ac.signal]));
signalRef.deref().addEventListener('abort', handler);
}
dependantSymbol ??= Object.getOwnPropertySymbols(ac.signal).find(
(s) => s.toString() === 'Symbol(kDependantSignals)'
);
setImmediate(() => {
// Removing the event listener at some moment in the future
// Which will then allow the signal to be GCed
signalRef?.deref()?.removeEventListener('abort', handler);
run(iteration + 1);
});
}
run(1);
};
function runShortLivedSourceSignal(limit, done) {
const signalRefs = new Set();
function run(iteration) {
if (iteration > limit) {
globalThis.gc();
done(signalRefs);
return;
}
const ac = new AbortController();
signalRefs.add(new WeakRef(ac.signal));
AbortSignal.any([ac.signal]);
setImmediate(() => run(iteration + 1));
}
run(1);
};
function runWithOrphanListeners(limit, done) {
let composedSignalRef;
const composedSignalRefs = [];
const handler = () => { };
function run(iteration) {
const ac = new AbortController();
if (iteration > limit) {
setImmediate(() => {
globalThis.gc();
setImmediate(() => {
globalThis.gc();
done(composedSignalRefs);
});
});
return;
}
composedSignalRef = new WeakRef(AbortSignal.any([ac.signal]));
composedSignalRef.deref().addEventListener('abort', handler);
const otherComposedSignalRef = new WeakRef(AbortSignal.any([composedSignalRef.deref()]));
otherComposedSignalRef.deref().addEventListener('abort', handler);
composedSignalRefs.push(composedSignalRef, otherComposedSignalRef);
setImmediate(() => {
run(iteration + 1);
});
}
run(1);
}
const limit = 10_000;
describe('when there is a long-lived signal', () => {
it('drops settled dependant signals', (t, done) => {
makeSubsequentCalls(limit, (signal, depandantSignalsKey) => {
setImmediate(() => {
t.assert.strictEqual(signal[depandantSignalsKey].size, 0);
done();
});
});
});
it('keeps all active dependant signals', (t, done) => {
makeSubsequentCalls(limit, (signal, depandantSignalsKey) => {
t.assert.strictEqual(signal[depandantSignalsKey].size, limit);
done();
}, true);
});
});
it('does not prevent source signal from being GCed if it is short-lived', (t, done) => {
runShortLivedSourceSignal(limit, (signalRefs) => {
setImmediate(() => {
const unGCedSignals = [...signalRefs].filter((ref) => ref.deref());
t.assert.strictEqual(unGCedSignals.length, 0);
done();
});
});
});
it('drops settled dependant signals when signal is composite', (t, done) => {
const controllers = Array.from({ length: 2 }, () => new AbortController());
// Using WeakRefs to avoid this test to retain information that will make the test fail
const composedSignal1 = new WeakRef(AbortSignal.any([controllers[0].signal]));
const composedSignalRef = new WeakRef(AbortSignal.any([composedSignal1.deref(), controllers[1].signal]));
const kDependantSignals = Object.getOwnPropertySymbols(controllers[0].signal).find(
(s) => s.toString() === 'Symbol(kDependantSignals)'
);
t.assert.strictEqual(controllers[0].signal[kDependantSignals].size, 2);
t.assert.strictEqual(controllers[1].signal[kDependantSignals].size, 1);
setImmediate(() => {
globalThis.gc({ execution: 'async' }).then(async () => {
await gcUntil('all signals are GCed', () => {
const totalDependantSignals = Math.max(
controllers[0].signal[kDependantSignals].size,
controllers[1].signal[kDependantSignals].size
);
return composedSignalRef.deref() === undefined && totalDependantSignals === 0;
});
done();
});
});
});
it('drops settled signals even when there are listeners', (t, done) => {
runWithOrphanListeners(limit, async (signalRefs) => {
await gcUntil('all signals are GCed', () => {
const unGCedSignals = [...signalRefs].filter((ref) => ref.deref());
return unGCedSignals.length === 0;
});
done();
});
});

View File

@ -0,0 +1,55 @@
// Flags: --expose-internals --no-warnings
'use strict';
const { hasCrypto } = require('../common');
// This tests that the accessor properties do not raise assertions
// when called with incompatible receivers.
const assert = require('assert');
const { test } = require('node:test');
// Objects that call StreamBase::AddMethods, when setting up
// their prototype
const { internalBinding } = require('internal/test/binding');
const { TTY } = internalBinding('tty_wrap');
const { UDP } = internalBinding('udp_wrap');
test('Should throw instead of raise assertions', () => {
assert.throws(() => {
UDP.prototype.fd; // eslint-disable-line no-unused-expressions
}, TypeError);
const StreamWrapProto = Object.getPrototypeOf(TTY.prototype);
const properties = ['bytesRead', 'fd', '_externalStream'];
properties.forEach((property) => {
// Should throw instead of raise assertions
assert.throws(() => {
TTY.prototype[property]; // eslint-disable-line no-unused-expressions
}, TypeError, `Missing expected TypeError for TTY.prototype.${property}`);
// Should not throw for Object.getOwnPropertyDescriptor
assert.strictEqual(
typeof Object.getOwnPropertyDescriptor(StreamWrapProto, property),
'object',
'typeof property descriptor ' + property + ' is not \'object\''
);
});
});
test('There are accessor properties in crypto too', { skip: !hasCrypto }, () => {
// There are accessor properties in crypto too
const crypto = internalBinding('crypto'); // eslint-disable-line node-core/crypto-check
assert.throws(() => {
// eslint-disable-next-line no-unused-expressions
crypto.SecureContext.prototype._external;
}, TypeError);
assert.strictEqual(
typeof Object.getOwnPropertyDescriptor(
crypto.SecureContext.prototype, '_external'),
'object'
);
});

View File

@ -0,0 +1,40 @@
// Flags: --no-async-context-frame
'use strict';
require('../common');
const {
AsyncLocalStorage,
} = require('async_hooks');
const {
strictEqual,
throws,
} = require('assert');
// ============================================================================
// The defaultValue option
const als1 = new AsyncLocalStorage();
strictEqual(als1.getStore(), undefined, 'value should be undefined');
const als2 = new AsyncLocalStorage({ defaultValue: 'default' });
strictEqual(als2.getStore(), 'default', 'value should be "default"');
const als3 = new AsyncLocalStorage({ defaultValue: 42 });
strictEqual(als3.getStore(), 42, 'value should be 42');
const als4 = new AsyncLocalStorage({ defaultValue: null });
strictEqual(als4.getStore(), null, 'value should be null');
throws(() => new AsyncLocalStorage(null), {
code: 'ERR_INVALID_ARG_TYPE',
});
// ============================================================================
// The name option
const als5 = new AsyncLocalStorage({ name: 'test' });
strictEqual(als5.name, 'test');
const als6 = new AsyncLocalStorage();
strictEqual(als6.name, '');

View File

@ -0,0 +1,40 @@
// Flags: --async-context-frame
'use strict';
require('../common');
const {
AsyncLocalStorage,
} = require('async_hooks');
const {
strictEqual,
throws,
} = require('assert');
// ============================================================================
// The defaultValue option
const als1 = new AsyncLocalStorage();
strictEqual(als1.getStore(), undefined, 'value should be undefined');
const als2 = new AsyncLocalStorage({ defaultValue: 'default' });
strictEqual(als2.getStore(), 'default', 'value should be "default"');
const als3 = new AsyncLocalStorage({ defaultValue: 42 });
strictEqual(als3.getStore(), 42, 'value should be 42');
const als4 = new AsyncLocalStorage({ defaultValue: null });
strictEqual(als4.getStore(), null, 'value should be null');
throws(() => new AsyncLocalStorage(null), {
code: 'ERR_INVALID_ARG_TYPE',
});
// ============================================================================
// The name option
const als5 = new AsyncLocalStorage({ name: 'test' });
strictEqual(als5.name, 'test');
const als6 = new AsyncLocalStorage();
strictEqual(als6.name, '');

View File

@ -0,0 +1,18 @@
'use strict';
require('../common');
const { test } = require('node:test');
// This test ensures Math functions don't fail with an "illegal instruction"
// error on ARM devices (primarily on the Raspberry Pi 1)
// See https://github.com/nodejs/node/issues/1376
// and https://code.google.com/p/v8/issues/detail?id=4019
// Iterate over all Math functions
test('Iterate over all Math functions', () => {
Object.getOwnPropertyNames(Math).forEach((functionName) => {
if (!/[A-Z]/.test(functionName)) {
// The function names don't have capital letters.
Math[functionName](-0.5);
}
});
});

View File

@ -0,0 +1,237 @@
'use strict';
const common = require('../common');
const assert = require('assert');
// Run all tests in parallel and check their outcome at the end.
const promises = [];
// Thenable object without `catch` method,
// shouldn't be considered as a valid Thenable
const invalidThenable = {
then: (fulfill, reject) => {
fulfill();
},
};
// Function that returns a Thenable function,
// a function with `catch` and `then` methods attached,
// shouldn't be considered as a valid Thenable.
const invalidThenableFunc = () => {
function f() {}
f.then = (fulfill, reject) => {
fulfill();
};
f.catch = () => {};
return f;
};
// Test assert.rejects() and assert.doesNotReject() by checking their
// expected output and by verifying that they do not work sync
// Check `assert.rejects`.
{
const rejectingFn = async () => assert.fail();
const errObj = {
code: 'ERR_ASSERTION',
name: 'AssertionError',
message: 'Failed'
};
// `assert.rejects` accepts a function or a promise
// or a thenable as first argument.
promises.push(assert.rejects(rejectingFn, errObj));
promises.push(assert.rejects(rejectingFn(), errObj));
const validRejectingThenable = {
then: (fulfill, reject) => {
reject({ code: 'FAIL' });
},
catch: () => {}
};
promises.push(assert.rejects(validRejectingThenable, { code: 'FAIL' }));
// `assert.rejects` should not accept thenables that
// use a function as `obj` and that have no `catch` handler.
promises.push(assert.rejects(
assert.rejects(invalidThenable, {}),
{
code: 'ERR_INVALID_ARG_TYPE'
})
);
promises.push(assert.rejects(
assert.rejects(invalidThenableFunc, {}),
{
code: 'ERR_INVALID_RETURN_VALUE'
})
);
const err = new Error('foobar');
const validate = () => { return 'baz'; };
promises.push(assert.rejects(
() => assert.rejects(Promise.reject(err), validate),
{
message: 'The "validate" validation function is expected to ' +
"return \"true\". Received 'baz'\n\nCaught error:\n\n" +
'Error: foobar',
code: 'ERR_ASSERTION',
actual: err,
expected: validate,
name: 'AssertionError',
operator: 'rejects',
}
));
}
{
const handler = (err) => {
assert(err instanceof assert.AssertionError,
`${err.name} is not instance of AssertionError`);
assert.strictEqual(err.code, 'ERR_ASSERTION');
assert.strictEqual(err.message,
'Missing expected rejection (mustNotCall).');
assert.strictEqual(err.operator, 'rejects');
assert.ok(!err.stack.includes('at Function.rejects'));
return true;
};
let promise = assert.rejects(async () => {}, common.mustNotCall());
promises.push(assert.rejects(promise, common.mustCall(handler)));
promise = assert.rejects(() => {}, common.mustNotCall());
promises.push(assert.rejects(promise, {
name: 'TypeError',
code: 'ERR_INVALID_RETURN_VALUE',
// FIXME(JakobJingleheimer): This should match on key words, like /Promise/ and /undefined/.
message: 'Expected instance of Promise to be returned ' +
'from the "promiseFn" function but got undefined.'
}));
promise = assert.rejects(Promise.resolve(), common.mustNotCall());
promises.push(assert.rejects(promise, common.mustCall(handler)));
}
{
const THROWN_ERROR = new Error();
promises.push(assert.rejects(() => {
throw THROWN_ERROR;
}, {}).catch(common.mustCall((err) => {
assert.strictEqual(err, THROWN_ERROR);
})));
}
promises.push(assert.rejects(
assert.rejects('fail', {}),
{
code: 'ERR_INVALID_ARG_TYPE',
message: 'The "promiseFn" argument must be of type function or an ' +
"instance of Promise. Received type string ('fail')"
}
));
{
const handler = (generated, actual, err) => {
assert.strictEqual(err.generatedMessage, generated);
assert.strictEqual(err.code, 'ERR_ASSERTION');
assert.strictEqual(err.actual, actual);
assert.strictEqual(err.operator, 'rejects');
assert.match(err.stack, /rejects/);
return true;
};
const err = new Error();
promises.push(assert.rejects(
assert.rejects(Promise.reject(null), { code: 'FOO' }),
handler.bind(null, true, null)
));
promises.push(assert.rejects(
assert.rejects(Promise.reject(5), { code: 'FOO' }, 'AAAAA'),
handler.bind(null, false, 5)
));
promises.push(assert.rejects(
assert.rejects(Promise.reject(err), { code: 'FOO' }, 'AAAAA'),
handler.bind(null, false, err)
));
}
// Check `assert.doesNotReject`.
{
// `assert.doesNotReject` accepts a function or a promise
// or a thenable as first argument.
/* eslint-disable no-restricted-syntax */
let promise = assert.doesNotReject(() => new Map(), common.mustNotCall());
promises.push(assert.rejects(promise, {
message: 'Expected instance of Promise to be returned ' +
'from the "promiseFn" function but got an instance of Map.',
code: 'ERR_INVALID_RETURN_VALUE',
name: 'TypeError'
}));
promises.push(assert.doesNotReject(async () => {}));
promises.push(assert.doesNotReject(Promise.resolve()));
// `assert.doesNotReject` should not accept thenables that
// use a function as `obj` and that have no `catch` handler.
const validFulfillingThenable = {
then: (fulfill, reject) => {
fulfill();
},
catch: () => {}
};
promises.push(assert.doesNotReject(validFulfillingThenable));
promises.push(assert.rejects(
assert.doesNotReject(invalidThenable),
{
code: 'ERR_INVALID_ARG_TYPE'
})
);
promises.push(assert.rejects(
assert.doesNotReject(invalidThenableFunc),
{
code: 'ERR_INVALID_RETURN_VALUE'
})
);
const handler1 = (err) => {
assert(err instanceof assert.AssertionError,
`${err.name} is not instance of AssertionError`);
assert.strictEqual(err.code, 'ERR_ASSERTION');
assert.strictEqual(err.message, 'Failed');
return true;
};
const handler2 = (err) => {
assert(err instanceof assert.AssertionError,
`${err.name} is not instance of AssertionError`);
assert.strictEqual(err.code, 'ERR_ASSERTION');
assert.strictEqual(err.message,
'Got unwanted rejection.\nActual message: "Failed"');
assert.strictEqual(err.operator, 'doesNotReject');
assert.ok(err.stack);
assert.ok(!err.stack.includes('at Function.doesNotReject'));
return true;
};
const rejectingFn = async () => assert.fail();
promise = assert.doesNotReject(rejectingFn, common.mustCall(handler1));
promises.push(assert.rejects(promise, common.mustCall(handler2)));
promise = assert.doesNotReject(rejectingFn(), common.mustCall(handler1));
promises.push(assert.rejects(promise, common.mustCall(handler2)));
promise = assert.doesNotReject(() => assert.fail(), common.mustNotCall());
promises.push(assert.rejects(promise, common.mustCall(handler1)));
promises.push(assert.rejects(
assert.doesNotReject(123),
{
code: 'ERR_INVALID_ARG_TYPE',
message: 'The "promiseFn" argument must be of type ' +
'function or an instance of Promise. Received type number (123)'
}
));
/* eslint-enable no-restricted-syntax */
}
// Make sure all async code gets properly executed.
Promise.all(promises).then(common.mustCall());

View File

@ -0,0 +1,48 @@
'use strict';
// Do not read filesystem when creating AssertionError messages for code in
// builtin modules.
require('../common');
const assert = require('assert');
const EventEmitter = require('events');
const e = new EventEmitter();
e.on('hello', assert);
if (process.argv[2] !== 'child') {
const tmpdir = require('../common/tmpdir');
tmpdir.refresh();
const { spawnSync } = require('child_process');
let threw = false;
try {
e.emit('hello', false);
} catch (err) {
const frames = err.stack.split('\n');
const [, filename, line, column] = frames[1].match(/\((.+):(\d+):(\d+)\)/);
// Spawn a child process to avoid the error having been cached in the assert
// module's `errorCache` Map.
const { output, status, error } =
spawnSync(process.execPath,
[process.argv[1], 'child', filename, line, column],
{ cwd: tmpdir.path, env: process.env });
assert.ifError(error);
assert.strictEqual(status, 0, `Exit code: ${status}\n${output}`);
threw = true;
}
assert.ok(threw);
} else {
const { writeFileSync } = require('fs');
const [, , , filename, line, column] = process.argv;
const data = `${'\n'.repeat(line - 1)}${' '.repeat(column - 1)}` +
'ok(failed(badly));';
writeFileSync(filename, data);
assert.throws(
() => e.emit('hello', false),
{
message: 'false == true'
}
);
}

View File

@ -0,0 +1,128 @@
'use strict';
const common = require('../common');
const assert = require('assert');
// This test ensures that assert.CallTracker.calls() works as intended.
const tracker = new assert.CallTracker();
function bar() {}
const err = {
code: 'ERR_INVALID_ARG_TYPE',
};
// Ensures calls() throws on invalid input types.
assert.throws(() => {
const callsbar = tracker.calls(bar, '1');
callsbar();
}, err
);
assert.throws(() => {
const callsbar = tracker.calls(bar, 0.1);
callsbar();
}, { code: 'ERR_OUT_OF_RANGE' }
);
assert.throws(() => {
const callsbar = tracker.calls(bar, true);
callsbar();
}, err
);
assert.throws(() => {
const callsbar = tracker.calls(bar, () => {});
callsbar();
}, err
);
assert.throws(() => {
const callsbar = tracker.calls(bar, null);
callsbar();
}, err
);
// Expects an error as tracker.calls() cannot be called within a process exit
// handler.
process.on('exit', () => {
assert.throws(() => tracker.calls(bar, 1), {
code: 'ERR_UNAVAILABLE_DURING_EXIT',
});
});
const msg = 'Expected to throw';
function func() {
throw new Error(msg);
}
const callsfunc = tracker.calls(func, 1);
// Expects callsfunc() to call func() which throws an error.
assert.throws(
() => callsfunc(),
{ message: msg }
);
{
const tracker = new assert.CallTracker();
const callsNoop = tracker.calls(1);
callsNoop();
tracker.verify();
}
{
const tracker = new assert.CallTracker();
const callsNoop = tracker.calls(undefined, 1);
callsNoop();
tracker.verify();
}
{
function func() {}
const tracker = new assert.CallTracker();
const callsfunc = tracker.calls(func);
assert.strictEqual(callsfunc.length, 0);
}
{
function func(a, b, c = 2) {}
const tracker = new assert.CallTracker();
const callsfunc = tracker.calls(func);
assert.strictEqual(callsfunc.length, 2);
}
{
function func(a, b, c = 2) {}
delete func.length;
const tracker = new assert.CallTracker();
const callsfunc = tracker.calls(func);
assert.strictEqual(Object.hasOwn(callsfunc, 'length'), false);
}
{
const ArrayIteratorPrototype = Reflect.getPrototypeOf(
Array.prototype.values()
);
const { next } = ArrayIteratorPrototype;
ArrayIteratorPrototype.next = common.mustNotCall(
'%ArrayIteratorPrototype%.next'
);
Object.prototype.get = common.mustNotCall('%Object.prototype%.get');
const customPropertyValue = Symbol();
function func(a, b, c = 2) {
return a + b + c;
}
func.customProperty = customPropertyValue;
Object.defineProperty(func, 'length', { get: common.mustNotCall() });
const tracker = new assert.CallTracker();
const callsfunc = tracker.calls(func);
assert.strictEqual(Object.hasOwn(callsfunc, 'length'), true);
assert.strictEqual(callsfunc.customProperty, customPropertyValue);
assert.strictEqual(callsfunc(1, 2, 3), 6);
ArrayIteratorPrototype.next = next;
delete Object.prototype.get;
}

View File

@ -0,0 +1,73 @@
'use strict';
require('../common');
const assert = require('assert');
const { describe, it } = require('node:test');
describe('assert.CallTracker.getCalls()', { concurrency: !process.env.TEST_PARALLEL }, () => {
const tracker = new assert.CallTracker();
it('should return empty list when no calls', () => {
const fn = tracker.calls();
assert.deepStrictEqual(tracker.getCalls(fn), []);
});
it('should return calls', () => {
const fn = tracker.calls(() => {});
const arg1 = {};
const arg2 = {};
fn(arg1, arg2);
fn.call(arg2, arg2);
assert.deepStrictEqual(tracker.getCalls(fn), [
{ arguments: [arg1, arg2], thisArg: undefined },
{ arguments: [arg2], thisArg: arg2 }]);
});
it('should throw when getting calls of a non-tracked function', () => {
[() => {}, 1, true, null, undefined, {}, []].forEach((fn) => {
assert.throws(() => tracker.getCalls(fn), { code: 'ERR_INVALID_ARG_VALUE' });
});
});
it('should return a frozen object', () => {
const fn = tracker.calls();
fn();
const calls = tracker.getCalls(fn);
assert.throws(() => calls.push(1), /object is not extensible/);
assert.throws(() => Object.assign(calls[0], { foo: 'bar' }), /object is not extensible/);
assert.throws(() => calls[0].arguments.push(1), /object is not extensible/);
});
});
describe('assert.CallTracker.reset()', () => {
const tracker = new assert.CallTracker();
it('should reset calls', () => {
const fn = tracker.calls();
fn();
fn();
fn();
assert.strictEqual(tracker.getCalls(fn).length, 3);
tracker.reset(fn);
assert.deepStrictEqual(tracker.getCalls(fn), []);
});
it('should reset all calls', () => {
const fn1 = tracker.calls();
const fn2 = tracker.calls();
fn1();
fn2();
assert.strictEqual(tracker.getCalls(fn1).length, 1);
assert.strictEqual(tracker.getCalls(fn2).length, 1);
tracker.reset();
assert.deepStrictEqual(tracker.getCalls(fn1), []);
assert.deepStrictEqual(tracker.getCalls(fn2), []);
});
it('should throw when resetting a non-tracked function', () => {
[() => {}, 1, true, null, {}, []].forEach((fn) => {
assert.throws(() => tracker.reset(fn), { code: 'ERR_INVALID_ARG_VALUE' });
});
});
});

View File

@ -0,0 +1,26 @@
'use strict';
require('../common');
const assert = require('assert');
// This test ensures that the assert.CallTracker.report() works as intended.
const tracker = new assert.CallTracker();
function foo() {}
const callsfoo = tracker.calls(foo, 1);
// Ensures that foo was added to the callChecks array.
assert.strictEqual(tracker.report()[0].operator, 'foo');
callsfoo();
// Ensures that foo was removed from the callChecks array after being called the
// expected number of times.
assert.strictEqual(typeof tracker.report()[0], 'undefined');
callsfoo();
// Ensures that foo was added back to the callChecks array after being called
// more than the expected number of times.
assert.strictEqual(tracker.report()[0].operator, 'foo');

View File

@ -0,0 +1,53 @@
'use strict';
require('../common');
const assert = require('assert');
// This test ensures that assert.CallTracker.verify() works as intended.
const tracker = new assert.CallTracker();
const generic_msg = 'Functions were not called the expected number of times';
function foo() {}
function bar() {}
const callsfoo = tracker.calls(foo, 1);
const callsbar = tracker.calls(bar, 1);
// Expects an error as callsfoo() and callsbar() were called less than one time.
assert.throws(
() => tracker.verify(),
{ message: generic_msg }
);
callsfoo();
// Expects an error as callsbar() was called less than one time.
assert.throws(
() => tracker.verify(),
{ message: 'Expected the bar function to be executed 1 time(s) but was executed 0 time(s).' }
);
callsbar();
// Will throw an error if callsfoo() and callsbar isn't called exactly once.
tracker.verify();
const callsfoobar = tracker.calls(foo, 1);
callsfoo();
// Expects an error as callsfoo() was called more than once and callsfoobar() was called less than one time.
assert.throws(
() => tracker.verify(),
{ message: generic_msg }
);
callsfoobar();
// Expects an error as callsfoo() was called more than once
assert.throws(
() => tracker.verify(),
{ message: 'Expected the foo function to be executed 1 time(s) but was executed 2 time(s).' }
);

View File

@ -0,0 +1,74 @@
'use strict';
const { hasCrypto } = require('../common');
const { test } = require('node:test');
const assert = require('assert');
// Turn off no-restricted-properties because we are testing deepEqual!
/* eslint-disable no-restricted-properties */
// Disable colored output to prevent color codes from breaking assertion
// message comparisons. This should only be an issue when process.stdout
// is a TTY.
if (process.stdout.isTTY)
process.env.NODE_DISABLE_COLORS = '1';
test('', { skip: !hasCrypto }, () => {
// See https://github.com/nodejs/node/issues/10258
{
const date = new Date('2016');
function FakeDate() {}
FakeDate.prototype = Date.prototype;
const fake = new FakeDate();
assert.notDeepEqual(date, fake);
assert.notDeepEqual(fake, date);
// For deepStrictEqual we check the runtime type,
// then reveal the fakeness of the fake date
assert.throws(
() => assert.deepStrictEqual(date, fake),
{
message: 'Expected values to be strictly deep-equal:\n' +
'+ actual - expected\n' +
'\n' +
'+ 2016-01-01T00:00:00.000Z\n' +
'- Date {}\n'
}
);
assert.throws(
() => assert.deepStrictEqual(fake, date),
{
message: 'Expected values to be strictly deep-equal:\n' +
'+ actual - expected\n' +
'\n' +
'+ Date {}\n' +
'- 2016-01-01T00:00:00.000Z\n'
}
);
}
{ // At the moment global has its own type tag
const fakeGlobal = {};
Object.setPrototypeOf(fakeGlobal, Object.getPrototypeOf(globalThis));
for (const prop of Object.keys(globalThis)) {
fakeGlobal[prop] = global[prop];
}
assert.notDeepEqual(fakeGlobal, globalThis);
// Message will be truncated anyway, don't validate
assert.throws(() => assert.deepStrictEqual(fakeGlobal, globalThis),
assert.AssertionError);
}
{ // At the moment process has its own type tag
const fakeProcess = {};
Object.setPrototypeOf(fakeProcess, Object.getPrototypeOf(process));
for (const prop of Object.keys(process)) {
fakeProcess[prop] = process[prop];
}
assert.notDeepEqual(fakeProcess, process);
// Message will be truncated anyway, don't validate
assert.throws(() => assert.deepStrictEqual(fakeProcess, process),
assert.AssertionError);
}
});
/* eslint-enable */

View File

@ -0,0 +1,89 @@
'use strict';
require('../common');
const assert = require('assert');
const { test } = require('node:test');
// Disable colored output to prevent color codes from breaking assertion
// message comparisons. This should only be an issue when process.stdout
// is a TTY.
if (process.stdout.isTTY) {
process.env.NODE_DISABLE_COLORS = '1';
}
const defaultStartMessage = 'Expected values to be strictly deep-equal:\n' +
'+ actual - expected\n' +
'\n';
test('Handle error causes', () => {
assert.deepStrictEqual(new Error('a', { cause: new Error('x') }), new Error('a', { cause: new Error('x') }));
assert.deepStrictEqual(
new Error('a', { cause: new RangeError('x') }),
new Error('a', { cause: new RangeError('x') }),
);
assert.throws(() => {
assert.deepStrictEqual(new Error('a', { cause: new Error('x') }), new Error('a', { cause: new Error('y') }));
}, { message: defaultStartMessage + ' [Error: a] {\n' +
'+ [cause]: [Error: x]\n' +
'- [cause]: [Error: y]\n' +
' }\n' });
assert.throws(() => {
assert.deepStrictEqual(new Error('a', { cause: new Error('x') }), new Error('a', { cause: new TypeError('x') }));
}, { message: defaultStartMessage + ' [Error: a] {\n' +
'+ [cause]: [Error: x]\n' +
'- [cause]: [TypeError: x]\n' +
' }\n' });
assert.throws(() => {
assert.deepStrictEqual(new Error('a'), new Error('a', { cause: new Error('y') }));
}, { message: defaultStartMessage + '+ [Error: a]\n' +
'- [Error: a] {\n' +
'- [cause]: [Error: y]\n' +
'- }\n' });
assert.throws(() => {
assert.deepStrictEqual(new Error('a'), new Error('a', { cause: { prop: 'value' } }));
}, { message: defaultStartMessage + '+ [Error: a]\n' +
'- [Error: a] {\n' +
'- [cause]: {\n' +
'- prop: \'value\'\n' +
'- }\n' +
'- }\n' });
assert.notDeepStrictEqual(new Error('a', { cause: new Error('x') }), new Error('a', { cause: new Error('y') }));
assert.notDeepStrictEqual(
new Error('a', { cause: { prop: 'value' } }),
new Error('a', { cause: { prop: 'a different value' } })
);
});
test('Handle undefined causes', () => {
assert.deepStrictEqual(new Error('a', { cause: undefined }), new Error('a', { cause: undefined }));
assert.notDeepStrictEqual(new Error('a', { cause: 'undefined' }), new Error('a', { cause: undefined }));
assert.notDeepStrictEqual(new Error('a', { cause: undefined }), new Error('a'));
assert.notDeepStrictEqual(new Error('a'), new Error('a', { cause: undefined }));
assert.throws(() => {
assert.deepStrictEqual(new Error('a'), new Error('a', { cause: undefined }));
}, { message: defaultStartMessage +
'+ [Error: a]\n' +
'- [Error: a] {\n' +
'- [cause]: undefined\n' +
'- }\n' });
assert.throws(() => {
assert.deepStrictEqual(new Error('a', { cause: undefined }), new Error('a'));
}, { message: defaultStartMessage +
'+ [Error: a] {\n' +
'+ [cause]: undefined\n' +
'+ }\n' +
'- [Error: a]\n' });
assert.throws(() => {
assert.deepStrictEqual(new Error('a', { cause: undefined }), new Error('a', { cause: 'undefined' }));
}, { message: defaultStartMessage + ' [Error: a] {\n' +
'+ [cause]: undefined\n' +
'- [cause]: \'undefined\'\n' +
' }\n' });
});

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,31 @@
'use strict';
const { spawnPromisified } = require('../common');
const assert = require('node:assert');
const { describe, it } = require('node:test');
const fileImports = {
commonjs: 'const assert = require("assert");',
module: 'import assert from "assert";',
};
describe('ensure the assert.ok throwing similar error messages for esm and cjs files', () => {
it('should return code 1 for each command', async () => {
const errorsMessages = [];
for (const [inputType, header] of Object.entries(fileImports)) {
const { stderr, code } = await spawnPromisified(process.execPath, [
'--input-type',
inputType,
'--eval',
`${header}\nassert.ok(0 === 2);\n`,
]);
assert.strictEqual(code, 1);
// For each error message, filter the lines which will starts with AssertionError
errorsMessages.push(
stderr.split('\n').find((s) => s.startsWith('AssertionError'))
);
}
assert.strictEqual(errorsMessages.length, 2);
assert.deepStrictEqual(errorsMessages[0], errorsMessages[1]);
});
});

View File

@ -0,0 +1,70 @@
// Flags: --no-warnings
'use strict';
const { expectWarning } = require('../common');
const assert = require('assert');
const { test } = require('node:test');
expectWarning(
'DeprecationWarning',
'assert.fail() with more than one argument is deprecated. ' +
'Please use assert.strictEqual() instead or only pass a message.',
'DEP0094'
);
test('Two args only, operator defaults to "!="', () => {
assert.throws(() => {
assert.fail('first', 'second');
}, {
code: 'ERR_ASSERTION',
name: 'AssertionError',
message: '\'first\' != \'second\'',
operator: '!=',
actual: 'first',
expected: 'second',
generatedMessage: true
});
});
test('Three args', () => {
assert.throws(() => {
assert.fail('ignored', 'ignored', 'another custom message');
}, {
code: 'ERR_ASSERTION',
name: 'AssertionError',
message: 'another custom message',
operator: 'fail',
actual: 'ignored',
expected: 'ignored',
generatedMessage: false
});
});
test('Three args with custom Error', () => {
assert.throws(() => {
assert.fail(typeof 1, 'object', new TypeError('another custom message'));
}, {
name: 'TypeError',
message: 'another custom message'
});
});
test('No third arg (but a fourth arg)', () => {
assert.throws(() => {
assert.fail('first', 'second', undefined, 'operator');
}, {
code: 'ERR_ASSERTION',
name: 'AssertionError',
message: '\'first\' operator \'second\'',
operator: 'operator',
actual: 'first',
expected: 'second'
});
});
test('The stackFrameFunction should exclude the foo frame', () => {
assert.throws(
function foo() { assert.fail('first', 'second', 'message', '!==', foo); },
(err) => !/^\s*at\sfoo\b/m.test(err.stack)
);
});

View File

@ -0,0 +1,50 @@
'use strict';
require('../common');
const assert = require('assert');
const { test } = require('node:test');
test('No args', () => {
assert.throws(
() => { assert.fail(); },
{
code: 'ERR_ASSERTION',
name: 'AssertionError',
message: 'Failed',
operator: 'fail',
actual: undefined,
expected: undefined,
generatedMessage: true,
stack: /Failed/
}
);
});
test('One arg = message', () => {
assert.throws(() => {
assert.fail('custom message');
}, {
code: 'ERR_ASSERTION',
name: 'AssertionError',
message: 'custom message',
operator: 'fail',
actual: undefined,
expected: undefined,
generatedMessage: false
});
});
test('One arg = Error', () => {
assert.throws(() => {
assert.fail(new TypeError('custom message'));
}, {
name: 'TypeError',
message: 'custom message'
});
});
test('Object prototype get', () => {
Object.prototype.get = () => { throw new Error('failed'); };
assert.throws(() => assert.fail(''), { code: 'ERR_ASSERTION' });
delete Object.prototype.get;
});

View File

@ -0,0 +1,26 @@
'use strict';
// Verify that asserting in the very first line produces the expected result.
require('../common');
const assert = require('assert');
const { test } = require('node:test');
const { path } = require('../common/fixtures');
test('Verify that asserting in the very first line produces the expected result', () => {
assert.throws(
() => require(path('assert-first-line')),
{
name: 'AssertionError',
message: "The expression evaluated to a falsy value:\n\n ässört.ok('')\n"
}
);
assert.throws(
() => require(path('assert-long-line')),
{
name: 'AssertionError',
message: "The expression evaluated to a falsy value:\n\n assert.ok('')\n"
}
);
});

View File

@ -0,0 +1,104 @@
'use strict';
require('../common');
const assert = require('assert');
const { test } = require('node:test');
test('Test that assert.ifError has the correct stack trace of both stacks', () => {
let err;
// Create some random error frames.
(function a() {
(function b() {
(function c() {
err = new Error('test error');
})();
})();
})();
const msg = err.message;
const stack = err.stack;
(function x() {
(function y() {
(function z() {
let threw = false;
try {
assert.ifError(err);
} catch (e) {
assert.strictEqual(e.message,
'ifError got unwanted exception: test error');
assert.strictEqual(err.message, msg);
assert.strictEqual(e.actual, err);
assert.strictEqual(e.actual.stack, stack);
assert.strictEqual(e.expected, null);
assert.strictEqual(e.operator, 'ifError');
threw = true;
}
assert(threw);
})();
})();
})();
});
test('General ifError tests', () => {
assert.throws(
() => {
const error = new Error();
error.stack = 'Error: containing weird stack\nYes!\nI am part of a stack.';
assert.ifError(error);
},
(error) => {
assert(!error.stack.includes('Yes!'));
return true;
}
);
assert.throws(
() => assert.ifError(new TypeError()),
{
message: 'ifError got unwanted exception: TypeError'
}
);
assert.throws(
() => assert.ifError({ stack: false }),
{
message: 'ifError got unwanted exception: { stack: false }'
}
);
assert.throws(
() => assert.ifError({ constructor: null, message: '' }),
{
message: 'ifError got unwanted exception: '
}
);
assert.throws(
() => { assert.ifError(false); },
{
message: 'ifError got unwanted exception: false'
}
);
});
test('Should not throw', () => {
assert.ifError(null);
assert.ifError();
assert.ifError(undefined);
});
test('https://github.com/nodejs/node-v0.x-archive/issues/2893', () => {
let threw = false;
try {
// eslint-disable-next-line no-restricted-syntax
assert.throws(() => {
assert.ifError(null);
});
} catch (e) {
threw = true;
assert.strictEqual(e.message, 'Missing expected exception.');
assert(!e.stack.includes('throws'), e);
}
assert(threw);
});

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,114 @@
'use strict';
require('../common');
const assert = require('assert');
const { test, suite } = require('node:test');
function makeBlock(f) {
const args = Array.prototype.slice.call(arguments, 1);
return function() {
return f.apply(this, args);
};
}
suite('equalArrayPairs', () => {
const equalArrayPairs = [
[new Uint8Array(1e5), new Uint8Array(1e5)],
[new Uint16Array(1e5), new Uint16Array(1e5)],
[new Uint32Array(1e5), new Uint32Array(1e5)],
[new Uint8ClampedArray(1e5), new Uint8ClampedArray(1e5)],
[new Int8Array(1e5), new Int8Array(1e5)],
[new Int16Array(1e5), new Int16Array(1e5)],
[new Int32Array(1e5), new Int32Array(1e5)],
[new Float16Array(1e5), new Float16Array(1e5)],
[new Float32Array(1e5), new Float32Array(1e5)],
[new Float64Array(1e5), new Float64Array(1e5)],
[new Float32Array([+0.0]), new Float32Array([+0.0])],
[new Uint8Array([1, 2, 3, 4]).subarray(1), new Uint8Array([2, 3, 4])],
[new Uint16Array([1, 2, 3, 4]).subarray(1), new Uint16Array([2, 3, 4])],
[new Uint32Array([1, 2, 3, 4]).subarray(1, 3), new Uint32Array([2, 3])],
[new ArrayBuffer(3), new ArrayBuffer(3)],
[new SharedArrayBuffer(3), new SharedArrayBuffer(3)],
];
for (const arrayPair of equalArrayPairs) {
test('', () => {
// eslint-disable-next-line no-restricted-properties
assert.deepEqual(arrayPair[0], arrayPair[1]);
assert.deepStrictEqual(arrayPair[0], arrayPair[1]);
});
}
});
suite('looseEqualArrayPairs', () => {
const looseEqualArrayPairs = [
[new Float16Array([+0.0]), new Float16Array([-0.0])],
[new Float32Array([+0.0]), new Float32Array([-0.0])],
[new Float64Array([+0.0]), new Float64Array([-0.0])],
];
for (const arrayPair of looseEqualArrayPairs) {
test('', () => {
// eslint-disable-next-line no-restricted-properties
assert.deepEqual(arrayPair[0], arrayPair[1]);
assert.throws(
makeBlock(assert.deepStrictEqual, arrayPair[0], arrayPair[1]),
assert.AssertionError
);
});
}
});
suite('notEqualArrayPairs', () => {
const notEqualArrayPairs = [
[new ArrayBuffer(3), new SharedArrayBuffer(3)],
[new Int16Array(256), new Uint16Array(256)],
[new Int16Array([256]), new Uint16Array([256])],
[new Float64Array([+0.0]), new Float32Array([-0.0])],
[new Uint8Array(2), new Uint8Array(3)],
[new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6])],
[new Uint8ClampedArray([300, 2, 3]), new Uint8Array([300, 2, 3])],
[new Uint16Array([2]), new Uint16Array([3])],
[new Uint16Array([0]), new Uint16Array([256])],
[new Int16Array([0]), new Uint16Array([256])],
[new Int16Array([-256]), new Uint16Array([0xff00])], // same bits
[new Int32Array([-256]), new Uint32Array([0xffffff00])], // ditto
[new Float16Array([0.1]), new Float16Array([0.0])],
[new Float16Array([0.1]), new Float16Array([0.1, 0.2])],
[new Float32Array([0.1]), new Float32Array([0.0])],
[new Float32Array([0.1]), new Float32Array([0.1, 0.2])],
[new Float64Array([0.1]), new Float64Array([0.0])],
[new Uint8Array([1, 2, 3]).buffer, new Uint8Array([4, 5, 6]).buffer],
[
new Uint8Array(new SharedArrayBuffer(3)).fill(1).buffer,
new Uint8Array(new SharedArrayBuffer(3)).fill(2).buffer,
],
[new ArrayBuffer(2), new ArrayBuffer(3)],
[new SharedArrayBuffer(2), new SharedArrayBuffer(3)],
[new ArrayBuffer(2), new SharedArrayBuffer(3)],
[
new Uint8Array(new ArrayBuffer(3)).fill(1).buffer,
new Uint8Array(new SharedArrayBuffer(3)).fill(2).buffer,
],
[new ArrayBuffer(3), new SharedArrayBuffer(3)],
[new SharedArrayBuffer(2), new ArrayBuffer(2)],
];
for (const arrayPair of notEqualArrayPairs) {
test('', () => {
assert.throws(
// eslint-disable-next-line no-restricted-properties
makeBlock(assert.deepEqual, arrayPair[0], arrayPair[1]),
assert.AssertionError
);
assert.throws(
makeBlock(assert.deepStrictEqual, arrayPair[0], arrayPair[1]),
assert.AssertionError
);
assert.throws(
makeBlock(assert.partialDeepStrictEqual, arrayPair[0], arrayPair[1]),
assert.AssertionError
);
});
}
});

1599
test/parallel/test-assert.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,26 @@
// Test async-hooks fired on right
// asyncIds & triggerAsyncId for async-await
'use strict';
require('../common');
const async_hooks = require('async_hooks');
const assert = require('assert');
const asyncIds = [];
async_hooks.createHook({
init: (asyncId, type, triggerAsyncId) => {
asyncIds.push([triggerAsyncId, asyncId]);
}
}).enable();
async function main() {
await null;
}
main().then(() => {
// Verify the relationships between async ids
// 1 => 2, 2 => 3 etc
assert.strictEqual(asyncIds[0][1], asyncIds[1][0]);
assert.strictEqual(asyncIds[0][1], asyncIds[3][0]);
assert.strictEqual(asyncIds[1][1], asyncIds[2][0]);
});

View File

@ -0,0 +1,41 @@
'use strict';
// This tests that AsyncResource throws an error if bad parameters are passed
require('../common');
const assert = require('assert');
const async_hooks = require('async_hooks');
const { AsyncResource } = async_hooks;
// Setup init hook such parameters are validated
async_hooks.createHook({
init() {}
}).enable();
assert.throws(() => {
return new AsyncResource();
}, {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
});
assert.throws(() => {
new AsyncResource('');
}, {
code: 'ERR_ASYNC_TYPE',
name: 'TypeError',
});
assert.throws(() => {
new AsyncResource('type', -4);
}, {
code: 'ERR_INVALID_ASYNC_ID',
name: 'RangeError',
});
assert.throws(() => {
new AsyncResource('type', Math.PI);
}, {
code: 'ERR_INVALID_ASYNC_ID',
name: 'RangeError',
});

View File

@ -0,0 +1,37 @@
'use strict';
// Test that async ids that are added to the destroy queue while running a
// `destroy` callback are handled correctly.
const common = require('../common');
const assert = require('assert');
const async_hooks = require('async_hooks');
const initCalls = new Set();
let destroyResCallCount = 0;
let res2;
async_hooks.createHook({
init: common.mustCallAtLeast((id, provider) => {
if (provider === 'foobar')
initCalls.add(id);
}, 2),
destroy: common.mustCallAtLeast((id) => {
if (!initCalls.has(id)) return;
switch (destroyResCallCount++) {
case 0:
// Trigger the second `destroy` call.
res2.emitDestroy();
break;
case 2:
assert.fail('More than 2 destroy() invocations');
break;
}
}, 2)
}).enable();
const res1 = new async_hooks.AsyncResource('foobar');
res2 = new async_hooks.AsyncResource('foobar');
res1.emitDestroy();
process.on('exit', () => assert.strictEqual(destroyResCallCount, 2));

View File

@ -0,0 +1,21 @@
'use strict';
// This tests that AsyncHooks throws an error if bad parameters are passed.
require('../common');
const assert = require('assert');
const async_hooks = require('async_hooks');
const nonFunctionArray = [null, -1, 1, {}, []];
['init', 'before', 'after', 'destroy', 'promiseResolve'].forEach(
(functionName) => {
nonFunctionArray.forEach((nonFunction) => {
assert.throws(() => {
async_hooks.createHook({ [functionName]: nonFunction });
}, {
code: 'ERR_ASYNC_CALLBACK',
name: 'TypeError',
message: `hook.${functionName} must be a function`,
});
});
});

View File

@ -0,0 +1,77 @@
'use strict';
require('../common');
const assert = require('assert');
const async_hooks = require('async_hooks');
// Regression test for:
// - https://github.com/nodejs/node/issues/38814
// - https://github.com/nodejs/node/issues/38815
const layers = new Map();
// Only init to start context-based promise hook
async_hooks.createHook({
init(asyncId, type) {
layers.set(asyncId, {
type,
init: true,
before: false,
after: false,
promiseResolve: false
});
},
before(asyncId) {
if (layers.has(asyncId)) {
layers.get(asyncId).before = true;
}
},
after(asyncId) {
if (layers.has(asyncId)) {
layers.get(asyncId).after = true;
}
},
promiseResolve(asyncId) {
if (layers.has(asyncId)) {
layers.get(asyncId).promiseResolve = true;
}
}
}).enable();
// With destroy, this should switch to native
// and disable context - based promise hook
async_hooks.createHook({
init() { },
destroy() { }
}).enable();
async function main() {
return Promise.resolve();
}
main();
process.on('exit', () => {
assert.deepStrictEqual(Array.from(layers.values()), [
{
type: 'PROMISE',
init: true,
before: true,
after: true,
promiseResolve: true
},
{
type: 'PROMISE',
init: true,
before: false,
after: false,
promiseResolve: true
},
{
type: 'PROMISE',
init: true,
before: true,
after: true,
promiseResolve: true
},
]);
});

View File

@ -0,0 +1,27 @@
'use strict';
// Flags: --expose_gc
// This test ensures that userland-only AsyncResources cause a destroy event to
// be emitted when they get gced.
const common = require('../common');
const assert = require('assert');
const async_hooks = require('async_hooks');
const destroyedIds = new Set();
async_hooks.createHook({
destroy: common.mustCallAtLeast((asyncId) => {
destroyedIds.add(asyncId);
}, 1)
}).enable();
let asyncId = null;
{
const res = new async_hooks.AsyncResource('foobar');
asyncId = res.asyncId();
}
setImmediate(() => {
globalThis.gc();
setImmediate(() => assert.ok(destroyedIds.has(asyncId)));
});

View File

@ -0,0 +1,22 @@
'use strict';
const common = require('../common');
const async_hooks = require('async_hooks');
const { isMainThread } = require('worker_threads');
if (!isMainThread) {
common.skip('Worker bootstrapping works differently -> different AsyncWraps');
}
const hook = async_hooks.createHook({
init: common.mustCall(2),
before: common.mustCall(1),
after: common.mustNotCall()
}).enable();
Promise.resolve(1).then(common.mustCall(() => {
hook.disable();
Promise.resolve(42).then(common.mustCall());
process.nextTick(common.mustCall());
}));

View File

@ -0,0 +1,21 @@
'use strict';
// Flags: --expose_gc
// This test ensures that userland-only AsyncResources cause a destroy event to
// be emitted when they get gced.
const common = require('../common');
const async_hooks = require('async_hooks');
const hook = async_hooks.createHook({
destroy: common.mustCallAtLeast(1) // only 1 immediate is destroyed
}).enable();
new async_hooks.AsyncResource('foobar', { requireManualDestroy: true });
setImmediate(() => {
globalThis.gc();
setImmediate(() => {
hook.disable();
});
});

View File

@ -0,0 +1,25 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const async_hooks = require('async_hooks');
// This test ensures that fast-path PromiseHook assigns async ids
// to already created promises when the native hook function is
// triggered on before event.
let initialAsyncId;
const promise = new Promise((resolve) => {
setTimeout(() => {
initialAsyncId = async_hooks.executionAsyncId();
async_hooks.createHook({
after: common.mustCall(2)
}).enable();
resolve();
}, 0);
});
promise.then(common.mustCall(() => {
const id = async_hooks.executionAsyncId();
assert.notStrictEqual(id, initialAsyncId);
assert.ok(id > 0);
}));

View File

@ -0,0 +1,17 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const async_hooks = require('async_hooks');
// Regression test for https://github.com/nodejs/node/issues/27585.
async_hooks.createHook({ init: () => {} }).enable().disable().enable();
async_hooks.createHook({ init: () => {} }).enable();
async function main() {
const initialAsyncId = async_hooks.executionAsyncId();
await 0;
assert.notStrictEqual(async_hooks.executionAsyncId(), initialAsyncId);
}
main().then(common.mustCall());

View File

@ -0,0 +1,20 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const async_hooks = require('async_hooks');
const hook = async_hooks.createHook({
init: common.mustCall(1),
before: common.mustNotCall(),
after: common.mustNotCall(),
destroy: common.mustNotCall()
});
assert.strictEqual(hook.enable(), hook);
assert.strictEqual(hook.enable(), hook);
setImmediate(common.mustCall());
assert.strictEqual(hook.disable(), hook);
assert.strictEqual(hook.disable(), hook);
assert.strictEqual(hook.disable(), hook);

View File

@ -0,0 +1,13 @@
'use strict';
const common = require('../common');
const async_hooks = require('async_hooks');
Promise.resolve(1).then(common.mustCall(() => {
async_hooks.createHook({
init: common.mustCall(),
before: common.mustCallAtLeast(),
after: common.mustCallAtLeast(2)
}).enable();
process.nextTick(common.mustCall());
}));

View File

@ -0,0 +1,19 @@
'use strict';
const common = require('../common');
const async_hooks = require('async_hooks');
const fs = require('fs');
const nestedHook = async_hooks.createHook({
init: common.mustCall()
});
async_hooks.createHook({
init: common.mustCall(() => {
nestedHook.enable();
}, 2)
}).enable();
fs.access(__filename, common.mustCall(() => {
fs.access(__filename, common.mustCall());
}));

View File

@ -0,0 +1,54 @@
'use strict';
const common = require('../common');
const sleep = require('util').promisify(setTimeout);
const assert = require('assert');
const { executionAsyncResource, createHook } = require('async_hooks');
const { createServer, get } = require('http');
const sym = Symbol('cls');
// Tests continuation local storage with the currentResource API
// through an async function
assert.ok(executionAsyncResource());
createHook({
init(asyncId, type, triggerAsyncId, resource) {
const cr = executionAsyncResource();
resource[sym] = cr[sym];
}
}).enable();
async function handler(req, res) {
executionAsyncResource()[sym] = { state: req.url };
await sleep(10);
const { state } = executionAsyncResource()[sym];
res.setHeader('content-type', 'application/json');
res.end(JSON.stringify({ state }));
}
const server = createServer(function(req, res) {
handler(req, res);
});
function test(n) {
get(`http://localhost:${server.address().port}/${n}`, common.mustCall(function(res) {
res.setEncoding('utf8');
let body = '';
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', common.mustCall(function() {
assert.deepStrictEqual(JSON.parse(body), { state: `/${n}` });
}));
}));
}
server.listen(0, common.mustCall(function() {
server.unref();
for (let i = 0; i < 10; i++) {
test(i);
}
}));

View File

@ -0,0 +1,49 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const { executionAsyncResource, createHook } = require('async_hooks');
const { createServer, get } = require('http');
const sym = Symbol('cls');
// Tests continuation local storage with the executionAsyncResource API
assert.ok(executionAsyncResource());
createHook({
init(asyncId, type, triggerAsyncId, resource) {
const cr = executionAsyncResource();
resource[sym] = cr[sym];
}
}).enable();
const server = createServer(function(req, res) {
executionAsyncResource()[sym] = { state: req.url };
setTimeout(function() {
const { state } = executionAsyncResource()[sym];
res.setHeader('content-type', 'application/json');
res.end(JSON.stringify({ state }));
}, 10);
});
function test(n) {
get(`http://localhost:${server.address().port}/${n}`, common.mustCall(function(res) {
res.setEncoding('utf8');
let body = '';
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', common.mustCall(function() {
assert.deepStrictEqual(JSON.parse(body), { state: `/${n}` });
}));
}));
}
server.listen(0, common.mustCall(function() {
server.unref();
for (let i = 0; i < 10; i++) {
test(i);
}
}));

View File

@ -0,0 +1,53 @@
'use strict';
require('../common');
const assert = require('assert');
const childProcess = require('child_process');
const os = require('os');
if (process.argv[2] === 'child') {
child(process.argv[3], process.argv[4]);
} else {
main();
}
function child(type, valueType) {
const { createHook } = require('async_hooks');
const fs = require('fs');
createHook({
[type]() {
if (valueType === 'symbol') {
throw Symbol('foo');
}
// eslint-disable-next-line no-throw-literal
throw null;
}
}).enable();
// Trigger `promiseResolve`.
new Promise((resolve) => resolve())
// Trigger `after`/`destroy`.
.then(() => fs.promises.readFile(__filename, 'utf8'))
// Make process exit with code 0 if no error caught.
.then(() => process.exit(0));
}
function main() {
const types = [ 'init', 'before', 'after', 'destroy', 'promiseResolve' ];
const valueTypes = [
[ 'null', 'Error: null' ],
[ 'symbol', 'Error: Symbol(foo)' ],
];
for (const type of types) {
for (const [valueType, expect] of valueTypes) {
const cp = childProcess.spawnSync(
process.execPath,
[ __filename, 'child', type, valueType ],
{
encoding: 'utf8',
});
assert.strictEqual(cp.status, 1, type);
assert.strictEqual(cp.stderr.trim().split(os.EOL)[0], expect, type);
}
}
}

View File

@ -0,0 +1,84 @@
'use strict';
// Flags: --expose-internals
const common = require('../common');
const assert = require('assert');
const { async_id_symbol } = require('internal/async_hooks').symbols;
const async_hooks = require('async_hooks');
const http = require('http');
// Regression test for https://github.com/nodejs/node/issues/19859
// Checks that an http.Agent emits a destroy for the old asyncId before calling
// asyncReset()s when reusing a socket handle. The setup is nearly identical to
// parallel/test-async-hooks-http-agent (which focuses on the assertion that
// a fresh asyncId is assigned to the net.Socket instance).
const destroyedIds = new Set();
async_hooks.createHook({
destroy: common.mustCallAtLeast((asyncId) => {
destroyedIds.add(asyncId);
}, 1)
}).enable();
// Make sure a single socket is transparently reused for 2 requests.
const agent = new http.Agent({
keepAlive: true,
keepAliveMsecs: Infinity,
maxSockets: 1
});
const server = http.createServer(common.mustCall((req, res) => {
req.once('data', common.mustCallAtLeast(() => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('foo');
}));
req.on('end', common.mustCall(() => {
res.end('bar');
}));
}, 2)).listen(0, common.mustCall(() => {
const port = server.address().port;
const payload = 'hello world';
// First request. This is useless except for adding a socket to the
// agents pool for reuse.
const r1 = http.request({
agent, port, method: 'POST'
}, common.mustCall((res) => {
// Remember which socket we used.
const socket = res.socket;
const asyncIdAtFirstRequest = socket[async_id_symbol];
assert.ok(asyncIdAtFirstRequest > 0, `${asyncIdAtFirstRequest} > 0`);
// Check that request and response share their socket.
assert.strictEqual(r1.socket, socket);
res.on('data', common.mustCallAtLeast());
res.on('end', common.mustCall(() => {
// setImmediate() to give the agent time to register the freed socket.
setImmediate(common.mustCall(() => {
// The socket is free for reuse now.
assert.strictEqual(socket[async_id_symbol], -1);
// second request:
const r2 = http.request({
agent, port, method: 'POST'
}, common.mustCall((res) => {
assert.ok(destroyedIds.has(asyncIdAtFirstRequest));
// Empty payload, to hit the “right” code path.
r2.end('');
res.on('data', common.mustCallAtLeast());
res.on('end', common.mustCall(() => {
// Clean up to let the event loop stop.
server.close();
agent.destroy();
}));
}));
// Schedule a payload to be written immediately, but do not end the
// request just yet.
r2.write(payload);
}));
}));
}));
r1.end(payload);
}));

View File

@ -0,0 +1,80 @@
'use strict';
// Flags: --expose-internals
const common = require('../common');
const assert = require('assert');
const { async_id_symbol } = require('internal/async_hooks').symbols;
const http = require('http');
// Regression test for https://github.com/nodejs/node/issues/13325
// Checks that an http.Agent properly asyncReset()s a reused socket handle, and
// re-assigns the fresh async id to the reused `net.Socket` instance.
// Make sure a single socket is transparently reused for 2 requests.
const agent = new http.Agent({
keepAlive: true,
keepAliveMsecs: Infinity,
maxSockets: 1
});
const server = http.createServer(common.mustCall((req, res) => {
req.once('data', common.mustCallAtLeast(() => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('foo');
}));
req.on('end', common.mustCall(() => {
res.end('bar');
}));
}, 2)).listen(0, common.mustCall(() => {
const port = server.address().port;
const payload = 'hello world';
// First request. This is useless except for adding a socket to the
// agents pool for reuse.
const r1 = http.request({
agent, port, method: 'POST'
}, common.mustCall((res) => {
// Remember which socket we used.
const socket = res.socket;
const asyncIdAtFirstRequest = socket[async_id_symbol];
assert.ok(asyncIdAtFirstRequest > 0, `${asyncIdAtFirstRequest} > 0`);
// Check that request and response share their socket.
assert.strictEqual(r1.socket, socket);
res.on('data', common.mustCallAtLeast());
res.on('end', common.mustCall(() => {
// setImmediate() to give the agent time to register the freed socket.
setImmediate(common.mustCall(() => {
// The socket is free for reuse now.
assert.strictEqual(socket[async_id_symbol], -1);
// Second request. To re-create the exact conditions from the
// referenced issue, we use a POST request without chunked encoding
// (hence the Content-Length header) and call .end() after the
// response header has already been received.
const r2 = http.request({
agent, port, method: 'POST', headers: {
'Content-Length': payload.length
}
}, common.mustCall((res) => {
const asyncId = res.socket[async_id_symbol];
assert.ok(asyncId > 0, `${asyncId} > 0`);
assert.strictEqual(r2.socket, socket);
// Empty payload, to hit the “right” code path.
r2.end('');
res.on('data', common.mustCallAtLeast());
res.on('end', common.mustCall(() => {
// Clean up to let the event loop stop.
server.close();
agent.destroy();
}));
}));
// Schedule a payload to be written immediately, but do not end the
// request just yet.
r2.write(payload);
}));
}));
}));
r1.end(payload);
}));

View File

@ -0,0 +1,92 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const async_hooks = require('async_hooks');
const http = require('http');
// Regression test for https://github.com/nodejs/node/issues/19859.
// Checks that matching destroys are emitted when creating new/reusing old http
// parser instances.
const N = 50;
const KEEP_ALIVE = 100;
const createdIdsIncomingMessage = [];
const createdIdsClientRequest = [];
const destroyedIdsIncomingMessage = [];
const destroyedIdsClientRequest = [];
async_hooks.createHook({
init: (asyncId, type) => {
if (type === 'HTTPINCOMINGMESSAGE') {
createdIdsIncomingMessage.push(asyncId);
}
if (type === 'HTTPCLIENTREQUEST') {
createdIdsClientRequest.push(asyncId);
}
},
destroy: (asyncId) => {
if (createdIdsIncomingMessage.includes(asyncId)) {
destroyedIdsIncomingMessage.push(asyncId);
}
if (createdIdsClientRequest.includes(asyncId)) {
destroyedIdsClientRequest.push(asyncId);
}
if (destroyedIdsClientRequest.length === N && keepAliveAgent) {
keepAliveAgent.destroy();
keepAliveAgent = undefined;
}
if (destroyedIdsIncomingMessage.length === N && server.listening) {
server.close();
}
}
}).enable();
const server = http.createServer((req, res) => {
req.on('close', common.mustCall(() => {
req.on('readable', common.mustNotCall());
}));
res.end('Hello');
});
let keepAliveAgent = new http.Agent({
keepAlive: true,
keepAliveMsecs: KEEP_ALIVE,
});
server.listen(0, () => {
for (let i = 0; i < N; ++i) {
(function makeRequest() {
http.get({
port: server.address().port,
agent: keepAliveAgent
}, (res) => {
res.resume();
});
})();
}
});
function checkOnExit() {
assert.strictEqual(createdIdsIncomingMessage.length, N);
assert.strictEqual(createdIdsClientRequest.length, N);
assert.strictEqual(destroyedIdsIncomingMessage.length, N);
assert.strictEqual(destroyedIdsClientRequest.length, N);
assert.deepStrictEqual(destroyedIdsIncomingMessage.sort(),
createdIdsIncomingMessage.sort());
assert.deepStrictEqual(destroyedIdsClientRequest.sort(),
createdIdsClientRequest.sort());
}
process.on('SIGTERM', () => {
// Catching SIGTERM and calling `process.exit(1)` so that the `exit` event
// is triggered and the assertions are checked. This can be useful for
// troubleshooting this test if it times out.
process.exit(1);
});
// Ordinary exit.
process.on('exit', checkOnExit);

View File

@ -0,0 +1,24 @@
'use strict';
// Flags: --expose_gc
// This test ensures that userland-only AsyncResources cause a destroy event to
// be emitted when they get gced.
const common = require('../common');
const async_hooks = require('async_hooks');
const hook = async_hooks.createHook({
destroy: common.mustCallAtLeast(2) // 1 immediate + manual destroy
}).enable();
{
const res = new async_hooks.AsyncResource('foobar');
res.emitDestroy();
}
setImmediate(() => {
globalThis.gc();
setImmediate(() => {
hook.disable();
});
});

View File

@ -0,0 +1,44 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const async_hooks = require('async_hooks');
const EXPECTED_INITS = 2;
let p_er = null;
let p_inits = 0;
// Not useful to place common.mustCall() around 'exit' event b/c it won't be
// able to check it anyway.
process.on('exit', (code) => {
if (code !== 0)
return;
if (p_er !== null)
throw p_er;
// Expecting exactly 2 PROMISE types to reach init.
assert.strictEqual(p_inits, EXPECTED_INITS);
});
const mustCallInit = common.mustCall(function init(id, type, tid, resource) {
if (type !== 'PROMISE')
return;
p_inits++;
}, EXPECTED_INITS);
const hook = async_hooks.createHook({
init: mustCallInit
// Enable then disable to test whether disable() actually works.
}).enable().disable().disable();
new Promise(common.mustCall((res) => {
res(42);
})).then(common.mustCall((val) => {
hook.enable().enable();
const p = new Promise((res) => res(val));
hook.disable();
return p;
})).then(common.mustCall((val2) => {
hook.enable();
const p = new Promise((res) => res(val2));
hook.disable();
return p;
})).catch((er) => p_er = er);

View File

@ -0,0 +1,35 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const async_hooks = require('async_hooks');
const { isMainThread } = require('worker_threads');
if (!isMainThread) {
common.skip('Worker bootstrapping works differently -> different async IDs');
}
const promiseAsyncIds = [];
async_hooks.createHook({
init: common.mustCallAtLeast((id, type, triggerId) => {
if (type === 'PROMISE') {
// Check that the last known Promise is triggering the creation of
// this one.
assert.strictEqual(triggerId,
promiseAsyncIds[promiseAsyncIds.length - 1] || 1);
promiseAsyncIds.push(id);
}
}, 3),
before: common.mustCall((id) => {
assert.strictEqual(id, promiseAsyncIds[1]);
}),
after: common.mustCall((id) => {
assert.strictEqual(id, promiseAsyncIds[1]);
})
}).enable();
Promise.resolve(42).then(common.mustCall(() => {
assert.strictEqual(async_hooks.executionAsyncId(), promiseAsyncIds[1]);
assert.strictEqual(async_hooks.triggerAsyncId(), promiseAsyncIds[0]);
Promise.resolve(10);
}));

View File

@ -0,0 +1,29 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const async_hooks = require('async_hooks');
const { isMainThread } = require('worker_threads');
if (!isMainThread) {
common.skip('Worker bootstrapping works differently -> different async IDs');
}
const initCalls = [];
const resolveCalls = [];
async_hooks.createHook({
init: common.mustCall((id, type, triggerId, resource) => {
assert.strictEqual(type, 'PROMISE');
initCalls.push({ id, triggerId, resource });
}, 2),
promiseResolve: common.mustCall((id) => {
assert.strictEqual(initCalls[resolveCalls.length].id, id);
resolveCalls.push(id);
}, 2)
}).enable();
const a = Promise.resolve(42);
a.then(common.mustCall());
assert.strictEqual(initCalls[0].triggerId, 1);
assert.strictEqual(initCalls[1].triggerId, initCalls[0].id);

View File

@ -0,0 +1,20 @@
'use strict';
require('../common');
const assert = require('assert');
const async_hooks = require('async_hooks');
// This test verifies that the async ID stack can grow indefinitely.
function recurse(n) {
const a = new async_hooks.AsyncResource('foobar');
a.runInAsyncScope(() => {
assert.strictEqual(a.asyncId(), async_hooks.executionAsyncId());
assert.strictEqual(a.triggerAsyncId(), async_hooks.triggerAsyncId());
if (n >= 0)
recurse(n - 1);
assert.strictEqual(a.asyncId(), async_hooks.executionAsyncId());
assert.strictEqual(a.triggerAsyncId(), async_hooks.triggerAsyncId());
});
}
recurse(1000);

View File

@ -0,0 +1,11 @@
'use strict';
require('../common');
const { AsyncResource } = require('async_hooks');
try {
new AsyncResource('foo').runInAsyncScope(() => { throw new Error('bar'); });
} catch {
// Continue regardless of error.
}
// Should abort (fail the case) if async id is not matching.

View File

@ -0,0 +1,17 @@
'use strict';
// Test that passing thisArg to runInAsyncScope() works.
const common = require('../common');
const assert = require('assert');
const { AsyncResource } = require('async_hooks');
const thisArg = {};
const res = new AsyncResource('fhqwhgads');
function callback() {
assert.strictEqual(this, thisArg);
}
res.runInAsyncScope(common.mustCall(callback), thisArg);

View File

@ -0,0 +1,32 @@
'use strict';
// Regression test for https://github.com/nodejs/node/issues/13262
const common = require('../common');
const assert = require('assert');
const async_hooks = require('async_hooks');
const { isMainThread } = require('worker_threads');
if (!isMainThread) {
common.skip('Worker bootstrapping works differently -> different async IDs');
}
let seenId, seenResource;
async_hooks.createHook({
init: common.mustCall((id, provider, triggerAsyncId, resource) => {
seenId = id;
seenResource = resource;
assert.strictEqual(provider, 'Immediate');
assert.strictEqual(triggerAsyncId, 1);
}),
before: common.mustNotCall(),
after: common.mustNotCall(),
destroy: common.mustCall((id) => {
assert.strictEqual(seenId, id);
})
}).enable();
const immediate = setImmediate(common.mustNotCall());
assert.strictEqual(immediate, seenResource);
clearImmediate(immediate);

View File

@ -0,0 +1,15 @@
// Flags: --expose-gc
'use strict';
require('../common');
const asyncHooks = require('async_hooks');
const vm = require('vm');
// This is a regression test for https://github.com/nodejs/node/issues/39019
//
// It should not segfault.
const hook = asyncHooks.createHook({ init() {} }).enable();
vm.createContext();
globalThis.gc();
hook.disable();

View File

@ -0,0 +1,15 @@
'use strict';
const common = require('../common');
const { Worker } = require('worker_threads');
const w = new Worker(`
const { createHook } = require('async_hooks');
setImmediate(async () => {
createHook({ init() {} }).enable();
await 0;
process.exit();
});
`, { eval: true });
w.on('exit', common.mustCall());

View File

@ -0,0 +1,21 @@
'use strict';
const common = require('../common');
const { Worker } = require('worker_threads');
// Like test-async-hooks-worker-promise.js but with the `await` and `createHook`
// lines switched, because that resulted in different assertion failures
// (one a Node.js assertion and one a V8 DCHECK) and it seems prudent to
// cover both of those failures.
const w = new Worker(`
const { createHook } = require('async_hooks');
setImmediate(async () => {
await 0;
createHook({ init() {} }).enable();
process.exit();
});
`, { eval: true });
w.postMessage({});
w.on('exit', common.mustCall());

View File

@ -0,0 +1,20 @@
'use strict';
const common = require('../common');
const { Worker } = require('worker_threads');
// Like test-async-hooks-worker-promise.js but with an additional statement
// after the `process.exit()` call, that shouldnt really make a difference
// but apparently does.
const w = new Worker(`
const { createHook } = require('async_hooks');
setImmediate(async () => {
createHook({ init() {} }).enable();
await 0;
process.exit();
process._rawDebug('THIS SHOULD NEVER BE REACHED');
});
`, { eval: true });
w.on('exit', common.mustCall());

View File

@ -0,0 +1,25 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const { Worker } = require('worker_threads');
// Like test-async-hooks-worker-promise.js but doing a trivial counter increase
// after process.exit(). This should not make a difference, but apparently it
// does. This is *also* different from test-async-hooks-worker-promise-3.js,
// in that the statement is an ArrayBuffer access rather than a full method,
// which *also* makes a difference even though it shouldnt.
const workerData = new Int32Array(new SharedArrayBuffer(4));
const w = new Worker(`
const { createHook } = require('async_hooks');
const { workerData } = require('worker_threads');
setImmediate(async () => {
createHook({ init() {} }).enable();
await 0;
process.exit();
workerData[0]++;
});
`, { eval: true, workerData });
w.on('exit', common.mustCall(() => assert.strictEqual(workerData[0], 0)));

View File

@ -0,0 +1,17 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const { AsyncLocalStorage } = require('async_hooks');
[1, false, '', {}, []].forEach((i) => {
assert.throws(() => AsyncLocalStorage.bind(i), {
code: 'ERR_INVALID_ARG_TYPE'
});
});
const fn = common.mustCall(AsyncLocalStorage.bind(() => 123));
assert.strictEqual(fn(), 123);
const fn2 = AsyncLocalStorage.bind(common.mustCall((arg) => assert.strictEqual(arg, 'test')));
fn2('test');

View File

@ -0,0 +1,35 @@
'use strict';
require('../common');
const assert = require('assert');
const vm = require('vm');
const { AsyncLocalStorage } = require('async_hooks');
// Regression test for https://github.com/nodejs/node/issues/38781
const context = vm.createContext({
AsyncLocalStorage,
assert
});
vm.runInContext(`
const storage = new AsyncLocalStorage()
async function test() {
return storage.run({ test: 'vm' }, async () => {
assert.strictEqual(storage.getStore().test, 'vm');
await 42;
assert.strictEqual(storage.getStore().test, 'vm');
});
}
test()
`, context);
const storage = new AsyncLocalStorage();
async function test() {
return storage.run({ test: 'main context' }, async () => {
assert.strictEqual(storage.getStore().test, 'main context');
await 42;
assert.strictEqual(storage.getStore().test, 'main context');
});
}
test();

View File

@ -0,0 +1,15 @@
'use strict';
const common = require('../common');
const { AsyncLocalStorage } = require('async_hooks');
// Regression test for: https://github.com/nodejs/node/issues/34556
const als = new AsyncLocalStorage();
const done = common.mustCall();
function run(count) {
if (count !== 0) return als.run({}, run, --count);
done();
}
run(1000);

View File

@ -0,0 +1,37 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const { AsyncLocalStorage } = require('async_hooks');
// Verify that `enterWith()` does not leak the store to the parent context in a promise.
const als = new AsyncLocalStorage();
async function asyncFunctionAfterAwait() {
await 0;
als.enterWith('after await');
}
function promiseThen() {
return Promise.resolve()
.then(() => {
als.enterWith('inside then');
});
}
async function asyncFunctionBeforeAwait() {
als.enterWith('before await');
await 0;
}
async function main() {
await asyncFunctionAfterAwait();
await promiseThen();
assert.strictEqual(als.getStore(), undefined);
// This is a known limitation of the `enterWith` API.
await asyncFunctionBeforeAwait();
assert.strictEqual(als.getStore(), 'before await');
}
main().then(common.mustCall());

View File

@ -0,0 +1,28 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const { AsyncLocalStorage } = require('async_hooks');
const als = new AsyncLocalStorage();
// The _propagate function only exists on the old JavaScript implementation.
if (typeof als._propagate === 'function') {
// The als instance should be getting removed from the storageList in
// lib/async_hooks.js when exit(...) is called, therefore when the nested runs
// are called there should be no copy of the als in the storageList to run the
// _propagate method on.
als._propagate = common.mustNotCall('_propagate() should not be called');
}
const done = common.mustCall();
const data = true;
function run(count) {
if (count === 0) return done();
assert.notStrictEqual(als.getStore(), data);
als.run(data, () => {
als.exit(run, --count);
});
}
run(100);

View File

@ -0,0 +1,83 @@
'use strict';
const common = require('../common');
const assert = require('node:assert');
const { AsyncLocalStorage } = require('node:async_hooks');
const http = require('node:http');
// Similar as test-async-hooks-http-agent added via
// https://github.com/nodejs/node/issues/13325 but verifies
// AsyncLocalStorage functionality instead async_hooks
const cls = new AsyncLocalStorage();
// Make sure a single socket is transparently reused for 2 requests.
const agent = new http.Agent({
keepAlive: true,
keepAliveMsecs: Infinity,
maxSockets: 1
});
const server = http.createServer(common.mustCall((req, res) => {
req.once('data', common.mustCallAtLeast(() => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('foo');
}));
req.on('end', common.mustCall(() => {
res.end('bar');
}));
}, 2)).listen(0, common.mustCall(() => {
const port = server.address().port;
const payload = 'hello world';
// First request. This is useless except for adding a socket to the
// agents pool for reuse.
cls.run('first', common.mustCall(() => {
assert.strictEqual(cls.getStore(), 'first');
const r1 = http.request({
agent, port, method: 'POST'
}, common.mustCall((res) => {
assert.strictEqual(cls.getStore(), 'first');
res.on('data', common.mustCallAtLeast(() => {
assert.strictEqual(cls.getStore(), 'first');
}));
res.on('end', common.mustCall(() => {
assert.strictEqual(cls.getStore(), 'first');
// setImmediate() to give the agent time to register the freed socket.
setImmediate(common.mustCall(() => {
assert.strictEqual(cls.getStore(), 'first');
cls.run('second', common.mustCall(() => {
// Second request. To re-create the exact conditions from the
// referenced issue, we use a POST request without chunked encoding
// (hence the Content-Length header) and call .end() after the
// response header has already been received.
const r2 = http.request({
agent, port, method: 'POST', headers: {
'Content-Length': payload.length
}
}, common.mustCall((res) => {
assert.strictEqual(cls.getStore(), 'second');
// Empty payload, to hit the “right” code path.
r2.end('');
res.on('data', common.mustCallAtLeast(() => {
assert.strictEqual(cls.getStore(), 'second');
}));
res.on('end', common.mustCall(() => {
assert.strictEqual(cls.getStore(), 'second');
// Clean up to let the event loop stop.
server.close();
agent.destroy();
}));
}));
// Schedule a payload to be written immediately, but do not end the
// request just yet.
r2.write(payload);
}));
}));
}));
}));
r1.end(payload);
}));
}));

View File

@ -0,0 +1,65 @@
'use strict';
const common = require('../common');
const Countdown = require('../common/countdown');
const assert = require('assert');
const { AsyncLocalStorage } = require('async_hooks');
const http = require('http');
const cls = new AsyncLocalStorage();
const NUM_CLIENTS = 10;
// Run multiple clients that receive data from a server
// in multiple chunks, in a single non-closure function.
// Use the AsyncLocalStorage (ALS) APIs to maintain the context
// and data download. Make sure that individual clients
// receive their respective data, with no conflicts.
// Set up a server that sends large buffers of data, filled
// with cardinal numbers, increasing per request
let index = 0;
const server = http.createServer((q, r) => {
// Send a large chunk as response, otherwise the data
// may be sent in a single chunk, and the callback in the
// client may be called only once, defeating the purpose of test
r.end((index++ % 10).toString().repeat(1024 * 1024));
});
const countdown = new Countdown(NUM_CLIENTS, () => {
server.close();
});
server.listen(0, common.mustCall(() => {
for (let i = 0; i < NUM_CLIENTS; i++) {
cls.run(new Map(), common.mustCall(() => {
const options = { port: server.address().port };
const req = http.get(options, common.mustCall((res) => {
const store = cls.getStore();
store.set('data', '');
// Make ondata and onend non-closure
// functions and fully dependent on ALS
res.setEncoding('utf8');
res.on('data', ondata);
res.on('end', common.mustCall(onend));
}));
req.end();
}));
}
}));
// Accumulate the current data chunk with the store data
function ondata(d) {
const store = cls.getStore();
assert.notStrictEqual(store, undefined);
let chunk = store.get('data');
chunk += d;
store.set('data', chunk);
}
// Retrieve the store data, and test for homogeneity
function onend() {
const store = cls.getStore();
assert.notStrictEqual(store, undefined);
const data = store.get('data');
assert.strictEqual(data, data[0].repeat(data.length));
countdown.dec();
}

View File

@ -0,0 +1,67 @@
'use strict';
const common = require('../common');
const { AsyncLocalStorage } = require('node:async_hooks');
const assert = require('node:assert');
// Verify that ALS instances are independent of each other.
{
// Verify als2.enterWith() and als2.run inside als1.run()
const als1 = new AsyncLocalStorage();
const als2 = new AsyncLocalStorage();
assert.strictEqual(als1.getStore(), undefined);
assert.strictEqual(als2.getStore(), undefined);
als1.run('store1', common.mustCall(() => {
assert.strictEqual(als1.getStore(), 'store1');
assert.strictEqual(als2.getStore(), undefined);
als2.run('store2', common.mustCall(() => {
assert.strictEqual(als1.getStore(), 'store1');
assert.strictEqual(als2.getStore(), 'store2');
}));
assert.strictEqual(als1.getStore(), 'store1');
assert.strictEqual(als2.getStore(), undefined);
als2.enterWith('store3');
assert.strictEqual(als1.getStore(), 'store1');
assert.strictEqual(als2.getStore(), 'store3');
}));
assert.strictEqual(als1.getStore(), undefined);
assert.strictEqual(als2.getStore(), 'store3');
}
{
// Verify als1.disable() has no side effects to als2 and als3
const als1 = new AsyncLocalStorage();
const als2 = new AsyncLocalStorage();
const als3 = new AsyncLocalStorage();
als3.enterWith('store3');
als1.run('store1', common.mustCall(() => {
assert.strictEqual(als1.getStore(), 'store1');
assert.strictEqual(als2.getStore(), undefined);
assert.strictEqual(als3.getStore(), 'store3');
als2.run('store2', common.mustCall(() => {
assert.strictEqual(als1.getStore(), 'store1');
assert.strictEqual(als2.getStore(), 'store2');
assert.strictEqual(als3.getStore(), 'store3');
als1.disable();
assert.strictEqual(als1.getStore(), undefined);
assert.strictEqual(als2.getStore(), 'store2');
assert.strictEqual(als3.getStore(), 'store3');
}));
assert.strictEqual(als1.getStore(), undefined);
assert.strictEqual(als2.getStore(), undefined);
assert.strictEqual(als3.getStore(), 'store3');
}));
assert.strictEqual(als1.getStore(), undefined);
assert.strictEqual(als2.getStore(), undefined);
assert.strictEqual(als3.getStore(), 'store3');
}

View File

@ -0,0 +1,16 @@
'use strict';
const common = require('../common');
const { strictEqual } = require('assert');
const { AsyncLocalStorage } = require('async_hooks');
const asyncLocalStorage = new AsyncLocalStorage();
const runInAsyncScope =
asyncLocalStorage.run(123, common.mustCall(() => AsyncLocalStorage.snapshot()));
const result =
asyncLocalStorage.run(321, common.mustCall(() => {
return runInAsyncScope(() => {
return asyncLocalStorage.getStore();
});
}));
strictEqual(result, 123);

View File

@ -0,0 +1,21 @@
'use strict';
// This tests that using falsy values in createHook throws an error.
require('../common');
const assert = require('assert');
const async_hooks = require('async_hooks');
const falsyValues = [0, 1, false, true, null, 'hello'];
for (const badArg of falsyValues) {
const hookNames = ['init', 'before', 'after', 'destroy', 'promiseResolve'];
for (const hookName of hookNames) {
assert.throws(() => {
async_hooks.createHook({ [hookName]: badArg });
}, {
code: 'ERR_ASYNC_CALLBACK',
name: 'TypeError',
message: `hook.${hookName} must be a function`
});
}
}

View File

@ -0,0 +1,39 @@
// Flags: --expose-internals
'use strict';
const common = require('../common');
const { internalBinding } = require('internal/test/binding');
const async_wrap = internalBinding('async_wrap');
const assert = require('assert');
const async_hooks = require('async_hooks');
const RUNS = 5;
let test_id = null;
let run_cntr = 0;
let hooks = null;
process.on('beforeExit', common.mustCall(() => {
process.removeAllListeners('uncaughtException');
hooks.disable();
assert.strictEqual(test_id, null);
assert.strictEqual(run_cntr, RUNS);
}));
hooks = async_hooks.createHook({
destroy(id) {
if (id === test_id) {
run_cntr++;
test_id = null;
}
},
}).enable();
(function runner(n) {
assert.strictEqual(test_id, null);
if (n <= 0) return;
test_id = (Math.random() * 1e9) >>> 0;
async_wrap.queueDestroyAsyncId(test_id);
setImmediate(common.mustCall(runner), n - 1);
})(RUNS);

View File

@ -0,0 +1,25 @@
'use strict';
require('../common');
if (process.argv[2] === 'async') {
async function fn() {
fn();
throw new Error();
}
return (async function() { await fn(); })();
}
const assert = require('assert');
const { spawnSync } = require('child_process');
const ret = spawnSync(
process.execPath,
['--unhandled-rejections=none', '--stack_size=150', __filename, 'async'],
{ maxBuffer: Infinity }
);
assert.strictEqual(ret.status, 0,
`EXIT CODE: ${ret.status}, STDERR:\n${ret.stderr}`);
const stderr = ret.stderr.toString('utf8', 0, 2048);
assert.doesNotMatch(stderr, /async.*hook/i);
assert.ok(stderr.includes('Maximum call stack size exceeded'), stderr);

View File

@ -0,0 +1,39 @@
'use strict';
// Regression test for https://github.com/nodejs/node/issues/13237
const common = require('../common');
const assert = require('assert');
const { isMainThread } = require('worker_threads');
if (!isMainThread) {
common.skip('Worker bootstrapping works differently -> different timing');
}
const async_hooks = require('async_hooks');
const seenEvents = [];
const p = new Promise((resolve) => resolve(1));
p.then(() => seenEvents.push('then'));
const hooks = async_hooks.createHook({
init: common.mustNotCall(),
before: common.mustCall((id) => {
assert.ok(id > 1);
seenEvents.push('before');
}),
after: common.mustCall((id) => {
assert.ok(id > 1);
seenEvents.push('after');
hooks.disable();
})
});
setImmediate(() => {
assert.deepStrictEqual(seenEvents, ['before', 'then', 'after']);
});
hooks.enable(); // After `setImmediate` in order to not catch its init event.

View File

@ -0,0 +1,66 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto) common.skip('missing crypto');
const fixtures = require('../common/fixtures');
// An HTTP Agent reuses a TLSSocket, and makes a failed call to `asyncReset`.
// Refs: https://github.com/nodejs/node/issues/13045
const assert = require('assert');
const https = require('https');
const serverOptions = {
key: fixtures.readKey('agent1-key.pem'),
cert: fixtures.readKey('agent1-cert.pem'),
ca: fixtures.readKey('ca1-cert.pem')
};
const server = https.createServer(
serverOptions,
common.mustCall((req, res) => {
res.end('hello world\n');
}, 2)
);
server.listen(
0,
common.mustCall(function() {
const port = this.address().port;
const clientOptions = {
agent: new https.Agent({
keepAlive: true,
rejectUnauthorized: false
}),
port: port
};
const req = https.get(
clientOptions,
common.mustCall((res) => {
assert.strictEqual(res.statusCode, 200);
res.on('error', (err) => assert.fail(err));
res.socket.on('error', (err) => assert.fail(err));
res.resume();
// Drain the socket and wait for it to be free to reuse
res.socket.once('free', () => {
// This is the pain point. Internally the Agent will call
// `socket._handle.asyncReset()` and if the _handle does not implement
// `asyncReset` this will throw TypeError
const req2 = https.get(
clientOptions,
common.mustCall((res2) => {
assert.strictEqual(res.statusCode, 200);
res2.on('error', (err) => assert.fail(err));
res2.socket.on('error', (err) => assert.fail(err));
// This should be the end of the test
res2.destroy();
server.close();
})
);
req2.on('error', (err) => assert.fail(err));
});
})
);
req.on('error', (err) => assert.fail(err));
})
);

View File

@ -0,0 +1,28 @@
'use strict';
require('../common');
const assert = require('assert');
const async_hooks = require('async_hooks');
const triggerAsyncId = async_hooks.triggerAsyncId;
const triggerAsyncId0 = triggerAsyncId();
let triggerAsyncId1;
process.nextTick(() => {
process.nextTick(() => {
triggerAsyncId1 = triggerAsyncId();
// Async resources having different causal ancestry
// should have different triggerAsyncIds
assert.notStrictEqual(
triggerAsyncId0,
triggerAsyncId1);
});
process.nextTick(() => {
const triggerAsyncId2 = triggerAsyncId();
// Async resources having the same causal ancestry
// should have the same triggerAsyncId
assert.strictEqual(
triggerAsyncId1,
triggerAsyncId2);
});
});

View File

@ -0,0 +1,46 @@
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const async_hooks = require('async_hooks');
const call_log = [0, 0, 0, 0]; // [before, callback, exception, after];
let call_id = null;
let hooks = null;
process.on('beforeExit', common.mustCall(() => {
process.removeAllListeners('uncaughtException');
hooks.disable();
assert.strictEqual(typeof call_id, 'number');
assert.deepStrictEqual(call_log, [1, 1, 1, 1]);
}));
hooks = async_hooks.createHook({
init(id, type) {
if (type === 'RANDOMBYTESREQUEST')
call_id = id;
},
before(id) {
if (id === call_id) call_log[0]++;
},
after(id) {
if (id === call_id) call_log[3]++;
},
}).enable();
process.on('uncaughtException', common.mustCall(() => {
assert.strictEqual(call_id, async_hooks.executionAsyncId());
call_log[2]++;
}));
require('crypto').randomBytes(1, common.mustCall(() => {
assert.strictEqual(call_id, async_hooks.executionAsyncId());
call_log[1]++;
throw new Error();
}));

View File

@ -0,0 +1,56 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const { AsyncResource, executionAsyncId } = require('async_hooks');
const fn = common.mustCall(AsyncResource.bind(() => {
return executionAsyncId();
}));
setImmediate(() => {
const asyncId = executionAsyncId();
assert.notStrictEqual(asyncId, fn());
});
const asyncResource = new AsyncResource('test');
[1, false, '', {}, []].forEach((i) => {
assert.throws(() => asyncResource.bind(i), {
code: 'ERR_INVALID_ARG_TYPE'
});
});
const fn2 = asyncResource.bind((a, b) => {
return executionAsyncId();
});
assert.strictEqual(fn2.asyncResource, asyncResource);
assert.strictEqual(fn2.length, 2);
setImmediate(() => {
const asyncId = executionAsyncId();
assert.strictEqual(asyncResource.asyncId(), fn2());
assert.notStrictEqual(asyncId, fn2());
});
const foo = {};
const fn3 = asyncResource.bind(common.mustCall(function() {
assert.strictEqual(this, foo);
}), foo);
fn3();
const fn4 = asyncResource.bind(common.mustCall(function() {
assert.strictEqual(this, undefined);
}));
fn4();
const fn5 = asyncResource.bind(common.mustCall(function() {
assert.strictEqual(this, false);
}), false);
fn5();
const fn6 = asyncResource.bind(common.mustCall(function() {
assert.strictEqual(this, 'test');
}));
fn6.call('test');

View File

@ -0,0 +1,7 @@
'use strict';
require('../common');
const assert = require('assert');
// https://github.com/nodejs/node/issues/21219
assert.strictEqual(Atomics.wake, undefined);

View File

@ -0,0 +1,33 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
require('../common');
const assert = require('assert');
let exception = null;
try {
eval('"\\uc/ef"');
} catch (e) {
exception = e;
}
assert(exception instanceof SyntaxError);

View File

@ -0,0 +1,35 @@
'use strict';
require('../common');
const assert = require('assert');
const child_process = require('child_process');
const { debuglog, inspect } = require('util');
const debug = debuglog('test');
const p = child_process.spawnSync(
process.execPath, [ '--completion-bash' ]);
assert.ifError(p.error);
const output = p.stdout.toString().trim().replace(/\r/g, '');
debug(output);
const prefix = `_node_complete() {
local cur_word options
cur_word="\${COMP_WORDS[COMP_CWORD]}"
if [[ "\${cur_word}" == -* ]] ; then
COMPREPLY=( $(compgen -W '`.replace(/\r/g, '');
const suffix = `' -- "\${cur_word}") )
return 0
else
COMPREPLY=( $(compgen -f "\${cur_word}") )
return 0
fi
}
complete -o filenames -o nospace -o bashdefault -F _node_complete node node_g`
.replace(/\r/g, '');
assert.ok(
output.includes(prefix),
`Expect\n\n ${inspect(output)}\n\nto include\n\n${inspect(prefix)}`);
assert.ok(
output.includes(suffix),
`Expect\n\n ${inspect(output)}\n\nto include\n\n${inspect(suffix)}`);

View File

@ -0,0 +1,27 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const { mustNotCall } = require('../common');
process.on('beforeExit', mustNotCall('exit should not allow this to occur'));
process.exit();

View File

@ -0,0 +1,38 @@
'use strict';
require('../common');
// This tests the CLI parser for our benchmark suite.
const assert = require('assert');
const CLI = require('../../benchmark/_cli.js');
const originalArgv = process.argv;
function testFilterPattern(filters, excludes, filename, expectedResult) {
process.argv = process.argv.concat(...filters.map((p) => ['--filter', p]));
process.argv = process.argv.concat(...excludes.map((p) => ['--exclude', p]));
process.argv = process.argv.concat(['bench']);
const cli = new CLI('', { 'arrayArgs': ['filter', 'exclude'] });
assert.deepStrictEqual(cli.shouldSkip(filename), expectedResult);
process.argv = originalArgv;
}
testFilterPattern([], [], 'foo', false);
testFilterPattern(['foo'], [], 'foo', false);
testFilterPattern(['foo'], [], 'bar', true);
testFilterPattern(['foo', 'bar'], [], 'foo', false);
testFilterPattern(['foo', 'bar'], [], 'bar', false);
testFilterPattern([], ['foo'], 'foo', true);
testFilterPattern([], ['foo'], 'bar', false);
testFilterPattern([], ['foo', 'bar'], 'foo', true);
testFilterPattern([], ['foo', 'bar'], 'bar', true);
testFilterPattern(['foo'], ['bar'], 'foo', false);
testFilterPattern(['foo'], ['bar'], 'foo-bar', true);

View File

@ -0,0 +1,33 @@
// Flags: --expose-internals
'use strict';
require('../common');
const { internalBinding } = require('internal/test/binding');
const constants = internalBinding('constants');
const assert = require('assert');
assert.deepStrictEqual(
Object.keys(constants).sort(), ['crypto', 'fs', 'internal', 'os', 'trace', 'zlib']
);
assert.deepStrictEqual(
Object.keys(constants.os).sort(), ['UV_UDP_REUSEADDR', 'dlopen', 'errno',
'priority', 'signals']
);
// Make sure all the constants objects don't inherit from Object.prototype
const inheritedProperties = Object.getOwnPropertyNames(Object.prototype);
function test(obj) {
assert(obj);
assert.strictEqual(Object.prototype.toString.call(obj), '[object Object]');
assert.strictEqual(Object.getPrototypeOf(obj), null);
inheritedProperties.forEach((property) => {
assert.strictEqual(property in obj, false);
});
}
[
constants, constants.crypto, constants.fs, constants.internal, constants.os, constants.trace,
constants.zlib, constants.os.dlopen, constants.os.errno, constants.os.signals,
].forEach(test);

View File

@ -0,0 +1,54 @@
// Flags: --no-warnings
'use strict';
const common = require('../common');
// Because registering a Blob URL requires generating a random
// UUID, it can only be done if crypto support is enabled.
if (!common.hasCrypto)
common.skip('missing crypto');
const {
URL,
} = require('url');
const {
Blob,
resolveObjectURL,
} = require('buffer');
const assert = require('assert');
(async () => {
const blob = new Blob(['hello']);
const id = URL.createObjectURL(blob);
assert.strictEqual(typeof id, 'string');
const otherBlob = resolveObjectURL(id);
assert.ok(otherBlob instanceof Blob);
assert.strictEqual(otherBlob.constructor, Blob);
assert.strictEqual(otherBlob.size, 5);
assert.strictEqual(
Buffer.from(await otherBlob.arrayBuffer()).toString(),
'hello');
URL.revokeObjectURL(id);
// should do nothing
URL.revokeObjectURL(id);
assert.strictEqual(resolveObjectURL(id), undefined);
// Leaving a Blob registered should not cause an assert
// when Node.js exists
URL.createObjectURL(new Blob());
})().then(common.mustCall());
['not a url', undefined, 1, 'blob:nodedata:1:wrong', {}].forEach((i) => {
assert.strictEqual(resolveObjectURL(i), undefined);
});
[undefined, 1, '', false, {}].forEach((i) => {
assert.throws(() => URL.createObjectURL(i), {
code: 'ERR_INVALID_ARG_TYPE',
});
});

View File

@ -0,0 +1,145 @@
'use strict';
const common = require('../common');
const {
strictEqual,
rejects,
throws,
} = require('assert');
const { TextDecoder } = require('util');
const {
writeFileSync,
openAsBlob,
} = require('fs');
const {
unlink
} = require('fs/promises');
const { Blob } = require('buffer');
const tmpdir = require('../common/tmpdir');
const testfile = tmpdir.resolve('test-file-backed-blob.txt');
const testfile2 = tmpdir.resolve('test-file-backed-blob2.txt');
const testfile3 = tmpdir.resolve('test-file-backed-blob3.txt');
const testfile4 = tmpdir.resolve('test-file-backed-blob4.txt');
const testfile5 = tmpdir.resolve('test-file-backed-blob5.txt');
tmpdir.refresh();
const data = `${'a'.repeat(1000)}${'b'.repeat(2000)}`;
writeFileSync(testfile, data);
writeFileSync(testfile2, data);
writeFileSync(testfile3, data.repeat(100));
writeFileSync(testfile4, '');
writeFileSync(testfile5, '');
(async () => {
const blob = await openAsBlob(testfile);
const ab = await blob.arrayBuffer();
const dec = new TextDecoder();
strictEqual(dec.decode(new Uint8Array(ab)), data);
strictEqual(await blob.text(), data);
// Can be read multiple times
let stream = blob.stream();
let check = '';
for await (const chunk of stream)
check = dec.decode(chunk);
strictEqual(check, data);
// Can be combined with other Blob's and read
const combined = new Blob(['hello', blob, 'world']);
const ab2 = await combined.arrayBuffer();
strictEqual(dec.decode(ab2.slice(0, 5)), 'hello');
strictEqual(dec.decode(ab2.slice(5, -5)), data);
strictEqual(dec.decode(ab2.slice(-5)), 'world');
// If the file is modified tho, the stream errors.
writeFileSync(testfile, data + 'abc');
stream = blob.stream();
const read = async () => {
// eslint-disable-next-line no-unused-vars, no-empty
for await (const _ of stream) {}
};
await rejects(read(), { name: 'NotReadableError' });
await unlink(testfile);
})().then(common.mustCall());
(async () => {
// Refs: https://github.com/nodejs/node/issues/47683
const blob = await openAsBlob(testfile2);
const res = blob.slice(10, 20);
const ab = await res.arrayBuffer();
strictEqual(res.size, ab.byteLength);
let length = 0;
const stream = await res.stream();
for await (const chunk of stream)
length += chunk.length;
strictEqual(res.size, length);
const res1 = blob.slice(995, 1005);
strictEqual(await res1.text(), data.slice(995, 1005));
// Refs: https://github.com/nodejs/node/issues/53908
for (const res2 of [
blob.slice(995, 1005).slice(),
blob.slice(995).slice(0, 10),
blob.slice(0, 1005).slice(995),
]) {
strictEqual(await res2.text(), data.slice(995, 1005));
}
await unlink(testfile2);
})().then(common.mustCall());
(async () => {
const blob = await openAsBlob(testfile3);
const stream = blob.stream();
const read = async () => {
// eslint-disable-next-line no-unused-vars
for await (const _ of stream) {
writeFileSync(testfile3, data + 'abc');
}
};
await rejects(read(), { name: 'NotReadableError' });
await unlink(testfile3);
})().then(common.mustCall());
(async () => {
const blob = await openAsBlob(testfile4);
strictEqual(blob.size, 0);
strictEqual(await blob.text(), '');
writeFileSync(testfile4, 'abc');
await rejects(blob.text(), { name: 'NotReadableError' });
await unlink(testfile4);
})().then(common.mustCall());
(async () => {
const blob = await openAsBlob(testfile5);
strictEqual(blob.size, 0);
writeFileSync(testfile5, 'abc');
const stream = blob.stream();
const reader = stream.getReader();
await rejects(() => reader.read(), { name: 'NotReadableError' });
await unlink(testfile5);
})().then(common.mustCall());
(async () => {
// We currently do not allow File-backed blobs to be cloned or transferred
// across worker threads. This is largely because the underlying FdEntry
// is bound to the Environment/Realm under which is was created.
const blob = await openAsBlob(__filename);
throws(() => structuredClone(blob), {
code: 'ERR_INVALID_STATE',
message: 'Invalid state: File-backed Blobs are not cloneable'
});
})().then(common.mustCall());

510
test/parallel/test-blob.js Normal file
View File

@ -0,0 +1,510 @@
// Flags: --no-warnings --expose-internals
'use strict';
const common = require('../common');
const assert = require('assert');
const { Blob } = require('buffer');
const { inspect } = require('util');
const { EOL } = require('os');
const { kState } = require('internal/webstreams/util');
{
const b = new Blob();
assert.strictEqual(b.size, 0);
assert.strictEqual(b.type, '');
}
assert.throws(() => new Blob(false), {
code: 'ERR_INVALID_ARG_TYPE'
});
assert.throws(() => new Blob('hello'), {
code: 'ERR_INVALID_ARG_TYPE'
});
assert.throws(() => new Blob({}), {
code: 'ERR_INVALID_ARG_TYPE'
});
{
const b = new Blob([]);
assert(b);
assert.strictEqual(b.size, 0);
assert.strictEqual(b.type, '');
b.arrayBuffer().then(common.mustCall((ab) => {
assert.deepStrictEqual(ab, new ArrayBuffer(0));
}));
b.text().then(common.mustCall((text) => {
assert.strictEqual(text, '');
}));
const c = b.slice();
assert.strictEqual(c.size, 0);
}
{
assert.strictEqual(new Blob([], { type: 1 }).type, '1');
assert.strictEqual(new Blob([], { type: false }).type, 'false');
assert.strictEqual(new Blob([], { type: {} }).type, '[object object]');
}
{
const b = new Blob([Buffer.from('abc')]);
assert.strictEqual(b.size, 3);
b.text().then(common.mustCall((text) => {
assert.strictEqual(text, 'abc');
}));
}
{
const b = new Blob([new ArrayBuffer(3)]);
assert.strictEqual(b.size, 3);
b.text().then(common.mustCall((text) => {
assert.strictEqual(text, '\0\0\0');
}));
}
{
const b = new Blob([new Uint8Array(3)]);
assert.strictEqual(b.size, 3);
b.text().then(common.mustCall((text) => {
assert.strictEqual(text, '\0\0\0');
}));
}
{
const b = new Blob([new Blob(['abc'])]);
assert.strictEqual(b.size, 3);
b.text().then(common.mustCall((text) => {
assert.strictEqual(text, 'abc');
}));
}
{
const b = new Blob(['hello', Buffer.from('world')]);
assert.strictEqual(b.size, 10);
b.text().then(common.mustCall((text) => {
assert.strictEqual(text, 'helloworld');
}));
}
{
const b = new Blob(['hello', new Uint8Array([0xed, 0xa0, 0x88])]);
assert.strictEqual(b.size, 8);
b.text().then(common.mustCall((text) => {
assert.strictEqual(text, 'hello\ufffd\ufffd\ufffd');
assert.strictEqual(text.length, 8);
}));
}
{
const b = new Blob(
[
'h',
'e',
'l',
'lo',
Buffer.from('world'),
]);
assert.strictEqual(b.size, 10);
b.text().then(common.mustCall((text) => {
assert.strictEqual(text, 'helloworld');
}));
}
{
const b = new Blob(['hello', Buffer.from('world')]);
assert.strictEqual(b.size, 10);
assert.strictEqual(b.type, '');
const c = b.slice(1, -1, 'foo');
assert.strictEqual(c.type, 'foo');
c.text().then(common.mustCall((text) => {
assert.strictEqual(text, 'elloworl');
}));
const d = c.slice(1, -1);
d.text().then(common.mustCall((text) => {
assert.strictEqual(text, 'llowor');
}));
const e = d.slice(1, -1);
e.text().then(common.mustCall((text) => {
assert.strictEqual(text, 'lowo');
}));
const f = e.slice(1, -1);
f.text().then(common.mustCall((text) => {
assert.strictEqual(text, 'ow');
}));
const g = f.slice(1, -1);
assert.strictEqual(g.type, '');
g.text().then(common.mustCall((text) => {
assert.strictEqual(text, '');
}));
const h = b.slice(-1, 1);
assert.strictEqual(h.size, 0);
const i = b.slice(1, 100);
assert.strictEqual(i.size, 9);
const j = b.slice(1, 2, false);
assert.strictEqual(j.type, 'false');
assert.strictEqual(b.size, 10);
assert.strictEqual(b.type, '');
}
{
const b = new Blob([Buffer.from('hello'), Buffer.from('world')]);
const mc = new MessageChannel();
mc.port1.onmessage = common.mustCall(({ data }) => {
data.text().then(common.mustCall((text) => {
assert.strictEqual(text, 'helloworld');
}));
mc.port1.close();
});
mc.port2.postMessage(b);
b.text().then(common.mustCall((text) => {
assert.strictEqual(text, 'helloworld');
}));
}
{
const b = new Blob(['hello'], { type: '\x01' });
assert.strictEqual(b.type, '');
}
{
const descriptor =
Object.getOwnPropertyDescriptor(Blob.prototype, Symbol.toStringTag);
assert.deepStrictEqual(descriptor, {
configurable: true,
enumerable: false,
value: 'Blob',
writable: false
});
}
{
const descriptors = Object.getOwnPropertyDescriptors(Blob.prototype);
const enumerable = [
'size',
'type',
'slice',
'stream',
'text',
'arrayBuffer',
'bytes',
];
for (const prop of enumerable) {
assert.notStrictEqual(descriptors[prop], undefined);
assert.strictEqual(descriptors[prop].enumerable, true);
}
}
{
const b = new Blob(['test', 42]);
b.text().then(common.mustCall((text) => {
assert.strictEqual(text, 'test42');
}));
}
{
const b = new Blob();
assert.strictEqual(inspect(b, { depth: null }),
'Blob { size: 0, type: \'\' }');
assert.strictEqual(inspect(b, { depth: 1 }),
'Blob { size: 0, type: \'\' }');
assert.strictEqual(inspect(b, { depth: -1 }), '[Blob]');
}
{
// The Blob has to be over a specific size for the data to
// be copied asynchronously..
const b = new Blob(['hello', 'there'.repeat(820)]);
b.arrayBuffer().then(common.mustCall());
}
(async () => {
const b = new Blob(['hello']);
const reader = b.stream().getReader();
let res = await reader.read();
assert.strictEqual(res.value.byteLength, 5);
assert(!res.done);
res = await reader.read();
assert(res.done);
})().then(common.mustCall());
(async () => {
const b = new Blob(Array(10).fill('hello'));
const reader = b.stream().getReader();
const chunks = [];
while (true) {
const res = await reader.read();
if (res.done) break;
assert.strictEqual(res.value.byteLength, 5);
chunks.push(res.value);
}
assert.strictEqual(chunks.length, 10);
})().then(common.mustCall());
(async () => {
const b = new Blob(Array(10).fill('hello'));
const reader = b.stream().getReader();
const chunks = [];
while (true) {
const res = await reader.read();
if (chunks.length === 5) {
reader.cancel('boom');
break;
}
if (res.done) break;
assert.strictEqual(res.value.byteLength, 5);
chunks.push(res.value);
}
assert.strictEqual(chunks.length, 5);
reader.closed.then(common.mustCall());
})().then(common.mustCall());
(async () => {
const b = new Blob(['A', 'B', 'C']);
const stream = b.stream();
const chunks = [];
const decoder = new TextDecoder();
await stream.pipeTo(new WritableStream({
write(chunk) {
chunks.push(decoder.decode(chunk, { stream: true }));
}
}));
assert.strictEqual(chunks.join(''), 'ABC');
})().then(common.mustCall());
(async () => {
const b = new Blob(['A', 'B', 'C']);
const stream = b.stream();
const chunks = [];
const decoder = new TextDecoder();
await stream.pipeTo(
new WritableStream({
write(chunk) {
chunks.push(decoder.decode(chunk, { stream: true }));
},
})
);
assert.strictEqual(chunks.join(''), 'ABC');
})().then(common.mustCall());
(async () => {
// Ref: https://github.com/nodejs/node/issues/48668
const chunks = [];
const stream = new Blob(['Hello world']).stream();
const decoder = new TextDecoder();
await Promise.resolve();
await stream.pipeTo(
new WritableStream({
write(chunk) {
chunks.push(decoder.decode(chunk, { stream: true }));
},
})
);
assert.strictEqual(chunks.join(''), 'Hello world');
})().then(common.mustCall());
(async () => {
// Ref: https://github.com/nodejs/node/issues/48668
if (common.hasCrypto) {
// Can only do this test if we have node built with crypto
const file = new Blob(['<svg></svg>'], { type: 'image/svg+xml' });
const url = URL.createObjectURL(file);
const res = await fetch(url);
const blob = await res.blob();
assert.strictEqual(blob.size, 11);
assert.strictEqual(blob.type, 'image/svg+xml');
assert.strictEqual(await blob.text(), '<svg></svg>');
}
})().then(common.mustCall());
(async () => {
const b = new Blob(Array(10).fill('hello'));
const stream = b.stream();
const reader = stream.getReader();
assert.strictEqual(stream[kState].controller.desiredSize, 0);
const { value, done } = await reader.read();
assert.strictEqual(value.byteLength, 5);
assert(!done);
setTimeout(() => {
// The blob stream is now a byte stream hence after the first read,
// it should pull in the next 'hello' which is 5 bytes hence -5.
assert.strictEqual(stream[kState].controller.desiredSize, 0);
}, 0);
})().then(common.mustCall());
(async () => {
const blob = new Blob(['hello', 'world']);
const stream = blob.stream();
const reader = stream.getReader({ mode: 'byob' });
const decoder = new TextDecoder();
const chunks = [];
while (true) {
const { value, done } = await reader.read(new Uint8Array(100));
if (done) break;
chunks.push(decoder.decode(value, { stream: true }));
}
assert.strictEqual(chunks.join(''), 'helloworld');
})().then(common.mustCall());
(async () => {
const b = new Blob(Array(10).fill('hello'));
const stream = b.stream();
const reader = stream.getReader({ mode: 'byob' });
assert.strictEqual(stream[kState].controller.desiredSize, 0);
const { value, done } = await reader.read(new Uint8Array(100));
assert.strictEqual(value.byteLength, 5);
assert(!done);
setTimeout(() => {
assert.strictEqual(stream[kState].controller.desiredSize, 0);
}, 0);
})().then(common.mustCall());
(async () => {
const b = new Blob(Array(10).fill('hello'));
const stream = b.stream();
const reader = stream.getReader({ mode: 'byob' });
assert.strictEqual(stream[kState].controller.desiredSize, 0);
const { value, done } = await reader.read(new Uint8Array(2));
assert.strictEqual(value.byteLength, 2);
assert(!done);
setTimeout(() => {
assert.strictEqual(stream[kState].controller.desiredSize, -3);
}, 0);
})().then(common.mustCall());
{
const b = new Blob(['hello\n'], { endings: 'native' });
assert.strictEqual(b.size, EOL.length + 5);
[1, {}, 'foo'].forEach((endings) => {
assert.throws(() => new Blob([], { endings }), {
code: 'ERR_INVALID_ARG_VALUE',
});
});
}
{
assert.throws(() => Reflect.get(Blob.prototype, 'type', {}), {
code: 'ERR_INVALID_THIS',
});
assert.throws(() => Reflect.get(Blob.prototype, 'size', {}), {
code: 'ERR_INVALID_THIS',
});
assert.throws(() => Blob.prototype.slice(Blob.prototype, 0, 1), {
code: 'ERR_INVALID_THIS',
});
assert.throws(() => Blob.prototype.stream.call(), {
code: 'ERR_INVALID_THIS',
});
}
(async () => {
await assert.rejects(() => Blob.prototype.arrayBuffer.call(), {
code: 'ERR_INVALID_THIS',
});
await assert.rejects(() => Blob.prototype.text.call(), {
code: 'ERR_INVALID_THIS',
});
await assert.rejects(() => Blob.prototype.bytes.call(), {
code: 'ERR_INVALID_THIS',
});
})().then(common.mustCall());
(async () => {
const blob = new Blob([
new Uint8Array([0x50, 0x41, 0x53, 0x53]),
new Int8Array([0x50, 0x41, 0x53, 0x53]),
new Uint16Array([0x4150, 0x5353]),
new Int16Array([0x4150, 0x5353]),
new Uint32Array([0x53534150]),
new Int32Array([0x53534150]),
new Float32Array([0xD341500000]),
]);
assert.strictEqual(blob.size, 28);
assert.strictEqual(blob.type, '');
})().then(common.mustCall());
{
// Testing the defaults
[undefined, null, { __proto__: null }, { type: undefined }, {
get type() {}, // eslint-disable-line getter-return
}].forEach((options) => {
assert.strictEqual(
new Blob([], options).type,
new Blob([]).type,
);
});
Reflect.defineProperty(Object.prototype, 'type', {
__proto__: null,
configurable: true,
get: common.mustCall(() => 3, 7),
});
[{}, [], () => {}, Number, new Number(), new String(), new Boolean()].forEach(
(options) => {
assert.strictEqual(new Blob([], options).type, '3');
},
);
[0, '', true, Symbol(), 0n].forEach((options) => {
assert.throws(() => new Blob([], options), { code: 'ERR_INVALID_ARG_TYPE' });
});
delete Object.prototype.type;
}
(async () => {
// Refs: https://github.com/nodejs/node/issues/47301
const random = Buffer.alloc(256).fill('0');
const chunks = [];
for (let i = 0; i < random.length; i += 2) {
chunks.push(random.subarray(i, i + 2));
}
await new Blob(chunks).arrayBuffer();
})().then(common.mustCall());
{
const blob = new Blob(['hello']);
assert.ok(blob.slice(0, 1).constructor === Blob);
assert.ok(blob.slice(0, 1) instanceof Blob);
assert.ok(blob.slice(0, 1.5) instanceof Blob);
}
(async () => {
const blob = new Blob(['hello']);
assert.ok(structuredClone(blob).constructor === Blob);
assert.ok(structuredClone(blob) instanceof Blob);
assert.ok(structuredClone(blob).size === blob.size);
assert.ok(structuredClone(blob).size === blob.size);
assert.ok((await structuredClone(blob).text()) === (await blob.text()));
})().then(common.mustCall());
(async () => {
const blob = new Blob(['hello']);
const { arrayBuffer } = Blob.prototype;
Blob.prototype.arrayBuffer = common.mustNotCall();
try {
assert.strictEqual(await blob.text(), 'hello');
} finally {
Blob.prototype.arrayBuffer = arrayBuffer;
}
})().then(common.mustCall());

View File

@ -0,0 +1,31 @@
'use strict';
const common = require('../common');
const {
BlockList,
} = require('net');
const {
ok,
notStrictEqual,
} = require('assert');
const blocklist = new BlockList();
blocklist.addAddress('123.123.123.123');
const mc = new MessageChannel();
mc.port1.onmessage = common.mustCall(({ data }) => {
notStrictEqual(data, blocklist);
ok(data.check('123.123.123.123'));
ok(!data.check('123.123.123.124'));
data.addAddress('123.123.123.124');
ok(blocklist.check('123.123.123.124'));
ok(data.check('123.123.123.124'));
mc.port1.close();
});
mc.port2.postMessage(blocklist);

View File

@ -0,0 +1,289 @@
'use strict';
require('../common');
const {
BlockList,
SocketAddress,
} = require('net');
const assert = require('assert');
const util = require('util');
{
const blockList = new BlockList();
[1, [], {}, null, 1n, undefined, null].forEach((i) => {
assert.throws(() => blockList.addAddress(i), {
code: 'ERR_INVALID_ARG_TYPE'
});
});
[1, [], {}, null, 1n, null].forEach((i) => {
assert.throws(() => blockList.addAddress('1.1.1.1', i), {
code: 'ERR_INVALID_ARG_TYPE'
});
});
assert.throws(() => blockList.addAddress('1.1.1.1', 'foo'), {
code: 'ERR_INVALID_ARG_VALUE'
});
[1, [], {}, null, 1n, undefined, null].forEach((i) => {
assert.throws(() => blockList.addRange(i), {
code: 'ERR_INVALID_ARG_TYPE'
});
assert.throws(() => blockList.addRange('1.1.1.1', i), {
code: 'ERR_INVALID_ARG_TYPE'
});
});
[1, [], {}, null, 1n, null].forEach((i) => {
assert.throws(() => blockList.addRange('1.1.1.1', '1.1.1.2', i), {
code: 'ERR_INVALID_ARG_TYPE'
});
});
assert.throws(() => blockList.addRange('1.1.1.1', '1.1.1.2', 'foo'), {
code: 'ERR_INVALID_ARG_VALUE'
});
}
{
const blockList = new BlockList();
blockList.addAddress('1.1.1.1');
blockList.addAddress('8592:757c:efae:4e45:fb5d:d62a:0d00:8e17', 'ipv6');
blockList.addAddress('::ffff:1.1.1.2', 'ipv6');
assert(blockList.check('1.1.1.1'));
assert(!blockList.check('1.1.1.1', 'ipv6'));
assert(!blockList.check('8592:757c:efae:4e45:fb5d:d62a:0d00:8e17'));
assert(blockList.check('8592:757c:efae:4e45:fb5d:d62a:0d00:8e17', 'ipv6'));
assert(blockList.check('::ffff:1.1.1.1', 'ipv6'));
assert(blockList.check('::ffff:1.1.1.1', 'IPV6'));
assert(blockList.check('1.1.1.2'));
assert(!blockList.check('1.2.3.4'));
assert(!blockList.check('::1', 'ipv6'));
}
{
const blockList = new BlockList();
const sa1 = new SocketAddress({ address: '1.1.1.1' });
const sa2 = new SocketAddress({
address: '8592:757c:efae:4e45:fb5d:d62a:0d00:8e17',
family: 'ipv6'
});
const sa3 = new SocketAddress({ address: '1.1.1.2' });
blockList.addAddress(sa1);
blockList.addAddress(sa2);
blockList.addAddress('::ffff:1.1.1.2', 'ipv6');
assert(blockList.check('1.1.1.1'));
assert(blockList.check(sa1));
assert(!blockList.check('1.1.1.1', 'ipv6'));
assert(!blockList.check('8592:757c:efae:4e45:fb5d:d62a:0d00:8e17'));
assert(blockList.check('8592:757c:efae:4e45:fb5d:d62a:0d00:8e17', 'ipv6'));
assert(blockList.check(sa2));
assert(blockList.check('::ffff:1.1.1.1', 'ipv6'));
assert(blockList.check('::ffff:1.1.1.1', 'IPV6'));
assert(blockList.check('1.1.1.2'));
assert(blockList.check(sa3));
assert(!blockList.check('1.2.3.4'));
assert(!blockList.check('::1', 'ipv6'));
}
{
const blockList = new BlockList();
blockList.addRange('1.1.1.1', '1.1.1.10');
blockList.addRange('::1', '::f', 'ipv6');
assert(!blockList.check('1.1.1.0'));
for (let n = 1; n <= 10; n++)
assert(blockList.check(`1.1.1.${n}`));
assert(!blockList.check('1.1.1.11'));
assert(!blockList.check('::0', 'ipv6'));
for (let n = 0x1; n <= 0xf; n++) {
assert(blockList.check(`::${n.toString(16)}`, 'ipv6'),
`::${n.toString(16)} check failed`);
}
assert(!blockList.check('::10', 'ipv6'));
}
{
const blockList = new BlockList();
const sa1 = new SocketAddress({ address: '1.1.1.1' });
const sa2 = new SocketAddress({ address: '1.1.1.10' });
const sa3 = new SocketAddress({ address: '::1', family: 'ipv6' });
const sa4 = new SocketAddress({ address: '::f', family: 'ipv6' });
blockList.addRange(sa1, sa2);
blockList.addRange(sa3, sa4);
assert(!blockList.check('1.1.1.0'));
for (let n = 1; n <= 10; n++)
assert(blockList.check(`1.1.1.${n}`));
assert(!blockList.check('1.1.1.11'));
assert(!blockList.check('::0', 'ipv6'));
for (let n = 0x1; n <= 0xf; n++) {
assert(blockList.check(`::${n.toString(16)}`, 'ipv6'),
`::${n.toString(16)} check failed`);
}
assert(!blockList.check('::10', 'ipv6'));
}
{
const blockList = new BlockList();
blockList.addSubnet('1.1.1.0', 16);
blockList.addSubnet('8592:757c:efae:4e45::', 64, 'ipv6');
assert(blockList.check('1.1.0.1'));
assert(blockList.check('1.1.1.1'));
assert(!blockList.check('1.2.0.1'));
assert(blockList.check('::ffff:1.1.0.1', 'ipv6'));
assert(blockList.check('8592:757c:efae:4e45:f::', 'ipv6'));
assert(blockList.check('8592:757c:efae:4e45::f', 'ipv6'));
assert(!blockList.check('8592:757c:efae:4f45::f', 'ipv6'));
}
{
const blockList = new BlockList();
const sa1 = new SocketAddress({ address: '1.1.1.0' });
const sa2 = new SocketAddress({ address: '1.1.1.1' });
blockList.addSubnet(sa1, 16);
blockList.addSubnet('8592:757c:efae:4e45::', 64, 'ipv6');
assert(blockList.check('1.1.0.1'));
assert(blockList.check(sa2));
assert(!blockList.check('1.2.0.1'));
assert(blockList.check('::ffff:1.1.0.1', 'ipv6'));
assert(blockList.check('8592:757c:efae:4e45:f::', 'ipv6'));
assert(blockList.check('8592:757c:efae:4e45::f', 'ipv6'));
assert(!blockList.check('8592:757c:efae:4f45::f', 'ipv6'));
}
{
const blockList = new BlockList();
blockList.addAddress('1.1.1.1');
blockList.addRange('10.0.0.1', '10.0.0.10');
blockList.addSubnet('8592:757c:efae:4e45::', 64, 'IpV6'); // Case insensitive
const rulesCheck = [
'Subnet: IPv6 8592:757c:efae:4e45::/64',
'Range: IPv4 10.0.0.1-10.0.0.10',
'Address: IPv4 1.1.1.1',
];
assert.deepStrictEqual(blockList.rules, rulesCheck);
assert(blockList.check('1.1.1.1'));
assert(blockList.check('10.0.0.5'));
assert(blockList.check('::ffff:10.0.0.5', 'ipv6'));
assert(blockList.check('8592:757c:efae:4e45::f', 'ipv6'));
assert(!blockList.check('123.123.123.123'));
assert(!blockList.check('8592:757c:efaf:4e45:fb5d:d62a:0d00:8e17', 'ipv6'));
assert(!blockList.check('::ffff:123.123.123.123', 'ipv6'));
}
{
// This test validates boundaries of non-aligned CIDR bit prefixes
const blockList = new BlockList();
blockList.addSubnet('10.0.0.0', 27);
blockList.addSubnet('8592:757c:efaf::', 51, 'ipv6');
for (let n = 0; n <= 31; n++)
assert(blockList.check(`10.0.0.${n}`));
assert(!blockList.check('10.0.0.32'));
assert(blockList.check('8592:757c:efaf:0:0:0:0:0', 'ipv6'));
assert(blockList.check('8592:757c:efaf:1fff:ffff:ffff:ffff:ffff', 'ipv6'));
assert(!blockList.check('8592:757c:efaf:2fff:ffff:ffff:ffff:ffff', 'ipv6'));
}
{
// Regression test for https://github.com/nodejs/node/issues/39074
const blockList = new BlockList();
blockList.addRange('10.0.0.2', '10.0.0.10');
// IPv4 checks against IPv4 range.
assert(blockList.check('10.0.0.2'));
assert(blockList.check('10.0.0.10'));
assert(!blockList.check('192.168.0.3'));
assert(!blockList.check('2.2.2.2'));
assert(!blockList.check('255.255.255.255'));
// IPv6 checks against IPv4 range.
assert(blockList.check('::ffff:0a00:0002', 'ipv6'));
assert(blockList.check('::ffff:0a00:000a', 'ipv6'));
assert(!blockList.check('::ffff:c0a8:0003', 'ipv6'));
assert(!blockList.check('::ffff:0202:0202', 'ipv6'));
assert(!blockList.check('::ffff:ffff:ffff', 'ipv6'));
}
{
const blockList = new BlockList();
assert.throws(() => blockList.addRange('1.1.1.2', '1.1.1.1'), /ERR_INVALID_ARG_VALUE/);
}
{
const blockList = new BlockList();
assert.throws(() => blockList.addSubnet(1), /ERR_INVALID_ARG_TYPE/);
assert.throws(() => blockList.addSubnet('1.1.1.1', ''),
/ERR_INVALID_ARG_TYPE/);
assert.throws(() => blockList.addSubnet('1.1.1.1', NaN), /ERR_OUT_OF_RANGE/);
assert.throws(() => blockList.addSubnet('', 1, 1), /ERR_INVALID_ARG_TYPE/);
assert.throws(() => blockList.addSubnet('', 1, ''), /ERR_INVALID_ARG_VALUE/);
assert.throws(() => blockList.addSubnet('1.1.1.1', -1, 'ipv4'),
/ERR_OUT_OF_RANGE/);
assert.throws(() => blockList.addSubnet('1.1.1.1', 33, 'ipv4'),
/ERR_OUT_OF_RANGE/);
assert.throws(() => blockList.addSubnet('::', -1, 'ipv6'),
/ERR_OUT_OF_RANGE/);
assert.throws(() => blockList.addSubnet('::', 129, 'ipv6'),
/ERR_OUT_OF_RANGE/);
}
{
const blockList = new BlockList();
assert.throws(() => blockList.check(1), /ERR_INVALID_ARG_TYPE/);
assert.throws(() => blockList.check('', 1), /ERR_INVALID_ARG_TYPE/);
}
{
const blockList = new BlockList();
const ret = util.inspect(blockList, { depth: -1 });
assert.strictEqual(ret, '[BlockList]');
}
{
const blockList = new BlockList();
const ret = util.inspect(blockList, { depth: null });
assert(ret.includes('rules: []'));
}
{
// Test for https://github.com/nodejs/node/issues/43360
const blocklist = new BlockList();
blocklist.addSubnet('1.1.1.1', 32, 'ipv4');
assert(blocklist.check('1.1.1.1'));
assert(!blocklist.check('1.1.1.2'));
assert(!blocklist.check('2.3.4.5'));
}
{
assert(BlockList.isBlockList(new BlockList()));
assert(!BlockList.isBlockList({}));
}

View File

@ -0,0 +1,249 @@
'use strict';
// This list must be computed before we require any builtins to
// to eliminate the noise.
const list = process.moduleLoadList.slice();
const common = require('../common');
const assert = require('assert');
const { inspect } = require('util');
const preExecIndex =
list.findIndex((i) => i.includes('pre_execution'));
const actual = {
beforePreExec: new Set(list.slice(0, preExecIndex)),
atRunTime: new Set(list.slice(preExecIndex)),
};
// Currently, we don't add additional builtins to worker snapshots.
// So for worker snapshots we'll just concatenate the two. Once we
// add more builtins to worker snapshots, we should also distinguish
// the two stages for them.
const expected = {};
expected.beforePreExec = new Set([
'Internal Binding builtins',
'Internal Binding encoding_binding',
'Internal Binding modules',
'Internal Binding errors',
'Internal Binding util',
'NativeModule internal/errors',
'Internal Binding config',
'Internal Binding timers',
'Internal Binding async_context_frame',
'NativeModule internal/async_context_frame',
'Internal Binding async_wrap',
'Internal Binding task_queue',
'Internal Binding symbols',
'NativeModule internal/async_hooks',
'Internal Binding constants',
'Internal Binding types',
'NativeModule internal/util',
'NativeModule internal/util/types',
'NativeModule internal/validators',
'NativeModule internal/linkedlist',
'NativeModule internal/priority_queue',
'NativeModule internal/assert',
'NativeModule internal/util/inspect',
'NativeModule internal/util/debuglog',
'NativeModule internal/streams/utils',
'NativeModule internal/timers',
'NativeModule events',
'Internal Binding buffer',
'Internal Binding string_decoder',
'NativeModule internal/buffer',
'NativeModule buffer',
'Internal Binding messaging',
'NativeModule internal/worker/js_transferable',
'Internal Binding process_methods',
'NativeModule internal/process/per_thread',
'Internal Binding credentials',
'NativeModule internal/process/promises',
'NativeModule internal/fixed_queue',
'NativeModule async_hooks',
'NativeModule internal/process/task_queues',
'NativeModule timers',
'Internal Binding trace_events',
'NativeModule internal/constants',
'NativeModule path',
'NativeModule internal/process/execution',
'NativeModule internal/process/permission',
'NativeModule internal/process/warning',
'NativeModule internal/console/constructor',
'NativeModule internal/console/global',
'NativeModule internal/querystring',
'NativeModule querystring',
'Internal Binding url',
'Internal Binding url_pattern',
'Internal Binding blob',
'NativeModule internal/url',
'NativeModule util',
'NativeModule internal/webidl',
'Internal Binding performance',
'Internal Binding permission',
'NativeModule internal/perf/utils',
'NativeModule internal/event_target',
'Internal Binding mksnapshot',
'NativeModule internal/v8/startup_snapshot',
'NativeModule internal/process/signal',
'Internal Binding fs',
'NativeModule internal/encoding',
'NativeModule internal/blob',
'NativeModule internal/fs/utils',
'NativeModule fs',
'Internal Binding options',
'NativeModule internal/options',
'NativeModule internal/source_map/source_map_cache',
'Internal Binding contextify',
'NativeModule internal/vm',
'NativeModule internal/modules/helpers',
'NativeModule internal/modules/customization_hooks',
'NativeModule internal/modules/package_json_reader',
'Internal Binding module_wrap',
'NativeModule internal/modules/cjs/loader',
'NativeModule diagnostics_channel',
'Internal Binding wasm_web_api',
'NativeModule internal/events/abort_listener',
'NativeModule internal/modules/typescript',
]);
expected.atRunTime = new Set([
'Internal Binding worker',
'NativeModule internal/modules/run_main',
'NativeModule internal/net',
'NativeModule internal/dns/utils',
'NativeModule internal/process/pre_execution',
'NativeModule internal/modules/esm/utils',
]);
const { isMainThread } = require('worker_threads');
if (isMainThread) {
[
'NativeModule url',
].forEach(expected.beforePreExec.add.bind(expected.beforePreExec));
} else { // Worker.
[
'NativeModule diagnostics_channel',
'NativeModule internal/abort_controller',
'NativeModule internal/error_serdes',
'NativeModule internal/perf/event_loop_utilization',
'NativeModule internal/process/worker_thread_only',
'NativeModule internal/streams/add-abort-signal',
'NativeModule internal/streams/compose',
'NativeModule internal/streams/destroy',
'NativeModule internal/streams/duplex',
'NativeModule internal/streams/duplexpair',
'NativeModule internal/streams/end-of-stream',
'NativeModule internal/streams/from',
'NativeModule internal/streams/legacy',
'NativeModule internal/streams/operators',
'NativeModule internal/streams/passthrough',
'NativeModule internal/streams/pipeline',
'NativeModule internal/streams/readable',
'NativeModule internal/streams/state',
'NativeModule internal/streams/transform',
'NativeModule internal/streams/utils',
'NativeModule internal/streams/writable',
'NativeModule internal/worker',
'NativeModule internal/worker/io',
'NativeModule internal/worker/messaging',
'NativeModule stream',
'NativeModule stream/promises',
'NativeModule string_decoder',
'NativeModule worker_threads',
].forEach(expected.atRunTime.add.bind(expected.atRunTime));
// For now we'll concatenate the two stages for workers. We prefer
// atRunTime here because that's what currently happens for these.
}
if (common.isWindows) {
// On Windows fs needs SideEffectFreeRegExpPrototypeExec which uses vm.
expected.atRunTime.add('NativeModule vm');
}
if (common.hasIntl) {
expected.beforePreExec.add('Internal Binding icu');
}
if (process.features.inspector) {
expected.beforePreExec.add('Internal Binding inspector');
expected.beforePreExec.add('NativeModule internal/util/inspector');
expected.atRunTime.add('NativeModule internal/inspector_async_hook');
}
// This is loaded if the test is run with NODE_V8_COVERAGE.
if (process.env.NODE_V8_COVERAGE) {
expected.atRunTime.add('Internal Binding profiler');
}
// Accumulate all the errors and print them at the end instead of throwing
// immediately which makes it harder to update the test.
const errorLogs = [];
function err(message) {
if (typeof message === 'string') {
errorLogs.push(message);
} else {
// Show the items in individual lines for easier copy-pasting.
errorLogs.push(inspect(message, { compact: false }));
}
}
if (isMainThread) {
const missing = expected.beforePreExec.difference(actual.beforePreExec);
const extra = actual.beforePreExec.difference(expected.beforePreExec);
if (missing.size !== 0) {
err('These builtins are now no longer loaded before pre-execution.');
err('If this is intentional, remove them from `expected.beforePreExec`.');
err('\n--- These could be removed from expected.beforePreExec ---');
err([...missing].sort());
err('');
}
if (extra.size !== 0) {
err('These builtins are now unexpectedly loaded before pre-execution.');
err('If this is intentional, add them to `expected.beforePreExec`.');
err('\n# Note: loading more builtins before pre-execution can lead to ' +
'startup performance regression or invalid snapshots.');
err('- Consider lazy loading builtins that are not used universally.');
err('- Make sure that the builtins do not access environment dependent ' +
'states e.g. command line arguments or environment variables ' +
'during loading.');
err('- When in doubt, ask @nodejs/startup.');
err('\n--- These could be added to expected.beforePreExec ---');
err([...extra].sort());
err('');
}
}
if (!isMainThread) {
// For workers, just merge beforePreExec into atRunTime for now.
// When we start adding modules to the worker snapshot, this branch
// can be removed and we can just remove the isMainThread
// conditions.
expected.beforePreExec.forEach(expected.atRunTime.add.bind(expected.atRunTime));
actual.beforePreExec.forEach(actual.atRunTime.add.bind(actual.atRunTime));
}
{
const missing = expected.atRunTime.difference(actual.atRunTime);
const extra = actual.atRunTime.difference(expected.atRunTime);
if (missing.size !== 0) {
err('These builtins are now no longer loaded at run time.');
err('If this is intentional, remove them from `expected.atRunTime`.');
err('\n--- These could be removed from expected.atRunTime ---');
err([...missing].sort());
err('');
}
if (extra.size !== 0) {
err('These builtins are now unexpectedly loaded at run time.');
err('If this is intentional, add them to `expected.atRunTime`.');
err('\n# Note: loading more builtins at run time can lead to ' +
'startup performance regression.');
err('- Consider lazy loading builtins that are not used universally.');
err('\n--- These could be added to expected.atRunTime ---');
err([...extra].sort());
err('');
}
}
assert.strictEqual(errorLogs.length, 0, errorLogs.join('\n'));

View File

@ -0,0 +1,40 @@
'use strict';
require('../common');
const { BroadcastChannel } = require('worker_threads');
const { inspect } = require('util');
const assert = require('assert');
// This test checks BroadcastChannel custom inspect outputs
{
const bc = new BroadcastChannel('name');
assert.throws(() => bc[inspect.custom].call(), {
code: 'ERR_INVALID_THIS',
});
bc.close();
}
{
const bc = new BroadcastChannel('name');
assert.strictEqual(inspect(bc, { depth: -1 }), 'BroadcastChannel');
bc.close();
}
{
const bc = new BroadcastChannel('name');
assert.strictEqual(
inspect(bc),
"BroadcastChannel { name: 'name', active: true }"
);
bc.close();
}
{
const bc = new BroadcastChannel('name');
assert.strictEqual(
inspect(bc, { depth: null }),
"BroadcastChannel { name: 'name', active: true }"
);
bc.close();
}

View File

@ -0,0 +1,39 @@
'use strict';
require('../common');
const { strictEqual, throws } = require('assert');
const buffer = require('buffer');
// Exported on the global object
strictEqual(globalThis.atob, buffer.atob);
strictEqual(globalThis.btoa, buffer.btoa);
// Throws type error on no argument passed
throws(() => buffer.atob(), /TypeError/);
throws(() => buffer.btoa(), /TypeError/);
strictEqual(atob(' '), '');
strictEqual(atob(' Y\fW\tJ\njZ A=\r= '), 'abcd');
strictEqual(atob(null), '\x9Eée');
strictEqual(atob(NaN), '5£');
strictEqual(atob(Infinity), '"wâ\x9E+r');
strictEqual(atob(true), '¶»\x9E');
strictEqual(atob(1234), '×mø');
strictEqual(atob([]), '');
strictEqual(atob({ toString: () => '' }), '');
strictEqual(atob({ [Symbol.toPrimitive]: () => '' }), '');
throws(() => atob(Symbol()), /TypeError/);
[
undefined, false, () => {}, {}, [1],
0, 1, 0n, 1n, -Infinity,
'a', 'a\n\n\n', '\ra\r\r', ' a ', '\t\t\ta', 'a\f\f\f', '\ta\r \n\f',
].forEach((value) =>
// See #2 - https://html.spec.whatwg.org/multipage/webappapis.html#dom-atob
throws(() => atob(value), {
constructor: DOMException,
name: 'InvalidCharacterError',
code: 5,
}));

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,152 @@
'use strict';
require('../common');
const assert = require('assert');
const LENGTH = 16;
const ab = new ArrayBuffer(LENGTH);
const dv = new DataView(ab);
const ui = new Uint8Array(ab);
const buf = Buffer.from(ab);
assert.ok(buf instanceof Buffer);
assert.strictEqual(buf.parent, buf.buffer);
assert.strictEqual(buf.buffer, ab);
assert.strictEqual(buf.length, ab.byteLength);
buf.fill(0xC);
for (let i = 0; i < LENGTH; i++) {
assert.strictEqual(ui[i], 0xC);
ui[i] = 0xF;
assert.strictEqual(buf[i], 0xF);
}
buf.writeUInt32LE(0xF00, 0);
buf.writeUInt32BE(0xB47, 4);
buf.writeDoubleLE(3.1415, 8);
assert.strictEqual(dv.getUint32(0, true), 0xF00);
assert.strictEqual(dv.getUint32(4), 0xB47);
assert.strictEqual(dv.getFloat64(8, true), 3.1415);
// Now test protecting users from doing stupid things
assert.throws(function() {
function AB() { }
Object.setPrototypeOf(AB, ArrayBuffer);
Object.setPrototypeOf(AB.prototype, ArrayBuffer.prototype);
Buffer.from(new AB());
}, {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The first argument must be of type string or an instance of ' +
'Buffer, ArrayBuffer, or Array or an Array-like Object. Received ' +
'an instance of AB'
});
// Test the byteOffset and length arguments
{
const ab = new Uint8Array(5);
ab[0] = 1;
ab[1] = 2;
ab[2] = 3;
ab[3] = 4;
ab[4] = 5;
const buf = Buffer.from(ab.buffer, 1, 3);
assert.strictEqual(buf.length, 3);
assert.strictEqual(buf[0], 2);
assert.strictEqual(buf[1], 3);
assert.strictEqual(buf[2], 4);
buf[0] = 9;
assert.strictEqual(ab[1], 9);
assert.throws(() => Buffer.from(ab.buffer, 6), {
code: 'ERR_BUFFER_OUT_OF_BOUNDS',
name: 'RangeError',
message: '"offset" is outside of buffer bounds'
});
assert.throws(() => Buffer.from(ab.buffer, 3, 6), {
code: 'ERR_BUFFER_OUT_OF_BOUNDS',
name: 'RangeError',
message: '"length" is outside of buffer bounds'
});
}
// Test the deprecated Buffer() version also
{
const ab = new Uint8Array(5);
ab[0] = 1;
ab[1] = 2;
ab[2] = 3;
ab[3] = 4;
ab[4] = 5;
const buf = Buffer(ab.buffer, 1, 3);
assert.strictEqual(buf.length, 3);
assert.strictEqual(buf[0], 2);
assert.strictEqual(buf[1], 3);
assert.strictEqual(buf[2], 4);
buf[0] = 9;
assert.strictEqual(ab[1], 9);
assert.throws(() => Buffer(ab.buffer, 6), {
code: 'ERR_BUFFER_OUT_OF_BOUNDS',
name: 'RangeError',
message: '"offset" is outside of buffer bounds'
});
assert.throws(() => Buffer(ab.buffer, 3, 6), {
code: 'ERR_BUFFER_OUT_OF_BOUNDS',
name: 'RangeError',
message: '"length" is outside of buffer bounds'
});
}
{
// If byteOffset is not numeric, it defaults to 0.
const ab = new ArrayBuffer(10);
const expected = Buffer.from(ab, 0);
assert.deepStrictEqual(Buffer.from(ab, 'fhqwhgads'), expected);
assert.deepStrictEqual(Buffer.from(ab, NaN), expected);
assert.deepStrictEqual(Buffer.from(ab, {}), expected);
assert.deepStrictEqual(Buffer.from(ab, []), expected);
// If byteOffset can be converted to a number, it will be.
assert.deepStrictEqual(Buffer.from(ab, [1]), Buffer.from(ab, 1));
// If byteOffset is Infinity, throw.
assert.throws(() => {
Buffer.from(ab, Infinity);
}, {
code: 'ERR_BUFFER_OUT_OF_BOUNDS',
name: 'RangeError',
message: '"offset" is outside of buffer bounds'
});
}
{
// If length is not numeric, it defaults to 0.
const ab = new ArrayBuffer(10);
const expected = Buffer.from(ab, 0, 0);
assert.deepStrictEqual(Buffer.from(ab, 0, 'fhqwhgads'), expected);
assert.deepStrictEqual(Buffer.from(ab, 0, NaN), expected);
assert.deepStrictEqual(Buffer.from(ab, 0, {}), expected);
assert.deepStrictEqual(Buffer.from(ab, 0, []), expected);
// If length can be converted to a number, it will be.
assert.deepStrictEqual(Buffer.from(ab, 0, [1]), Buffer.from(ab, 0, 1));
// If length is Infinity, throw.
assert.throws(() => {
Buffer.from(ab, 0, Infinity);
}, {
code: 'ERR_BUFFER_OUT_OF_BOUNDS',
name: 'RangeError',
message: '"length" is outside of buffer bounds'
});
}
// Test an array like entry with the length set to NaN.
assert.deepStrictEqual(Buffer.from({ length: NaN }), Buffer.alloc(0));

View File

@ -0,0 +1,46 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
require('../common');
const assert = require('assert');
// ASCII conversion in node.js simply masks off the high bits,
// it doesn't do transliteration.
assert.strictEqual(Buffer.from('hérité').toString('ascii'), 'hC)ritC)');
// 71 characters, 78 bytes. The character is a triple-byte sequence.
const input = 'Cest, graphiquement, la réunion dun accent aigu ' +
'et dun accent grave.';
const expected = 'Cb\u0000\u0019est, graphiquement, la rC)union ' +
'db\u0000\u0019un accent aigu et db\u0000\u0019un ' +
'accent grave.';
const buf = Buffer.from(input);
for (let i = 0; i < expected.length; ++i) {
assert.strictEqual(buf.slice(i).toString('ascii'), expected.slice(i));
// Skip remainder of multi-byte sequence.
if (input.charCodeAt(i) > 65535) ++i;
if (input.charCodeAt(i) > 127) ++i;
}

View File

@ -0,0 +1,37 @@
// Flags: --expose-internals
'use strict';
require('../common');
const assert = require('assert');
const { internalBinding } = require('internal/test/binding');
const { arrayBufferViewHasBuffer } = internalBinding('util');
const tests = [
{ length: 0, expectOnHeap: true },
{ length: 48, expectOnHeap: true },
{ length: 96, expectOnHeap: false },
{ length: 1024, expectOnHeap: false },
];
for (const { length, expectOnHeap } of tests) {
const arrays = [
new Uint8Array(length),
new Uint16Array(length / 2),
new Uint32Array(length / 4),
new Float32Array(length / 4),
new Float64Array(length / 8),
Buffer.alloc(length),
Buffer.allocUnsafeSlow(length),
// Buffer.allocUnsafe() is missing because it may use pooled allocations.
];
for (const array of arrays) {
const isOnHeap = !arrayBufferViewHasBuffer(array);
assert.strictEqual(isOnHeap, expectOnHeap,
`mismatch: ${isOnHeap} vs ${expectOnHeap} ` +
`for ${array.constructor.name}, length = ${length}`);
// Consistency check: Accessing .buffer should create it.
array.buffer; // eslint-disable-line no-unused-expressions
assert(arrayBufferViewHasBuffer(array));
}
}

View File

@ -0,0 +1,48 @@
'use strict';
require('../common');
const assert = require('assert');
// Test hex strings and bad hex strings
{
const buf = Buffer.alloc(4);
assert.strictEqual(buf.length, 4);
assert.deepStrictEqual(buf, Buffer.from([0, 0, 0, 0]));
assert.strictEqual(buf.write('abcdxx', 0, 'hex'), 2);
assert.deepStrictEqual(buf, Buffer.from([0xab, 0xcd, 0x00, 0x00]));
assert.strictEqual(buf.toString('hex'), 'abcd0000');
assert.strictEqual(buf.write('abcdef01', 0, 'hex'), 4);
assert.deepStrictEqual(buf, Buffer.from([0xab, 0xcd, 0xef, 0x01]));
assert.strictEqual(buf.toString('hex'), 'abcdef01');
const copy = Buffer.from(buf.toString('hex'), 'hex');
assert.strictEqual(buf.toString('hex'), copy.toString('hex'));
}
{
const buf = Buffer.alloc(5);
assert.strictEqual(buf.write('abcdxx', 1, 'hex'), 2);
assert.strictEqual(buf.toString('hex'), '00abcd0000');
}
{
const buf = Buffer.alloc(4);
assert.deepStrictEqual(buf, Buffer.from([0, 0, 0, 0]));
assert.strictEqual(buf.write('xxabcd', 0, 'hex'), 0);
assert.deepStrictEqual(buf, Buffer.from([0, 0, 0, 0]));
assert.strictEqual(buf.write('xxab', 1, 'hex'), 0);
assert.deepStrictEqual(buf, Buffer.from([0, 0, 0, 0]));
assert.strictEqual(buf.write('cdxxab', 0, 'hex'), 1);
assert.deepStrictEqual(buf, Buffer.from([0xcd, 0, 0, 0]));
}
{
const buf = Buffer.alloc(256);
for (let i = 0; i < 256; i++)
buf[i] = i;
const hex = buf.toString('hex');
assert.deepStrictEqual(Buffer.from(hex, 'hex'), buf);
const badHex = `${hex.slice(0, 256)}xx${hex.slice(256, 510)}`;
assert.deepStrictEqual(Buffer.from(badHex, 'hex'), buf.slice(0, 128));
}

View File

@ -0,0 +1,60 @@
'use strict';
require('../common');
const assert = require('assert');
const buf = Buffer.allocUnsafe(9);
['LE', 'BE'].forEach(function(endianness) {
// Should allow simple BigInts to be written and read
let val = 123456789n;
buf[`writeBigInt64${endianness}`](val, 0);
let rtn = buf[`readBigInt64${endianness}`](0);
assert.strictEqual(rtn, val);
// Should allow INT64_MAX to be written and read
val = 0x7fffffffffffffffn;
buf[`writeBigInt64${endianness}`](val, 0);
rtn = buf[`readBigInt64${endianness}`](0);
assert.strictEqual(rtn, val);
// Should read and write a negative signed 64-bit integer
val = -123456789n;
buf[`writeBigInt64${endianness}`](val, 0);
assert.strictEqual(buf[`readBigInt64${endianness}`](0), val);
// Should read and write an unsigned 64-bit integer
val = 123456789n;
buf[`writeBigUInt64${endianness}`](val, 0);
assert.strictEqual(buf[`readBigUInt64${endianness}`](0), val);
assert.strictEqual(buf[`writeBigUInt64${endianness}`](val, 0), 8);
assert.strictEqual(buf[`writeBigInt64${endianness}`](val, 0), 8);
assert.strictEqual(buf[`writeBigUInt64${endianness}`](val, 1), 9);
assert.strictEqual(buf[`writeBigInt64${endianness}`](val, 1), 9);
// Should throw a RangeError upon INT64_MAX+1 being written
assert.throws(function() {
const val = 0x8000000000000000n;
buf[`writeBigInt64${endianness}`](val, 0);
}, RangeError);
// Should throw a RangeError upon UINT64_MAX+1 being written
assert.throws(function() {
const val = 0x10000000000000000n;
buf[`writeBigUInt64${endianness}`](val, 0);
}, {
code: 'ERR_OUT_OF_RANGE',
message: 'The value of "value" is out of range. It must be ' +
'>= 0n and < 2n ** 64n. Received 18_446_744_073_709_551_616n'
});
// Should throw a TypeError upon invalid input
assert.throws(function() {
buf[`writeBigInt64${endianness}`]('bad', 0);
}, TypeError);
// Should throw a TypeError upon invalid input
assert.throws(function() {
buf[`writeBigUInt64${endianness}`]('bad', 0);
}, TypeError);
});

View File

@ -0,0 +1,132 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const SlowBuffer = require('buffer').SlowBuffer;
const vm = require('vm');
[
[32, 'latin1'],
[NaN, 'utf8'],
[{}, 'latin1'],
[],
].forEach((args) => {
assert.throws(
() => Buffer.byteLength(...args),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "string" argument must be of type string or an instance ' +
'of Buffer or ArrayBuffer.' +
common.invalidArgTypeHelper(args[0])
}
);
});
assert(ArrayBuffer.isView(new Buffer(10)));
assert(ArrayBuffer.isView(new SlowBuffer(10)));
assert(ArrayBuffer.isView(Buffer.alloc(10)));
assert(ArrayBuffer.isView(Buffer.allocUnsafe(10)));
assert(ArrayBuffer.isView(Buffer.allocUnsafeSlow(10)));
assert(ArrayBuffer.isView(Buffer.from('')));
// buffer
const incomplete = Buffer.from([0xe4, 0xb8, 0xad, 0xe6, 0x96]);
assert.strictEqual(Buffer.byteLength(incomplete), 5);
const ascii = Buffer.from('abc');
assert.strictEqual(Buffer.byteLength(ascii), 3);
// ArrayBuffer
const buffer = new ArrayBuffer(8);
assert.strictEqual(Buffer.byteLength(buffer), 8);
// TypedArray
const int8 = new Int8Array(8);
assert.strictEqual(Buffer.byteLength(int8), 8);
const uint8 = new Uint8Array(8);
assert.strictEqual(Buffer.byteLength(uint8), 8);
const uintc8 = new Uint8ClampedArray(2);
assert.strictEqual(Buffer.byteLength(uintc8), 2);
const int16 = new Int16Array(8);
assert.strictEqual(Buffer.byteLength(int16), 16);
const uint16 = new Uint16Array(8);
assert.strictEqual(Buffer.byteLength(uint16), 16);
const int32 = new Int32Array(8);
assert.strictEqual(Buffer.byteLength(int32), 32);
const uint32 = new Uint32Array(8);
assert.strictEqual(Buffer.byteLength(uint32), 32);
const float32 = new Float32Array(8);
assert.strictEqual(Buffer.byteLength(float32), 32);
const float64 = new Float64Array(8);
assert.strictEqual(Buffer.byteLength(float64), 64);
// DataView
const dv = new DataView(new ArrayBuffer(2));
assert.strictEqual(Buffer.byteLength(dv), 2);
// Special case: zero length string
assert.strictEqual(Buffer.byteLength('', 'ascii'), 0);
assert.strictEqual(Buffer.byteLength('', 'HeX'), 0);
// utf8
assert.strictEqual(Buffer.byteLength('∑éllö wørl∂!', 'utf-8'), 19);
assert.strictEqual(Buffer.byteLength('κλμνξο', 'utf8'), 12);
assert.strictEqual(Buffer.byteLength('挵挶挷挸挹', 'utf-8'), 15);
assert.strictEqual(Buffer.byteLength('𠝹𠱓𠱸', 'UTF8'), 12);
// Without an encoding, utf8 should be assumed
assert.strictEqual(Buffer.byteLength('hey there'), 9);
assert.strictEqual(Buffer.byteLength('𠱸挶νξ#xx :)'), 17);
assert.strictEqual(Buffer.byteLength('hello world', ''), 11);
// It should also be assumed with unrecognized encoding
assert.strictEqual(Buffer.byteLength('hello world', 'abc'), 11);
assert.strictEqual(Buffer.byteLength('ßœ∑≈', 'unkn0wn enc0ding'), 10);
// base64
assert.strictEqual(Buffer.byteLength('aGVsbG8gd29ybGQ=', 'base64'), 11);
assert.strictEqual(Buffer.byteLength('aGVsbG8gd29ybGQ=', 'BASE64'), 11);
assert.strictEqual(Buffer.byteLength('bm9kZS5qcyByb2NrcyE=', 'base64'), 14);
assert.strictEqual(Buffer.byteLength('aGkk', 'base64'), 3);
assert.strictEqual(
Buffer.byteLength('bHNrZGZsa3NqZmtsc2xrZmFqc2RsZmtqcw==', 'base64'), 25
);
// base64url
assert.strictEqual(Buffer.byteLength('aGVsbG8gd29ybGQ', 'base64url'), 11);
assert.strictEqual(Buffer.byteLength('aGVsbG8gd29ybGQ', 'BASE64URL'), 11);
assert.strictEqual(Buffer.byteLength('bm9kZS5qcyByb2NrcyE', 'base64url'), 14);
assert.strictEqual(Buffer.byteLength('aGkk', 'base64url'), 3);
assert.strictEqual(
Buffer.byteLength('bHNrZGZsa3NqZmtsc2xrZmFqc2RsZmtqcw', 'base64url'), 25
);
// special padding
assert.strictEqual(Buffer.byteLength('aaa=', 'base64'), 2);
assert.strictEqual(Buffer.byteLength('aaaa==', 'base64'), 3);
assert.strictEqual(Buffer.byteLength('aaa=', 'base64url'), 2);
assert.strictEqual(Buffer.byteLength('aaaa==', 'base64url'), 3);
assert.strictEqual(Buffer.byteLength('Il était tué'), 14);
assert.strictEqual(Buffer.byteLength('Il était tué', 'utf8'), 14);
['ascii', 'latin1', 'binary']
.reduce((es, e) => es.concat(e, e.toUpperCase()), [])
.forEach((encoding) => {
assert.strictEqual(Buffer.byteLength('Il était tué', encoding), 12);
});
['ucs2', 'ucs-2', 'utf16le', 'utf-16le']
.reduce((es, e) => es.concat(e, e.toUpperCase()), [])
.forEach((encoding) => {
assert.strictEqual(Buffer.byteLength('Il était tué', encoding), 24);
});
// Test that ArrayBuffer from a different context is detected correctly
const arrayBuf = vm.runInNewContext('new ArrayBuffer()');
assert.strictEqual(Buffer.byteLength(arrayBuf), 0);
// Verify that invalid encodings are treated as utf8
for (let i = 1; i < 10; i++) {
const encoding = String(i).repeat(i);
assert.ok(!Buffer.isEncoding(encoding));
assert.strictEqual(Buffer.byteLength('foo', encoding),
Buffer.byteLength('foo', 'utf8'));
}

View File

@ -0,0 +1,94 @@
'use strict';
require('../common');
const assert = require('assert');
const a = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]);
const b = Buffer.from([5, 6, 7, 8, 9, 0, 1, 2, 3, 4]);
assert.strictEqual(a.compare(b), -1);
// Equivalent to a.compare(b).
assert.strictEqual(a.compare(b, 0), -1);
assert.throws(() => a.compare(b, '0'), { code: 'ERR_INVALID_ARG_TYPE' });
assert.strictEqual(a.compare(b, undefined), -1);
// Equivalent to a.compare(b).
assert.strictEqual(a.compare(b, 0, undefined, 0), -1);
// Zero-length target, return 1
assert.strictEqual(a.compare(b, 0, 0, 0), 1);
assert.throws(
() => a.compare(b, 0, '0', '0'),
{ code: 'ERR_INVALID_ARG_TYPE' }
);
// Equivalent to Buffer.compare(a, b.slice(6, 10))
assert.strictEqual(a.compare(b, 6, 10), 1);
// Zero-length source, return -1
assert.strictEqual(a.compare(b, 6, 10, 0, 0), -1);
// Zero-length source and target, return 0
assert.strictEqual(a.compare(b, 0, 0, 0, 0), 0);
assert.strictEqual(a.compare(b, 1, 1, 2, 2), 0);
// Equivalent to Buffer.compare(a.slice(4), b.slice(0, 5))
assert.strictEqual(a.compare(b, 0, 5, 4), 1);
// Equivalent to Buffer.compare(a.slice(1), b.slice(5))
assert.strictEqual(a.compare(b, 5, undefined, 1), 1);
// Equivalent to Buffer.compare(a.slice(2), b.slice(2, 4))
assert.strictEqual(a.compare(b, 2, 4, 2), -1);
// Equivalent to Buffer.compare(a.slice(4), b.slice(0, 7))
assert.strictEqual(a.compare(b, 0, 7, 4), -1);
// Equivalent to Buffer.compare(a.slice(4, 6), b.slice(0, 7));
assert.strictEqual(a.compare(b, 0, 7, 4, 6), -1);
// Null is ambiguous.
assert.throws(
() => a.compare(b, 0, null),
{ code: 'ERR_INVALID_ARG_TYPE' }
);
// Values do not get coerced.
assert.throws(
() => a.compare(b, 0, { valueOf: () => 5 }),
{ code: 'ERR_INVALID_ARG_TYPE' }
);
// Infinity should not be coerced.
assert.throws(
() => a.compare(b, Infinity, -Infinity),
{ code: 'ERR_OUT_OF_RANGE' }
);
// Zero length target because default for targetEnd <= targetSource
assert.strictEqual(a.compare(b, 0xff), 1);
assert.throws(
() => a.compare(b, '0xff'),
{ code: 'ERR_INVALID_ARG_TYPE' }
);
assert.throws(
() => a.compare(b, 0, '0xff'),
{ code: 'ERR_INVALID_ARG_TYPE' }
);
const oor = { code: 'ERR_OUT_OF_RANGE' };
assert.throws(() => a.compare(b, 0, 100, 0), oor);
assert.throws(() => a.compare(b, 0, 1, 0, 100), oor);
assert.throws(() => a.compare(b, -1), oor);
assert.throws(() => a.compare(b, 0, Infinity), oor);
assert.throws(() => a.compare(b, 0, 1, -1), oor);
assert.throws(() => a.compare(b, -Infinity, Infinity), oor);
assert.throws(() => a.compare(), {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "target" argument must be an instance of ' +
'Buffer or Uint8Array. Received undefined'
});

Some files were not shown because too many files have changed in this diff Show More