• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import * as vpath from "./_namespaces/vpath";
2import * as ts from "./_namespaces/ts";
3import * as FourSlash from "./_namespaces/FourSlash";
4import * as project from "./_namespaces/project";
5import * as RWC from "./_namespaces/RWC";
6import {
7    CompilerBaselineRunner, CompilerTestType, DefinitelyTypedRunner, DockerfileRunner, FourSlashRunner,
8    GeneratedFourslashRunner, IO, Parallel, RunnerBase, setLightMode, setShardId, setShards, Test262BaselineRunner,
9    TestRunnerKind, UserCodeRunner,
10} from "./_namespaces/Harness";
11
12/* eslint-disable prefer-const */
13export let runners: RunnerBase[] = [];
14export let iterations = 1;
15/* eslint-enable prefer-const */
16
17function runTests(runners: RunnerBase[]) {
18    for (let i = iterations; i > 0; i--) {
19        const seen = new Map<string, string>();
20        const dupes: [string, string][] = [];
21        for (const runner of runners) {
22            if (runner instanceof CompilerBaselineRunner || runner instanceof FourSlashRunner) {
23                for (const sf of runner.enumerateTestFiles()) {
24                    const full = typeof sf === "string" ? sf : sf.file;
25                    const base = vpath.basename(full).toLowerCase();
26                    // allow existing dupes in fourslash/shims and fourslash/server
27                    if (seen.has(base) && !/fourslash\/(shim|server)/.test(full)) {
28                        dupes.push([seen.get(base)!, full]);
29                    }
30                    else {
31                        seen.set(base, full);
32                    }
33                }
34            }
35            runner.initializeTests();
36        }
37        if (dupes.length) {
38            throw new Error(`${dupes.length} Tests with duplicate baseline names:
39${JSON.stringify(dupes, undefined, 2)}`);
40        }
41    }
42}
43
44function tryGetConfig(args: string[]) {
45    const prefix = "--config=";
46    const configPath = ts.forEach(args, arg => arg.lastIndexOf(prefix, 0) === 0 && arg.substr(prefix.length));
47    // strip leading and trailing quotes from the path (necessary on Windows since shell does not do it automatically)
48    return configPath && configPath.replace(/(^[\"'])|([\"']$)/g, "");
49}
50
51export function createRunner(kind: TestRunnerKind): RunnerBase {
52    switch (kind) {
53        case "conformance":
54            return new CompilerBaselineRunner(CompilerTestType.Conformance);
55        case "compiler":
56            return new CompilerBaselineRunner(CompilerTestType.Regressions);
57        case "compiler-oh":
58            return new CompilerBaselineRunner(CompilerTestType.OH);
59        case "fourslash":
60            return new FourSlashRunner(FourSlash.FourSlashTestType.Native);
61        case "fourslash-oh":
62            return new FourSlashRunner(FourSlash.FourSlashTestType.OH);
63        case "fourslash-shims":
64            return new FourSlashRunner(FourSlash.FourSlashTestType.Shims);
65        case "fourslash-shims-pp":
66            return new FourSlashRunner(FourSlash.FourSlashTestType.ShimsWithPreprocess);
67        case "fourslash-server":
68            return new FourSlashRunner(FourSlash.FourSlashTestType.Server);
69        case "project":
70            return new project.ProjectRunner();
71        case "rwc":
72            return new RWC.RWCRunner();
73        case "test262":
74            return new Test262BaselineRunner();
75        case "user":
76            return new UserCodeRunner();
77        case "dt":
78            return new DefinitelyTypedRunner();
79        case "docker":
80            return new DockerfileRunner();
81    }
82    return ts.Debug.fail(`Unknown runner kind ${kind}`);
83}
84
85// users can define tests to run in mytest.config that will override cmd line args, otherwise use cmd line args (test.config), otherwise no options
86
87const mytestconfigFileName = "mytest.config";
88const testconfigFileName = "test.config";
89
90const customConfig = tryGetConfig(IO.args());
91const testConfigContent =
92    customConfig && IO.fileExists(customConfig)
93        ? IO.readFile(customConfig)!
94        : IO.fileExists(mytestconfigFileName)
95            ? IO.readFile(mytestconfigFileName)!
96            : IO.fileExists(testconfigFileName) ? IO.readFile(testconfigFileName)! : "";
97
98export let taskConfigsFolder: string;
99export let workerCount: number;
100export let runUnitTests: boolean | undefined;
101export let stackTraceLimit: number | "full" | undefined;
102export let noColors = false;
103export let keepFailed = false;
104
105export interface TestConfig {
106    light?: boolean;
107    taskConfigsFolder?: string;
108    listenForWork?: boolean;
109    workerCount?: number;
110    stackTraceLimit?: number | "full";
111    test?: string[];
112    runners?: string[];
113    runUnitTests?: boolean;
114    noColors?: boolean;
115    timeout?: number;
116    keepFailed?: boolean;
117    shardId?: number;
118    shards?: number;
119}
120
121export interface TaskSet {
122    runner: TestRunnerKind;
123    files: string[];
124}
125
126export let configOption: string;
127export let globalTimeout: number;
128function handleTestConfig() {
129    if (testConfigContent !== "") {
130        const testConfig = JSON.parse(testConfigContent) as TestConfig;
131        if (testConfig.light) {
132            setLightMode(true);
133        }
134        if (testConfig.timeout) {
135            globalTimeout = testConfig.timeout;
136        }
137        runUnitTests = testConfig.runUnitTests;
138        if (testConfig.workerCount) {
139            workerCount = +testConfig.workerCount;
140        }
141        if (testConfig.taskConfigsFolder) {
142            taskConfigsFolder = testConfig.taskConfigsFolder;
143        }
144        if (testConfig.noColors !== undefined) {
145            noColors = testConfig.noColors;
146        }
147        if (testConfig.keepFailed) {
148            keepFailed = true;
149        }
150        if (testConfig.shardId) {
151            setShardId(testConfig.shardId);
152        }
153        if (testConfig.shards) {
154            setShards(testConfig.shards);
155        }
156
157        if (testConfig.stackTraceLimit === "full") {
158            (Error as any).stackTraceLimit = Infinity;
159            stackTraceLimit = testConfig.stackTraceLimit;
160        }
161        else if ((+testConfig.stackTraceLimit! | 0) > 0) {
162            (Error as any).stackTraceLimit = +testConfig.stackTraceLimit! | 0;
163            stackTraceLimit = +testConfig.stackTraceLimit! | 0;
164        }
165        if (testConfig.listenForWork) {
166            return true;
167        }
168
169        const runnerConfig = testConfig.runners || testConfig.test;
170        if (runnerConfig && runnerConfig.length > 0) {
171            if (testConfig.runners) {
172                runUnitTests = runnerConfig.indexOf("unittest") !== -1;
173            }
174            for (const option of runnerConfig) {
175                if (!option) {
176                    continue;
177                }
178
179                if (!configOption) {
180                    configOption = option;
181                }
182                else {
183                    configOption += "+" + option;
184                }
185
186                switch (option) {
187                    case "compiler":
188                        runners.push(new CompilerBaselineRunner(CompilerTestType.Conformance));
189                        runners.push(new CompilerBaselineRunner(CompilerTestType.Regressions));
190                        break;
191                    case "compiler-oh":
192                        runners.push(new CompilerBaselineRunner(CompilerTestType.OH));
193                        break;
194                    case "conformance":
195                        runners.push(new CompilerBaselineRunner(CompilerTestType.Conformance));
196                        break;
197                    case "project":
198                        runners.push(new project.ProjectRunner());
199                        break;
200                    case "fourslash":
201                        runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.Native));
202                        break;
203                    case "fourslash-oh":
204                        runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.OH));
205                        break;
206                    case "fourslash-shims":
207                        runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.Shims));
208                        break;
209                    case "fourslash-shims-pp":
210                        runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.ShimsWithPreprocess));
211                        break;
212                    case "fourslash-server":
213                        runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.Server));
214                        break;
215                    case "fourslash-generated":
216                        runners.push(new GeneratedFourslashRunner(FourSlash.FourSlashTestType.Native));
217                        break;
218                    case "rwc":
219                        runners.push(new RWC.RWCRunner());
220                        break;
221                    case "test262":
222                        runners.push(new Test262BaselineRunner());
223                        break;
224                    case "user":
225                        runners.push(new UserCodeRunner());
226                        break;
227                    case "dt":
228                        runners.push(new DefinitelyTypedRunner());
229                        break;
230                    case "docker":
231                        runners.push(new DockerfileRunner());
232                        break;
233                }
234            }
235        }
236    }
237
238    if (runners.length === 0) {
239        // compiler
240        runners.push(new CompilerBaselineRunner(CompilerTestType.Conformance));
241        runners.push(new CompilerBaselineRunner(CompilerTestType.Regressions));
242        runners.push(new CompilerBaselineRunner(CompilerTestType.OH));
243        runners.push(new project.ProjectRunner());
244
245        // language services
246        runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.Native));
247        runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.OH));
248        runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.Shims));
249        runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.ShimsWithPreprocess));
250        runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.Server));
251        // runners.push(new GeneratedFourslashRunner());
252
253        // CRON-only tests
254        if (process.env.TRAVIS_EVENT_TYPE === "cron") {
255            runners.push(new UserCodeRunner());
256            runners.push(new DockerfileRunner());
257        }
258    }
259    if (runUnitTests === undefined) {
260        runUnitTests = runners.length !== 1; // Don't run unit tests when running only one runner if unit tests were not explicitly asked for
261    }
262    return false;
263}
264
265function beginTests() {
266    ts.Debug.loggingHost = {
267        log(_level, s) {
268            console.log(s || "");
269        }
270    };
271
272    if (ts.Debug.isDebugging) {
273        ts.Debug.enableDebugInfo();
274    }
275
276    // run tests in en-US by default.
277    let savedUILocale: string | undefined;
278    beforeEach(() => {
279        savedUILocale = ts.getUILocale();
280        ts.setUILocale("en-US");
281    });
282    afterEach(() => ts.setUILocale(savedUILocale));
283
284    runTests(runners);
285
286    if (!runUnitTests) {
287        // patch `describe` to skip unit tests
288        (global as any).describe = ts.noop;
289    }
290}
291
292export let isWorker: boolean;
293function startTestEnvironment() {
294    // For debugging convenience.
295    (globalThis as any).ts = ts;
296
297    isWorker = handleTestConfig();
298    if (isWorker) {
299        return Parallel.Worker.start();
300    }
301    else if (taskConfigsFolder && workerCount && workerCount > 1) {
302        return Parallel.Host.start();
303    }
304    beginTests();
305}
306
307startTestEnvironment();
308
309
310// This brings in all of the unittests.
311
312// If running as emitted CJS, we want to start the tests here after startTestEnvironment.
313// If running bundled, we will do this in Harness.ts.
314if (__filename.endsWith("runner.js")) {
315    require("./tests");
316}