1import * as ts from "./_namespaces/ts"; 2import * as compiler from "./_namespaces/compiler"; 3import * as Utils from "./_namespaces/Utils"; 4import { Baseline, Compiler, IO, RunnerBase, TestCaseParser, TestRunnerKind } from "./_namespaces/Harness"; 5 6// In harness baselines, null is different than undefined. See `generateActual` in `harness.ts`. 7export class Test262BaselineRunner extends RunnerBase { 8 private static readonly basePath = "internal/cases/test262"; 9 private static readonly helpersFilePath = "tests/cases/test262-harness/helpers.d.ts"; 10 private static readonly helperFile: Compiler.TestFile = { 11 unitName: Test262BaselineRunner.helpersFilePath, 12 content: IO.readFile(Test262BaselineRunner.helpersFilePath)!, 13 }; 14 private static readonly testFileExtensionRegex = /\.js$/; 15 private static readonly options: ts.CompilerOptions = { 16 allowNonTsExtensions: true, 17 target: ts.ScriptTarget.Latest, 18 module: ts.ModuleKind.CommonJS 19 }; 20 private static readonly baselineOptions: Baseline.BaselineOptions = { 21 Subfolder: "test262", 22 Baselinefolder: "internal/baselines" 23 }; 24 25 private static getTestFilePath(filename: string): string { 26 return Test262BaselineRunner.basePath + "/" + filename; 27 } 28 29 private runTest(filePath: string) { 30 describe("test262 test for " + filePath, () => { 31 // Mocha holds onto the closure environment of the describe callback even after the test is done. 32 // Everything declared here should be cleared out in the "after" callback. 33 let testState: { 34 filename: string; 35 compilerResult: compiler.CompilationResult; 36 inputFiles: Compiler.TestFile[]; 37 }; 38 39 before(() => { 40 const content = IO.readFile(filePath)!; 41 const testFilename = ts.removeFileExtension(filePath).replace(/\//g, "_") + ".test"; 42 const testCaseContent = TestCaseParser.makeUnitsFromTest(content, testFilename); 43 44 const inputFiles: Compiler.TestFile[] = testCaseContent.testUnitData.map(unit => { 45 const unitName = Test262BaselineRunner.getTestFilePath(unit.name); 46 return { unitName, content: unit.content }; 47 }); 48 49 // Emit the results 50 testState = { 51 filename: testFilename, 52 inputFiles, 53 compilerResult: undefined!, // TODO: GH#18217 54 }; 55 56 testState.compilerResult = Compiler.compileFiles( 57 [Test262BaselineRunner.helperFile].concat(inputFiles), 58 /*otherFiles*/ [], 59 /* harnessOptions */ undefined, 60 Test262BaselineRunner.options, 61 /* currentDirectory */ undefined); 62 }); 63 64 after(() => { 65 testState = undefined!; 66 }); 67 68 it("has the expected emitted code", () => { 69 const files = Array.from(testState.compilerResult.js.values()).filter(f => f.file !== Test262BaselineRunner.helpersFilePath); 70 Baseline.runBaseline(testState.filename + ".output.js", Compiler.collateOutputs(files), Test262BaselineRunner.baselineOptions); 71 }); 72 73 it("has the expected errors", () => { 74 const errors = testState.compilerResult.diagnostics; 75 // eslint-disable-next-line no-null/no-null 76 const baseline = errors.length === 0 ? null : Compiler.getErrorBaseline(testState.inputFiles, errors); 77 Baseline.runBaseline(testState.filename + ".errors.txt", baseline, Test262BaselineRunner.baselineOptions); 78 }); 79 80 it("satisfies invariants", () => { 81 const sourceFile = testState.compilerResult.program!.getSourceFile(Test262BaselineRunner.getTestFilePath(testState.filename)); 82 Utils.assertInvariants(sourceFile, /*parent:*/ undefined); 83 }); 84 85 it("has the expected AST", () => { 86 const sourceFile = testState.compilerResult.program!.getSourceFile(Test262BaselineRunner.getTestFilePath(testState.filename))!; 87 Baseline.runBaseline(testState.filename + ".AST.txt", Utils.sourceFileToJSON(sourceFile), Test262BaselineRunner.baselineOptions); 88 }); 89 }); 90 } 91 92 public kind(): TestRunnerKind { 93 return "test262"; 94 } 95 96 public enumerateTestFiles() { 97 // see also: `enumerateTestFiles` in tests/webTestServer.ts 98 return ts.map(this.enumerateFiles(Test262BaselineRunner.basePath, Test262BaselineRunner.testFileExtensionRegex, { recursive: true }), ts.normalizePath); 99 } 100 101 public initializeTests() { 102 // this will set up a series of describe/it blocks to run between the setup and cleanup phases 103 if (this.tests.length === 0) { 104 const testFiles = this.getTestFiles(); 105 testFiles.forEach(fn => { 106 this.runTest(fn); 107 }); 108 } 109 else { 110 this.tests.forEach(test => this.runTest(typeof test === "string" ? test : test.file)); 111 } 112 } 113}