diff --git a/Gulpfile.ts b/Gulpfile.ts index 4ca099b56e4..873ea0991bd 100644 --- a/Gulpfile.ts +++ b/Gulpfile.ts @@ -978,8 +978,9 @@ gulp.task(instrumenterJsPath, /*help*/ false, [servicesFile], () => { .pipe(gulp.dest(builtLocalDirectory)); }); -gulp.task("tsc-instrumented", "Builds an instrumented tsc.js", ["local", loggedIOJsPath, instrumenterJsPath, servicesFile], (done) => { - exec(host, [instrumenterJsPath, "record", "iocapture", builtLocalCompiler], done, done); +gulp.task("tsc-instrumented", "Builds an instrumented tsc.js - run with --test=[testname]", ["local", loggedIOJsPath, instrumenterJsPath, servicesFile], (done) => { + const test = cmdLineOptions["tests"] || "iocapture"; + exec(host, [instrumenterJsPath, "record", test, builtLocalCompiler], done, done); }); gulp.task("update-sublime", "Updates the sublime plugin's tsserver", ["local", serverFile], () => { diff --git a/Jakefile.js b/Jakefile.js index 7de0635542b..7b4991b74b6 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -1104,9 +1104,10 @@ var instrumenterPath = harnessDirectory + 'instrumenter.ts'; var instrumenterJsPath = builtLocalDirectory + 'instrumenter.js'; compileFile(instrumenterJsPath, [instrumenterPath], [tscFile, instrumenterPath].concat(libraryTargets), [], /*useBuiltCompiler*/ true, { lib: "es6", types: ["node"], noOutFile: true, outDir: builtLocalDirectory }); -desc("Builds an instrumented tsc.js"); +desc("Builds an instrumented tsc.js - run with test=[testname]"); task('tsc-instrumented', [loggedIOJsPath, instrumenterJsPath, tscFile], function () { - var cmd = host + ' ' + instrumenterJsPath + ' record iocapture ' + builtLocalDirectory + compilerFilename; + var test = process.env.test || process.env.tests || process.env.t || "iocapture"; + var cmd = host + ' ' + instrumenterJsPath + " record " + test + " " + builtLocalDirectory + compilerFilename; console.log(cmd); var ex = jake.createExec([cmd]); ex.addListener("cmdEnd", function () { diff --git a/src/harness/harness.ts b/src/harness/harness.ts index a5671b832e9..7c4ccc7c960 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1717,8 +1717,12 @@ namespace Harness { return resultName; } - function sanitizeTestFilePath(name: string) { - return ts.toPath(ts.normalizeSlashes(name.replace(/[\^<>:"|?*%]/g, "_")).replace(/\.\.\//g, "__dotdot/"), "", Utils.canonicalizeForHarness); + export function sanitizeTestFilePath(name: string) { + const path = ts.toPath(ts.normalizeSlashes(name.replace(/[\^<>:"|?*%]/g, "_")).replace(/\.\.\//g, "__dotdot/"), "", Utils.canonicalizeForHarness); + if (ts.startsWith(path, "/")) { + return path.substring(1); + } + return path; } // This does not need to exist strictly speaking, but many tests will need to be updated if it's removed @@ -2068,7 +2072,7 @@ namespace Harness { for (let {done, value} = gen.next(); !done; { done, value } = gen.next()) { const [name, content, count] = value as [string, string, number | undefined]; if (count === 0) continue; // Allow error reporter to skip writing files without errors - const relativeFileName = relativeFileBase + (ts.startsWith(name, "/") ? "" : "/") + name + extension; + const relativeFileName = relativeFileBase + "/" + name + extension; const actualFileName = localPath(relativeFileName, opts && opts.Baselinefolder, opts && opts.Subfolder); const comparison = compareToBaseline(content, relativeFileName, opts); try { diff --git a/src/harness/loggedIO.ts b/src/harness/loggedIO.ts index 125283dcb22..3123024f419 100644 --- a/src/harness/loggedIO.ts +++ b/src/harness/loggedIO.ts @@ -5,8 +5,10 @@ /// interface FileInformation { - contents: string; + contents?: string; + contentsPath?: string; codepage: number; + bom?: string; } interface FindFileResult { @@ -27,13 +29,15 @@ interface IOLog { filesRead: IOLogFile[]; filesWritten: { path: string; - contents: string; + contents?: string; + contentsPath?: string; bom: boolean; }[]; filesDeleted: string[]; filesAppended: { path: string; - contents: string; + contents?: string; + contentsPath?: string; }[]; fileExists: { path: string; @@ -129,6 +133,72 @@ namespace Playback { }; } + export function newStyleLogIntoOldStyleLog(log: IOLog, host: ts.System | Harness.IO, baseName: string) { + for (const file of log.filesAppended) { + if (file.contentsPath) { + file.contents = host.readFile(ts.combinePaths(baseName, file.contentsPath)); + delete file.contentsPath; + } + } + for (const file of log.filesWritten) { + if (file.contentsPath) { + file.contents = host.readFile(ts.combinePaths(baseName, file.contentsPath)); + delete file.contentsPath; + } + } + for (const file of log.filesRead) { + if (file.result.contentsPath) { + // `readFile` strips away a BOM (and actually reinerprets the file contents according to the correct encoding) + // - but this has the unfortunate sideeffect of removing the BOM from any outputs based on the file, so we readd it here. + file.result.contents = (file.result.bom || "") + host.readFile(ts.combinePaths(baseName, file.result.contentsPath)); + delete file.result.contentsPath; + } + } + return log; + } + + export function oldStyleLogIntoNewStyleLog(log: IOLog, writeFile: typeof Harness.IO.writeFile, baseTestName: string) { + if (log.filesAppended) { + for (const file of log.filesAppended) { + if (file.contents !== undefined) { + file.contentsPath = ts.combinePaths("appended", Harness.Compiler.sanitizeTestFilePath(file.path)); + writeFile(ts.combinePaths(baseTestName, file.contentsPath), file.contents); + delete file.contents; + } + } + } + if (log.filesWritten) { + for (const file of log.filesWritten) { + if (file.contents !== undefined) { + file.contentsPath = ts.combinePaths("written", Harness.Compiler.sanitizeTestFilePath(file.path)); + writeFile(ts.combinePaths(baseTestName, file.contentsPath), file.contents); + delete file.contents; + } + } + } + if (log.filesRead) { + for (const file of log.filesRead) { + const { contents } = file.result; + if (contents !== undefined) { + file.result.contentsPath = ts.combinePaths("read", Harness.Compiler.sanitizeTestFilePath(file.path)); + writeFile(ts.combinePaths(baseTestName, file.result.contentsPath), contents); + const len = contents.length; + if (len >= 2 && contents.charCodeAt(0) === 0xfeff) { + file.result.bom = "\ufeff"; + } + if (len >= 2 && contents.charCodeAt(0) === 0xfffe) { + file.result.bom = "\ufffe"; + } + if (len >= 3 && contents.charCodeAt(0) === 0xefbb && contents.charCodeAt(1) === 0xbf) { + file.result.bom = "\uefbb\xbf"; + } + delete file.result.contents; + } + } + } + return log; + } + function initWrapper(wrapper: PlaybackSystem, underlying: ts.System): void; function initWrapper(wrapper: PlaybackIO, underlying: Harness.IO): void; function initWrapper(wrapper: PlaybackSystem | PlaybackIO, underlying: ts.System | Harness.IO): void { @@ -164,9 +234,9 @@ namespace Playback { wrapper.endRecord = () => { if (recordLog !== undefined) { let i = 0; - const fn = () => recordLogFileNameBase + i + ".json"; - while (underlying.fileExists(fn())) i++; - underlying.writeFile(fn(), JSON.stringify(recordLog)); + const fn = () => recordLogFileNameBase + i; + while (underlying.fileExists(fn() + ".json")) i++; + underlying.writeFile(ts.combinePaths(fn(), "test.json"), JSON.stringify(oldStyleLogIntoNewStyleLog(recordLog, (path, string) => underlying.writeFile(ts.combinePaths(fn(), path), string), fn()), null, 4)); // tslint:disable-line:no-null-keyword recordLog = undefined; } }; diff --git a/src/harness/parallel/host.ts b/src/harness/parallel/host.ts index 9434f021097..36fb3cd08d1 100644 --- a/src/harness/parallel/host.ts +++ b/src/harness/parallel/host.ts @@ -57,8 +57,19 @@ namespace Harness.Parallel.Host { for (const file of files) { let size: number; if (!perfData) { - size = statSync(file).size; - + try { + size = statSync(file).size; + } + catch { + // May be a directory + try { + size = Harness.IO.listFiles(file, /.*/g, { recursive: true }).reduce((acc, elem) => acc + statSync(elem).size, 0); + } + catch { + // Unknown test kind, just return 0 and let the historical analysis take over after one run + size = 0; + } + } } else { const hashedName = hashName(runner.kind(), file); diff --git a/src/harness/rwcRunner.ts b/src/harness/rwcRunner.ts index fc0c9178c20..eba845d44ec 100644 --- a/src/harness/rwcRunner.ts +++ b/src/harness/rwcRunner.ts @@ -36,7 +36,7 @@ namespace RWC { Subfolder: "rwc", Baselinefolder: "internal/baselines" }; - const baseName = /(.*)\/(.*).json/.exec(ts.normalizeSlashes(jsonPath))[2]; + const baseName = ts.getBaseFileName(jsonPath); let currentDirectory: string; let useCustomLibraryFile: boolean; let skipTypeAndSymbolbaselines = false; @@ -61,7 +61,7 @@ namespace RWC { this.timeout(800000); // Allow long timeouts for RWC compilations let opts: ts.ParsedCommandLine; - const ioLog: IOLog = JSON.parse(Harness.IO.readFile(jsonPath)); + 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; skipTypeAndSymbolbaselines = ioLog.filesRead.reduce((acc, elem) => (elem && elem.result && elem.result.contents) ? acc + elem.result.contents.length : acc, 0) > typeAndSymbolSizeLimit; @@ -253,7 +253,7 @@ namespace RWC { class RWCRunner extends RunnerBase { public enumerateTestFiles() { - return Harness.IO.listFiles("internal/cases/rwc/", /.+\.json$/); + return Harness.IO.getDirectories("internal/cases/rwc/"); } public kind(): TestRunnerKind {