Files
scriptc/tests/node-parallel/common/index.cjs
T

189 lines
5.6 KiB
JavaScript

"use strict";
// A minimal test/common for building Node's test/parallel files with scriptc:
// the helpers those files use, with the semantics of Node's
// test/common/index.js. Add a helper here, with Node's semantics, when a
// lane's files need it.
const assert = require("assert");
const os = require("os");
const { inspect } = require("util");
const hasIntl = true;
const isDebug = false;
const isWindows = process.platform === "win32";
const isAIX = os.type() === "AIX";
const isIBMi = os.type() === "OS400";
const isSunOS = process.platform === "sunos";
const hasCrypto = Boolean(process.versions.openssl);
const mustCallChecks = [];
function runCallChecks(exitCode) {
if (exitCode !== 0) return;
const failed = mustCallChecks.filter((context) => {
if ("minimum" in context) return context.actual < context.minimum;
return context.actual !== context.exact;
});
for (const context of failed) {
console.log(
"Mismatched %s function calls. Expected %s, actual %d.",
context.name,
String(context.exact ?? context.minimum),
context.actual,
);
}
if (failed.length) process.exit(1);
}
function noop() {}
function mustCallInner(fn, criteria, field) {
if (typeof fn === "number") {
criteria = fn;
fn = noop;
} else if (fn === undefined) {
fn = noop;
}
if (criteria === undefined) criteria = 1;
const context = { actual: 0, name: fn.name || "<anonymous>" };
context[field] = criteria;
if (mustCallChecks.length === 0) process.on("exit", runCallChecks);
mustCallChecks.push(context);
return function (...args) {
context.actual++;
return fn.apply(this, args);
};
}
function mustCall(fn, exact) {
return mustCallInner(fn, exact, "exact");
}
function mustCallAtLeast(fn, minimum) {
return mustCallInner(fn, minimum, "minimum");
}
function mustSucceed(fn, exact) {
return mustCall(function (err, ...args) {
assert.ifError(err);
if (typeof fn === "function") return fn.apply(this, args);
}, exact);
}
function mustNotCall(msg) {
return function mustNotCall() {
assert.fail(msg || "function should not have been called");
};
}
function skip(msg) {
console.log(`1..0 # Skipped: ${msg}`);
process.exit(0);
}
function expectWarning(name, expected, code) {
if (typeof expected === "string") expected = [[expected, code]];
else if (expected.length !== 0 && !Array.isArray(expected[0]))
expected = [[expected[0], expected[1]]];
const check = mustCall((warning) => {
const [message, warningCode] = expected.shift();
assert.strictEqual(warning.name, name);
if (typeof message === "string") assert.strictEqual(warning.message, message);
else assert.match(warning.message, message);
assert.strictEqual(warning.code, warningCode);
}, expected.length);
process.on("warning", (warning) => {
if (warning.name !== name)
throw new TypeError(`"${warning.name}" was triggered without being expected.`);
check(warning);
});
}
function expectsError(validator, exact) {
return mustCall((...args) => {
if (args.length !== 1) assert.fail(`Expected one argument, got ${inspect(args)}`);
const error = args.pop();
assert.strictEqual(Object.prototype.propertyIsEnumerable.call(error, "message"), false);
assert.throws(() => {
throw error;
}, validator);
return true;
}, exact);
}
function platformTimeout(ms) {
if (isAIX || isIBMi) return (typeof ms === "bigint" ? 2n : 2) * ms;
return ms;
}
function invalidArgTypeHelper(input) {
if (input == null) return ` Received ${input}`;
if (typeof input === "function") return ` Received function ${input.name}`;
if (typeof input === "object") {
const name = input.constructor?.name;
if (typeof name === "string" && name !== "") return ` Received an instance of ${name}`;
return ` Received ${inspect(input, { depth: -1 })}`;
}
let inspected = inspect(input, { colors: false });
if (inspected.length > 28) inspected = `${inspected.slice(inspected, 0, 25)}...`;
return ` Received type ${typeof input} (${inspected})`;
}
function spawnPromisified(...args) {
const { spawn } = require("child_process");
let stderr = "";
let stdout = "";
const child = spawn(...args);
child.stderr.setEncoding("utf8");
child.stderr.on("data", (data) => {
stderr += data;
});
child.stdout.setEncoding("utf8");
child.stdout.on("data", (data) => {
stdout += data;
});
return new Promise((resolve, reject) => {
child.on("close", (code, signal) => resolve({ code, signal, stderr, stdout }));
child.on("error", (code, signal) => reject({ code, signal, stderr, stdout }));
});
}
module.exports = {
hasCrypto,
hasIntl,
isAIX,
isDebug,
isIBMi,
isSunOS,
isWindows,
mustCall,
mustCallAtLeast,
mustNotCall,
mustSucceed,
skip,
expectWarning,
expectsError,
invalidArgTypeHelper,
platformTimeout,
spawnPromisified,
get hasIPv6() {
const re = isWindows ? /Loopback Pseudo-Interface/ : isIBMi ? /\*LOOPBACK/ : /lo/;
const interfaces = os.networkInterfaces();
return Object.keys(interfaces).some(
(name) => re.test(name) && interfaces[name].some(({ family }) => family === "IPv6"),
);
},
inFreeBSDJail: false,
localhostIPv4: "127.0.0.1",
// The helpers below load their modules on first use, so that files of other
// lanes do not load node:net or the tmpdir helper.
get defaultAutoSelectFamilyAttemptTimeout() {
return require("net").getDefaultAutoSelectFamilyAttemptTimeout();
},
get PIPE() {
const path = require("path");
const pipePrefix = isWindows
? "\\\\.\\pipe\\"
: path.relative(process.cwd(), `${require("./tmpdir").path}/`);
return path.join(pipePrefix, `node-test.${process.pid}.sock`);
},
};