Files
TypeScript/src/harness/rwcRunner.ts
Nathan Shively-Sanders 4e4f7507d0 Fix getDefaultLibraryFile + turn off lib
1. getDefaultLibraryFile should use ts to normalise the file and find
the filename.
2. lib should be turned off at the same time that noLib is turned on to
avoid a pointless error.
2017-11-07 16:08:57 -08:00

258 lines
13 KiB
TypeScript

/// <reference path="harness.ts"/>
/// <reference path="runnerbase.ts" />
/// <reference path="loggedIO.ts" />
/// <reference path="..\compiler\commandLineParser.ts"/>
// In harness baselines, null is different than undefined. See `generateActual` in `harness.ts`.
/* tslint:disable:no-null-keyword */
namespace RWC {
function runWithIOLog(ioLog: IoLog, fn: (oldIO: Harness.Io) => void) {
const oldIO = Harness.IO;
const wrappedIO = Playback.wrapIO(oldIO);
wrappedIO.startReplayFromData(ioLog);
Harness.IO = wrappedIO;
try {
fn(oldIO);
} finally {
wrappedIO.endReplay();
Harness.IO = oldIO;
}
}
function isTsConfigFile(file: { path: string }): boolean {
return file.path.indexOf("tsconfig") !== -1 && file.path.indexOf("json") !== -1;
}
export function runRWCTest(jsonPath: string) {
describe("Testing a rwc project: " + jsonPath, () => {
let inputFiles: Harness.Compiler.TestFile[] = [];
let otherFiles: Harness.Compiler.TestFile[] = [];
let tsconfigFiles: Harness.Compiler.TestFile[] = [];
let compilerResult: Harness.Compiler.CompilerResult;
let compilerOptions: ts.CompilerOptions;
const baselineOpts: Harness.Baseline.BaselineOptions = {
Subfolder: "rwc",
Baselinefolder: "internal/baselines"
};
const baseName = ts.getBaseFileName(jsonPath);
let currentDirectory: string;
let useCustomLibraryFile: boolean;
after(() => {
// Mocha holds onto the closure environment of the describe callback even after the test is done.
// Therefore we have to clean out large objects after the test is done.
inputFiles = [];
otherFiles = [];
tsconfigFiles = [];
compilerResult = undefined;
compilerOptions = undefined;
currentDirectory = undefined;
// useCustomLibraryFile is a flag specified in the json object to indicate whether to use built/local/lib.d.ts
// or to use lib.d.ts inside the json object. If the flag is true, use the lib.d.ts inside json file
// otherwise use the lib.d.ts from built/local
useCustomLibraryFile = undefined;
});
it("can compile", function(this: Mocha.ITestCallbackContext) {
this.timeout(800000); // Allow long timeouts for RWC compilations
let opts: ts.ParsedCommandLine;
const ioLog: IoLog = Playback.newStyleLogIntoOldStyleLog(JSON.parse(Harness.IO.readFile(`internal/cases/rwc/${jsonPath}/test.json`)), Harness.IO, `internal/cases/rwc/${baseName}`);
currentDirectory = ioLog.currentDirectory;
useCustomLibraryFile = ioLog.useCustomLibraryFile;
runWithIOLog(ioLog, () => {
opts = ts.parseCommandLine(ioLog.arguments, fileName => Harness.IO.readFile(fileName));
assert.equal(opts.errors.length, 0);
// To provide test coverage of output javascript file,
// we will set noEmitOnError flag to be false.
opts.options.noEmitOnError = false;
});
runWithIOLog(ioLog, oldIO => {
let fileNames = opts.fileNames;
const tsconfigFile = ts.forEach(ioLog.filesRead, f => isTsConfigFile(f) ? f : undefined);
if (tsconfigFile) {
const tsconfigFileContents = getHarnessCompilerInputUnit(tsconfigFile.path);
tsconfigFiles.push({ unitName: tsconfigFile.path, content: tsconfigFileContents.content });
const parsedTsconfigFileContents = ts.parseJsonText(tsconfigFile.path, tsconfigFileContents.content);
const configParseHost: ts.ParseConfigHost = {
useCaseSensitiveFileNames: Harness.IO.useCaseSensitiveFileNames(),
fileExists: Harness.IO.fileExists,
readDirectory: Harness.IO.readDirectory,
readFile: Harness.IO.readFile
};
const configParseResult = ts.parseJsonSourceFileConfigFileContent(parsedTsconfigFileContents, configParseHost, ts.getDirectoryPath(tsconfigFile.path));
fileNames = configParseResult.fileNames;
opts.options = ts.extend(opts.options, configParseResult.options);
ts.setConfigFileInOptions(opts.options, configParseResult.options.configFile);
}
// Deduplicate files so they are only printed once in baselines (they are deduplicated within the compiler already)
const uniqueNames = ts.createMap<true>();
for (const fileName of fileNames) {
// Must maintain order, build result list while checking map
const normalized = ts.normalizeSlashes(fileName);
if (!uniqueNames.has(normalized)) {
uniqueNames.set(normalized, true);
// Load the file
inputFiles.push(getHarnessCompilerInputUnit(fileName));
}
}
// Add files to compilation
const isInInputList = (resolvedPath: string) => (inputFile: { unitName: string; content: string; }) => inputFile.unitName === resolvedPath;
for (const fileRead of ioLog.filesRead) {
// Check if the file is already added into the set of input files.
const resolvedPath = ts.normalizeSlashes(Harness.IO.resolvePath(fileRead.path));
const inInputList = ts.forEach(inputFiles, isInInputList(resolvedPath));
if (isTsConfigFile(fileRead)) {
continue;
}
if (!Harness.isDefaultLibraryFile(fileRead.path)) {
if (inInputList) {
continue;
}
otherFiles.push(getHarnessCompilerInputUnit(fileRead.path));
}
else if (!opts.options.noLib && Harness.isDefaultLibraryFile(fileRead.path)) {
if (!inInputList) {
// If useCustomLibraryFile is true, we will use lib.d.ts from json object
// otherwise use the lib.d.ts from built/local
// Majority of RWC code will be using built/local/lib.d.ts instead of
// lib.d.ts inside json file. However, some RWC cases will still use
// their own version of lib.d.ts because they have customized lib.d.ts
if (useCustomLibraryFile) {
inputFiles.push(getHarnessCompilerInputUnit(fileRead.path));
}
else {
// set the flag to put default library to the beginning of the list
inputFiles.unshift(Harness.getDefaultLibraryFile(fileRead.path, oldIO));
}
}
}
}
// do not use lib since we already read it in above
opts.options.lib = undefined;
opts.options.noLib = true;
// Emit the results
compilerOptions = undefined;
const output = Harness.Compiler.compileFiles(
inputFiles,
otherFiles,
/* harnessOptions */ undefined,
opts.options,
// Since each RWC json file specifies its current directory in its json file, we need
// to pass this information in explicitly instead of acquiring it from the process.
currentDirectory);
compilerOptions = output.options;
compilerResult = output.result;
});
function getHarnessCompilerInputUnit(fileName: string): Harness.Compiler.TestFile {
const unitName = ts.normalizeSlashes(Harness.IO.resolvePath(fileName));
let content: string;
try {
content = Harness.IO.readFile(unitName);
}
catch (e) {
content = Harness.IO.readFile(fileName);
}
return { unitName, content };
}
});
it("has the expected emitted code", () => {
Harness.Baseline.runMultifileBaseline(baseName, "", () => {
return Harness.Compiler.iterateOutputs(compilerResult.files);
}, baselineOpts, [".js", ".jsx"]);
});
it("has the expected declaration file content", () => {
Harness.Baseline.runMultifileBaseline(baseName, "", () => {
if (!compilerResult.declFilesCode.length) {
return null;
}
return Harness.Compiler.iterateOutputs(compilerResult.declFilesCode);
}, baselineOpts, [".d.ts"]);
});
it("has the expected source maps", () => {
Harness.Baseline.runMultifileBaseline(baseName, "", () => {
if (!compilerResult.sourceMaps.length) {
return null;
}
return Harness.Compiler.iterateOutputs(compilerResult.sourceMaps);
}, baselineOpts, [".map"]);
});
it("has the expected errors", () => {
Harness.Baseline.runMultifileBaseline(baseName, ".errors.txt", () => {
if (compilerResult.errors.length === 0) {
return null;
}
// Do not include the library in the baselines to avoid noise
const baselineFiles = tsconfigFiles.concat(inputFiles, otherFiles).filter(f => !Harness.isDefaultLibraryFile(f.unitName));
const errors = compilerResult.errors.filter(e => !e.file || !Harness.isDefaultLibraryFile(e.file.fileName));
return Harness.Compiler.iterateErrorBaseline(baselineFiles, errors);
}, baselineOpts);
});
// Ideally, a generated declaration file will have no errors. But we allow generated
// declaration file errors as part of the baseline.
it("has the expected errors in generated declaration files", () => {
if (compilerOptions.declaration && !compilerResult.errors.length) {
Harness.Baseline.runMultifileBaseline(baseName, ".dts.errors.txt", () => {
if (compilerResult.errors.length === 0) {
return null;
}
const declContext = Harness.Compiler.prepareDeclarationCompilationContext(
inputFiles, otherFiles, compilerResult, /*harnessSettings*/ undefined, compilerOptions, currentDirectory
);
// Reset compilerResult before calling into `compileDeclarationFiles` so the memory from the original compilation can be freed
compilerResult = undefined;
const declFileCompilationResult = Harness.Compiler.compileDeclarationFiles(declContext);
return Harness.Compiler.iterateErrorBaseline(tsconfigFiles.concat(declFileCompilationResult.declInputFiles, declFileCompilationResult.declOtherFiles), declFileCompilationResult.declResult.errors);
}, baselineOpts);
}
});
});
}
}
class RWCRunner extends RunnerBase {
public enumerateTestFiles() {
return Harness.IO.getDirectories("internal/cases/rwc/");
}
public kind(): TestRunnerKind {
return "rwc";
}
/** Setup the runner's tests so that they are ready to be executed by the harness
* The first test should be a describe/it block that sets up the harness's compiler instance appropriately
*/
public initializeTests(): void {
// Read in and evaluate the test list
for (const test of this.tests && this.tests.length ? this.tests : this.enumerateTestFiles()) {
this.runTest(test);
}
}
private runTest(jsonFileName: string) {
RWC.runRWCTest(jsonFileName);
}
}