mirror of
https://github.com/oven-sh/bun
synced 2026-02-16 05:42:43 +00:00
Enable ASAN with linux-x64-asan in CI
This commit is contained in:
@@ -25,17 +25,23 @@ import {
|
||||
} from "node:fs";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { userInfo } from "node:os";
|
||||
import { basename, dirname, join, relative, sep } from "node:path";
|
||||
import { basename, dirname, join, relative, sep, extname } from "node:path";
|
||||
import { parseArgs } from "node:util";
|
||||
import {
|
||||
getAbi,
|
||||
getAbiVersion,
|
||||
getArch,
|
||||
getBranch,
|
||||
getBuildLabel,
|
||||
getBuildUrl,
|
||||
getCommit,
|
||||
getDistro,
|
||||
getDistroVersion,
|
||||
getEnv,
|
||||
getFileUrl,
|
||||
getHostname,
|
||||
getLoggedInUserCountOrDetails,
|
||||
getOs,
|
||||
getSecret,
|
||||
getShell,
|
||||
getWindowsExitReason,
|
||||
@@ -156,7 +162,116 @@ if (options["quiet"]) {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @typedef {Object} TestExpectation
|
||||
* @property {string} filename
|
||||
* @property {string[]} expectations
|
||||
* @property {string[] | undefined} bugs
|
||||
* @property {string[] | undefined} modifiers
|
||||
* @property {string | undefined} comment
|
||||
*/
|
||||
|
||||
/**
|
||||
* @returns {TestExpectation[]}
|
||||
*/
|
||||
function getTestExpectations() {
|
||||
const expectationsPath = join(cwd, "test", "expectations.txt");
|
||||
if (!existsSync(expectationsPath)) {
|
||||
return [];
|
||||
}
|
||||
const lines = readFileSync(expectationsPath, "utf-8").split(/\r?\n/);
|
||||
|
||||
/** @type {TestExpectation[]} */
|
||||
const expectations = [];
|
||||
|
||||
for (const line of lines) {
|
||||
const content = line.trim();
|
||||
if (!content || content.startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let comment;
|
||||
const commentIndex = content.indexOf("#");
|
||||
let cleanLine = content;
|
||||
if (commentIndex !== -1) {
|
||||
comment = content.substring(commentIndex + 1).trim();
|
||||
cleanLine = content.substring(0, commentIndex).trim();
|
||||
}
|
||||
|
||||
let modifiers = [];
|
||||
let remaining = cleanLine;
|
||||
let modifierMatch = remaining.match(/^\[(.*?)\]/);
|
||||
if (modifierMatch) {
|
||||
modifiers = modifierMatch[1].trim().split(/\s+/);
|
||||
remaining = remaining.substring(modifierMatch[0].length).trim();
|
||||
}
|
||||
|
||||
let expectationValues = ["Skip"];
|
||||
const expectationMatch = remaining.match(/\[(.*?)\]$/);
|
||||
if (expectationMatch) {
|
||||
expectationValues = expectationMatch[1].trim().split(/\s+/);
|
||||
remaining = remaining.substring(0, remaining.length - expectationMatch[0].length).trim();
|
||||
}
|
||||
|
||||
const filename = remaining.trim();
|
||||
if (filename) {
|
||||
expectations.push({
|
||||
filename,
|
||||
expectations: expectationValues,
|
||||
bugs: undefined,
|
||||
modifiers: modifiers.length ? modifiers : undefined,
|
||||
comment,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return expectations;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} testPath
|
||||
* @returns {string[]}
|
||||
*/
|
||||
function getTestModifiers(testPath) {
|
||||
const ext = extname(testPath);
|
||||
const filename = basename(testPath, ext);
|
||||
const modifiers = filename.split("-").filter(value => value !== "bun");
|
||||
|
||||
const os = getOs();
|
||||
const arch = getArch();
|
||||
modifiers.push(os, arch, `${os}-${arch}`);
|
||||
|
||||
const distro = getDistro();
|
||||
if (distro) {
|
||||
modifiers.push(distro, `${os}-${distro}`, `${os}-${arch}-${distro}`);
|
||||
const distroVersion = getDistroVersion();
|
||||
if (distroVersion) {
|
||||
modifiers.push(
|
||||
distroVersion,
|
||||
`${distro}-${distroVersion}`,
|
||||
`${os}-${distro}-${distroVersion}`,
|
||||
`${os}-${arch}-${distro}-${distroVersion}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const abi = getAbi();
|
||||
if (abi) {
|
||||
modifiers.push(abi, `${os}-${abi}`, `${os}-${arch}-${abi}`);
|
||||
const abiVersion = getAbiVersion();
|
||||
if (abiVersion) {
|
||||
modifiers.push(
|
||||
abiVersion,
|
||||
`${abi}-${abiVersion}`,
|
||||
`${os}-${abi}-${abiVersion}`,
|
||||
`${os}-${arch}-${abi}-${abiVersion}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return modifiers.map(value => value.toUpperCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<TestResult[]>}
|
||||
*/
|
||||
async function runTests() {
|
||||
@@ -168,10 +283,14 @@ async function runTests() {
|
||||
}
|
||||
!isQuiet && console.log("Bun:", execPath);
|
||||
|
||||
const expectations = getTestExpectations();
|
||||
const modifiers = getTestModifiers(execPath);
|
||||
!isQuiet && console.log("Modifiers:", modifiers);
|
||||
|
||||
const revision = getRevision(execPath);
|
||||
!isQuiet && console.log("Revision:", revision);
|
||||
|
||||
const tests = getRelevantTests(testsPath);
|
||||
const tests = getRelevantTests(testsPath, modifiers, expectations);
|
||||
!isQuiet && console.log("Running tests:", tests.length);
|
||||
|
||||
/** @type {VendorTest[] | undefined} */
|
||||
@@ -693,8 +812,8 @@ async function spawnBun(execPath, { args, cwd, timeout, env, stdout, stderr }) {
|
||||
BUN_RUNTIME_TRANSPILER_CACHE_PATH: "0",
|
||||
BUN_INSTALL_CACHE_DIR: tmpdirPath,
|
||||
SHELLOPTS: isWindows ? "igncr" : undefined, // ignore "\r" on Windows
|
||||
// Used in Node.js tests.
|
||||
TEST_TMPDIR: tmpdirPath,
|
||||
ASAN_OPTIONS: "allow_user_segv_handler=1",
|
||||
TEST_TMPDIR: tmpdirPath, // Used in Node.js tests.
|
||||
};
|
||||
|
||||
if (env) {
|
||||
@@ -1072,9 +1191,6 @@ function isHidden(path) {
|
||||
return /node_modules|node.js/.test(dirname(path)) || /^\./.test(basename(path));
|
||||
}
|
||||
|
||||
/** Files with these extensions are not treated as test cases */
|
||||
const IGNORED_EXTENSIONS = new Set([".md"]);
|
||||
|
||||
/**
|
||||
* @param {string} cwd
|
||||
* @returns {string[]}
|
||||
@@ -1084,13 +1200,14 @@ function getTests(cwd) {
|
||||
const dirname = join(cwd, path);
|
||||
for (const entry of readdirSync(dirname, { encoding: "utf-8", withFileTypes: true })) {
|
||||
const { name } = entry;
|
||||
const ext = name.slice(name.lastIndexOf("."));
|
||||
const filename = join(path, name);
|
||||
if (isHidden(filename) || IGNORED_EXTENSIONS.has(ext)) {
|
||||
if (isHidden(filename)) {
|
||||
continue;
|
||||
}
|
||||
if (entry.isFile() && isTest(filename)) {
|
||||
yield filename;
|
||||
if (entry.isFile()) {
|
||||
if (isTest(filename)) {
|
||||
yield filename;
|
||||
}
|
||||
} else if (entry.isDirectory()) {
|
||||
yield* getFiles(cwd, filename);
|
||||
}
|
||||
@@ -1226,9 +1343,11 @@ async function getVendorTests(cwd) {
|
||||
|
||||
/**
|
||||
* @param {string} cwd
|
||||
* @param {string[]} testModifiers
|
||||
* @param {TestExpectation[]} testExpectations
|
||||
* @returns {string[]}
|
||||
*/
|
||||
function getRelevantTests(cwd) {
|
||||
function getRelevantTests(cwd, testModifiers, testExpectations) {
|
||||
let tests = getTests(cwd);
|
||||
const availableTests = [];
|
||||
const filteredTests = [];
|
||||
@@ -1272,6 +1391,25 @@ function getRelevantTests(cwd) {
|
||||
}
|
||||
}
|
||||
|
||||
const skipExpectations = testExpectations
|
||||
.filter(
|
||||
({ modifiers, expectations }) =>
|
||||
!modifiers?.length || testModifiers.some(modifier => modifiers?.includes(modifier)),
|
||||
)
|
||||
.map(({ filename }) => filename.replace("test/", ""));
|
||||
if (skipExpectations.length) {
|
||||
const skippedTests = availableTests.filter(testPath => skipExpectations.some(filter => isMatch(testPath, filter)));
|
||||
if (skippedTests.length) {
|
||||
for (const testPath of skippedTests) {
|
||||
const index = availableTests.indexOf(testPath);
|
||||
if (index !== -1) {
|
||||
availableTests.splice(index, 1);
|
||||
}
|
||||
}
|
||||
!isQuiet && console.log("Skipping tests:", skipExpectations, skippedTests.length, "/", availableTests.length);
|
||||
}
|
||||
}
|
||||
|
||||
const shardId = parseInt(options["shard"]);
|
||||
const maxShards = parseInt(options["max-shards"]);
|
||||
if (filters?.length) {
|
||||
@@ -1368,13 +1506,17 @@ async function getExecPathFromBuildKite(target, buildId) {
|
||||
await spawnSafe({
|
||||
command: "buildkite-agent",
|
||||
args,
|
||||
timeout: 60000,
|
||||
});
|
||||
|
||||
for (const entry of readdirSync(releasePath, { recursive: true, encoding: "utf-8" })) {
|
||||
if (/^bun.*\.zip$/i.test(entry) && !entry.includes("-profile.zip")) {
|
||||
zipPath = join(releasePath, entry);
|
||||
break downloadLoop;
|
||||
}
|
||||
zipPath = readdirSync(releasePath, { recursive: true, encoding: "utf-8" })
|
||||
.filter(filename => /^bun.*\.zip$/i.test(filename))
|
||||
.map(filename => join(releasePath, filename))
|
||||
.sort((a, b) => b.includes("profile") - a.includes("profile"))
|
||||
.at(0);
|
||||
|
||||
if (zipPath) {
|
||||
break downloadLoop;
|
||||
}
|
||||
|
||||
console.warn(`Waiting for ${target}.zip to be available...`);
|
||||
@@ -1390,12 +1532,12 @@ async function getExecPathFromBuildKite(target, buildId) {
|
||||
const releaseFiles = readdirSync(releasePath, { recursive: true, encoding: "utf-8" });
|
||||
for (const entry of releaseFiles) {
|
||||
const execPath = join(releasePath, entry);
|
||||
if (/bun(?:\.exe)?$/i.test(entry) && statSync(execPath).isFile()) {
|
||||
if (/bun(?:-[a-z]+)?(?:\.exe)?$/i.test(entry) && statSync(execPath).isFile()) {
|
||||
return execPath;
|
||||
}
|
||||
}
|
||||
|
||||
console.warn(`Found ${releaseFiles.length} files in ${releasePath}:`);
|
||||
console.warn(`Found ${releaseFiles.length} files in ${releasePath}:`, releaseFiles);
|
||||
throw new Error(`Could not find executable from BuildKite: ${releasePath}`);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user