Simplify rwc runner to use the common functionality to get errors, js files etc as part of harness compilation

This commit is contained in:
Sheetal Nandi 2014-08-20 15:26:33 -07:00
parent e2071a344c
commit 58fca3773f
6 changed files with 84 additions and 138 deletions

View file

@ -71,6 +71,7 @@ var harnessSources = [
"fourslashRunner.ts",
"projectsRunner.ts",
"unittestrunner.ts",
"loggedIO.ts",
"rwcRunner.ts",
"runner.ts"
].map(function (f) {

View file

@ -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);

View file

@ -539,8 +539,8 @@ module Harness {
getCurrentDirectory: sys.getCurrentDirectory,
getCancellationToken: (): any => undefined,
getSourceFile: (fn, languageVersion) => {
if (fn in filemap) {
return filemap[fn];
if (ts.getCanonicalFileName(fn) in filemap) {
return filemap[ts.getCanonicalFileName(fn)];
} else {
var lib = defaultLibFileName;
if (fn === defaultLibFileName) {
@ -726,7 +726,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);
@ -770,6 +770,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[]
) {

View file

@ -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 }),

View file

@ -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;

View file

@ -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 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 });
var inputFiles: { unitName: string; content: string; }[] = [];
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));
}
}
var otherFiles: { unitName: string; content: string; }[] = [];
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,36 @@ 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 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);
}, false, baselineOpts);
});