Skip to content

Commit f5ad787

Browse files
authored
Always recreate the file watcher when rename event occurs (#48997)
* Convert some of the watchEnvironment tests to baselines for updating later * Add tests for inode watching by making fsWatch part of system function that tests presence before creating fs watch * Refactor for simpler tests * Accept map of file content or file or symlink or folder * Add test when rename event occurs when file has already reappeared * On rename event for the file, replace file watcher irrespective of file presence * Fix regex * Ensure that when doing inode watching watchers is replaces only on disappearance or appearance * Some logging for debugging further * Revert "Some logging for debugging further" This reverts commit dd2164a. * Add test when rename event occurs on mac with ~ appended to file name * If the relativeFileName ends with tilde, remove it from the event * Some logging for debugging further * Revert "Some logging for debugging further" This reverts commit e1ba8a8. * Add documentation and fail safe the event firing
1 parent ce63935 commit f5ad787

File tree

541 files changed

+43236
-7794
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

541 files changed

+43236
-7794
lines changed

Diff for: src/compiler/sys.ts

+179-145
Large diffs are not rendered by default.

Diff for: src/harness/virtualFileSystemWithWatch.ts

+159-234
Large diffs are not rendered by default.

Diff for: src/testRunner/unittests/tsbuildWatch/noEmit.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@ namespace ts.tscWatch {
66
commandLineArgs: ["-b", "-w", "-verbose"],
77
sys: () => createWatchedSystem(
88
[
9-
libFile,
9+
{ path: libFile.path, content: libContent },
1010
{ path: `${projectRoot}/a.js`, content: "" },
1111
{ path: `${projectRoot}/b.ts`, content: "" },
1212
{ path: `${projectRoot}/tsconfig.json`, content: JSON.stringify({ compilerOptions: { allowJs: true, noEmit: true } }) },
13-
{ path: libFile.path, content: libContent }
1413
],
1514
{ currentDirectory: projectRoot }
1615
),

Diff for: src/testRunner/unittests/tscWatch/emit.ts

+6-15
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,12 @@ namespace ts.tscWatch {
66
scenario,
77
subScenario: `emit with outFile or out setting/${subScenario}`,
88
commandLineArgs: ["--w", "-p", "/a/tsconfig.json"],
9-
sys: () => {
10-
const config: File = {
11-
path: "/a/tsconfig.json",
12-
content: JSON.stringify({ compilerOptions: { out, outFile } })
13-
};
14-
const f1: File = {
15-
path: "/a/a.ts",
16-
content: "let x = 1"
17-
};
18-
const f2: File = {
19-
path: "/a/b.ts",
20-
content: "let y = 1"
21-
};
22-
return createWatchedSystem([f1, f2, config, libFile]);
23-
},
9+
sys: () => createWatchedSystem({
10+
"/a/a.ts": "let x = 1",
11+
"/a/b.ts": "let y = 1",
12+
"/a/tsconfig.json": JSON.stringify({ compilerOptions: { out, outFile } }),
13+
[libFile.path]: libFile.content,
14+
}),
2415
changes: [
2516
{
2617
caption: "Make change in the file",

Diff for: src/testRunner/unittests/tscWatch/forceConsistentCasingInFileNames.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace ts.tscWatch {
2020
scenario: "forceConsistentCasingInFileNames",
2121
subScenario,
2222
commandLineArgs: ["--w", "--p", tsconfig.path],
23-
sys: () => createWatchedSystem([loggerFile, anotherFile, tsconfig, libFile, tsconfig]),
23+
sys: () => createWatchedSystem([loggerFile, anotherFile, tsconfig, libFile]),
2424
changes
2525
});
2626
}

Diff for: src/testRunner/unittests/tscWatch/helpers.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@ namespace ts.tscWatch {
77
export import libFile = TestFSWithWatch.libFile;
88
export import createWatchedSystem = TestFSWithWatch.createWatchedSystem;
99
export import checkArray = TestFSWithWatch.checkArray;
10-
export import checkWatchedFiles = TestFSWithWatch.checkWatchedFiles;
11-
export import checkWatchedFilesDetailed = TestFSWithWatch.checkWatchedFilesDetailed;
12-
export import checkWatchedDirectories = TestFSWithWatch.checkWatchedDirectories;
13-
export import checkWatchedDirectoriesDetailed = TestFSWithWatch.checkWatchedDirectoriesDetailed;
1410
export import checkOutputContains = TestFSWithWatch.checkOutputContains;
1511
export import checkOutputDoesNotContain = TestFSWithWatch.checkOutputDoesNotContain;
1612

@@ -436,7 +432,7 @@ namespace ts.tscWatch {
436432
return sys;
437433
}
438434

439-
export function createSystemWithSolutionBuild(solutionRoots: readonly string[], files: readonly TestFSWithWatch.FileOrFolderOrSymLink[], params?: TestFSWithWatch.TestServerHostCreationParameters) {
435+
export function createSystemWithSolutionBuild(solutionRoots: readonly string[], files: TestFSWithWatch.FileOrFolderOrSymLinkMap | readonly TestFSWithWatch.FileOrFolderOrSymLink[], params?: TestFSWithWatch.TestServerHostCreationParameters) {
440436
return solutionBuildWithBaseline(createWatchedSystem(files, params), solutionRoots);
441437
}
442438
}

Diff for: src/testRunner/unittests/tscWatch/watchEnvironment.ts

+136
Original file line numberDiff line numberDiff line change
@@ -581,5 +581,141 @@ namespace ts.tscWatch {
581581
verifyWorker("-extendedDiagnostics");
582582
});
583583
});
584+
585+
verifyTscWatch({
586+
scenario,
587+
subScenario: `fsWatch/when using file watching thats when rename occurs when file is still on the disk`,
588+
commandLineArgs: ["-w", "--extendedDiagnostics"],
589+
sys: () => createWatchedSystem(
590+
{
591+
[libFile.path]: libFile.content,
592+
[`${projectRoot}/main.ts`]: `import { foo } from "./foo"; foo();`,
593+
[`${projectRoot}/foo.ts`]: `export declare function foo(): string;`,
594+
[`${projectRoot}/tsconfig.json`]: JSON.stringify({
595+
watchOptions: { watchFile: "useFsEvents" },
596+
files: ["foo.ts", "main.ts"]
597+
}),
598+
},
599+
{ currentDirectory: projectRoot, }
600+
),
601+
changes: [
602+
{
603+
caption: "Introduce error such that when callback happens file is already appeared",
604+
// vm's wq generates this kind of event
605+
// Skip delete event so inode changes but when the create's rename occurs file is on disk
606+
change: sys => sys.modifyFile(`${projectRoot}/foo.ts`, `export declare function foo2(): string;`, {
607+
invokeFileDeleteCreateAsPartInsteadOfChange: true,
608+
ignoreDelete: true,
609+
}),
610+
timeouts: sys => sys.checkTimeoutQueueLengthAndRun(1),
611+
},
612+
{
613+
caption: "Replace file with rename event that fixes error",
614+
change: sys => sys.modifyFile(`${projectRoot}/foo.ts`, `export declare function foo(): string;`, { invokeFileDeleteCreateAsPartInsteadOfChange: true, }),
615+
timeouts: sys => sys.checkTimeoutQueueLengthAndRun(1),
616+
},
617+
]
618+
});
619+
620+
describe("with fsWatch on inodes", () => {
621+
verifyTscWatch({
622+
scenario,
623+
subScenario: `fsWatch/when using file watching thats on inode`,
624+
commandLineArgs: ["-w", "--extendedDiagnostics"],
625+
sys: () => createWatchedSystem(
626+
{
627+
[libFile.path]: libFile.content,
628+
[`${projectRoot}/main.ts`]: `import { foo } from "./foo"; foo();`,
629+
[`${projectRoot}/foo.d.ts`]: `export function foo(): string;`,
630+
[`${projectRoot}/tsconfig.json`]: JSON.stringify({ watchOptions: { watchFile: "useFsEvents" }, files: ["foo.d.ts", "main.ts"] }),
631+
},
632+
{
633+
currentDirectory: projectRoot,
634+
inodeWatching: true
635+
}
636+
),
637+
changes: [
638+
{
639+
caption: "Replace file with rename event that introduces error",
640+
change: sys => sys.modifyFile(`${projectRoot}/foo.d.ts`, `export function foo2(): string;`, { invokeFileDeleteCreateAsPartInsteadOfChange: true }),
641+
timeouts: sys => sys.checkTimeoutQueueLengthAndRun(2),
642+
},
643+
{
644+
caption: "Replace file with rename event that fixes error",
645+
change: sys => sys.modifyFile(`${projectRoot}/foo.d.ts`, `export function foo(): string;`, { invokeFileDeleteCreateAsPartInsteadOfChange: true }),
646+
timeouts: sys => sys.checkTimeoutQueueLengthAndRun(2),
647+
},
648+
]
649+
});
650+
651+
verifyTscWatch({
652+
scenario,
653+
subScenario: `fsWatch/when using file watching thats on inode when rename event ends with tilde`,
654+
commandLineArgs: ["-w", "--extendedDiagnostics"],
655+
sys: () => createWatchedSystem(
656+
{
657+
[libFile.path]: libFile.content,
658+
[`${projectRoot}/main.ts`]: `import { foo } from "./foo"; foo();`,
659+
[`${projectRoot}/foo.d.ts`]: `export function foo(): string;`,
660+
[`${projectRoot}/tsconfig.json`]: JSON.stringify({ watchOptions: { watchFile: "useFsEvents" }, files: ["foo.d.ts", "main.ts"] }),
661+
},
662+
{
663+
currentDirectory: projectRoot,
664+
inodeWatching: true
665+
}
666+
),
667+
changes: [
668+
{
669+
caption: "Replace file with rename event that introduces error",
670+
change: sys => sys.modifyFile(`${projectRoot}/foo.d.ts`, `export function foo2(): string;`, { invokeFileDeleteCreateAsPartInsteadOfChange: true, useTildeAsSuffixInRenameEventFileName: true }),
671+
timeouts: sys => sys.checkTimeoutQueueLengthAndRun(2),
672+
},
673+
{
674+
caption: "Replace file with rename event that fixes error",
675+
change: sys => sys.modifyFile(`${projectRoot}/foo.d.ts`, `export function foo(): string;`, { invokeFileDeleteCreateAsPartInsteadOfChange: true, useTildeAsSuffixInRenameEventFileName: true }),
676+
timeouts: sys => sys.checkTimeoutQueueLengthAndRun(2),
677+
},
678+
]
679+
});
680+
681+
verifyTscWatch({
682+
scenario,
683+
subScenario: `fsWatch/when using file watching thats on inode when rename occurs when file is still on the disk`,
684+
commandLineArgs: ["-w", "--extendedDiagnostics"],
685+
sys: () => createWatchedSystem(
686+
{
687+
[libFile.path]: libFile.content,
688+
[`${projectRoot}/main.ts`]: `import { foo } from "./foo"; foo();`,
689+
[`${projectRoot}/foo.ts`]: `export declare function foo(): string;`,
690+
[`${projectRoot}/tsconfig.json`]: JSON.stringify({
691+
watchOptions: { watchFile: "useFsEvents" },
692+
files: ["foo.ts", "main.ts"]
693+
}),
694+
},
695+
{
696+
currentDirectory: projectRoot,
697+
inodeWatching: true,
698+
}
699+
),
700+
changes: [
701+
{
702+
caption: "Introduce error such that when callback happens file is already appeared",
703+
// vm's wq generates this kind of event
704+
// Skip delete event so inode changes but when the create's rename occurs file is on disk
705+
change: sys => sys.modifyFile(`${projectRoot}/foo.ts`, `export declare function foo2(): string;`, {
706+
invokeFileDeleteCreateAsPartInsteadOfChange: true,
707+
ignoreDelete: true,
708+
skipInodeCheckOnCreate: true
709+
}),
710+
timeouts: sys => sys.checkTimeoutQueueLengthAndRun(1),
711+
},
712+
{
713+
caption: "Replace file with rename event that fixes error",
714+
change: sys => sys.modifyFile(`${projectRoot}/foo.ts`, `export declare function foo(): string;`, { invokeFileDeleteCreateAsPartInsteadOfChange: true, }),
715+
timeouts: sys => sys.checkTimeoutQueueLengthAndRun(1),
716+
},
717+
]
718+
});
719+
});
584720
});
585721
}

0 commit comments

Comments
 (0)