Merge pull request #495 from Microsoft/rwcRunner
Simplifies RWC Runner to use HarnessCompiler.compileFiles so it can be used to baseline errors and sourcemaps more like compiler runner
This commit is contained in:
commit
753364768e
1
Jakefile
1
Jakefile
|
@ -71,6 +71,7 @@ var harnessSources = [
|
|||
"fourslashRunner.ts",
|
||||
"projectsRunner.ts",
|
||||
"unittestrunner.ts",
|
||||
"loggedIO.ts",
|
||||
"rwcRunner.ts",
|
||||
"runner.ts"
|
||||
].map(function (f) {
|
||||
|
|
|
@ -1898,9 +1898,9 @@ module FourSlash {
|
|||
fourslashSourceFile = fourslashSourceFile || ts.createSourceFile(tsFn, Harness.IO.readFile(tsFn), ts.ScriptTarget.ES5, /*version*/ 0, /*isOpen*/ false);
|
||||
|
||||
var files: { [filename: string]: ts.SourceFile; } = {};
|
||||
files[fourslashFilename] = fourslashSourceFile;
|
||||
files[fileName] = ts.createSourceFile(fileName, content, ts.ScriptTarget.ES5, /*version*/ 0, /*isOpen*/ false);
|
||||
files[Harness.Compiler.defaultLibFileName] = Harness.Compiler.defaultLibSourceFile;
|
||||
files[ts.getCanonicalFileName(fourslashFilename)] = fourslashSourceFile;
|
||||
files[ts.getCanonicalFileName(fileName)] = ts.createSourceFile(fileName, content, ts.ScriptTarget.ES5, /*version*/ 0, /*isOpen*/ false);
|
||||
files[ts.getCanonicalFileName(Harness.Compiler.defaultLibFileName)] = Harness.Compiler.defaultLibSourceFile;
|
||||
|
||||
var host = Harness.Compiler.createCompilerHost(files, (fn, contents) => result = contents);
|
||||
var program = ts.createProgram([fourslashFilename, fileName], { out: "fourslashTestOutput.js" }, host);
|
||||
|
|
|
@ -539,8 +539,8 @@ module Harness {
|
|||
getCurrentDirectory: sys.getCurrentDirectory,
|
||||
getCancellationToken: (): any => undefined,
|
||||
getSourceFile: (fn, languageVersion) => {
|
||||
if (fn in filemap) {
|
||||
return filemap[fn];
|
||||
if (Object.prototype.hasOwnProperty.call(filemap, ts.getCanonicalFileName(fn))) {
|
||||
return filemap[ts.getCanonicalFileName(fn)];
|
||||
} else {
|
||||
var lib = defaultLibFileName;
|
||||
if (fn === defaultLibFileName) {
|
||||
|
@ -729,7 +729,7 @@ module Harness {
|
|||
var filemap: { [name: string]: ts.SourceFile; } = {};
|
||||
var register = (file: { unitName: string; content: string; }) => {
|
||||
var filename = Path.switchToForwardSlashes(file.unitName);
|
||||
filemap[filename] = ts.createSourceFile(filename, file.content, options.target);
|
||||
filemap[ts.getCanonicalFileName(filename)] = ts.createSourceFile(filename, file.content, options.target);
|
||||
};
|
||||
inputFiles.forEach(register);
|
||||
otherFiles.forEach(register);
|
||||
|
@ -773,6 +773,21 @@ module Harness {
|
|||
return { filename: err.file && err.file.filename, start: err.start, end: err.start + err.length, line: errorLineInfo.line, character: errorLineInfo.character, message: err.messageText };
|
||||
}
|
||||
|
||||
export function minimalDiagnosticsToString(diagnostics: MinimalDiagnostic[]) {
|
||||
// This is copied from tsc.ts's reportError to replicate what tsc does
|
||||
var errors = "";
|
||||
ts.forEach(diagnostics, diagnotic => {
|
||||
if (diagnotic.filename) {
|
||||
errors += diagnotic.filename + "(" + diagnotic.line + "," + diagnotic.character + "): " + diagnotic.message + sys.newLine;
|
||||
}
|
||||
else {
|
||||
errors += diagnotic.message + sys.newLine;
|
||||
}
|
||||
});
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
export function getErrorBaseline(inputFiles: { unitName: string; content: string }[],
|
||||
diagnostics: MinimalDiagnostic[]
|
||||
) {
|
||||
|
|
|
@ -173,8 +173,6 @@ module Playback {
|
|||
var results = logArray.filter(e => pathsAreEquivalent(e.path, expectedPath, wrapper));
|
||||
if (results.length === 0) {
|
||||
if (defaultValue === undefined) {
|
||||
console.log('Resolved path: ' + wrapper.resolvePath(expectedPath));
|
||||
console.log('Filenames were: ' + logArray.map(x => x.path).join(', '));
|
||||
throw new Error('No matching result in log array for path: ' + expectedPath);
|
||||
} else {
|
||||
return defaultValue;
|
||||
|
@ -191,7 +189,7 @@ module Playback {
|
|||
}
|
||||
|
||||
function noOpReplay(name: string) {
|
||||
console.log("Swallowed write operation during replay: " + name);
|
||||
//console.log("Swallowed write operation during replay: " + name);
|
||||
}
|
||||
|
||||
export function wrapSystem(underlying: System): PlaybackSystem {
|
||||
|
@ -257,7 +255,7 @@ module Playback {
|
|||
|
||||
wrapper.resolvePath = recordReplay(wrapper.resolvePath, underlying)(
|
||||
(path) => callAndRecord(underlying.resolvePath(path), recordLog.pathsResolved, { path: path }),
|
||||
memoize((path) => findResultByFields(replayLog.pathsResolved, { path: path }, replayLog.currentDirectory ? replayLog.currentDirectory + '/' + path : path)));
|
||||
memoize((path) => findResultByFields(replayLog.pathsResolved, { path: path }, !ts.isRootedDiskPath(ts.normalizeSlashes(path)) && replayLog.currentDirectory ? replayLog.currentDirectory + '/' + path : ts.normalizeSlashes(path))));
|
||||
|
||||
wrapper.readFile = recordReplay(wrapper.readFile, underlying)(
|
||||
(path) => callAndRecord(underlying.readFile(path), recordLog.filesRead, { path: path, codepage: 0 }),
|
||||
|
|
|
@ -293,27 +293,13 @@ class ProjectRunner extends RunnerBase {
|
|||
}
|
||||
|
||||
function getErrorsBaseline(compilerResult: CompileProjectFilesResult) {
|
||||
// This is copied from tc.ts's reportError to replicate what tc does
|
||||
var errors = "";
|
||||
for (var i = 0; i < compilerResult.errors.length; i++) {
|
||||
var error = compilerResult.errors[i];
|
||||
// TODO(jfreeman): Remove assert
|
||||
ts.Debug.assert(error.messageText.indexOf("{NL}") < 0);
|
||||
if (error.file) {
|
||||
var loc = error.file.getLineAndCharacterFromPosition(error.start);
|
||||
errors += error.file.filename + "(" + loc.line + "," + loc.character + "): " + error.messageText + sys.newLine;
|
||||
}
|
||||
else {
|
||||
errors += error.messageText + sys.newLine;
|
||||
}
|
||||
}
|
||||
|
||||
var inputFiles = ts.map(ts.filter(compilerResult.program.getSourceFiles(),
|
||||
sourceFile => sourceFile.filename !== "lib.d.ts"),
|
||||
sourceFile => {
|
||||
return { unitName: sourceFile.filename, content: sourceFile.text };
|
||||
});
|
||||
var diagnostics = ts.map(compilerResult.errors, error => Harness.Compiler.getMinimalDiagnostic(error));
|
||||
var errors = Harness.Compiler.minimalDiagnosticsToString(diagnostics);
|
||||
errors += sys.newLine + sys.newLine + Harness.Compiler.getErrorBaseline(inputFiles, diagnostics);
|
||||
|
||||
return errors;
|
||||
|
|
|
@ -5,27 +5,6 @@
|
|||
/// <reference path='..\compiler\commandLineParser.ts'/>
|
||||
|
||||
module RWC {
|
||||
class RWCEmitter implements Harness.Compiler.IEmitterIOHost {
|
||||
public outputs: { [filename: string]: string; } = {};
|
||||
|
||||
constructor() { }
|
||||
|
||||
writeFile(path: string, contents: string, writeByteOrderMark: boolean) {
|
||||
if (path in this.outputs) throw new Error('Emitter attempted to write to "' + path + '" twice');
|
||||
this.outputs[path] = contents;
|
||||
}
|
||||
|
||||
directoryExists(s: string) {
|
||||
return false;
|
||||
}
|
||||
fileExists(s: string) {
|
||||
return true;
|
||||
}
|
||||
resolvePath(s: string) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
function runWithIOLog(ioLog: IOLog, fn: () => void) {
|
||||
var oldSys = sys;
|
||||
|
||||
|
@ -41,32 +20,26 @@ module RWC {
|
|||
}
|
||||
}
|
||||
|
||||
function collateOutputs(emitterIOHost: RWCEmitter, fnTest: (s: string) => {}, clean?: (s: string) => string) {
|
||||
function collateOutputs(outputFiles: Harness.Compiler.GeneratedFile[], clean?: (s: string) => string) {
|
||||
// Collect, test, and sort the filenames
|
||||
var files: string[] = [];
|
||||
for (var fn in emitterIOHost.outputs) {
|
||||
if (emitterIOHost.outputs.hasOwnProperty(fn) && fnTest(fn)) {
|
||||
files.push(fn);
|
||||
}
|
||||
}
|
||||
function cleanName(fn: string) {
|
||||
var lastSlash = Harness.Path.switchToForwardSlashes(fn).lastIndexOf('/');
|
||||
return fn.substr(lastSlash + 1).toLowerCase();
|
||||
}
|
||||
files.sort((a, b) => cleanName(a).localeCompare(cleanName(b)));
|
||||
outputFiles.sort((a, b) => cleanName(a.fileName).localeCompare(cleanName(b.fileName)));
|
||||
|
||||
// Emit them
|
||||
var result = '';
|
||||
files.forEach(fn => {
|
||||
ts.forEach(outputFiles, outputFile => {
|
||||
// Some extra spacing if this isn't the first file
|
||||
if (result.length) result = result + '\r\n\r\n';
|
||||
|
||||
// Filename header + content
|
||||
result = result + '/*====== ' + fn + ' ======*/\r\n';
|
||||
result = result + '/*====== ' + outputFile.fileName + ' ======*/\r\n';
|
||||
if (clean) {
|
||||
result = result + clean(emitterIOHost.outputs[fn]);
|
||||
result = result + clean(outputFile.code);
|
||||
} else {
|
||||
result = result + emitterIOHost.outputs[fn];
|
||||
result = result + outputFile.code;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
|
@ -86,90 +59,54 @@ module RWC {
|
|||
});
|
||||
});
|
||||
|
||||
var emitterIOHost = new RWCEmitter();
|
||||
var inputFiles: { unitName: string; content: string; }[] = [];
|
||||
var otherFiles: { unitName: string; content: string; }[] = [];
|
||||
var compilerResult: Harness.Compiler.CompilerResult;
|
||||
it('can compile', () => {
|
||||
runWithIOLog(ioLog, () => {
|
||||
harnessCompiler.reset();
|
||||
var inputList: string[] = opts.filenames;
|
||||
var noDefaultLib = false;
|
||||
var libPath = Harness.IO.directoryName(sys.getExecutingFilePath()) + '/lib.d.ts';
|
||||
|
||||
if (!opts.options.noResolve) {
|
||||
var filemap: any = {};
|
||||
var host: ts.CompilerHost = {
|
||||
getCurrentDirectory: () => sys.getCurrentDirectory(),
|
||||
getCancellationToken: (): any => undefined,
|
||||
getSourceFile: (fileName, languageVersion) => {
|
||||
var fileContents: string;
|
||||
try {
|
||||
if (libPath === fileName) {
|
||||
fileContents = Harness.IO.readFile(Harness.libFolder + "lib.d.ts");
|
||||
}
|
||||
else {
|
||||
fileContents = sys.readFile(fileName);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
// Leave fileContents undefined;
|
||||
}
|
||||
return ts.createSourceFile(fileName, fileContents, languageVersion);
|
||||
},
|
||||
getDefaultLibFilename: () => libPath,
|
||||
writeFile: (fn, contents) => emitterIOHost.writeFile(fn, contents, false),
|
||||
getCanonicalFileName: ts.getCanonicalFileName,
|
||||
useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames,
|
||||
getNewLine: () => sys.newLine
|
||||
};
|
||||
|
||||
var resolvedProgram = ts.createProgram(opts.filenames, opts.options, host);
|
||||
resolvedProgram.getSourceFiles().forEach(sourceFile => {
|
||||
noDefaultLib = noDefaultLib || sourceFile.hasNoDefaultLib;
|
||||
if (inputList.indexOf(sourceFile.filename) === -1) {
|
||||
inputList.push(sourceFile.filename);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!opts.options.noLib && !noDefaultLib) {
|
||||
inputList.push(libPath);
|
||||
}
|
||||
|
||||
harnessCompiler.reset();
|
||||
harnessCompiler.setCompilerSettingsFromOptions(opts.options);
|
||||
|
||||
// Load the files
|
||||
inputList.forEach((item: string) => {
|
||||
var resolvedPath = libPath === item ? item : Harness.Path.switchToForwardSlashes(sys.resolvePath(item));
|
||||
try {
|
||||
if (libPath === item) {
|
||||
var content = Harness.IO.readFile(Harness.libFolder + "lib.d.ts");
|
||||
}
|
||||
else {
|
||||
var content = sys.readFile(resolvedPath);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
// Leave content undefined.
|
||||
}
|
||||
harnessCompiler.addInputFile({ unitName: resolvedPath, content: content });
|
||||
ts.forEach(opts.filenames, fileName => {
|
||||
inputFiles.push(getHarnessCompilerInputUnit(fileName));
|
||||
});
|
||||
|
||||
harnessCompiler.setCompilerOptions();
|
||||
if (!opts.options.noLib) {
|
||||
// Find the lib.d.ts file in the input file and add it to the input files list
|
||||
var libFile = ts.forEach(ioLog.filesRead, fileRead=> Harness.isLibraryFile(fileRead.path) ? fileRead.path : undefined);
|
||||
if (libFile) {
|
||||
inputFiles.push(getHarnessCompilerInputUnit(libFile));
|
||||
}
|
||||
}
|
||||
|
||||
ts.forEach(ioLog.filesRead, fileRead => {
|
||||
var resolvedPath = Harness.Path.switchToForwardSlashes(sys.resolvePath(fileRead.path));
|
||||
var inInputList = ts.forEach(inputFiles, inputFile=> inputFile.unitName === resolvedPath);
|
||||
if (!inInputList) {
|
||||
// Add the file to other files
|
||||
otherFiles.push(getHarnessCompilerInputUnit(fileRead.path));
|
||||
}
|
||||
});
|
||||
|
||||
// do not use lib since we shouldnt be reading any files that arent in the ioLog
|
||||
opts.options.noLib = true;
|
||||
|
||||
// Emit the results
|
||||
harnessCompiler.emitAll(emitterIOHost);
|
||||
var compilationErrors = harnessCompiler.reportCompilationErrors();
|
||||
|
||||
// Create an error baseline
|
||||
compilationErrors.forEach(err => {
|
||||
if (err.filename) {
|
||||
errors += err.filename + ' (' + err.line + "," + err.character + "): " + err.message + '\r\n';
|
||||
}
|
||||
else {
|
||||
errors += err.message + '\r\n';
|
||||
}
|
||||
});
|
||||
harnessCompiler.compileFiles(inputFiles, otherFiles, compileResult => {
|
||||
compilerResult = compileResult;
|
||||
}, /*settingsCallback*/ undefined, opts.options);
|
||||
});
|
||||
|
||||
function getHarnessCompilerInputUnit(fileName: string) {
|
||||
var resolvedPath = Harness.Path.switchToForwardSlashes(sys.resolvePath(fileName));
|
||||
try {
|
||||
var content = sys.readFile(resolvedPath);
|
||||
}
|
||||
catch (e) {
|
||||
// Leave content undefined.
|
||||
}
|
||||
return { unitName: resolvedPath, content: content };
|
||||
}
|
||||
});
|
||||
|
||||
// Baselines
|
||||
|
@ -178,27 +115,46 @@ module RWC {
|
|||
|
||||
it('has the expected emitted code', () => {
|
||||
Harness.Baseline.runBaseline('has the expected emitted code', baseName + '.output.js', () => {
|
||||
return collateOutputs(emitterIOHost, fn => Harness.Compiler.isJS(fn), s => SyntacticCleaner.clean(s));
|
||||
return collateOutputs(compilerResult.files, s => SyntacticCleaner.clean(s));
|
||||
}, false, baselineOpts);
|
||||
});
|
||||
|
||||
it('has the expected declaration file content', () => {
|
||||
Harness.Baseline.runBaseline('has the expected declaration file content', baseName + '.d.ts', () => {
|
||||
var result = collateOutputs(emitterIOHost, fn => Harness.Compiler.isDTS(fn));
|
||||
return result.length > 0 ? result : null;
|
||||
if (compilerResult.errors.length || !compilerResult.declFilesCode.length) {
|
||||
return null;
|
||||
}
|
||||
return collateOutputs(compilerResult.declFilesCode);
|
||||
}, false, baselineOpts);
|
||||
});
|
||||
|
||||
it('has the expected source maps', () => {
|
||||
Harness.Baseline.runBaseline('has the expected source maps', baseName + '.map', () => {
|
||||
var result = collateOutputs(emitterIOHost, fn => fn.substr(fn.length - '.map'.length) === '.map');
|
||||
return result.length > 0 ? result : null;
|
||||
if (!compilerResult.sourceMaps.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return collateOutputs(compilerResult.sourceMaps);
|
||||
}, false, baselineOpts);
|
||||
});
|
||||
|
||||
it('has correct source map record', () => {
|
||||
if (compilerResult.sourceMapRecord) {
|
||||
Harness.Baseline.runBaseline('has correct source map record', baseName + '.sourcemap.txt', () => {
|
||||
return compilerResult.sourceMapRecord;
|
||||
}, false, baselineOpts);
|
||||
}
|
||||
});
|
||||
|
||||
it('has the expected errors', () => {
|
||||
Harness.Baseline.runBaseline('has the expected errors', baseName + '.errors.txt', () => {
|
||||
return errors.length > 0 ? errors : null;
|
||||
if (compilerResult.errors.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Harness.Compiler.minimalDiagnosticsToString(compilerResult.errors) +
|
||||
sys.newLine + sys.newLine +
|
||||
Harness.Compiler.getErrorBaseline(inputFiles.concat(otherFiles), compilerResult.errors);
|
||||
}, false, baselineOpts);
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue