summaryrefslogtreecommitdiff
path: root/tests/node_compat/test.ts
diff options
context:
space:
mode:
Diffstat (limited to 'tests/node_compat/test.ts')
-rw-r--r--tests/node_compat/test.ts168
1 files changed, 168 insertions, 0 deletions
diff --git a/tests/node_compat/test.ts b/tests/node_compat/test.ts
new file mode 100644
index 000000000..13ff429b5
--- /dev/null
+++ b/tests/node_compat/test.ts
@@ -0,0 +1,168 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+/**
+ * This script will run the test files specified in the configuration file.
+ *
+ * Each test file will be run independently (in a separate process as this is
+ * what Node.js is doing) and we wait until it completes. If the process reports
+ * an abnormal code, the test is reported and the test suite will fail
+ * immediately.
+ *
+ * Some tests check for presence of certain `process.exitCode`.
+ * Some tests depends on directories/files created by other tests - they must
+ * all share the same working directory.
+ */
+
+import { magenta } from "@test_util/std/fmt/colors.ts";
+import { pooledMap } from "@test_util/std/async/pool.ts";
+import { dirname, fromFileUrl, join } from "@test_util/std/path/mod.ts";
+import { fail } from "@test_util/std/assert/mod.ts";
+import {
+ config,
+ getPathsFromTestSuites,
+ partitionParallelTestPaths,
+} from "./common.ts";
+
+// If the test case is invoked like
+// deno test -A cli/tests/node_compat/test.ts -- <test-names>
+// Use the <test-names> as filters
+const filters = Deno.args;
+const hasFilters = filters.length > 0;
+const toolsPath = dirname(fromFileUrl(import.meta.url));
+const testPaths = partitionParallelTestPaths(
+ getPathsFromTestSuites(config.tests),
+);
+const cwd = new URL(".", import.meta.url);
+const windowsIgnorePaths = new Set(
+ getPathsFromTestSuites(config.windowsIgnore),
+);
+const darwinIgnorePaths = new Set(
+ getPathsFromTestSuites(config.darwinIgnore),
+);
+
+const decoder = new TextDecoder();
+let testSerialId = 0;
+
+async function runTest(t: Deno.TestContext, path: string): Promise<void> {
+ // If filter patterns are given and any pattern doesn't match
+ // to the file path, then skip the case
+ if (
+ filters.length > 0 &&
+ filters.every((pattern) => !path.includes(pattern))
+ ) {
+ return;
+ }
+ const ignore =
+ (Deno.build.os === "windows" && windowsIgnorePaths.has(path)) ||
+ (Deno.build.os === "darwin" && darwinIgnorePaths.has(path));
+ await t.step({
+ name: `Node.js compatibility "${path}"`,
+ ignore,
+ sanitizeOps: false,
+ sanitizeResources: false,
+ sanitizeExit: false,
+ fn: async () => {
+ const testCase = join(toolsPath, "test", path);
+
+ const v8Flags = ["--stack-size=4000"];
+ const testSource = await Deno.readTextFile(testCase);
+ const envVars: Record<string, string> = {};
+ // TODO(kt3k): Parse `Flags` directive correctly
+ if (testSource.includes("Flags: --expose_externalize_string")) {
+ v8Flags.push("--expose-externalize-string");
+ // TODO(bartlomieju): disable verifying globals if that V8 flag is
+ // present. Even though we should be able to pass a list of globals
+ // that are allowed, it doesn't work, because the list is expected to
+ // contain actual JS objects, not strings :)).
+ envVars["NODE_TEST_KNOWN_GLOBALS"] = "0";
+ }
+
+ const args = [
+ "run",
+ "-A",
+ "--quiet",
+ //"--unsafely-ignore-certificate-errors",
+ "--unstable-bare-node-builtins",
+ "--v8-flags=" + v8Flags.join(),
+ "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
+ const command = new Deno.Command(Deno.execPath(), {
+ args,
+ env: {
+ TEST_SERIAL_ID: String(testSerialId++),
+ ...envVars,
+ },
+ cwd,
+ });
+ const { code, stdout, stderr } = await command.output();
+
+ if (code !== 0) {
+ // If the test case failed, show the stdout, stderr, and instruction
+ // for repeating the single test case.
+ if (stdout.length) {
+ console.log(decoder.decode(stdout));
+ }
+ const stderrOutput = decoder.decode(stderr);
+ const repeatCmd = magenta(
+ `./target/debug/deno test -A cli/tests/node_compat/test.ts -- ${path}`,
+ );
+ const msg = `"${magenta(path)}" failed:
+
+${stderrOutput}
+
+You can repeat only this test with the command:
+
+ ${repeatCmd}
+`;
+ console.log(msg);
+ fail(msg);
+ } else if (hasFilters) {
+ // Even if the test case is successful, shows the stdout and stderr
+ // when test case filtering is specified.
+ if (stdout.length) console.log(decoder.decode(stdout));
+ if (stderr.length) console.log(decoder.decode(stderr));
+ }
+ },
+ });
+}
+
+Deno.test("Node.js compatibility", async (t) => {
+ for (const path of testPaths.sequential) {
+ await runTest(t, path);
+ }
+ const testPool = pooledMap(
+ navigator.hardwareConcurrency,
+ testPaths.parallel,
+ (path) => runTest(t, path),
+ );
+ const testCases = [];
+ for await (const testCase of testPool) {
+ testCases.push(testCase);
+ }
+ await Promise.all(testCases);
+});
+
+function checkConfigTestFilesOrder(testFileLists: Array<string[]>) {
+ for (const testFileList of testFileLists) {
+ const sortedTestList = JSON.parse(JSON.stringify(testFileList));
+ sortedTestList.sort();
+ if (JSON.stringify(testFileList) !== JSON.stringify(sortedTestList)) {
+ throw new Error(
+ `File names in \`config.json\` are not correct order.`,
+ );
+ }
+ }
+}
+
+if (!hasFilters) {
+ Deno.test("checkConfigTestFilesOrder", function () {
+ checkConfigTestFilesOrder([
+ ...Object.keys(config.ignore).map((suite) => config.ignore[suite]),
+ ...Object.keys(config.tests).map((suite) => config.tests[suite]),
+ ]);
+ });
+}