From a1b9983cdcc161c8d6dac0dcd9674470148bb805 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 28 Mar 2022 11:22:08 -0700 Subject: [PATCH] Tests pass again Next: Make a way to create a vfs server from the start. --- src/server/editorServices.ts | 21 ++++++++++----- .../tsserver/applyChangesToOpenFiles.ts | 12 ++++----- src/vfs/virtualFileSystemWithWatch.ts | 26 ++++++++++++++----- 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index db37d243bef..343303ef33b 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -3693,7 +3693,7 @@ namespace ts.server { // 1. set some internal tsserver state for mocked FS (if it hasn't already been set, this might not be the first message) if (!this.fs) { // -nervous laugh- - this.fs = ts.TestFSWithWatch.createVirtualServerHost([]) + this.fs = ts.TestFSWithWatch.createVirtualServerHost([], { withSafeList: false }) ;(this as any).host = this.fs ;(this.session as any).host = this.fs } @@ -3705,8 +3705,8 @@ namespace ts.server { while (!(it = createdFiles.next()).done) { const document = it.value if (document.fileContent) { - // TODO: This isn't recursive, have to create parents AND walk past ones that already exist. - this.fs.createDirectory(ts.getDirectoryPath(document.file)); + if (!this.fs.directoryExists(ts.getDirectoryPath(document.file))) + this.fs.createDirectory(ts.getDirectoryPath(document.file), /*recursive*/ true); this.fs.writeFile(document.file, document.fileContent); } } @@ -3714,10 +3714,17 @@ namespace ts.server { if (updatedFiles) { let it - while (!(it = updatedFiles.next()).done) - if (it.value.fileContent) - // TODO: Also should check that file exists, probably creating if it doesn't - this.fs.modifyFile(it.value.file, it.value.fileContent) + while (!(it = updatedFiles.next()).done) { + if (it.value.fileContent) { + if (this.fs.fileExists(it.value.file)) { + this.fs.modifyFile(it.value.file, it.value.fileContent) + } + else { + this.fs.createDirectory(ts.getDirectoryPath(it.value.file), /*recursive*/ true); + this.fs.writeFile(it.value.file, it.value.fileContent); + } + } + } } if (deletedFiles) { // TODO: Probably want to delete empty parent folders while they are empty too diff --git a/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts b/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts index e51664dedb2..828fa445458 100644 --- a/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts +++ b/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts @@ -45,7 +45,8 @@ ${'content' in file ? file.content : file.fileContent}`; // 1. make sure that everything in files is there assert.isDefined(host) const fs = (host as any).fs as ESMap - assert.equal(fs.size, files.length) + console.log(Array.from(fs.values() as any as Iterable).filter(ts.TestFSWithWatch.isFsFile)) + assert.equal(Array.from(fs.values() as any as Iterable).filter(ts.TestFSWithWatch.isFsFile).length, files.length) for (const { file, fileContent } of files) { assert(host?.fileExists(file)) assert.equal(host!.readFile(file), fileContent) @@ -53,9 +54,8 @@ ${'content' in file ? file.content : file.fileContent}`; // 2. make sure nothing else is } it("with updateFileSystem request", () => { - // 1. Create a server host with no files, then updateFS and make sure everything works as before - // Things still to test - // after file watchers are implemented: + // TODO: Create a virtual host and make sure it works with session etc + // TODO: File watchers still seem wrong // 5. send updateFS request with a create/update/delete of a watched file, assert that file watchers fired // 6. probably some other watcher tests, not sure what const host = createServerHost([]); // old path goes into virtualFileSystemWithWatch.ts, so I guess it's getting the old host, not the replaced one @@ -76,9 +76,7 @@ ${'content' in file ? file.content : file.fileContent}`; }); const service = session.getProjectService(); // session -> service -> project const project = service.configuredProjects.get(config.file)!; - const v = (session as any).host.vfs const fakehost = (session as any).host as ts.TestFSWithWatch.VirtualServerHost - assert.isDefined(v); assert.isDefined(project); assert.equal(fakehost.fsWatches.size, 0) assert.equal(fakehost.fsWatchesRecursive.size, 0) @@ -163,7 +161,7 @@ ${'content' in file ? file.content : file.fileContent}`; verifyText(service, file3.file, fileContentWithComment(file3)); assert.equal(fakehost.fsWatches.size, 0) assert.equal(fakehost.fsWatchesRecursive.size, 0) - assert.equal(fakehost.watchedFiles.size, 1) + assert.equal(fakehost.watchedFiles.size, 0) }); function verifyText(service: server.ProjectService, file: string, expected: string) { diff --git a/src/vfs/virtualFileSystemWithWatch.ts b/src/vfs/virtualFileSystemWithWatch.ts index 783338088b2..28e090c22ca 100644 --- a/src/vfs/virtualFileSystemWithWatch.ts +++ b/src/vfs/virtualFileSystemWithWatch.ts @@ -40,14 +40,16 @@ interface Array { length: number; [n: number]: T; }` environmentVariables?: ESMap; runWithoutRecursiveWatches?: boolean; runWithFallbackPolling?: boolean; + withSafeList?: boolean; } export function createVirtualWatchedSystem(fileOrFolderList: readonly FileOrFolderOrSymLink[], params?: TestServerHostCreationParameters): VirtualServerHost { - return new VirtualServerHost(/*withSafelist*/ false, fileOrFolderList, params); + return new VirtualServerHost(!!params?.withSafeList, fileOrFolderList, params); } export function createVirtualServerHost(fileOrFolderList: readonly FileOrFolderOrSymLink[], params?: TestServerHostCreationParameters): VirtualServerHost { - const host = new VirtualServerHost(/*withSafelist*/ true, fileOrFolderList, params); + const withSafeList = params?.withSafeList !== undefined ? params.withSafeList : true; + const host = new VirtualServerHost(withSafeList, fileOrFolderList, params); // Just like sys, patch the host to use writeFile patchWriteFileEnsuringDirectory(host); return host; @@ -100,15 +102,15 @@ interface Array { length: number; [n: number]: T; }` export type FSEntry = FsFile | FsFolder | FsSymLink; - function isFsFolder(s: FSEntry | undefined): s is FsFolder { + export function isFsFolder(s: FSEntry | undefined): s is FsFolder { return !!s && isArray((s as FsFolder).entries); } - function isFsFile(s: FSEntry | undefined): s is FsFile { + export function isFsFile(s: FSEntry | undefined): s is FsFile { return !!s && isString((s as FsFile).content); } - function isFsSymLink(s: FSEntry | undefined): s is FsSymLink { + export function isFsSymLink(s: FSEntry | undefined): s is FsSymLink { return !!s && isString((s as FsSymLink).symLink); } @@ -853,11 +855,21 @@ interface Array { length: number; [n: number]: T; }` this.immediateCallbacks.unregister(timeoutId); } - createDirectory(directoryName: string): void { + createDirectory(directoryName: string, recursive?: boolean): void { const folder = this.toFsFolder(directoryName); - // base folder has to be present const base = getDirectoryPath(folder.path); + if (recursive && base !== directoryName && !this.getRealFolder(base)) { + if (base === '/') { + // create / if not present, then continue + this.ensureFolder('/', false); + } + else { + this.createDirectory(base, recursive) + } + } + + // base folder has to be present const baseFolder = this.fs.get(base) as FsFolder; Debug.assert(isFsFolder(baseFolder));