More PR comments

1. Move VirtualServerHost code to correct vfs/harness location.
2. Serialise watch and timeout information.
3. Improve test by modifying an unopened file.
4. Test can run arbitrary actions in addition to commands.
This commit is contained in:
Nathan Shively-Sanders
2022-06-14 09:03:33 -07:00
parent be9147290a
commit b31aaf3eef
8 changed files with 172 additions and 72 deletions
+18 -28
View File
@@ -113,6 +113,12 @@ interface Array<T> { length: number; [n: number]: T; }`
delete this.map[key];
}
}
serialize(baseline: string[]) {
for (const id in this.map) {
baseline.push(`${id} at time ${this.map[id].time} in ${this.map[id].ms} ms: ${this.map[id].args[1]}`)
}
}
}
type TimeOutCallback = (...args: any[]) => void;
@@ -243,7 +249,7 @@ interface Array<T> { length: number; [n: number]: T; }`
if (this.inodes) this.inodes.set(path, this.nextInode++);
}
override getInode(path: Path): number | undefined {
if (this.inodes) return this.inodes.get(path)
if (this.inodes) return this.inodes.get(path);
}
protected override deleteInode(path: Path) {
if (this.inodes) this.inodes.delete(path);
@@ -414,6 +420,10 @@ interface Array<T> { length: number; [n: number]: T; }`
this.timeoutCallbacks.unregister(timeoutId);
}
serializeTimeout(baseline: string[]) {
this.timeoutCallbacks.serialize(baseline)
}
clearScreen(): void {
this.screenClears.push(this.output.length);
}
@@ -490,14 +500,11 @@ interface Array<T> { length: number; [n: number]: T; }`
this.clearOutput();
}
serializeWatches(baseline: string[] = []) {
serializeMultiMap(baseline, "WatchedFiles", this.watchedFiles, ({ fileName, pollingInterval }) => ({ fileName, pollingInterval }));
baseline.push("");
serializeMultiMap(baseline, "FsWatches", this.fsWatches, serializeTestFsWatcher);
baseline.push("");
serializeMultiMap(baseline, "FsWatchesRecursive", this.fsWatchesRecursive, serializeTestFsWatcher);
baseline.push("");
return baseline;
readonly exitMessage = "System Exit";
exitCode: number | undefined;
exit(exitCode?: number) {
this.exitCode = exitCode;
throw new Error(this.exitMessage);
}
override getEnvironmentVariable(name: string) {
@@ -505,7 +512,7 @@ interface Array<T> { length: number; [n: number]: T; }`
}
}
export function snap(fshost: VirtualFS.VirtualServerHost): ESMap<Path, FSEntry> {
export function snap(fshost: VirtualServerHost): ESMap<Path, FSEntry> {
const result = new Map<Path, FSEntry>();
fshost.fs.forEach((value, key) => {
const cloneValue = clone(value);
@@ -518,7 +525,7 @@ interface Array<T> { length: number; [n: number]: T; }`
return result;
}
export function diff(fshost: VirtualFS.VirtualServerHost, baseline: string[], base: ESMap<Path, FSEntry> = new Map()) {
export function diff(fshost: VirtualServerHost, baseline: string[], base: ESMap<Path, FSEntry> = new Map()) {
fshost.fs.forEach((newFsEntry, path) => {
diffFsEntry(baseline, base.get(path), newFsEntry, fshost.getInode(path), fshost.writtenFiles);
});
@@ -598,23 +605,6 @@ interface Array<T> { length: number; [n: number]: T; }`
}
}
function serializeTestFsWatcher({ directoryName, inode }: VirtualFsWatcher) {
return {
directoryName,
inode,
};
}
function serializeMultiMap<T, U>(baseline: string[], caption: string, multiMap: MultiMap<string, T>, valueMapper: (value: T) => U) {
baseline.push(`${caption}::`);
multiMap.forEach((values, key) => {
baseline.push(`${key}:`);
for (const value of values) {
baseline.push(` ${JSON.stringify(valueMapper(value))}`);
}
});
}
function baselineOutputs(baseline: string[], output: readonly string[], start: number, end = output.length) {
let baselinedOutput: string[] | undefined;
for (let i = start; i < end; i++) {
+3 -3
View File
@@ -416,7 +416,7 @@ namespace ts.server {
storeFilesChangingSignatureDuringEmit: host.storeFilesChangingSignatureDuringEmit,
// VirtualFS
ensureFileOrFolder: fshost.ensureFileOrFolder.bind(fshost),
} as any;
} as ServerHost;
}
const noopConfigFileWatcher: FileWatcher = { close: noop };
@@ -3757,13 +3757,13 @@ namespace ts.server {
updateFileSystem(updatedFiles: protocol.FileSystemRequestArgs[] | undefined, deletedFiles?: string[]) {
const fshost = this.host as unknown as FileServerHost;
if (updatedFiles) {
Debug.assert(fshost.ensureFileOrFolder)
Debug.assert(fshost.ensureFileOrFolder);
for (const { file, fileContent } of updatedFiles) {
fshost.ensureFileOrFolder({ path: file, content: fileContent });
}
}
if (deletedFiles) {
Debug.assert(fshost.deleteFile)
Debug.assert(fshost.deleteFile);
for (const file of deletedFiles) {
fshost.deleteFile(file, /*deleteEmptyParentFolders*/ true);
}
+1 -1
View File
@@ -327,7 +327,7 @@ interface Symbol {
originalReadCall?: System["readFile"],
) {
const buildInfoPath = getTsBuildInfoEmitOutputFilePath(options);
if (!buildInfoPath || !sys.writtenFiles!.has(toPathWithSystem(sys, buildInfoPath))) return;
if (!buildInfoPath || !sys.writtenFiles.has(toPathWithSystem(sys, buildInfoPath))) return;
if (!sys.fileExists(buildInfoPath)) return;
const buildInfo = getBuildInfo((originalReadCall || sys.readFile).call(sys, buildInfoPath, "utf8")!);
@@ -22,42 +22,46 @@ namespace ts.projectSystem {
};
const lib: protocol.FileSystemRequestArgs = {
file: "/fshost/lib/lib.d.ts",
fileContent: `/// <reference no-default-lib="true"/>
interface Boolean {}
interface Function {}
interface CallableFunction {}
interface NewableFunction {}
interface IArguments {}
interface Number { toExponential: any; }
interface Object {}
interface RegExp {}
interface String { charAt: any; }
interface Array<T> { length: number; [n: number]: T; }`
};
fileContent: libFile.content
};
function fileContentWithComment(file: protocol.FileSystemRequestArgs) {
return `// some copy right notice
${file.fileContent}`;
}
function baselineFileSystem(scenario: string, subScenario: string, requests: [string, Partial<protocol.Request>][], host: VirtualFS.VirtualServerHost, session: TestSession) {
function baselineFileSystem(scenario: string, subScenario: string, requests: [string, (() => void) | Partial<protocol.Request>][], host: TestServerHost, fshost: VirtualFS.VirtualServerHost, session: TestSession) {
const history: string[] = [];
let prev = VirtualFS.snap(host);
let prev = VirtualFS.snap(fshost);
let prev2 = VirtualFS.snap(host);
for (const [name, request] of requests) {
session.executeCommandSeq(request);
if (typeof request === "function") {
request();
}
else {
session.executeCommandSeq(request);
}
history.push("");
history.push("#### " + name);
VirtualFS.diff(host, history, prev);
prev = VirtualFS.snap(host);
VirtualFS.diff(fshost, history, prev);
prev = VirtualFS.snap(fshost);
VirtualFS.diff(host, history, prev2);
prev2 = VirtualFS.snap(host);
host.serializeTimeout(history)
}
history.push("### fshost watches")
fshost.serializeWatches(history)
history.push("### host watches")
host.serializeWatches(history)
Harness.Baseline.runBaseline(`tsserver/${scenario}/${subScenario.split(" ").join("-")}.txt`, history.join("\r\n"));
baselineTsserverLogs(scenario, subScenario, session);
}
it("with updateFileSystem request", () => {
const host = VirtualFS.createVirtualServerHost({ executingFilePath: "/host/tsc.js" });
const host = VirtualFS.createServerHost({ executingFilePath: "/host/tsc.js" });
const fshost = VirtualFS.createVirtualServerHost({ executingFilePath: "/fshost/tsc.js" });
const session = createSession(host, { fshost, logger: createLoggerWithInMemoryLogs(), canUseEvents: true });
const requests: [string, Partial<protocol.Request>][] = [
const requests: [string, (() => void) | Partial<protocol.Request>][] = [
["Initial updateFileSystem", {
command: protocol.CommandTypes.UpdateFileSystem,
arguments:{
@@ -96,6 +100,22 @@ ${file.fileContent}`;
deleted: [],
},
}],
["ensure /host/app.ts exists", () => host.ensureFileOrFolder({
path: "/host/b/app.ts",
content: 'console.log("hello")'
})],
["modify /host/app.ts", () => host.modifyFile(
"/host/b/app.ts",
"console.log('goodbye')"
)],
["ensure /fshost/file4.ts exists", () => fshost.ensureFileOrFolder({
path:"/fshost/b/file4.ts",
content: "console.log('file4 exists')",
})],
["modify /fshost/file4", () => fshost.modifyFile(
"/fshost/b/file4.ts",
"console.log('file4 modified')"
)],
["delete", {
command: protocol.CommandTypes.UpdateFileSystem,
arguments:{
@@ -119,17 +139,7 @@ ${file.fileContent}`;
const scenario = "updateFileSystem";
const subScenario = "open and close files";
baselineFileSystem(scenario, subScenario, requests, fshost, session);
host.ensureFileOrFolder({
path: "/host/b/app.ts",
content: 'console.log("hello")'
});
host.modifyFile("/host/b/app.ts", "console.log('goodbye')");
assert.isFalse(host.fileExists("/fshost/b/app.ts"), "host fileExists /fshost/b/app.ts");
assert.isTrue(fshost.fileExists("/fshost/b/app.ts"), "fshost fileExists /fshost/b/app.ts");
assert.isTrue(host.fileExists("/host/b/app.ts"), "host fileExists /host/b/app.ts");
assert.isFalse(fshost.fileExists("/host/b/app.ts"), "fshost fileExists /host/b/app.ts");
baselineFileSystem(scenario, subScenario, requests, host, fshost, session);
});
});
}
@@ -42,7 +42,7 @@ namespace ts.projectSystem {
if (isVfs) {
serverMode = LanguageServiceMode.Semantic;
fshost = VirtualFS.createVirtualServerHost({ executingFilePath: "/a/lib/tsc.js" });
(fshost as VirtualFS.VirtualServerHost).ensureFileOrFolder(libFile);
(fshost).ensureFileOrFolder(libFile);
}
const logger = logLevel !== undefined ? new server.MainProcessLogger(logLevel, webHost) : nullLogger();
const session = new TestWorkerSession(webSys, fshost, webHost, { serverMode }, logger);
+26 -5
View File
@@ -636,18 +636,39 @@ namespace ts.VirtualFS {
return fsEntry?.fullPath || realFullPath;
}
readonly exitMessage = "System Exit";
exitCode: number | undefined;
readonly resolvePath = (s: string) => s;
readonly getExecutingFilePath = () => this.executingFilePath;
readonly getCurrentDirectory = () => this.currentDirectory;
exit(exitCode?: number) {
this.exitCode = exitCode;
throw new Error(this.exitMessage);
serializeWatches(baseline: string[] = []) {
serializeMultiMap(baseline, "WatchedFiles", this.watchedFiles, ({ fileName, pollingInterval }) => ({ fileName, pollingInterval }));
baseline.push("");
serializeMultiMap(baseline, "FsWatches", this.fsWatches, serializeTestFsWatcher);
baseline.push("");
serializeMultiMap(baseline, "FsWatchesRecursive", this.fsWatchesRecursive, serializeTestFsWatcher);
baseline.push("");
return baseline;
}
getEnvironmentVariable(_name: string) {
return "";
}
}
function serializeTestFsWatcher({ directoryName, inode }: VirtualFsWatcher) {
return {
directoryName,
inode,
};
}
function serializeMultiMap<T, U>(baseline: string[], caption: string, multiMap: MultiMap<string, T>, valueMapper: (value: T) => U) {
baseline.push(`${caption}::`);
multiMap.forEach((values, key) => {
baseline.push(`${key}:`);
for (const value of values) {
baseline.push(` ${JSON.stringify(valueMapper(value))}`);
}
});
}
}
@@ -51,6 +51,8 @@ Project '/fshost/b/tsconfig.json' (Configured)
-----------------------------------------------
event:
{"seq":0,"type":"event","event":"projectLoadingFinish","body":{"projectName":"/fshost/b/tsconfig.json"}}
event:
{"seq":0,"type":"event","event":"telemetry","body":{"telemetryEventName":"projectInfo","payload":{"projectId":"bea14cc795bf8fe7209e389c09ff1c7f1d8288597401b8616cd5edcd3f073d77","fileStats":{"js":0,"jsSize":0,"jsx":0,"jsxSize":0,"ts":4,"tsSize":79,"tsx":0,"tsxSize":0,"dts":0,"dtsSize":0,"deferred":0,"deferredSize":0},"compilerOptions":{},"typeAcquisition":{"enable":false,"include":false,"exclude":false},"extends":false,"files":false,"include":false,"exclude":false,"compileOnSave":false,"configFileName":"tsconfig.json","projectType":"configured","languageServiceEnabled":true,"version":"FakeVersion"}}}
event:
{"seq":0,"type":"event","event":"configFileDiag","body":{"triggerFile":"/fshost/b/app.ts","configFile":"/fshost/b/tsconfig.json","diagnostics":[{"text":"File '/fshost/lib.d.ts' not found.\n The file is in the program because:\n Default library for target 'es3'","code":6053,"category":"error"},{"text":"Cannot find global type 'Array'.","code":2318,"category":"error"},{"text":"Cannot find global type 'Boolean'.","code":2318,"category":"error"},{"text":"Cannot find global type 'Function'.","code":2318,"category":"error"},{"text":"Cannot find global type 'IArguments'.","code":2318,"category":"error"},{"text":"Cannot find global type 'Number'.","code":2318,"category":"error"},{"text":"Cannot find global type 'Object'.","code":2318,"category":"error"},{"text":"Cannot find global type 'RegExp'.","code":2318,"category":"error"},{"text":"Cannot find global type 'String'.","code":2318,"category":"error"}]}}
Project '/fshost/b/tsconfig.json' (Configured)
@@ -82,15 +84,19 @@ request:{"command":"updateFileSystem","arguments":{"fileSystem":"memfs","files":
response:{"response":true,"responseRequired":true}
request:{"command":"updateFileSystem","arguments":{"fileSystem":"memfs","files":[{"file":"/fshost/b/app.ts","fileContent":"import { xyz } from './file3'; let y = xyz"}],"deleted":[]},"seq":5,"type":"request"}
response:{"response":true,"responseRequired":true}
DirectoryWatcher:: Triggered with /fshost/b/file4.ts :: WatchInfo: /fshost/b 1 undefined Config: /fshost/b/tsconfig.json WatchType: Wild card directory
Scheduled: /fshost/b/tsconfig.json
Scheduled: *ensureProjectForOpenFiles*
Elapsed:: *ms DirectoryWatcher:: Triggered with /fshost/b/file4.ts :: WatchInfo: /fshost/b 1 undefined Config: /fshost/b/tsconfig.json WatchType: Wild card directory
request:{"command":"updateFileSystem","arguments":{"fileSystem":"memfs","files":[],"deleted":["/fshost/b/commonFile1.ts"]},"seq":6,"type":"request"}
FileWatcher:: Triggered with /fshost/b/commonFile1.ts 2:: WatchInfo: /fshost/b/commonFile1.ts 500 undefined WatchType: Closed Script info
FileWatcher:: Close:: WatchInfo: /fshost/b/commonFile1.ts 500 undefined WatchType: Closed Script info
Scheduled: /fshost/b/tsconfig.json
Scheduled: *ensureProjectForOpenFiles*
Scheduled: /fshost/b/tsconfig.json, Cancelled earlier one
Scheduled: *ensureProjectForOpenFiles*, Cancelled earlier one
Elapsed:: *ms FileWatcher:: Triggered with /fshost/b/commonFile1.ts 2:: WatchInfo: /fshost/b/commonFile1.ts 500 undefined WatchType: Closed Script info
DirectoryWatcher:: Triggered with /fshost/b/commonFile1.ts :: WatchInfo: /fshost/b 1 undefined Config: /fshost/b/tsconfig.json WatchType: Wild card directory
Scheduled: /fshost/b/tsconfig.json
Scheduled: *ensureProjectForOpenFiles*
Scheduled: /fshost/b/tsconfig.json, Cancelled earlier one
Scheduled: *ensureProjectForOpenFiles*, Cancelled earlier one
Elapsed:: *ms DirectoryWatcher:: Triggered with /fshost/b/commonFile1.ts :: WatchInfo: /fshost/b 1 undefined Config: /fshost/b/tsconfig.json WatchType: Wild card directory
response:{"response":true,"responseRequired":true}
request:{"command":"close","arguments":{"file":"/fshost/b/app.ts"},"seq":7,"type":"request"}
@@ -30,23 +30,96 @@ interface Array<T> { length: number; [n: number]: T; }
#### Opening app.ts
#### Opening file3.ts
#### non-delete
#### change app.ts content
//// [/fshost/b/app.ts]
import { xyz } from './file3'; let y = xyz
#### ensure /host/app.ts exists
//// [/host/b/app.ts]
console.log("hello")
#### modify /host/app.ts
//// [/host/b/app.ts]
console.log('goodbye')
#### ensure /fshost/file4.ts exists
//// [/fshost/b/file4.ts]
console.log('file4 exists')
1 at time 12000 in 250 ms: /fshost/b/tsconfig.json
2 at time 12000 in 2500 ms: *ensureProjectForOpenFiles*
#### modify /fshost/file4
//// [/fshost/b/file4.ts]
console.log('file4 modified')
1 at time 12000 in 250 ms: /fshost/b/tsconfig.json
2 at time 12000 in 2500 ms: *ensureProjectForOpenFiles*
#### delete
//// [/fshost/b/commonFile1.ts] deleted
5 at time 12000 in 250 ms: /fshost/b/tsconfig.json
6 at time 12000 in 2500 ms: *ensureProjectForOpenFiles*
#### close
5 at time 12000 in 250 ms: /fshost/b/tsconfig.json
6 at time 12000 in 2500 ms: *ensureProjectForOpenFiles*
### fshost watches
WatchedFiles::
/fshost/b/tsconfig.json:
{"fileName":"/fshost/b/tsconfig.json","pollingInterval":250}
/fshost/b:
{"fileName":"/fshost/b","pollingInterval":500}
/fshost/b/commonfile2.ts:
{"fileName":"/fshost/b/commonFile2.ts","pollingInterval":250}
/fshost/lib.d.ts:
{"fileName":"/fshost/lib.d.ts","pollingInterval":250}
/fshost/b/node_modules/@types:
{"fileName":"/fshost/b/node_modules/@types","pollingInterval":500}
/fshost/b/app.ts:
{"fileName":"/fshost/b/app.ts","pollingInterval":250}
FsWatches::
/host/b/app.ts:
{"directoryName":"/host/b"}
FsWatchesRecursive::
/fshost/b:
{"directoryName":"/fshost/b"}
### host watches
WatchedFiles::
FsWatches::
FsWatchesRecursive::