From 131890a78186be932d71ba6084fd901fe7817bb0 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 12 Jun 2018 23:42:38 -0700 Subject: [PATCH 1/3] Add vfs diff, update tsbuild test --- package.json | 2 +- src/harness/vfs.ts | 269 ++++++++++++++++-- src/testRunner/unittests/tsbuild.ts | 102 ++++--- .../reference/outfile-concat-fileListing.txt | 43 --- tests/baselines/reference/outfile-concat.js | 91 +++++- .../baselines/reference/outfile-concat.js.map | 1 - tests/baselines/reference/third-output.js | 26 -- tests/baselines/reference/third-output.js.map | 1 - 8 files changed, 388 insertions(+), 147 deletions(-) delete mode 100644 tests/baselines/reference/outfile-concat-fileListing.txt delete mode 100644 tests/baselines/reference/outfile-concat.js.map delete mode 100644 tests/baselines/reference/third-output.js delete mode 100644 tests/baselines/reference/third-output.js.map diff --git a/package.json b/package.json index 95dc485908..70fa1e55a0 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "@types/minimatch": "latest", "@types/minimist": "latest", "@types/mkdirp": "latest", - "@types/mocha": "latest", + "@types/mocha": "^5.2.2", "@types/node": "8.5.5", "@types/q": "latest", "@types/run-sequence": "latest", diff --git a/src/harness/vfs.ts b/src/harness/vfs.ts index be612f64de..8cf1b5a2f1 100644 --- a/src/harness/vfs.ts +++ b/src/harness/vfs.ts @@ -671,6 +671,160 @@ namespace vfs { node.ctimeMs = time; } + /** + * Generates a `FileSet` patch containing all the entries in this `FileSystem` that are not in `base`. + * @param base The base file system. If not provided, this file system's `shadowRoot` is used (if present). + */ + public diff(base = this.shadowRoot) { + const differences: FileSet = {}; + const hasDifferences = base ? FileSystem.rootDiff(differences, this, base) : FileSystem.trackCreatedInodes(differences, this, this._getRootLinks()); + return hasDifferences ? differences : undefined; + } + + /** + * Generates a `FileSet` patch containing all the entries in `chagned` that are not in `base`. + */ + public static diff(changed: FileSystem, base: FileSystem) { + const differences: FileSet = {}; + return FileSystem.rootDiff(differences, changed, base) ? differences : undefined; + } + + private static diffWorker(container: FileSet, changed: FileSystem, changedLinks: ReadonlyMap | undefined, base: FileSystem, baseLinks: ReadonlyMap | undefined) { + if (changedLinks && !baseLinks) return FileSystem.trackCreatedInodes(container, changed, changedLinks); + if (baseLinks && !changedLinks) return FileSystem.trackDeletedInodes(container, baseLinks); + if (changedLinks && baseLinks) { + let hasChanges = false; + // track base items missing in changed + baseLinks.forEach((node, basename) => { + if (!changedLinks.has(basename)) { + container[basename] = isDirectory(node) ? new Rmdir() : new Unlink(); + hasChanges = true; + } + }); + // track changed items missing or differing in base + changedLinks.forEach((changedNode, basename) => { + const baseNode = baseLinks.get(basename); + if (baseNode) { + if (isDirectory(changedNode) && isDirectory(baseNode)) { + return hasChanges = FileSystem.directoryDiff(container, basename, changed, changedNode, base, baseNode) || hasChanges; + } + if (isFile(changedNode) && isFile(baseNode)) { + return hasChanges = FileSystem.fileDiff(container, basename, changed, changedNode, base, baseNode) || hasChanges; + } + if (isSymlink(changedNode) && isSymlink(baseNode)) { + return hasChanges = FileSystem.symlinkDiff(container, basename, changedNode, baseNode) || hasChanges; + } + } + return hasChanges = FileSystem.trackCreatedInode(container, basename, changed, changedNode) || hasChanges; + }); + return hasChanges; + } + return false; + } + + private static rootDiff(container: FileSet, changed: FileSystem, base: FileSystem) { + while (!changed._lazy.links && changed._shadowRoot) changed = changed._shadowRoot; + while (!base._lazy.links && base._shadowRoot) base = base._shadowRoot; + + // no difference if the file systems are the same reference + if (changed === base) return false; + + // no difference if the root links are empty and unshadowed + if (!changed._lazy.links && !changed._shadowRoot && !base._lazy.links && !base._shadowRoot) return false; + + return FileSystem.diffWorker(container, changed, changed._getRootLinks(), base, base._getRootLinks()); + } + + private static directoryDiff(container: FileSet, basename: string, changed: FileSystem, changedNode: DirectoryInode, base: FileSystem, baseNode: DirectoryInode) { + while (!changedNode.links && changedNode.shadowRoot) changedNode = changedNode.shadowRoot; + while (!baseNode.links && baseNode.shadowRoot) baseNode = baseNode.shadowRoot; + + // no difference if the nodes are the same reference + if (changedNode === baseNode) return false; + + // no difference if both nodes are non shadowed and have no entries + if (isEmptyNonShadowedDirectory(changedNode) && isEmptyNonShadowedDirectory(baseNode)) return false; + + // no difference if both nodes are unpopulated and point to the same mounted file system + if (!changedNode.links && !baseNode.links && + changedNode.resolver && changedNode.source !== undefined && + baseNode.resolver === changedNode.resolver && baseNode.source === changedNode.source) return false; + + // no difference if both nodes have identical children + const children: FileSet = {}; + if (!FileSystem.diffWorker(children, changed, changed._getLinks(changedNode), base, base._getLinks(baseNode))) { + return false; + } + + container[basename] = new Directory(children); + return true; + } + + private static fileDiff(container: FileSet, basename: string, changed: FileSystem, changedNode: FileInode, base: FileSystem, baseNode: FileInode) { + while (!changedNode.buffer && changedNode.shadowRoot) changedNode = changedNode.shadowRoot; + while (!baseNode.buffer && baseNode.shadowRoot) baseNode = baseNode.shadowRoot; + + // no difference if the nodes are the same reference + if (changedNode === baseNode) return false; + + // no difference if both nodes are non shadowed and have no entries + if (isEmptyNonShadowedFile(changedNode) && isEmptyNonShadowedFile(baseNode)) return false; + + // no difference if both nodes are unpopulated and point to the same mounted file system + if (!changedNode.buffer && !baseNode.buffer && + changedNode.resolver && changedNode.source !== undefined && + baseNode.resolver === changedNode.resolver && baseNode.source === changedNode.source) return false; + + const changedBuffer = changed._getBuffer(changedNode); + const baseBuffer = base._getBuffer(baseNode); + + // no difference if both buffers are the same reference + if (changedBuffer === baseBuffer) return false; + + // no difference if both buffers are identical + if (Buffer.compare(changedBuffer, baseBuffer) === 0) return false; + + container[basename] = new File(changedBuffer); + return true; + } + + private static symlinkDiff(container: FileSet, basename: string, changedNode: SymlinkInode, baseNode: SymlinkInode) { + // no difference if the nodes are the same reference + if (changedNode.symlink === baseNode.symlink) return false; + container[basename] = new Symlink(changedNode.symlink); + return true; + } + + private static trackCreatedInode(container: FileSet, basename: string, changed: FileSystem, node: Inode) { + if (isDirectory(node)) { + const children: FileSet = {}; + FileSystem.trackCreatedInodes(children, changed, changed._getLinks(node)); + container[basename] = new Directory(children); + } + else if (isSymlink(node)) { + container[basename] = new Symlink(node.symlink); + } + else { + container[basename] = new File(node.buffer || ""); + } + return true; + } + + private static trackCreatedInodes(container: FileSet, changed: FileSystem, changedLinks: ReadonlyMap) { + // no difference if links are empty + if (!changedLinks.size) return false; + + changedLinks.forEach((node, basename) => { FileSystem.trackCreatedInode(container, basename, changed, node); }); + return true; + } + + private static trackDeletedInodes(container: FileSet, baseLinks: ReadonlyMap) { + // no difference if links are empty + if (!baseLinks.size) return false; + baseLinks.forEach((node, basename) => { container[basename] = isDirectory(node) ? new Rmdir() : new Unlink(); }); + return true; + } + private _mknod(dev: number, type: typeof S_IFREG, mode: number, time?: number): FileInode; private _mknod(dev: number, type: typeof S_IFDIR, mode: number, time?: number): DirectoryInode; private _mknod(dev: number, type: typeof S_IFLNK, mode: number, time?: number): SymlinkInode; @@ -940,10 +1094,10 @@ namespace vfs { private _applyFilesWorker(files: FileSet, dirname: string, deferred: [Symlink | Link | Mount, string][]) { for (const key of Object.keys(files)) { - const value = this._normalizeFileSetEntry(files[key]); + const value = normalizeFileSetEntry(files[key]); const path = dirname ? vpath.resolve(dirname, key) : key; vpath.validate(path, vpath.ValidationFlags.Absolute); - if (value === null || value === undefined) { + if (value === null || value === undefined || value instanceof Rmdir || value instanceof Unlink) { if (this.stringComparer(vpath.dirname(path), path) === 0) { throw new TypeError("Roots cannot be deleted."); } @@ -967,19 +1121,6 @@ namespace vfs { } } } - - private _normalizeFileSetEntry(value: FileSet[string]) { - if (value === undefined || - value === null || - value instanceof Directory || - value instanceof File || - value instanceof Link || - value instanceof Symlink || - value instanceof Mount) { - return value; - } - return typeof value === "string" || Buffer.isBuffer(value) ? new File(value) : new Directory(value); - } } export interface FileSystemOptions { @@ -997,12 +1138,9 @@ namespace vfs { meta?: Record; } - export interface FileSystemCreateOptions { + export interface FileSystemCreateOptions extends FileSystemOptions { // Sets the documents to add to the file system. documents?: ReadonlyArray; - - // Sets the initial working directory for the file system. - cwd?: string; } export type Axis = "ancestors" | "ancestors-or-self" | "self" | "descendants-or-self" | "descendants"; @@ -1062,8 +1200,16 @@ namespace vfs { * * Unless overridden, `/.src` will be the current working directory for the virtual file system. */ - export function createFromFileSystem(host: FileSystemResolverHost, ignoreCase: boolean, { documents, cwd }: FileSystemCreateOptions = {}) { + export function createFromFileSystem(host: FileSystemResolverHost, ignoreCase: boolean, { documents, files, cwd, time, meta }: FileSystemCreateOptions = {}) { const fs = getBuiltLocal(host, ignoreCase).shadow(); + if (meta) { + for (const key of Object.keys(meta)) { + fs.meta.set(key, meta[key]); + } + } + if (time) { + fs.time(time); + } if (cwd) { fs.mkdirpSync(cwd); fs.chdir(cwd); @@ -1083,6 +1229,9 @@ namespace vfs { } } } + if (files) { + fs.apply(files); + } return fs; } @@ -1165,7 +1314,7 @@ namespace vfs { * A template used to populate files, directories, links, etc. in a virtual file system. */ export interface FileSet { - [name: string]: DirectoryLike | FileLike | Link | Symlink | Mount | null | undefined; + [name: string]: DirectoryLike | FileLike | Link | Symlink | Mount | Rmdir | Unlink | null | undefined; } export type DirectoryLike = FileSet | Directory; @@ -1201,6 +1350,24 @@ namespace vfs { } } + /** Removes a directory in a `FileSet` */ + export class Rmdir { + private _rmdirBrand?: never; // brand necessary for proper type guards + } + + /** Unlinks a file in a `FileSet` */ + export class Unlink { + private _unlinkBrand?: never; // brand necessary for proper type guards + } + + // prevents compiler errors due to unused brand properties + if (!0) { + // tslint:disable-next-line:no-string-literal + ignoreUnused(Rmdir.prototype["_rmdirBrand"]); + // tslint:disable-next-line:no-string-literal + ignoreUnused(Unlink.prototype["_unlinkBrand"]); + } + /** Extended options for a symbolic link in a `FileSet` */ export class Symlink { public readonly symlink: string; @@ -1273,6 +1440,14 @@ namespace vfs { meta?: collections.Metadata; } + function isEmptyNonShadowedDirectory(node: DirectoryInode) { + return !node.links && !node.shadowRoot && !node.resolver && !node.source; + } + + function isEmptyNonShadowedFile(node: FileInode) { + return !node.buffer && !node.shadowRoot && !node.resolver && !node.source; + } + function isFile(node: Inode | undefined): node is FileInode { return node !== undefined && (node.mode & S_IFMT) === S_IFREG; } @@ -1324,5 +1499,57 @@ namespace vfs { } return builtLocalCS; } + + function normalizeFileSetEntry(value: FileSet[string]) { + if (value === undefined || + value === null || + value instanceof Directory || + value instanceof File || + value instanceof Link || + value instanceof Symlink || + value instanceof Mount || + value instanceof Rmdir || + value instanceof Unlink) { + return value; + } + return typeof value === "string" || Buffer.isBuffer(value) ? new File(value) : new Directory(value); + } + + export function formatPatch(patch: FileSet) { + return formatPatchWorker("", patch); + } + + function formatPatchWorker(dirname: string, container: FileSet): string { + let text = ""; + for (const name of Object.keys(container)) { + const entry = normalizeFileSetEntry(container[name]); + const file = dirname ? vpath.combine(dirname, name) : name; + if (entry === null || entry === undefined || entry instanceof Unlink || entry instanceof Rmdir) { + text += `//// [${file}] unlink\r\n`; + } + else if (entry instanceof Rmdir) { + text += `//// [${vpath.addTrailingSeparator(file)}] rmdir\r\n`; + } + else if (entry instanceof Directory) { + text += formatPatchWorker(file, entry.files); + } + else if (entry instanceof File) { + const content = typeof entry.data === "string" ? entry.data : entry.data.toString("utf8"); + text += `//// [${file}]\r\n${content}\r\n\r\n`; + } + else if (entry instanceof Link) { + text += `//// [${file}] link(${entry.path})\r\n`; + } + else if (entry instanceof Symlink) { + text += `//// [${file}] symlink(${entry.symlink})\r\n`; + } + else if (entry instanceof Mount) { + text += `//// [${file}] mount(${entry.source})\r\n`; + } + } + return text; + } + + function ignoreUnused(_: void) { /*empty*/ } } // tslint:enable:no-null-keyword \ No newline at end of file diff --git a/src/testRunner/unittests/tsbuild.ts b/src/testRunner/unittests/tsbuild.ts index 9d093f61e0..41ab6e5b5a 100644 --- a/src/testRunner/unittests/tsbuild.ts +++ b/src/testRunner/unittests/tsbuild.ts @@ -12,7 +12,7 @@ namespace ts { export namespace Sample1 { tick(); - const projFs = loadProjectFromDisk("../../tests/projects/sample1"); + const projFs = loadProjectFromDisk("tests/projects/sample1"); const allExpectedOutputs = ["/src/tests/index.js", "/src/core/index.js", "/src/core/index.d.ts", @@ -236,40 +236,33 @@ namespace ts { } export namespace OutFile { - const outFileFs = loadProjectFromDisk("../../tests/projects/outfile-concat"); + const outFileFs = loadProjectFromDisk("tests/projects/outfile-concat"); describe("tsbuild - baseline sectioned sourcemaps", () => { - const fs = outFileFs.shadow(); - const host = new fakes.CompilerHost(fs); - const builder = createSolutionBuilder(host, buildHost, ["/src/third"], { dry: false, force: false, verbose: false }); - clearDiagnostics(); - builder.buildAllProjects(); - assertDiagnosticMessages(/*none*/); - - const files = [ - "/src/third/thirdjs/output/third-output.js", - "/src/third/thirdjs/output/third-output.js.map" - ]; - - for (const file of files) { - it(`Generates files matching the baseline - ${file}`, () => { - Harness.Baseline.runBaseline(getBaseFileName(file), () => { - return fs.readFileSync(file, "utf-8"); - }); - }); - } - - it(`Generates files matching the baseline - file listing for outFile-concat`, () => { - Harness.Baseline.runBaseline("outfile-concat-fileListing.txt", () => { - return fs.getFileListing(); + let fs: vfs.FileSystem | undefined; + before(() => { + fs = outFileFs.shadow(); + const host = new fakes.CompilerHost(fs); + const builder = createSolutionBuilder(host, buildHost, ["/src/third"], { dry: false, force: false, verbose: false }); + clearDiagnostics(); + builder.buildAllProjects(); + assertDiagnosticMessages(/*none*/); + }); + after(() => { + fs = undefined; + }); + it(`Generates files matching the baseline`, () => { + Harness.Baseline.runBaseline("outfile-concat.js", () => { + const patch = fs!.diff(); + // tslint:disable-next-line:no-null-keyword + return patch ? vfs.formatPatch(patch) : null; }); }); }); } describe("tsbuild - graph-ordering", () => { - const fs = new vfs.FileSystem(false); - const host = new fakes.CompilerHost(fs); + let host: fakes.CompilerHost | undefined; const deps: [string, string][] = [ ["A", "B"], ["B", "C"], @@ -280,7 +273,15 @@ namespace ts { ["F", "E"] ]; - writeProjects(fs, ["A", "B", "C", "D", "E", "F", "G"], deps); + before(() => { + const fs = new vfs.FileSystem(false); + host = new fakes.CompilerHost(fs); + writeProjects(fs, ["A", "B", "C", "D", "E", "F", "G"], deps); + }); + + after(() => { + host = undefined; + }); it("orders the graph correctly - specify two roots", () => { checkGraphOrdering(["A", "G"], ["A", "B", "C", "D", "E", "G"]); @@ -299,7 +300,7 @@ namespace ts { }); function checkGraphOrdering(rootNames: string[], expectedBuildSet: string[]) { - const builder = createSolutionBuilder(host, buildHost, rootNames, { dry: true, force: false, verbose: false }); + const builder = createSolutionBuilder(host!, buildHost, rootNames, { dry: true, force: false, verbose: false }); const projFileNames = rootNames.map(getProjectFileName); const graph = builder.getBuildGraph(projFileNames); @@ -394,32 +395,27 @@ namespace ts { } function loadProjectFromDisk(root: string): vfs.FileSystem { - const fs = new vfs.FileSystem(/*ignoreCase*/ false, { time }); - const rootPath = resolvePath(__dirname, root); - loadFsMirror(fs, rootPath, "/src"); - fs.mkdirpSync("/lib"); - const libs = ["es5", "dom", "webworker.importscripts", "scripthost"]; - for (const lib of libs) { - const content = Harness.IO.readFile(combinePaths(Harness.libFolder, `lib.${lib}.d.ts`)); - if (content === undefined) { - throw new Error(`Failed to read lib ${lib}`); - } - fs.writeFileSync(`/lib/lib.${lib}.d.ts`, content); - } - fs.writeFileSync("/lib/lib.d.ts", Harness.IO.readFile(combinePaths(Harness.libFolder, "lib.d.ts"))!); - fs.meta.set("defaultLibLocation", "/lib"); + const resolver = vfs.createResolver(Harness.IO); + const fs = new vfs.FileSystem(/*ignoreCase*/ true, { + files: { + ["/lib"]: new vfs.Mount(vpath.resolve(Harness.IO.getWorkspaceRoot(), "built/local"), patchResolver(resolver)), + ["/src"]: new vfs.Mount(vpath.resolve(Harness.IO.getWorkspaceRoot(), root), resolver) + }, + cwd: "/", + meta: { defaultLibLocation: "/lib" }, + time + }); fs.makeReadonly(); return fs; - } - - function loadFsMirror(vfs: vfs.FileSystem, localRoot: string, virtualRoot: string) { - vfs.mkdirpSync(virtualRoot); - for (const path of Harness.IO.readDirectory(localRoot)) { - const file = getBaseFileName(path); - vfs.writeFileSync(virtualRoot + "/" + file, Harness.IO.readFile(localRoot + "/" + file)!); - } - for (const dir of Harness.IO.getDirectories(localRoot)) { - loadFsMirror(vfs, localRoot + "/" + dir, virtualRoot + "/" + dir); + function patchResolver(resolver: vfs.FileSystemResolver): vfs.FileSystemResolver { + const allowedFiles = ["lib.d.ts", "lib.dom.d.ts", "lib.es5.d.ts", "lib.scripthost.d.ts", "lib.webworker.importscripts.d.ts"]; + return { + ...resolver, + readdirSync(path: string) { + const files = resolver.readdirSync(path); + return files.filter(file => allowedFiles.indexOf(file) !== -1); + } + }; } } } \ No newline at end of file diff --git a/tests/baselines/reference/outfile-concat-fileListing.txt b/tests/baselines/reference/outfile-concat-fileListing.txt deleted file mode 100644 index fc6a1e7b28..0000000000 --- a/tests/baselines/reference/outfile-concat-fileListing.txt +++ /dev/null @@ -1,43 +0,0 @@ -*/ - /lib/ - /lib/lib.d.ts - /lib/lib.dom.d.ts - /lib/lib.es5.d.ts - /lib/lib.scripthost.d.ts - /lib/lib.webworker.importscripts.d.ts - /src/ - /src/2/ - /src/2/second-output.d.ts - /src/2/second-output.d.ts.map - /src/2/second-output.js - /src/2/second-output.js.map - /src/first/ - /src/first/bin/ - /src/first/bin/first-output.d.ts - /src/first/bin/first-output.d.ts.map - /src/first/bin/first-output.js - /src/first/bin/first-output.js.map - /src/first/first_part1.ts - /src/first/first_part2.ts - /src/first/first_part3.ts - /src/first/tsconfig.json - /src/first_part1.ts - /src/first_part2.ts - /src/first_part3.ts - /src/second/ - /src/second/second_part1.ts - /src/second/second_part2.ts - /src/second/tsconfig.json - /src/second_part1.ts - /src/second_part2.ts - /src/third/ - /src/third/third_part1.ts - /src/third/thirdjs/ - /src/third/thirdjs/output/ - /src/third/thirdjs/output/third-output.d.ts - /src/third/thirdjs/output/third-output.d.ts.map - /src/third/thirdjs/output/third-output.js - /src/third/thirdjs/output/third-output.js.map - /src/third/tsconfig.json - /src/third_part1.ts - /src/tsconfig.json \ No newline at end of file diff --git a/tests/baselines/reference/outfile-concat.js b/tests/baselines/reference/outfile-concat.js index e3e5aa3d5f..22c69281e1 100644 --- a/tests/baselines/reference/outfile-concat.js +++ b/tests/baselines/reference/outfile-concat.js @@ -1,3 +1,88 @@ +//// [/src/2/second-output.d.ts] +declare namespace N { +} +declare namespace N { +} +declare class C { + doSomething(): void; +} +//# sourceMappingURL=second-output.d.ts.map + +//// [/src/2/second-output.d.ts.map] +{"version":3,"file":"second-output.d.ts","sourceRoot":"","sources":["../second/second_part1.ts","../second/second_part2.ts"],"names":[],"mappings":"AAAA,kBAAU,CAAC,CAAC;CAEX;AAED,kBAAU,CAAC,CAAC;CAMX;ACVD;IACI,WAAW;CAGd"} + +//// [/src/2/second-output.js] +var N; +(function (N) { + function f() { + console.log('testing'); + } + f(); +})(N || (N = {})); +var C = (function () { + function C() { + } + C.prototype.doSomething = function () { + console.log("something got done"); + }; + return C; +}()); +//# sourceMappingURL=second-output.js.map + +//// [/src/2/second-output.js.map] +{"version":3,"file":"second-output.js","sourceRoot":"","sources":["../second/second_part1.ts","../second/second_part2.ts"],"names":[],"mappings":"AAIA,IAAU,CAAC,CAMV;AAND,WAAU,CAAC;IACP;QACI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC3B,CAAC;IAED,CAAC,EAAE,CAAC;AACR,CAAC,EANS,CAAC,KAAD,CAAC,QAMV;ACVD;IAAA;IAIA,CAAC;IAHG,uBAAW,GAAX;QACI,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IACtC,CAAC;IACL,QAAC;AAAD,CAAC,AAJD,IAIC"} + +//// [/src/first/bin/first-output.d.ts] +interface TheFirst { + none: any; +} +declare const s = "Hello, world"; +interface NoJsForHereEither { + none: any; +} +declare function f(): string; +//# sourceMappingURL=first-output.d.ts.map + +//// [/src/first/bin/first-output.d.ts.map] +{"version":3,"file":"first-output.d.ts","sourceRoot":"","sources":["../first_part1.ts","../first_part2.ts","../first_part3.ts"],"names":[],"mappings":"AAAA,UAAU,QAAQ;IACd,IAAI,EAAE,GAAG,CAAC;CACb;AAED,QAAA,MAAM,CAAC,iBAAiB,CAAC;AAEzB,UAAU,iBAAiB;IACvB,IAAI,EAAE,GAAG,CAAC;CACb;AERD,6BAEC"} + +//// [/src/first/bin/first-output.js] +var s = "Hello, world"; +console.log(s); +console.log(f()); +function f() { + return "JS does hoists"; +} +//# sourceMappingURL=first-output.js.map + +//// [/src/first/bin/first-output.js.map] +{"version":3,"file":"first-output.js","sourceRoot":"","sources":["../first_part1.ts","../first_part2.ts","../first_part3.ts"],"names":[],"mappings":"AAIA,IAAM,CAAC,GAAG,cAAc,CAAC;AAMzB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;ACVf,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;ACAjB;IACI,OAAO,gBAAgB,CAAC;AAC5B,CAAC"} + +//// [/src/third/thirdjs/output/third-output.d.ts] +interface TheFirst { + none: any; +} +declare const s = "Hello, world"; +interface NoJsForHereEither { + none: any; +} +declare function f(): string; +//# sourceMappingURL=first-output.d.ts.map +declare namespace N { +} +declare namespace N { +} +declare class C { + doSomething(): void; +} +//# sourceMappingURL=second-output.d.ts.map +declare var c: C; +//# sourceMappingURL=third-output.d.ts.map + +//// [/src/third/thirdjs/output/third-output.d.ts.map] +{"version":3,"file":"third-output.d.ts","sections":[{"offset":{"line":0,"column":0},"map":{"version":3,"file":"first-output.d.ts","sourceRoot":"","sources":["../first_part1.ts","../first_part2.ts","../first_part3.ts"],"names":[],"mappings":"AAAA,UAAU,QAAQ;IACd,IAAI,EAAE,GAAG,CAAC;CACb;AAED,QAAA,MAAM,CAAC,iBAAiB,CAAC;AAEzB,UAAU,iBAAiB;IACvB,IAAI,EAAE,GAAG,CAAC;CACb;AERD,6BAEC"}},{"offset":{"line":9,"column":0},"map":{"version":3,"file":"second-output.d.ts","sourceRoot":"","sources":["../second/second_part1.ts","../second/second_part2.ts"],"names":[],"mappings":"AAAA,kBAAU,CAAC,CAAC;CAEX;AAED,kBAAU,CAAC,CAAC;CAMX;ACVD;IACI,WAAW;CAGd"}},{"offset":{"line":16,"column":43},"map":{"version":3,"file":"third-output.d.ts","sourceRoot":"","sources":["../../third_part1.ts"],"names":[],"mappings":";AAAA,QAAA,IAAI,CAAC,GAAU,CAAC"}}]} + +//// [/src/third/thirdjs/output/third-output.js] var s = "Hello, world"; console.log(s); console.log(f()); @@ -23,4 +108,8 @@ var C = (function () { //# sourceMappingURL=second-output.js.map var c = new C(); c.doSomething(); -//# sourceMappingURL=third-output.js.map \ No newline at end of file +//# sourceMappingURL=third-output.js.map + +//// [/src/third/thirdjs/output/third-output.js.map] +{"version":3,"file":"third-output.js","sections":[{"offset":{"line":0,"column":0},"map":{"version":3,"file":"first-output.js","sourceRoot":"","sources":["../first_part1.ts","../first_part2.ts","../first_part3.ts"],"names":[],"mappings":"AAIA,IAAM,CAAC,GAAG,cAAc,CAAC;AAMzB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;ACVf,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;ACAjB;IACI,OAAO,gBAAgB,CAAC;AAC5B,CAAC"}},{"offset":{"line":7,"column":0},"map":{"version":3,"file":"second-output.js","sourceRoot":"","sources":["../second/second_part1.ts","../second/second_part2.ts"],"names":[],"mappings":"AAIA,IAAU,CAAC,CAMV;AAND,WAAU,CAAC;IACP;QACI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC3B,CAAC;IAED,CAAC,EAAE,CAAC;AACR,CAAC,EANS,CAAC,KAAD,CAAC,QAMV;ACVD;IAAA;IAIA,CAAC;IAHG,uBAAW,GAAX;QACI,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IACtC,CAAC;IACL,QAAC;AAAD,CAAC,AAJD,IAIC"}},{"offset":{"line":22,"column":41},"map":{"version":3,"file":"third-output.js","sourceRoot":"","sources":["../../third_part1.ts"],"names":[],"mappings":";AAAA,IAAI,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;AAChB,CAAC,CAAC,WAAW,EAAE,CAAC"}}]} + diff --git a/tests/baselines/reference/outfile-concat.js.map b/tests/baselines/reference/outfile-concat.js.map deleted file mode 100644 index f8220832b2..0000000000 --- a/tests/baselines/reference/outfile-concat.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"third-output.js","sections":[{"offset":{"line":0,"column":0},"map":{"version":3,"file":"first-output.js","sourceRoot":"","sources":["first_part1.ts","first_part2.ts","first_part3.ts"],"names":[],"mappings":"AAIA,IAAM,CAAC,GAAG,cAAc,CAAC;AAMzB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;ACVf,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;ACAjB;IACI,OAAO,gBAAgB,CAAC;AAC5B,CAAC"}},{"offset":{"line":7,"column":0},"map":{"version":3,"file":"second-output.js","sourceRoot":"","sources":["second_part1.ts","second_part2.ts"],"names":[],"mappings":"AAIA,IAAU,CAAC,CAMV;AAND,WAAU,CAAC;IACP;QACI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC3B,CAAC;IAED,CAAC,EAAE,CAAC;AACR,CAAC,EANS,CAAC,KAAD,CAAC,QAMV;ACVD;IAAA;IAIA,CAAC;IAHG,uBAAW,GAAX;QACI,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IACtC,CAAC;IACL,QAAC;AAAD,CAAC,AAJD,IAIC"}},{"offset":{"line":22,"column":41},"map":{"version":3,"file":"third-output.js","sourceRoot":"","sources":["third_part1.ts"],"names":[],"mappings":";AAAA,IAAI,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;AAChB,CAAC,CAAC,WAAW,EAAE,CAAC"}}]} \ No newline at end of file diff --git a/tests/baselines/reference/third-output.js b/tests/baselines/reference/third-output.js deleted file mode 100644 index e3e5aa3d5f..0000000000 --- a/tests/baselines/reference/third-output.js +++ /dev/null @@ -1,26 +0,0 @@ -var s = "Hello, world"; -console.log(s); -console.log(f()); -function f() { - return "JS does hoists"; -} -//# sourceMappingURL=first-output.js.map -var N; -(function (N) { - function f() { - console.log('testing'); - } - f(); -})(N || (N = {})); -var C = (function () { - function C() { - } - C.prototype.doSomething = function () { - console.log("something got done"); - }; - return C; -}()); -//# sourceMappingURL=second-output.js.map -var c = new C(); -c.doSomething(); -//# sourceMappingURL=third-output.js.map \ No newline at end of file diff --git a/tests/baselines/reference/third-output.js.map b/tests/baselines/reference/third-output.js.map deleted file mode 100644 index 70b9ad69d5..0000000000 --- a/tests/baselines/reference/third-output.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"third-output.js","sections":[{"offset":{"line":0,"column":0},"map":{"version":3,"file":"first-output.js","sourceRoot":"","sources":["../first_part1.ts","../first_part2.ts","../first_part3.ts"],"names":[],"mappings":"AAIA,IAAM,CAAC,GAAG,cAAc,CAAC;AAMzB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;ACVf,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;ACAjB;IACI,OAAO,gBAAgB,CAAC;AAC5B,CAAC"}},{"offset":{"line":7,"column":0},"map":{"version":3,"file":"second-output.js","sourceRoot":"","sources":["../second/second_part1.ts","../second/second_part2.ts"],"names":[],"mappings":"AAIA,IAAU,CAAC,CAMV;AAND,WAAU,CAAC;IACP;QACI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC3B,CAAC;IAED,CAAC,EAAE,CAAC;AACR,CAAC,EANS,CAAC,KAAD,CAAC,QAMV;ACVD;IAAA;IAIA,CAAC;IAHG,uBAAW,GAAX;QACI,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IACtC,CAAC;IACL,QAAC;AAAD,CAAC,AAJD,IAIC"}},{"offset":{"line":22,"column":41},"map":{"version":3,"file":"third-output.js","sourceRoot":"","sources":["../../third_part1.ts"],"names":[],"mappings":";AAAA,IAAI,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;AAChB,CAAC,CAAC,WAAW,EAAE,CAAC"}}]} \ No newline at end of file From e756182e21a6d6b17168938c99a6600b81dbf5e8 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 13 Jun 2018 10:46:38 -0700 Subject: [PATCH 2/3] PR Feedback --- package.json | 2 +- src/harness/vfs.ts | 14 ++------------ src/testRunner/unittests/tsbuild.ts | 12 +----------- 3 files changed, 4 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index 70fa1e55a0..95dc485908 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "@types/minimatch": "latest", "@types/minimist": "latest", "@types/mkdirp": "latest", - "@types/mocha": "^5.2.2", + "@types/mocha": "latest", "@types/node": "8.5.5", "@types/q": "latest", "@types/run-sequence": "latest", diff --git a/src/harness/vfs.ts b/src/harness/vfs.ts index 8cf1b5a2f1..750a990522 100644 --- a/src/harness/vfs.ts +++ b/src/harness/vfs.ts @@ -1352,20 +1352,12 @@ namespace vfs { /** Removes a directory in a `FileSet` */ export class Rmdir { - private _rmdirBrand?: never; // brand necessary for proper type guards + public _rmdirBrand?: never; // brand necessary for proper type guards } /** Unlinks a file in a `FileSet` */ export class Unlink { - private _unlinkBrand?: never; // brand necessary for proper type guards - } - - // prevents compiler errors due to unused brand properties - if (!0) { - // tslint:disable-next-line:no-string-literal - ignoreUnused(Rmdir.prototype["_rmdirBrand"]); - // tslint:disable-next-line:no-string-literal - ignoreUnused(Unlink.prototype["_unlinkBrand"]); + public _unlinkBrand?: never; // brand necessary for proper type guards } /** Extended options for a symbolic link in a `FileSet` */ @@ -1549,7 +1541,5 @@ namespace vfs { } return text; } - - function ignoreUnused(_: void) { /*empty*/ } } // tslint:enable:no-null-keyword \ No newline at end of file diff --git a/src/testRunner/unittests/tsbuild.ts b/src/testRunner/unittests/tsbuild.ts index 41ab6e5b5a..2ae034ff67 100644 --- a/src/testRunner/unittests/tsbuild.ts +++ b/src/testRunner/unittests/tsbuild.ts @@ -398,7 +398,7 @@ namespace ts { const resolver = vfs.createResolver(Harness.IO); const fs = new vfs.FileSystem(/*ignoreCase*/ true, { files: { - ["/lib"]: new vfs.Mount(vpath.resolve(Harness.IO.getWorkspaceRoot(), "built/local"), patchResolver(resolver)), + ["/lib"]: new vfs.Mount(vpath.resolve(Harness.IO.getWorkspaceRoot(), "built/local"), resolver), ["/src"]: new vfs.Mount(vpath.resolve(Harness.IO.getWorkspaceRoot(), root), resolver) }, cwd: "/", @@ -407,15 +407,5 @@ namespace ts { }); fs.makeReadonly(); return fs; - function patchResolver(resolver: vfs.FileSystemResolver): vfs.FileSystemResolver { - const allowedFiles = ["lib.d.ts", "lib.dom.d.ts", "lib.es5.d.ts", "lib.scripthost.d.ts", "lib.webworker.importscripts.d.ts"]; - return { - ...resolver, - readdirSync(path: string) { - const files = resolver.readdirSync(path); - return files.filter(file => allowedFiles.indexOf(file) !== -1); - } - }; - } } } \ No newline at end of file From c4f1d7755dc93feeec51d87e2ab013ffc62936ff Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 13 Jun 2018 11:24:16 -0700 Subject: [PATCH 3/3] add vfs snapshot capability for future tests --- src/harness/vfs.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/harness/vfs.ts b/src/harness/vfs.ts index 750a990522..ae99187f5f 100644 --- a/src/harness/vfs.ts +++ b/src/harness/vfs.ts @@ -126,6 +126,24 @@ namespace vfs { return this._shadowRoot; } + /** + * Snapshots the current file system, effectively shadowing itself. This is useful for + * generating file system patches using `.diff()` from one snapshot to the next. Performs + * no action if this file system is read-only. + */ + public snapshot() { + if (this.isReadonly) return; + const fs = new FileSystem(this.ignoreCase, { time: this._time }); + fs._lazy = this._lazy; + fs._cwd = this._cwd; + fs._time = this._time; + fs._shadowRoot = this._shadowRoot; + fs._dirStack = this._dirStack; + fs.makeReadonly(); + this._lazy = {}; + this._shadowRoot = fs; + } + /** * Gets a shadow copy of this file system. Changes to the shadow copy do not affect the * original, allowing multiple copies of the same core file system without multiple copies