mirror of
https://github.com/denoland/deno.git
synced 2024-12-18 13:22:55 -05:00
193 lines
5.1 KiB
TypeScript
193 lines
5.1 KiB
TypeScript
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
|
import { partition } from "@std/collections/partition";
|
|
import { join } from "@std/path";
|
|
import * as JSONC from "@std/jsonc";
|
|
import { walk } from "@std/fs/walk";
|
|
import { relative } from "@std/path/posix/relative";
|
|
|
|
/**
|
|
* The test suite matches the folders inside the `test` folder inside the
|
|
* node repo
|
|
*
|
|
* Each test suite contains a list of files (which can be paths
|
|
* or a regex to match) that will be pulled from the node repo
|
|
*/
|
|
type TestSuites = Record<string, string[]>;
|
|
|
|
interface Config {
|
|
/** Ignored files won't regenerated by the update script */
|
|
ignore: TestSuites;
|
|
/**
|
|
* The files that will be run by the test suite
|
|
*
|
|
* The files to be generated with the update script must be listed here as well,
|
|
* but they won't be regenerated if they are listed in the `ignore` configuration
|
|
*/
|
|
tests: TestSuites;
|
|
windowsIgnore: TestSuites;
|
|
darwinIgnore: TestSuites;
|
|
}
|
|
|
|
export const config: Config = JSONC.parse(
|
|
await Deno.readTextFile(new URL("./config.jsonc", import.meta.url)),
|
|
) as unknown as Config;
|
|
|
|
export const ignoreList = Object.entries(config.ignore).reduce(
|
|
(total: RegExp[], [suite, paths]) => {
|
|
paths.forEach((path) => total.push(new RegExp(join(suite, path))));
|
|
return total;
|
|
},
|
|
[/package\.json/],
|
|
);
|
|
|
|
export function getPathsFromTestSuites(suites: TestSuites): string[] {
|
|
const testPaths: string[] = [];
|
|
for (const [dir, paths] of Object.entries(suites)) {
|
|
if (
|
|
["parallel", "internet", "pummel", "sequential", "pseudo-tty"].includes(
|
|
dir,
|
|
)
|
|
) {
|
|
for (const path of paths) {
|
|
testPaths.push(join(dir, path));
|
|
}
|
|
}
|
|
}
|
|
return testPaths;
|
|
}
|
|
|
|
const PARALLEL_PATTERN = /^parallel[\/\\]/;
|
|
|
|
export function partitionParallelTestPaths(
|
|
testPaths: string[],
|
|
): { parallel: string[]; sequential: string[] } {
|
|
const partitions = partition(testPaths, (p) => !!p.match(PARALLEL_PATTERN));
|
|
return { parallel: partitions[0], sequential: partitions[1] };
|
|
}
|
|
|
|
export const NODE_IGNORED_TEST_DIRS = [
|
|
"addons",
|
|
"async-hooks",
|
|
"cctest",
|
|
"common",
|
|
"doctool",
|
|
"embedding",
|
|
"fixtures",
|
|
"fuzzers",
|
|
"js-native-api",
|
|
"node-api",
|
|
"overlapped-checker",
|
|
"report",
|
|
"testpy",
|
|
"tick-processor",
|
|
"tools",
|
|
"v8-updates",
|
|
"wasi",
|
|
"wpt",
|
|
];
|
|
|
|
export const VENDORED_NODE_TEST = new URL(
|
|
"./runner/suite/test/",
|
|
import.meta.url,
|
|
);
|
|
export const NODE_COMPAT_TEST_DEST_URL = new URL(
|
|
"./test/",
|
|
import.meta.url,
|
|
);
|
|
|
|
export async function getNodeTests(): Promise<string[]> {
|
|
const paths: string[] = [];
|
|
const rootPath = VENDORED_NODE_TEST.href.slice(7);
|
|
for await (
|
|
const item of walk(VENDORED_NODE_TEST, { exts: [".js"] })
|
|
) {
|
|
const path = relative(rootPath, item.path);
|
|
if (NODE_IGNORED_TEST_DIRS.every((dir) => !path.startsWith(dir))) {
|
|
paths.push(path);
|
|
}
|
|
}
|
|
|
|
return paths.sort();
|
|
}
|
|
|
|
export async function getDenoTests() {
|
|
const paths: string[] = [];
|
|
const rootPath = NODE_COMPAT_TEST_DEST_URL.href.slice(7);
|
|
for await (
|
|
const item of walk(NODE_COMPAT_TEST_DEST_URL, { exts: [".js"] })
|
|
) {
|
|
const path = relative(rootPath, item.path);
|
|
paths.push(path);
|
|
}
|
|
|
|
return paths.sort();
|
|
}
|
|
|
|
let testSerialId = 0;
|
|
const cwd = new URL(".", import.meta.url);
|
|
|
|
export async function runNodeCompatTestCase(
|
|
testCase: string,
|
|
signal?: AbortSignal,
|
|
) {
|
|
const v8Flags = ["--stack-size=4000"];
|
|
const testSource = await Deno.readTextFile(testCase);
|
|
const envVars: Record<string, string> = {};
|
|
const knownGlobals: string[] = [];
|
|
parseFlags(testSource).forEach((flag) => {
|
|
switch (flag) {
|
|
case "--expose_externalize_string":
|
|
v8Flags.push("--expose-externalize-string");
|
|
knownGlobals.push("createExternalizableString");
|
|
break;
|
|
case "--expose-gc":
|
|
v8Flags.push("--expose-gc");
|
|
knownGlobals.push("gc");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
});
|
|
if (knownGlobals.length > 0) {
|
|
envVars["NODE_TEST_KNOWN_GLOBALS"] = knownGlobals.join(",");
|
|
}
|
|
// TODO(nathanwhit): once we match node's behavior on executing
|
|
// `node:test` tests when we run a file, we can remove this
|
|
const usesNodeTest = testSource.includes("node:test");
|
|
const args = [
|
|
usesNodeTest ? "test" : "run",
|
|
"-A",
|
|
"--quiet",
|
|
"--unstable-unsafe-proto",
|
|
"--unstable-bare-node-builtins",
|
|
"--unstable-fs",
|
|
"--v8-flags=" + v8Flags.join(),
|
|
];
|
|
if (usesNodeTest) {
|
|
// deno test typechecks by default + we want to pass script args
|
|
args.push("--no-check", "runner.ts", "--", testCase);
|
|
} else {
|
|
args.push("runner.ts", testCase);
|
|
}
|
|
|
|
// Pipe stdout in order to output each test result as Deno.test output
|
|
// That way the tests will respect the `--quiet` option when provided
|
|
return new Deno.Command(Deno.execPath(), {
|
|
args,
|
|
env: {
|
|
TEST_SERIAL_ID: String(testSerialId++),
|
|
...envVars,
|
|
},
|
|
cwd,
|
|
stdout: "piped",
|
|
stderr: "piped",
|
|
signal,
|
|
}).spawn();
|
|
}
|
|
|
|
/** Parses the special "Flags:"" syntax in Node.js test files */
|
|
function parseFlags(source: string): string[] {
|
|
const line = /^\/\/ Flags: (.+)$/um.exec(source);
|
|
if (line == null) return [];
|
|
return line[1].split(" ");
|
|
}
|