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}