• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import del from "del";
2import fs from "fs";
3import os from "os";
4import path from "path";
5import cmdLineOptions from "./options.mjs";
6import { exec } from "./utils.mjs";
7import { findUpFile, findUpRoot } from "./findUpDir.mjs";
8
9const mochaJs = path.resolve(findUpRoot(), "node_modules", "mocha", "bin", "_mocha");
10export const localBaseline = "tests/baselines/local/";
11export const refBaseline = "tests/baselines/reference/";
12export const localRwcBaseline = "internal/baselines/rwc/local";
13export const refRwcBaseline = "internal/baselines/rwc/reference";
14export const localTest262Baseline = "internal/baselines/test262/local";
15
16/**
17 * @param {string} runJs
18 * @param {string} defaultReporter
19 * @param {boolean} runInParallel
20 */
21export async function runConsoleTests(runJs, defaultReporter, runInParallel) {
22    let testTimeout = cmdLineOptions.timeout;
23    const tests = cmdLineOptions.tests;
24    const inspect = cmdLineOptions.break || cmdLineOptions.inspect;
25    const runners = cmdLineOptions.runners;
26    const light = cmdLineOptions.light;
27    const stackTraceLimit = cmdLineOptions.stackTraceLimit;
28    const testConfigFile = "test.config";
29    const failed = cmdLineOptions.failed;
30    const keepFailed = cmdLineOptions.keepFailed;
31    const shards = +cmdLineOptions.shards || undefined;
32    const shardId = +cmdLineOptions.shardId || undefined;
33    if (!cmdLineOptions.dirty) {
34        await cleanTestDirs();
35    }
36
37    if (fs.existsSync(testConfigFile)) {
38        fs.unlinkSync(testConfigFile);
39    }
40
41    let workerCount, taskConfigsFolder;
42    if (runInParallel) {
43        // generate name to store task configuration files
44        const prefix = os.tmpdir() + "/ts-tests";
45        let i = 1;
46        do {
47            taskConfigsFolder = prefix + i;
48            i++;
49        } while (fs.existsSync(taskConfigsFolder));
50        fs.mkdirSync(taskConfigsFolder);
51
52        workerCount = cmdLineOptions.workers;
53    }
54
55    if (tests && tests.toLocaleLowerCase() === "rwc") {
56        testTimeout = 400000;
57    }
58
59    if (tests || runners || light || testTimeout || taskConfigsFolder || keepFailed || shards || shardId) {
60        writeTestConfigFile(tests, runners, light, taskConfigsFolder, workerCount, stackTraceLimit, testTimeout, keepFailed, shards, shardId);
61    }
62
63    const colors = cmdLineOptions.colors;
64    const reporter = cmdLineOptions.reporter || defaultReporter;
65
66    /** @type {string[]} */
67    const args = [];
68
69    // timeout normally isn't necessary but Travis-CI has been timing out on compiler baselines occasionally
70    // default timeout is 2sec which really should be enough, but maybe we just need a small amount longer
71    if (!runInParallel) {
72        args.push(mochaJs);
73        args.push("-R", findUpFile("scripts/failed-tests.cjs"));
74        args.push("-O", '"reporter=' + reporter + (keepFailed ? ",keepFailed=true" : "") + '"');
75        if (tests) {
76            args.push("-g", `"${tests}"`);
77        }
78        if (failed) {
79            const grep = fs.readFileSync(".failed-tests", "utf8")
80                .split(/\r?\n/g)
81                .map(test => test.trim())
82                .filter(test => test.length > 0)
83                .map(regExpEscape)
84                .join("|");
85            const file = path.join(os.tmpdir(), ".failed-tests.json");
86            fs.writeFileSync(file, JSON.stringify({ grep }), "utf8");
87            args.push("--config", file);
88        }
89        if (colors) {
90            args.push("--colors");
91        }
92        else {
93            args.push("--no-colors");
94        }
95        if (inspect !== undefined) {
96            args.unshift((inspect === "" || inspect === true) ? "--inspect-brk" : "--inspect-brk="+inspect);
97            args.push("-t", "0");
98        }
99        else {
100            args.push("-t", "" + testTimeout);
101        }
102        args.push(runJs);
103    }
104    else {
105        // run task to load all tests and partition them between workers
106        args.push(runJs);
107    }
108
109    /** @type {number | undefined} */
110    let errorStatus;
111
112    /** @type {Error | undefined} */
113    let error;
114
115    try {
116        setNodeEnvToDevelopment();
117        const { exitCode } = await exec(process.execPath, args);
118        if (exitCode !== 0) {
119            errorStatus = exitCode;
120            error = new Error(`Process exited with status code ${errorStatus}.`);
121        }
122    }
123    catch (e) {
124        errorStatus = undefined;
125        error = /** @type {Error} */ (e);
126    }
127    finally {
128        restoreSavedNodeEnv();
129    }
130
131    await del("test.config");
132    await deleteTemporaryProjectOutput();
133
134    if (error !== undefined) {
135        process.exitCode = typeof errorStatus === "number" ? errorStatus : 2;
136        throw error;
137    }
138}
139
140export async function cleanTestDirs() {
141    await del([localBaseline, localRwcBaseline]);
142    await fs.promises.mkdir(localRwcBaseline, { recursive: true });
143    await fs.promises.mkdir(localBaseline, { recursive: true });
144}
145
146/**
147 * used to pass data from command line directly to run.js
148 * @param {string} tests
149 * @param {string} runners
150 * @param {boolean} light
151 * @param {string} [taskConfigsFolder]
152 * @param {string | number} [workerCount]
153 * @param {string} [stackTraceLimit]
154 * @param {string | number} [timeout]
155 * @param {boolean} [keepFailed]
156 * @param {number | undefined} [shards]
157 * @param {number | undefined} [shardId]
158 */
159export function writeTestConfigFile(tests, runners, light, taskConfigsFolder, workerCount, stackTraceLimit, timeout, keepFailed, shards, shardId) {
160    const testConfigContents = JSON.stringify({
161        test: tests ? [tests] : undefined,
162        runners: runners ? runners.split(",") : undefined,
163        light,
164        workerCount,
165        stackTraceLimit,
166        taskConfigsFolder,
167        noColor: !cmdLineOptions.colors,
168        timeout,
169        keepFailed,
170        shards,
171        shardId
172    });
173    console.info("Running tests with config: " + testConfigContents);
174    fs.writeFileSync("test.config", testConfigContents);
175}
176
177/** @type {string | undefined} */
178let savedNodeEnv;
179function setNodeEnvToDevelopment() {
180    savedNodeEnv = process.env.NODE_ENV;
181    process.env.NODE_ENV = "development";
182}
183
184function restoreSavedNodeEnv() {
185    process.env.NODE_ENV = savedNodeEnv;
186}
187
188function deleteTemporaryProjectOutput() {
189    return del(path.join(localBaseline, "projectOutput/"));
190}
191
192/**
193 * @param {string} text
194 */
195function regExpEscape(text) {
196    return text.replace(/[.*+?^${}()|\[\]\\]/g, "\\$&");
197}
198