Skip to content

Commit

Permalink
Make canUseWatchEvents test framework more generic so we can add more…
Browse files Browse the repository at this point in the history
… tests easily (#58962)
  • Loading branch information
sheetalkamat authored Jun 25, 2024
1 parent b384054 commit f2f91cc
Show file tree
Hide file tree
Showing 6 changed files with 1,435 additions and 1,077 deletions.
136 changes: 136 additions & 0 deletions src/testRunner/unittests/helpers/tsserver.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import path from "path";
import { incrementalVerifier } from "../../../harness/incrementalUtils.js";
import { patchServiceForStateBaseline } from "../../../harness/projectServiceStateLogger.js";
import {
Expand Down Expand Up @@ -67,6 +68,130 @@ export function patchHostTimeouts(
}
}

function patchSessionToHandleWatchEvents(session: TestSession) {
const event = session.event;
const idToClose = new Map<number, () => void>();
session.event = (data, eventName) => {
event.call(session, data, eventName);
switch (eventName) {
case ts.server.CreateFileWatcherEvent:
watchFile(data as ts.server.protocol.CreateFileWatcherEventBody);
break;
case ts.server.CreateDirectoryWatcherEvent:
watchDirectory(data as ts.server.protocol.CreateDirectoryWatcherEventBody);
break;
case ts.server.CloseFileWatcherEvent:
closeWatcher(data as ts.server.protocol.CloseFileWatcherEventBody);
break;
default:
break;
}
};

function watchFile(event: ts.server.protocol.CreateFileWatcherEventBody) {
createWatcher(
"watchFile",
event,
recordChange =>
session.host.watchUtils.pollingWatch(
session.host.toNormalizedAbsolutePath(event.path),
{
cb: (fileName, eventKind) =>
recordChange(
event.id,
session.host.windowsStyleRoot ?
path.win32.resolve(fileName) :
path.posix.resolve(fileName),
eventKind === ts.FileWatcherEventKind.Created ?
"created" :
eventKind === ts.FileWatcherEventKind.Deleted ? "deleted" : "updated",
/*ignoreUpdate*/ false,
),
pollingInterval: undefined!,
event,
},
),
);
}

function watchDirectory(event: ts.server.protocol.CreateDirectoryWatcherEventBody) {
createWatcher(
"watchDirectory",
event,
recordChange =>
session.host.watchUtils.fsWatch(
session.host.toNormalizedAbsolutePath(event.path),
event.recursive,
{
cb: (eventName, relativeFileName) => {
if (!relativeFileName) return;
const fileName = session.host.windowsStyleRoot ?
path.win32.join(event.path, relativeFileName) :
path.posix.join(event.path, relativeFileName);
if (eventName === "change") {
recordChange(
event.id,
fileName,
"updated",
!!event.ignoreUpdate,
);
}
else {
recordChange(
event.id,
fileName,
session.host.fileExists(fileName) || session.host.directoryExists(fileName) ?
"created" :
"deleted",
!!event.ignoreUpdate,
);
}
},
inode: undefined,
event,
},
),
);
}

function createWatcher(
watchType: string,
event: ts.server.protocol.CreateFileWatcherEventBody | ts.server.protocol.CreateDirectoryWatcherEventBody,
create: (
recordChange: (
id: number,
file: string,
eventType: "created" | "deleted" | "updated",
ignoreUpdate: boolean,
) => void,
) => ts.FileWatcher,
) {
session.logger.log(`Custom ${watchType}:: Added:: ${JSON.stringify(event)}`);
ts.Debug.assert(!idToClose.has(event.id));
const result = create((id, file, eventType, ignoreUpdate) => {
const ignored = eventType === "updated" && ignoreUpdate;
session.logger.log(`Custom ${watchType}:: Triggered${ignoreUpdate ? " Ignored" : ""}:: ${JSON.stringify(event)}:: ${file} ${eventType}`);
if (!ignored) {
let watchChange = session.watchChanges.get(id);
if (!watchChange) session.watchChanges.set(id, watchChange = { id });
(watchChange[eventType] ??= []).push(file);
}
});
idToClose.set(event.id, () => {
session.logger.log(`Custom ${watchType}:: Close:: ${JSON.stringify(event)}`);
result.close();
});
}

function closeWatcher(data: ts.server.protocol.CloseFileWatcherEventBody) {
const close = idToClose.get(data.id);
if (close) {
idToClose.delete(data.id);
close();
}
}
}

export interface TestSessionOptions extends ts.server.SessionOptions, TestTypingsInstallerOptions {
host: TestServerHost;
logger: LoggerWithInMemoryLogs;
Expand All @@ -90,6 +215,7 @@ export class TestSession extends ts.server.Session {
public override logger!: LoggerWithInMemoryLogs;
public override readonly typingsInstaller!: TestTypingsInstallerAdapter;
public serverCancellationToken: TestServerCancellationToken;
public watchChanges = new Map<number, ts.server.protocol.WatchChangeRequestArgs>();

constructor(optsOrHost: TestSessionConstructorOptions) {
const opts = getTestSessionPartialOptionsAndHost(optsOrHost);
Expand Down Expand Up @@ -125,6 +251,7 @@ export class TestSession extends ts.server.Session {
if (opts.regionDiagLineCountThreshold !== undefined) {
this.regionDiagLineCountThreshold = opts.regionDiagLineCountThreshold;
}
if (opts.canUseWatchEvents) patchSessionToHandleWatchEvents(this);
}

getProjectService() {
Expand Down Expand Up @@ -159,6 +286,15 @@ export class TestSession extends ts.server.Session {
request.type = "request";
return this.executeCommand(request);
}

public invokeWatchChanges() {
const changes = ts.singleOrMany(ts.arrayFrom(this.watchChanges.values()));
this.watchChanges.clear();
this.executeCommandSeq<ts.server.protocol.WatchChangeRequest>({
command: ts.server.protocol.CommandTypes.WatchChange,
arguments: changes,
});
}
}

export function createSessionWithCustomEventHandler(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import {
Watches,
WatchUtils,
} from "../../../harness/watchUtils.js";
import {
CreateDirectoryWatcherEventBody,
CreateFileWatcherEventBody,
} from "../../../server/protocol.js";
import {
append,
arrayFrom,
Expand Down Expand Up @@ -276,11 +280,13 @@ type TimeOutCallback = (...args: any[]) => void;
export interface TestFileWatcher {
cb: FileWatcherCallback;
pollingInterval: PollingInterval;
event?: CreateFileWatcherEventBody;
}

export interface TestFsWatcher {
cb: FsWatchCallback;
inode: number | undefined;
event?: CreateDirectoryWatcherEventBody;
}

export interface WatchInvokeOptions {
Expand Down
Loading

0 comments on commit f2f91cc

Please sign in to comment.