Compare commits

...

12 Commits

Author SHA1 Message Date
Meghan Denny
11ba4971b0 fix command to not cd 2024-08-09 18:37:25 -07:00
Meghan Denny
cfb30c5989 skip more 2024-08-09 18:37:09 -07:00
Meghan Denny
1887f073b7 dont expose these as enumerable on the global object 2024-08-09 16:31:35 -07:00
Meghan Denny
f1125d1827 exit early here so its eaiser to use during local dev 2024-08-09 16:30:45 -07:00
Meghan Denny
69c2ee0abd this was unused 2024-08-09 16:30:08 -07:00
Meghan Denny
700c922e0c simplify this import 2024-08-09 16:30:01 -07:00
Meghan Denny
fa0a8f393c add new guard for ones that are done 2024-08-09 16:21:12 -07:00
Meghan Denny
3cdf1f27d9 fix node checkout 2024-08-09 15:18:59 -07:00
Meghan Denny
4d8bf16ed9 ci: tempoarily skip non-linux-x64 2024-08-09 14:49:58 -07:00
Meghan Denny
cf9f9b4658 fix step label 2024-08-08 17:27:44 -07:00
Meghan Denny
dd94e5359e delete old runner 2024-08-08 16:59:23 -07:00
Meghan Denny
198c645097 ci: revive running node tests on linux x64 2024-08-08 16:59:10 -07:00
14 changed files with 294 additions and 1086 deletions

View File

@@ -18,6 +18,7 @@ steps:
# macOS aarch64
- key: "darwin-aarch64"
group: ":darwin: aarch64"
skip: true
steps:
- key: "darwin-aarch64-build-deps"
label: ":darwin: aarch64 - build-deps"
@@ -118,6 +119,7 @@ steps:
# macOS x64
- key: "darwin-x64"
group: ":darwin: x64"
skip: true
steps:
- key: "darwin-x64-build-deps"
label: ":darwin: x64 - build-deps"
@@ -218,6 +220,7 @@ steps:
# Linux aarch64
- key: "linux-aarch64"
group: ":linux: aarch64"
skip: true
steps:
- key: "linux-aarch64-build-deps"
label: ":linux: aarch64 - build-deps"
@@ -393,6 +396,7 @@ steps:
- key: "linux-x64-test-debian-12"
label: ":debian: 12 x64 - test-bun"
if: "build.branch != 'main'"
skip: true
parallelism: 5
soft_fail:
- exit_status: 2
@@ -451,6 +455,7 @@ steps:
- key: "linux-x64-test-ubuntu-2004"
label: ":ubuntu: 20.04 x64 - test-bun"
if: "build.branch != 'main'"
skip: true
parallelism: 5
soft_fail:
- exit_status: 2
@@ -477,9 +482,41 @@ steps:
command:
- "./scripts/runner.node.mjs --step linux-x64-build-bun"
- key: "linux-x64-test-ubuntu-2204-nodejs"
label: ":node: :ubuntu: 22.04 x64 - test-nodejs"
if: "build.branch != 'main'"
parallelism: 1
soft_fail:
- exit_status: 2
retry:
automatic:
- exit_status: 1
limit: 1
- exit_status: -1
limit: 3
- exit_status: 255
limit: 3
- signal_reason: agent_stop
limit: 3
- signal: SIGTERM
limit: 3
depends_on:
- "linux-x64-build-bun"
agents:
robobun: "true"
os: "linux"
arch: "x64"
distro: "ubuntu"
release: "22.04"
command:
- "git clone https://github.com/nodejs/node ~/node"
- "git -C ~/node checkout v22.5.1"
- "./scripts/runner.node.mjs --nodejs"
# Linux x64-baseline
- key: "linux-x64-baseline"
group: ":linux: x64-baseline"
skip: true
steps:
- key: "linux-x64-baseline-build-deps"
label: ":linux: x64-baseline - build-deps"
@@ -611,6 +648,7 @@ steps:
# Windows x64
- key: "windows-x64"
group: ":windows: x64"
skip: true
steps:
- key: "windows-x64-build-deps"
label: ":windows: x64 - build-deps"
@@ -696,6 +734,7 @@ steps:
# Windows x64-baseline
- key: "windows-x64-baseline"
group: ":windows: x64-baseline"
skip: true
steps:
- key: "windows-x64-baseline-build-deps"
label: ":windows: x64-baseline - build-deps"

View File

@@ -21,8 +21,7 @@ import {
} from "node:fs";
import { spawn, spawnSync } from "node:child_process";
import { tmpdir, hostname, userInfo, homedir } from "node:os";
import { join, basename, dirname, relative } from "node:path";
import { normalize as normalizeWindows } from "node:path/win32";
import { join, basename, dirname, relative, win32 } from "node:path";
import { isIP } from "node:net";
import { parseArgs } from "node:util";
@@ -36,7 +35,6 @@ const isWindows = process.platform === "win32";
const isGitHubAction = !!process.env["GITHUB_ACTIONS"];
const isBuildKite = !!process.env["BUILDKITE"];
const isBuildKiteTestSuite = !!process.env["BUILDKITE_ANALYTICS_TOKEN"];
const isCI = !!process.env["CI"] || isGitHubAction || isBuildKite;
const isAWS =
@@ -55,6 +53,7 @@ const gitRef = getGitRef();
const cwd = dirname(import.meta.dirname);
const testsPath = join(cwd, "test");
const tmpPath = getTmpdir();
const nodeTestsPath = join(homedir(), "node", "test", "parallel");
const { values: options, positionals: filters } = parseArgs({
allowPositionals: true,
@@ -93,6 +92,10 @@ const { values: options, positionals: filters } = parseArgs({
type: "string",
default: undefined,
},
["nodejs"]: {
type: "boolean",
default: false,
},
},
});
@@ -221,6 +224,11 @@ async function runTests() {
if (results.every(({ ok }) => ok)) {
for (const testPath of tests) {
if (options["nodejs"]) {
const testPathFull = join(nodeTestsPath, testPath);
await runTest(`node/test/parallel/${testPath}`, async () => spawnBunTest(execPath, testPathFull));
continue;
}
const title = relative(cwd, join(testsPath, testPath)).replace(/\\/g, "/");
await runTest(title, async () => spawnBunTest(execPath, join("test", testPath)));
}
@@ -233,6 +241,8 @@ async function runTests() {
reportOutputToGitHubAction("failing_tests", markdown);
}
if (!isCI) console.log("-------");
if (!isCI) console.log("passing", results.length - failedTests.length, "/", results.length);
return results;
}
@@ -387,6 +397,8 @@ async function spawnSafe(options) {
error = "timeout";
} else {
error = signalCode;
if (!isCI) console.log();
if (!isCI) onExit(signalCode);
}
} else if (exitCode === 1) {
const match = buffer.match(/\x1b\[31m\s(\d+) fail/);
@@ -527,12 +539,14 @@ async function spawnBun(execPath, { args, cwd, timeout, env, stdout, stderr }) {
* @returns {Promise<TestResult>}
*/
async function spawnBunTest(execPath, testPath) {
if (options["nodejs"] && !isCI) console.log(testPath);
const timeout = getTestTimeout(testPath);
const perTestTimeout = Math.ceil(timeout / 2);
const isReallyTest = isTestStrict(testPath);
const { ok, error, stdout } = await spawnBun(execPath, {
args: ["test", `--timeout=${perTestTimeout}`, testPath],
args: isReallyTest ? ["test", `--timeout=${perTestTimeout}`, testPath] : [testPath],
cwd: cwd,
timeout,
timeout: isReallyTest ? timeout : 1_000,
env: {
GITHUB_ACTIONS: "true", // always true so annotations are parsed
},
@@ -780,7 +794,7 @@ function getTmpdir() {
if (/cygwin|cygdrive/i.test(tmpdir) || !/^[a-z]/i.test(tmpdir)) {
continue;
}
return normalizeWindows(tmpdir);
return win32.normalize(tmpdir);
}
const appData = process.env["LOCALAPPDATA"];
if (appData) {
@@ -811,6 +825,15 @@ function isJavaScript(path) {
* @returns {boolean}
*/
function isTest(path) {
if (options["nodejs"]) return true;
return isTestStrict(path);
}
/**
* @param {string} path
* @returns {boolean}
*/
function isTestStrict(path) {
return isJavaScript(path) && /\.test|spec\./.test(basename(path));
}
@@ -828,6 +851,226 @@ function isHidden(path) {
*/
function getTests(cwd) {
function* getFiles(cwd, path) {
if (options["nodejs"]) {
const dirname = join(cwd, path);
console.log(dirname);
for (const entry of readdirSync(dirname, { encoding: "utf-8", withFileTypes: true })) {
const { name } = entry;
const filename = join(path, name);
if (!isCI) {
// skip these in debug mode so its easier to comment out individual ones to test
// guards in this list should only be moved here once theyre 100% passing
}
if (filename.startsWith("test-abortcontroller")) continue;
if (filename.startsWith("test-aborted")) continue;
if (filename.startsWith("test-abortsignal")) continue;
if (filename.startsWith("test-accessor")) continue;
if (filename.startsWith("test-arm")) continue;
if (filename.startsWith("test-assert")) continue;
if (filename.startsWith("test-async")) continue;
if (filename.startsWith("test-atomics")) continue;
if (filename.startsWith("test-bad")) continue;
if (filename.startsWith("test-bash")) continue;
if (filename.startsWith("test-beforeexit")) continue;
if (filename.startsWith("test-benchmark")) continue;
if (filename.startsWith("test-binding")) continue;
if (filename.startsWith("test-blob")) continue;
if (filename.startsWith("test-blocklist")) continue;
if (filename.startsWith("test-bootstrap")) continue;
if (filename.startsWith("test-broadcastchannel")) continue;
if (filename.startsWith("test-btoa")) continue;
if (filename.startsWith("test-buffer")) continue;
if (filename.startsWith("test-c-")) continue;
if (filename.startsWith("test-child")) continue;
if (filename.startsWith("test-cli-")) continue;
if (filename.startsWith("test-client")) continue;
if (filename.startsWith("test-cluster")) continue;
if (filename.startsWith("test-code")) continue;
if (filename.startsWith("test-common")) continue;
if (filename.startsWith("test-compile")) continue;
if (filename.startsWith("test-compression")) continue;
if (filename.startsWith("test-console")) continue;
if (filename.startsWith("test-constants")) continue;
if (filename.startsWith("test-corepack")) continue;
if (filename.startsWith("test-coverage")) continue;
if (filename.startsWith("test-crypto")) continue;
if (filename.startsWith("test-cwd")) continue;
if (filename.startsWith("test-datetime")) continue;
if (filename.startsWith("test-debugger")) continue;
if (filename.startsWith("test-delayed")) continue;
if (filename.startsWith("test-destroy")) continue;
if (filename.startsWith("test-dgram")) continue;
if (filename.startsWith("test-diagnostics")) continue;
if (filename.startsWith("test-directory")) continue;
if (filename.startsWith("test-disable")) continue;
if (filename.startsWith("test-dns")) continue;
if (filename.startsWith("test-domain")) continue;
if (filename.startsWith("test-domexception")) continue;
if (filename.startsWith("test-dotenv")) continue;
if (filename.startsWith("test-double")) continue;
if (filename.startsWith("test-dsa")) continue;
if (filename.startsWith("test-dummy")) continue;
if (filename.startsWith("test-emit")) continue;
if (filename.startsWith("test-env")) continue;
if (filename.startsWith("test-err-")) continue;
if (filename.startsWith("test-error-")) continue;
if (filename.startsWith("test-errors-")) continue;
if (filename.startsWith("test-eslint")) continue;
if (filename.startsWith("test-eval")) continue;
if (filename.startsWith("test-event-")) continue;
if (filename.startsWith("test-eventemitter")) continue;
if (filename.startsWith("test-events-")) continue;
if (filename.startsWith("test-eventsource")) continue;
if (filename.startsWith("test-eventtarget")) continue;
if (filename.startsWith("test-exception")) continue;
if (filename.startsWith("test-experimental")) continue;
if (filename.startsWith("test-fetch")) continue;
if (filename.startsWith("test-file-")) continue;
if (filename.startsWith("test-file.")) continue;
if (filename.startsWith("test-filehandle")) continue;
if (filename.startsWith("test-finalization")) continue;
if (filename.startsWith("test-fixed")) continue;
if (filename.startsWith("test-force")) continue;
if (filename.startsWith("test-freelist")) continue;
if (filename.startsWith("test-freeze")) continue;
if (filename.startsWith("test-fs")) continue;
if (filename.startsWith("test-gc")) continue;
if (filename.startsWith("test-global")) continue;
if (filename.startsWith("test-h2")) continue;
if (filename.startsWith("test-handle")) continue;
if (filename.startsWith("test-heap-")) continue;
if (filename.startsWith("test-heapdump")) continue;
if (filename.startsWith("test-heapsnapshot")) continue;
if (filename.startsWith("test-http-")) continue;
if (filename.startsWith("test-http.")) continue;
if (filename.startsWith("test-http2")) continue;
if (filename.startsWith("test-https")) continue;
if (filename.startsWith("test-icu")) continue;
if (filename.startsWith("test-inspect-")) continue;
if (filename.startsWith("test-inspector")) continue;
if (filename.startsWith("test-instanceof")) continue;
if (filename.startsWith("test-internal")) continue;
if (filename.startsWith("test-intl")) continue;
if (filename.startsWith("test-js")) continue;
if (filename.startsWith("test-kill")) continue;
if (filename.startsWith("test-listen")) continue;
if (filename.startsWith("test-macos")) continue;
if (filename.startsWith("test-math")) continue;
if (filename.startsWith("test-memory")) continue;
if (filename.startsWith("test-messagechannel")) continue;
if (filename.startsWith("test-messageevent")) continue;
if (filename.startsWith("test-messageport")) continue;
if (filename.startsWith("test-messaging")) continue;
if (filename.startsWith("test-microtask")) continue;
if (filename.startsWith("test-mime")) continue;
if (filename.startsWith("test-module")) continue;
if (filename.startsWith("test-navigator")) continue;
if (filename.startsWith("test-net")) continue;
if (filename.startsWith("test-next")) continue;
if (filename.startsWith("test-no")) continue;
if (filename.startsWith("test-npm")) continue;
if (filename.startsWith("test-openssl")) continue;
if (filename.startsWith("test-options")) continue;
if (filename.startsWith("test-os")) continue;
if (filename.startsWith("test-outgoing")) continue;
if (filename.startsWith("test-parse")) continue;
if (filename.startsWith("test-path")) continue;
if (filename.startsWith("test-pending")) continue;
if (filename.startsWith("test-perf-")) continue;
if (filename.startsWith("test-performance-")) continue;
if (filename.startsWith("test-performanceobserver-")) continue;
if (filename.startsWith("test-performanceobserver.")) continue;
if (filename.startsWith("test-permission")) continue;
if (filename.startsWith("test-pipe")) continue;
if (filename.startsWith("test-preload")) continue;
if (filename.startsWith("test-primitive")) continue;
if (filename.startsWith("test-primordials")) continue;
if (filename.startsWith("test-priority")) continue;
if (filename.startsWith("test-process")) continue;
if (filename.startsWith("test-promise")) continue;
if (filename.startsWith("test-punycode")) continue;
if (filename.startsWith("test-querystring")) continue;
if (filename.startsWith("test-queue")) continue;
if (filename.startsWith("test-quic")) continue;
if (filename.startsWith("test-readable")) continue;
if (filename.startsWith("test-readline")) continue;
if (filename.startsWith("test-ref")) continue;
if (filename.startsWith("test-regression")) continue;
if (filename.startsWith("test-release")) continue;
if (filename.startsWith("test-repl")) continue;
if (filename.startsWith("test-require")) continue;
if (filename.startsWith("test-resource")) continue;
if (filename.startsWith("test-runner")) continue;
if (filename.startsWith("test-safe")) continue;
if (filename.startsWith("test-security")) continue;
if (filename.startsWith("test-set")) continue;
if (filename.startsWith("test-shadow")) continue;
if (filename.startsWith("test-sigint")) continue;
if (filename.startsWith("test-signal")) continue;
if (filename.startsWith("test-single")) continue;
if (filename.startsWith("test-snapshot")) continue;
if (filename.startsWith("test-socket-")) continue;
if (filename.startsWith("test-socketaddress")) continue;
if (filename.startsWith("test-source")) continue;
if (filename.startsWith("test-spawn")) continue;
if (filename.startsWith("test-sqlite")) continue;
if (filename.startsWith("test-stack")) continue;
if (filename.startsWith("test-startup")) continue;
if (filename.startsWith("test-stdin")) continue;
if (filename.startsWith("test-stdio")) continue;
if (filename.startsWith("test-stdout")) continue;
if (filename.startsWith("test-strace")) continue;
if (filename.startsWith("test-stream-")) continue;
if (filename.startsWith("test-stream2-")) continue;
if (filename.startsWith("test-stream3-")) continue;
if (filename.startsWith("test-streams")) continue;
if (filename.startsWith("test-string-")) continue;
if (filename.startsWith("test-stringbytes")) continue;
if (filename.startsWith("test-structuredClone")) continue;
if (filename.startsWith("test-sync")) continue;
if (filename.startsWith("test-sys")) continue;
if (filename.startsWith("test-tcp")) continue;
if (filename.startsWith("test-throw")) continue;
if (filename.startsWith("test-tick")) continue;
if (filename.startsWith("test-timers")) continue;
if (filename.startsWith("test-tls")) continue;
if (filename.startsWith("test-tojson")) continue;
if (filename.startsWith("test-trace")) continue;
if (filename.startsWith("test-tracing")) continue;
if (filename.startsWith("test-tty-")) continue;
if (filename.startsWith("test-ttywrap")) continue;
if (filename.startsWith("test-tz")) continue;
if (filename.startsWith("test-unhandled")) continue;
if (filename.startsWith("test-unicode")) continue;
if (filename.startsWith("test-url")) continue;
if (filename.startsWith("test-utf8")) continue;
if (filename.startsWith("test-util")) continue;
if (filename.startsWith("test-uv")) continue;
if (filename.startsWith("test-v8")) continue;
if (filename.startsWith("test-validators")) continue;
if (filename.startsWith("test-vfs")) continue;
if (filename.startsWith("test-vm")) continue;
if (filename.startsWith("test-warn")) continue;
if (filename.startsWith("test-watch")) continue;
if (filename.startsWith("test-weakref")) continue;
if (filename.startsWith("test-webcrypto")) continue;
if (filename.startsWith("test-websocket")) continue;
if (filename.startsWith("test-webstorage")) continue;
if (filename.startsWith("test-webstream")) continue;
if (filename.startsWith("test-whatwg")) continue;
if (filename.startsWith("test-windows")) continue;
if (filename.startsWith("test-worker")) continue;
if (filename.startsWith("test-wrap")) continue;
if (filename.startsWith("test-x509")) continue;
if (filename.startsWith("test-zlib")) continue;
if (filename.startsWith("test-")) {
yield filename;
}
}
return;
}
const dirname = join(cwd, path);
for (const entry of readdirSync(dirname, { encoding: "utf-8", withFileTypes: true })) {
const { name } = entry;
@@ -842,6 +1085,9 @@ function getTests(cwd) {
}
}
}
if (options["nodejs"]) {
return [...getFiles(nodeTestsPath, "")].sort();
}
return [...getFiles(cwd, "")].sort();
}
@@ -1711,6 +1957,7 @@ async function getDoomsdayDate() {
* @param {string} signal
*/
async function beforeExit(signal) {
if (!isCI) return onExit(signal);
const endOfWorld = await getDoomsdayDate();
if (endOfWorld) {
const timeMin = 10 * 1000;

View File

@@ -3443,8 +3443,8 @@ void GlobalObject::addBuiltinGlobals(JSC::VM& vm)
PropertyAttribute::Accessor | 0);
// TODO: this should be usable on the lookup table. it crashed las time i tried it
putDirectCustomAccessor(vm, JSC::Identifier::fromString(vm, "onmessage"_s), JSC::CustomGetterSetter::create(vm, globalOnMessage, setGlobalOnMessage), 0);
putDirectCustomAccessor(vm, JSC::Identifier::fromString(vm, "onerror"_s), JSC::CustomGetterSetter::create(vm, globalOnError, setGlobalOnError), 0);
putDirectCustomAccessor(vm, JSC::Identifier::fromString(vm, "onmessage"_s), JSC::CustomGetterSetter::create(vm, globalOnMessage, setGlobalOnMessage), (unsigned)PropertyAttribute::DontEnum);
putDirectCustomAccessor(vm, JSC::Identifier::fromString(vm, "onerror"_s), JSC::CustomGetterSetter::create(vm, globalOnError, setGlobalOnError), (unsigned)PropertyAttribute::DontEnum);
// ----- Extensions to Built-in objects -----

View File

@@ -1,6 +0,0 @@
# Paths copied from Node.js repository
upstream/
# Paths for test runner
summary/
summary.md

View File

@@ -1 +0,0 @@
upstream/

View File

@@ -1,2 +0,0 @@
[test]
preload = ["./common/preload.js"]

View File

@@ -1,273 +0,0 @@
import { expect } from "bun:test";
function deepEqual(actual, expected, message) {
if (isIgnored(expected, message)) {
return;
}
try {
expect(actual).toEqual(expected);
} catch (cause) {
throwError(cause, message);
}
}
function deepStrictEqual(actual, expected, message) {
if (isIgnored(expected, message)) {
return;
}
try {
expect(actual).toStrictEqual(expected);
} catch (cause) {
throwError(cause, message);
}
}
function doesNotMatch(string, regexp, message) {
if (isIgnored(regexp, message)) {
return;
}
try {
expect(string).not.toMatch(regexp);
} catch (cause) {
throwError(cause, message);
}
}
function doesNotReject(asyncFn, error, message) {
if (isIgnored(error, message)) {
return;
}
try {
expect(asyncFn).rejects.toThrow(error);
} catch (cause) {
throwError(cause, message);
}
}
function doesNotThrow(fn, error, message) {
if (isIgnored(error, message)) {
return;
}
todo("doesNotThrow");
}
function equal(actual, expected, message) {
if (isIgnored(expected, message)) {
return;
}
try {
expect(actual).toBe(expected);
} catch (cause) {
throwError(cause, message);
}
}
function fail(actual, expected, message, operator, stackStartFn) {
if (isIgnored(expected, message)) {
return;
}
todo("fail");
}
function ifError(value) {
if (isIgnored(value)) {
return;
}
todo("ifError");
}
function match(string, regexp, message) {
if (isIgnored(regexp, message)) {
return;
}
try {
expect(string).toMatch(regexp);
} catch (cause) {
throwError(cause, message);
}
}
function notDeepEqual(actual, expected, message) {
if (isIgnored(expected, message)) {
return;
}
todo("notDeepEqual");
}
function notDeepStrictEqual(actual, expected, message) {
if (isIgnored(expected, message)) {
return;
}
todo("notDeepStrictEqual");
}
function notEqual(actual, expected, message) {
if (isIgnored(expected, message)) {
return;
}
try {
expect(actual).not.toBe(expected);
} catch (cause) {
throwError(cause, message);
}
}
function notStrictEqual(actual, expected, message) {
if (isIgnored(expected, message)) {
return;
}
try {
expect(actual).not.toStrictEqual(expected);
} catch (cause) {
throwError(cause, message);
}
}
function ok(value, message) {
if (isIgnored(message)) {
return;
}
equal(!!value, true, message);
}
function rejects(asyncFn, error, message) {
if (isIgnored(error, message)) {
return;
}
todo("rejects");
}
function strictEqual(actual, expected, message) {
if (isIgnored(expected, message)) {
return;
}
try {
expect(actual).toBe(expected);
} catch (cause) {
throwError(cause, message);
}
}
function throws(fn, error, message) {
try {
let result;
try {
result = fn();
} catch (cause) {
const matcher = toErrorMatcher(error);
expect(cause).toEqual(matcher);
return;
}
expect(result).toBe("Expected function to throw an error, instead it returned");
} catch (cause) {
throwError(cause, message);
}
}
function toErrorMatcher(expected) {
let message;
if (typeof expected === "string") {
message = expected;
} else if (expected instanceof RegExp) {
message = expected.source;
} else if (typeof expected === "object") {
message = expected.message;
}
for (const [expected, actual] of similarErrors) {
if (message && expected.test(message)) {
message = actual;
break;
}
}
if (!message) {
return expect.anything();
}
if (typeof expected === "object") {
return expect.objectContaining({
...expected,
message: expect.stringMatching(message),
});
}
return expect.stringMatching(message);
}
const similarErrors = [
[/Invalid typed array length/i, /length too large/i],
[/Unknown encoding/i, /Invalid encoding/i],
[
/The ".*" argument must be of type string or an instance of Buffer or ArrayBuffer/i,
/Invalid input, must be a string, Buffer, or ArrayBuffer/i,
],
[/The ".*" argument must be an instance of Buffer or Uint8Array./i, /Expected Buffer/i],
[/The ".*" argument must be an instance of Array./i, /Argument must be an array/i],
[/The value of ".*" is out of range./i, /Offset is out of bounds/i],
[/Attempt to access memory outside buffer bounds/i, /Out of bounds access/i],
];
const ignoredExpectations = [
// Reason: Bun has a nicer format for `Buffer.inspect()`.
/^<Buffer /,
];
function isIgnored(...expectations) {
for (const expected of expectations) {
let query;
if (typeof expected === "string") {
query = expected;
} else if (expected instanceof RegExp) {
query = expected.source;
} else {
continue;
}
for (const pattern of ignoredExpectations) {
if (pattern.test(query)) {
console.warn("Ignoring expectation:", expected);
return true;
}
}
}
return false;
}
function throwError(error, message) {
if (isIgnored(error, message)) {
return;
}
if (typeof message === "string") {
const gray = "\x1b[90m";
const reset = "\x1b[0m";
error.message += `\n${gray}note: ${message}${reset}`;
}
throw error;
}
function todo(name) {
throw new Error(`TODO: ${name}`);
}
export default ok;
export {
deepEqual,
deepStrictEqual,
doesNotMatch,
doesNotReject,
doesNotThrow,
equal,
fail,
ifError,
match,
notDeepEqual,
notDeepStrictEqual,
notEqual,
notStrictEqual,
ok,
rejects,
strictEqual,
throws,
};

View File

@@ -1,122 +0,0 @@
// https://github.com/nodejs/node/blob/c975384264dc553de62398be814d0c66fc1fc1fb/test/common/index.js
import { inspect } from "bun";
import { expect, afterAll } from "bun:test";
const hasIntl = true;
const hasCrypto = true;
const hasOpenSSL3 = false;
const hasOpenSSL31 = false;
const hasQuic = false;
const { platform, env } = process;
const isWindows = platform === "win32";
const isSunOS = platform === "sunos";
const isFreeBSD = platform === "freebsd";
const isOpenBSD = platform === "openbsd";
const isLinux = platform === "linux";
const isOSX = platform === "darwin";
const isAsan = false;
const isPi = false;
const isDumbTerminal = env.TERM === "dumb";
function mustCall(fn, n = 1) {
const callSite = getCallSite(mustCall);
let calls = 0;
const mustCallFn = function (...args) {
calls++;
return fn.apply(this, args);
};
afterAll(() => {
if (calls !== n) {
throw new Error(`function should be called exactly ${n} times:\n ${callSite}`);
}
});
return mustCallFn;
}
function mustNotCall() {
const callSite = getCallSite(mustNotCall);
return function mustNotCall(...args) {
const argsInfo = args.length > 0 ? `\ncalled with arguments: ${args.map(arg => inspect(arg)).join(", ")}` : "";
assert.fail(`${msg || "function should not have been called"} at ${callSite}` + argsInfo);
};
}
function printSkipMessage(message) {
console.warn(message);
}
function skip(message) {
printSkipMessage(message);
process.exit(0);
}
function expectsError(validator, exact) {
return mustCall((...args) => {
if (args.length !== 1) {
// Do not use `assert.strictEqual()` to prevent `inspect` from
// always being called.
assert.fail(`Expected one argument, got ${inspect(args)}`);
}
const error = args.pop();
// The error message should be non-enumerable
assert.strictEqual(Object.prototype.propertyIsEnumerable.call(error, "message"), false);
assert.throws(() => {
throw error;
}, validator);
return true;
}, exact);
}
function expectWarning(name, code, message) {
// Do nothing
}
function invalidArgTypeHelper(input) {
return ` Received: ${inspect(input)}`;
}
function getCallSite(fn) {
const originalStackFormatter = Error.prepareStackTrace;
Error.prepareStackTrace = (_, stack) => `${stack[0].getFileName()}:${stack[0].getLineNumber()}`;
const error = new Error();
Error.captureStackTrace(error, fn);
error.stack; // With the V8 Error API, the stack is not formatted until it is accessed
Error.prepareStackTrace = originalStackFormatter;
return error.stack;
}
export {
hasIntl,
hasCrypto,
hasOpenSSL3,
hasOpenSSL31,
hasQuic,
// ...
isWindows,
isSunOS,
isFreeBSD,
isOpenBSD,
isLinux,
isOSX,
isAsan,
isPi,
// ...
isDumbTerminal,
// ...
mustCall,
mustNotCall,
printSkipMessage,
skip,
expectsError,
expectWarning,
// ...
inspect,
invalidArgTypeHelper,
};

View File

@@ -1,10 +0,0 @@
const { mock } = require("bun:test");
const assert = require("./assert");
mock.module("assert", () => {
return assert;
});
mock.module("internal/test/binding", () => {
return {};
});

View File

@@ -1,32 +0,0 @@
import { spawnSync } from "node:child_process";
const isBun = !!process.isBun;
const os = process.platform === "win32" ? "windows" : process.platform;
const arch = process.arch === "arm64" ? "aarch64" : process.arch;
const version = isBun ? Bun.version : process.versions.node;
const revision = isBun ? Bun.revision : undefined;
const baseline = (() => {
if (!isBun || arch !== "x64") {
return undefined;
}
const { stdout } = spawnSync(process.execPath, ["--print", "Bun.unsafe.segfault()"], {
encoding: "utf8",
timeout: 5_000,
});
if (stdout.includes("baseline")) {
return true;
}
return undefined;
})();
const name = baseline ? `bun-${os}-${arch}-baseline` : `${isBun ? "bun" : "node"}-${os}-${arch}`;
console.log(
JSON.stringify({
name,
os,
arch,
version,
revision,
baseline,
}),
);

View File

@@ -1,6 +0,0 @@
{
"private": true,
"scripts": {
"test": "node runner.mjs --exec-path $(which bun-debug || which bun)"
}
}

View File

@@ -1,433 +0,0 @@
import { parseArgs } from "node:util";
import { spawnSync } from "node:child_process";
import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, writeFileSync, appendFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { basename, join } from "node:path";
import readline from "node:readline/promises";
const testPath = new URL("./", import.meta.url);
const nodePath = new URL("upstream/", testPath);
const nodeTestPath = new URL("test/", nodePath);
const metadataScriptPath = new URL("metadata.mjs", testPath);
const testJsonPath = new URL("tests.json", testPath);
const summariesPath = new URL("summary/", testPath);
const summaryMdPath = new URL("summary.md", testPath);
const cwd = new URL("../../", testPath);
async function main() {
const { values, positionals } = parseArgs({
allowPositionals: true,
options: {
help: {
type: "boolean",
short: "h",
},
baseline: {
type: "boolean",
},
interactive: {
type: "boolean",
short: "i",
},
"exec-path": {
type: "string",
},
pull: {
type: "boolean",
},
summary: {
type: "boolean",
},
},
});
if (values.help) {
printHelp();
return;
}
if (values.summary) {
printSummary();
return;
}
if (values.pull) {
pullTests(true);
return;
}
pullTests();
const summary = await runTests(values, positionals);
const regressedTests = appendSummary(summary);
printSummary(summary, regressedTests);
process.exit(regressedTests?.length ? 1 : 0);
}
function printHelp() {
console.log(`Usage: ${process.argv0} ${basename(import.meta.filename)} [options]`);
console.log();
console.log("Options:");
console.log(" -h, --help Show this help message");
console.log(" -e, --exec-path Path to the bun executable to run");
console.log(" -i, --interactive Pause and wait for input after a failing test");
console.log(" -s, --summary Print a summary of the tests (does not run tests)");
}
function pullTests(force) {
if (!force && existsSync(nodeTestPath)) {
return;
}
console.log("Pulling tests...");
const { status, error, stderr } = spawnSync(
"git",
["submodule", "update", "--init", "--recursive", "--progress", "--depth=1", "--checkout", "upstream"],
{
cwd: testPath,
stdio: "inherit",
},
);
if (error || status !== 0) {
throw error || new Error(stderr);
}
for (const { filename, status } of getTests(nodeTestPath)) {
if (status === "TODO") {
continue;
}
const src = new URL(filename, nodeTestPath);
const dst = new URL(filename, testPath);
try {
writeFileSync(dst, readFileSync(src));
} catch (error) {
if (error.code === "ENOENT") {
mkdirSync(new URL(".", dst), { recursive: true });
writeFileSync(dst, readFileSync(src));
} else {
throw error;
}
}
}
}
async function runTests(options, filters) {
const { interactive } = options;
const bunPath = process.isBun ? process.execPath : "bun";
const execPath = options["exec-path"] || bunPath;
let reader;
if (interactive) {
reader = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
}
const results = [];
const tests = getTests(testPath);
for (const { label, filename, status: filter } of tests) {
if (filters?.length && !filters.some(filter => label?.includes(filter))) {
continue;
}
if (filter !== "OK") {
results.push({ label, filename, status: filter });
continue;
}
const { pathname: filePath } = new URL(filename, testPath);
const tmp = tmpdirSync();
const timestamp = Date.now();
const {
status: exitCode,
signal: signalCode,
error: spawnError,
} = spawnSync(execPath, ["test", filePath], {
cwd: testPath,
stdio: "inherit",
env: {
PATH: process.env.PATH,
HOME: tmp,
TMPDIR: tmp,
TZ: "Etc/UTC",
FORCE_COLOR: "1",
BUN_DEBUG_QUIET_LOGS: "1",
BUN_GARBAGE_COLLECTOR_LEVEL: "1",
BUN_RUNTIME_TRANSPILER_CACHE_PATH: "0",
GITHUB_ACTIONS: "false", // disable for now
},
timeout: 30_000,
});
const duration = Math.ceil(Date.now() - timestamp);
const status = exitCode === 0 ? "PASS" : "FAIL";
let error;
if (signalCode) {
error = signalCode;
} else if (spawnError) {
const { message } = spawnError;
if (message.includes("timed out") || message.includes("timeout")) {
error = "TIMEOUT";
} else {
error = message;
}
} else if (exitCode !== 0) {
error = `code ${exitCode}`;
}
results.push({ label, filename, status, error, timestamp, duration });
if (reader && status === "FAIL") {
const answer = await reader.question("Continue? [Y/n] ");
if (answer.toUpperCase() !== "Y") {
break;
}
}
}
reader?.close();
return {
v: 1,
metadata: getMetadata(execPath),
tests: results,
};
}
function getTests(filePath) {
const tests = [];
const testData = JSON.parse(readFileSync(testJsonPath, "utf8"));
for (const filename of readdirSync(filePath, { recursive: true })) {
if (!isJavaScript(filename) || !isTest(filename)) {
continue;
}
let match;
for (const { label, pattern, skip: skipList = [], todo: todoList = [] } of testData) {
if (!filename.startsWith(pattern)) {
continue;
}
if (skipList.some(({ file }) => filename.endsWith(file))) {
tests.push({ label, filename, status: "SKIP" });
} else if (todoList.some(({ file }) => filename.endsWith(file))) {
tests.push({ label, filename, status: "TODO" });
} else {
tests.push({ label, filename, status: "OK" });
}
match = true;
break;
}
if (!match) {
tests.push({ filename, status: "TODO" });
}
}
return tests;
}
function appendSummary(summary) {
const { metadata, tests, ...extra } = summary;
const { name } = metadata;
const summaryPath = new URL(`${name}.json`, summariesPath);
const summaryData = {
metadata,
tests: tests.map(({ label, filename, status, error }) => ({ label, filename, status, error })),
...extra,
};
const regressedTests = [];
if (existsSync(summaryPath)) {
const previousData = JSON.parse(readFileSync(summaryPath, "utf8"));
const { v } = previousData;
if (v === 1) {
const { tests: previousTests } = previousData;
for (const { label, filename, status, error } of tests) {
if (status !== "FAIL") {
continue;
}
const previousTest = previousTests.find(({ filename: file }) => file === filename);
if (previousTest) {
const { status: previousStatus } = previousTest;
if (previousStatus !== "FAIL") {
regressedTests.push({ label, filename, error });
}
}
}
}
}
if (regressedTests.length) {
return regressedTests;
}
const summaryText = JSON.stringify(summaryData, null, 2);
try {
writeFileSync(summaryPath, summaryText);
} catch (error) {
if (error.code === "ENOENT") {
mkdirSync(summariesPath, { recursive: true });
writeFileSync(summaryPath, summaryText);
} else {
throw error;
}
}
}
function printSummary(summaryData, regressedTests) {
let metadataInfo = {};
let testInfo = {};
let labelInfo = {};
let errorInfo = {};
const summaryList = [];
if (summaryData) {
summaryList.push(summaryData);
} else {
for (const filename of readdirSync(summariesPath)) {
if (!filename.endsWith(".json")) {
continue;
}
const summaryPath = new URL(filename, summariesPath);
const summaryData = JSON.parse(readFileSync(summaryPath, "utf8"));
summaryList.push(summaryData);
}
}
for (const summaryData of summaryList) {
const { v, metadata, tests } = summaryData;
if (v !== 1) {
continue;
}
const { name, version, revision } = metadata;
if (revision) {
metadataInfo[name] =
`${version}-[\`${revision.slice(0, 7)}\`](https://github.com/oven-sh/bun/commit/${revision})`;
} else {
metadataInfo[name] = `${version}`;
}
for (const test of tests) {
const { label, filename, status, error } = test;
if (label) {
labelInfo[label] ||= { pass: 0, fail: 0, skip: 0, todo: 0, total: 0 };
labelInfo[label][status.toLowerCase()] += 1;
labelInfo[label].total += 1;
}
testInfo[name] ||= { pass: 0, fail: 0, skip: 0, todo: 0, total: 0 };
testInfo[name][status.toLowerCase()] += 1;
testInfo[name].total += 1;
if (status === "FAIL") {
errorInfo[filename] ||= {};
errorInfo[filename][name] = error;
}
}
}
let summaryMd = `## Node.js tests
`;
if (!summaryData) {
summaryMd += `
| Platform | Conformance | Passed | Failed | Skipped | Total |
| - | - | - | - | - | - |
`;
for (const [name, { pass, fail, skip, total }] of Object.entries(testInfo)) {
testInfo[name].coverage = (((pass + fail + skip) / total) * 100).toFixed(2);
testInfo[name].conformance = ((pass / total) * 100).toFixed(2);
}
for (const [name, { conformance, pass, fail, skip, total }] of Object.entries(testInfo)) {
summaryMd += `| \`${name}\` ${metadataInfo[name]} | ${conformance} % | ${pass} | ${fail} | ${skip} | ${total} |\n`;
}
}
summaryMd += `
| API | Conformance | Passed | Failed | Skipped | Total |
| - | - | - | - | - | - |
`;
for (const [label, { pass, fail, skip, total }] of Object.entries(labelInfo)) {
labelInfo[label].coverage = (((pass + fail + skip) / total) * 100).toFixed(2);
labelInfo[label].conformance = ((pass / total) * 100).toFixed(2);
}
for (const [label, { conformance, pass, fail, skip, total }] of Object.entries(labelInfo)) {
summaryMd += `| \`${label}\` | ${conformance} % | ${pass} | ${fail} | ${skip} | ${total} |\n`;
}
if (!summaryData) {
writeFileSync(summaryMdPath, summaryMd);
}
const githubSummaryPath = process.env.GITHUB_STEP_SUMMARY;
if (githubSummaryPath) {
appendFileSync(githubSummaryPath, summaryMd);
}
console.log("=".repeat(process.stdout.columns));
console.log("Summary by platform:");
console.table(testInfo);
console.log("Summary by label:");
console.table(labelInfo);
if (regressedTests?.length) {
const isTty = process.stdout.isTTY;
if (isTty) {
process.stdout.write("\x1b[31m");
}
const { name } = summaryData.metadata;
console.log(`Regressions found in ${regressedTests.length} tests for ${name}:`);
console.table(regressedTests);
if (isTty) {
process.stdout.write("\x1b[0m");
}
}
}
function isJavaScript(filename) {
return /\.(m|c)?js$/.test(filename);
}
function isTest(filename) {
return /^test-/.test(basename(filename));
}
function getMetadata(execPath) {
const { pathname: filePath } = metadataScriptPath;
const { status: exitCode, stdout } = spawnSync(execPath, [filePath], {
cwd,
stdio: ["ignore", "pipe", "ignore"],
env: {
PATH: process.env.PATH,
BUN_DEBUG_QUIET_LOGS: "1",
},
timeout: 5_000,
});
if (exitCode === 0) {
try {
return JSON.parse(stdout);
} catch {
// Ignore
}
}
return {
os: process.platform,
arch: process.arch,
};
}
main().catch(error => {
console.error(error);
process.exit(1);
});

View File

@@ -1,166 +0,0 @@
[
{
"label": "node:buffer",
"pattern": "parallel/test-buffer",
"skip": [
{
"file": "backing-arraybuffer.js",
"reason": "Internal binding checks if the buffer is on the heap"
}
],
"todo": [
{
"file": "constants.js",
"reason": "Hangs"
},
{
"file": "tostring-rangeerror.js",
"reason": "Hangs"
}
]
},
{
"label": "node:path",
"pattern": "parallel/test-path"
},
{
"label": "node:child_process",
"pattern": "parallel/test-child-process"
},
{
"label": "node:async_hooks",
"pattern": "parallel/test-async-hooks"
},
{
"label": "node:crypto",
"pattern": "parallel/test-crypto"
},
{
"label": "node:dgram",
"pattern": "parallel/test-dgram"
},
{
"label": "node:diagnostics_channel",
"pattern": "parallel/test-diagnostics-channel"
},
{
"label": "node:fs",
"pattern": "parallel/test-fs"
},
{
"label": "node:dns",
"pattern": "parallel/test-dns"
},
{
"label": "node:domain",
"pattern": "parallel/test-domain"
},
{
"label": "node:events",
"pattern": "parallel/test-event-emitter"
},
{
"label": "node:http",
"pattern": "parallel/test-http"
},
{
"label": "node:http2",
"pattern": "parallel/test-http2"
},
{
"label": "node:https",
"pattern": "parallel/test-https"
},
{
"label": "node:net",
"pattern": "parallel/test-net"
},
{
"label": "node:os",
"pattern": "parallel/test-os"
},
{
"label": "process",
"pattern": "parallel/test-process"
},
{
"label": "node:stream",
"pattern": "parallel/test-stream"
},
{
"label": "node:stream",
"pattern": "parallel/test-readable"
},
{
"label": "node:timers",
"pattern": "parallel/test-timers"
},
{
"label": "node:timers",
"pattern": "parallel/test-next-tick"
},
{
"label": "node:tls",
"pattern": "parallel/test-tls"
},
{
"label": "node:tty",
"pattern": "parallel/test-tty"
},
{
"label": "node:url",
"pattern": "parallel/test-url"
},
{
"label": "node:util",
"pattern": "parallel/test-util"
},
{
"label": "node:trace_events",
"pattern": "parallel/test-trace-events"
},
{
"label": "node:vm",
"pattern": "parallel/test-vm"
},
{
"label": "node:zlib",
"pattern": "parallel/test-zlib"
},
{
"label": "node:worker_threads",
"pattern": "parallel/test-worker"
},
{
"label": "node:readline",
"pattern": "parallel/test-readline"
},
{
"label": "web:crypto",
"pattern": "parallel/test-webcrypto"
},
{
"label": "web:streams",
"pattern": "parallel/test-webstream"
},
{
"label": "web:streams",
"pattern": "parallel/test-whatwg-webstreams"
},
{
"label": "web:encoding",
"pattern": "parallel/test-whatwg-encoding"
},
{
"label": "web:url",
"pattern": "parallel/test-whatwg-url"
},
{
"label": "web:websocket",
"pattern": "parallel/test-websocket"
},
{
"label": "web:performance",
"pattern": "parallel/test-performance"
}
]

View File

@@ -1,27 +0,0 @@
{
"include": [".", "../../packages/bun-types/index.d.ts"],
"compilerOptions": {
"lib": ["ESNext"],
"module": "ESNext",
"target": "ESNext",
"moduleResolution": "bundler",
"moduleDetection": "force",
"allowImportingTsExtensions": true,
"experimentalDecorators": true,
"noEmit": true,
"composite": true,
"strict": true,
"downlevelIteration": true,
"skipLibCheck": true,
"jsx": "preserve",
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"allowJs": true,
"resolveJsonModule": true,
"noImplicitThis": false,
"paths": {
"assert": ["./common/assert.js"]
}
},
"exclude": []
}