Skip to content

Commit 0adf36e

Browse files
committed
Add test that fails because file is written multiple times
Reported from #33061
1 parent 775a4dd commit 0adf36e

File tree

1 file changed

+150
-17
lines changed

1 file changed

+150
-17
lines changed

src/testRunner/unittests/tscWatch/incremental.ts

Lines changed: 150 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,17 @@ namespace ts.tscWatch {
1616
expectedIncrementalEmit?: ReadonlyArray<File>;
1717
expectedIncrementalErrors?: ReadonlyArray<string>;
1818
}
19-
function verifyIncrementalWatchEmit(input: VerifyIncrementalWatchEmitInput) {
19+
function verifyIncrementalWatchEmit(input: () => VerifyIncrementalWatchEmitInput) {
2020
it("with tsc --w", () => {
2121
verifyIncrementalWatchEmitWorker({
22-
input,
22+
input: input(),
2323
emitAndReportErrors: createWatchOfConfigFile,
2424
verifyErrors: checkOutputErrorsInitial
2525
});
2626
});
2727
it("with tsc", () => {
2828
verifyIncrementalWatchEmitWorker({
29-
input,
29+
input: input(),
3030
emitAndReportErrors: incrementalBuild,
3131
verifyErrors: checkNormalBuildErrors
3232
});
@@ -122,15 +122,15 @@ namespace ts.tscWatch {
122122

123123
function checkFileEmit(actual: Map<string>, expected: ReadonlyArray<File>) {
124124
assert.equal(actual.size, expected.length, `Actual: ${JSON.stringify(arrayFrom(actual.entries()), /*replacer*/ undefined, " ")}\nExpected: ${JSON.stringify(expected, /*replacer*/ undefined, " ")}`);
125-
expected.forEach(file => {
125+
for (const file of expected) {
126126
let expectedContent = file.content;
127127
let actualContent = actual.get(file.path);
128128
if (isBuildInfoFile(file.path)) {
129129
actualContent = actualContent && sanitizeBuildInfo(actualContent);
130130
expectedContent = sanitizeBuildInfo(expectedContent);
131131
}
132132
assert.equal(actualContent, expectedContent, `Emit for ${file.path}`);
133-
});
133+
}
134134
}
135135

136136
const libFileInfo: BuilderState.FileInfo = {
@@ -170,7 +170,7 @@ namespace ts.tscWatch {
170170
describe("own file emit without errors", () => {
171171
function verify(optionsToExtend?: CompilerOptions, expectedBuildinfoOptions?: CompilerOptions) {
172172
const modifiedFile2Content = file2.content.replace("y", "z").replace("20", "10");
173-
verifyIncrementalWatchEmit({
173+
verifyIncrementalWatchEmit(() => ({
174174
files: [libFile, file1, file2, configFile],
175175
optionsToExtend,
176176
expectedInitialEmit: [
@@ -226,7 +226,7 @@ namespace ts.tscWatch {
226226
}
227227
],
228228
expectedIncrementalErrors: emptyArray,
229-
});
229+
}));
230230
}
231231
verify();
232232
describe("with commandline parameters that are not relative", () => {
@@ -259,7 +259,7 @@ namespace ts.tscWatch {
259259
"file2.ts(1,7): error TS2322: Type '20' is not assignable to type 'string'.\n"
260260
];
261261
const modifiedFile1Content = file1.content.replace("x", "z");
262-
verifyIncrementalWatchEmit({
262+
verifyIncrementalWatchEmit(() => ({
263263
files: [libFile, file1, fileModified, configFile],
264264
expectedInitialEmit: [
265265
file1Js,
@@ -320,7 +320,7 @@ namespace ts.tscWatch {
320320
}
321321
],
322322
expectedIncrementalErrors: file2Errors,
323-
});
323+
}));
324324
});
325325

326326
describe("with --out", () => {
@@ -332,7 +332,7 @@ namespace ts.tscWatch {
332332
path: `${project}/out.js`,
333333
content: "var x = 10;\nvar y = 20;\n"
334334
};
335-
verifyIncrementalWatchEmit({
335+
verifyIncrementalWatchEmit(() => ({
336336
files: [libFile, file1, file2, config],
337337
expectedInitialEmit: [
338338
outFile,
@@ -353,7 +353,7 @@ namespace ts.tscWatch {
353353
}
354354
],
355355
expectedInitialErrors: emptyArray
356-
});
356+
}));
357357
});
358358

359359
});
@@ -397,7 +397,7 @@ namespace ts.tscWatch {
397397

398398
describe("own file emit without errors", () => {
399399
const modifiedFile2Content = file2.content.replace("y", "z").replace("20", "10");
400-
verifyIncrementalWatchEmit({
400+
verifyIncrementalWatchEmit(() => ({
401401
files: [libFile, file1, file2, config],
402402
expectedInitialEmit: [
403403
file1Js,
@@ -451,7 +451,7 @@ namespace ts.tscWatch {
451451
}
452452
],
453453
expectedIncrementalErrors: emptyArray,
454-
});
454+
}));
455455
});
456456

457457
describe("own file emit with errors", () => {
@@ -479,7 +479,7 @@ namespace ts.tscWatch {
479479
"file2.ts(1,14): error TS2322: Type '20' is not assignable to type 'string'.\n"
480480
];
481481
const modifiedFile1Content = file1.content.replace("x = 10", "z = 10");
482-
verifyIncrementalWatchEmit({
482+
verifyIncrementalWatchEmit(() => ({
483483
files: [libFile, file1, fileModified, config],
484484
expectedInitialEmit: [
485485
file1Js,
@@ -541,7 +541,7 @@ namespace ts.tscWatch {
541541
}
542542
],
543543
expectedIncrementalErrors: file2Errors,
544-
});
544+
}));
545545

546546
it("verify that state is read correctly", () => {
547547
const system = createWatchedSystem([libFile, file1, fileModified, config], { currentDirectory: project });
@@ -604,7 +604,7 @@ namespace ts.tscWatch {
604604
});
605605
`;
606606
}
607-
verifyIncrementalWatchEmit({
607+
verifyIncrementalWatchEmit(() => ({
608608
files: [libFile, file1, file2, config],
609609
expectedInitialEmit: [
610610
outFile,
@@ -625,7 +625,140 @@ namespace ts.tscWatch {
625625
}
626626
],
627627
expectedInitialErrors: emptyArray
628-
});
628+
}));
629+
});
630+
});
631+
632+
describe("incremental with circular references", () => {
633+
function getFileInfo(content: string): BuilderState.FileInfo {
634+
const signature = Harness.mockHash(content);
635+
return { version: signature, signature };
636+
}
637+
const config: File = {
638+
path: configFile.path,
639+
content: JSON.stringify({
640+
compilerOptions: {
641+
incremental: true,
642+
target: "es5",
643+
module: "commonjs",
644+
declaration: true,
645+
emitDeclarationOnly: true
646+
}
647+
})
648+
};
649+
const aTs: File = {
650+
path: `${project}/a.ts`,
651+
content: `import { B } from "./b";
652+
export interface A {
653+
b: B;
654+
}
655+
`
656+
};
657+
const bTs: File = {
658+
path: `${project}/b.ts`,
659+
content: `import { C } from "./c";
660+
export interface B {
661+
b: C;
662+
}
663+
`
664+
};
665+
const cTs: File = {
666+
path: `${project}/c.ts`,
667+
content: `import { A } from "./a";
668+
export interface C {
669+
a: A;
670+
}
671+
`
672+
};
673+
const indexTs: File = {
674+
path: `${project}/index.ts`,
675+
content: `export { A } from "./a";
676+
export { B } from "./b";
677+
export { C } from "./c";
678+
`
679+
};
680+
681+
verifyIncrementalWatchEmit(() => {
682+
const referencedMap: MapLike<string[]> = {
683+
"./a.ts": ["./b.ts"],
684+
"./b.ts": ["./c.ts"],
685+
"./c.ts": ["./a.ts"],
686+
"./index.ts": ["./a.ts", "./b.ts", "./c.ts"],
687+
};
688+
const initialProgram: ProgramBuildInfo = {
689+
fileInfos: {
690+
[libFilePath]: libFileInfo,
691+
"./c.ts": getFileInfo(cTs.content),
692+
"./b.ts": getFileInfo(bTs.content),
693+
"./a.ts": getFileInfo(aTs.content),
694+
"./index.ts": getFileInfo(indexTs.content)
695+
},
696+
options: {
697+
incremental: true,
698+
target: ScriptTarget.ES5,
699+
module: ModuleKind.CommonJS,
700+
declaration: true,
701+
emitDeclarationOnly: true,
702+
configFilePath: "./tsconfig.json"
703+
},
704+
referencedMap,
705+
exportedModulesMap: referencedMap,
706+
semanticDiagnosticsPerFile: [
707+
libFilePath,
708+
"./a.ts",
709+
"./b.ts",
710+
"./c.ts",
711+
"./index.ts",
712+
]
713+
};
714+
const { fileInfos, ...rest } = initialProgram;
715+
const expectedADts: File = { path: `${project}/a.d.ts`, content: aTs.content };
716+
const expectedBDts: File = { path: `${project}/b.d.ts`, content: bTs.content };
717+
const expectedCDts: File = { path: `${project}/c.d.ts`, content: cTs.content };
718+
const expectedIndexDts: File = { path: `${project}/index.d.ts`, content: indexTs.content };
719+
const modifiedATsContent = aTs.content.replace("b: B;", `b: B;
720+
foo: any;`);
721+
return {
722+
files: [libFile, aTs, bTs, cTs, indexTs, config],
723+
expectedInitialEmit: [
724+
expectedADts,
725+
expectedBDts,
726+
expectedCDts,
727+
expectedIndexDts,
728+
{
729+
path: `${project}/tsconfig.tsbuildinfo`,
730+
content: getBuildInfoText({
731+
program: initialProgram,
732+
version
733+
})
734+
}
735+
],
736+
expectedInitialErrors: emptyArray,
737+
modifyFs: host => host.writeFile(aTs.path, modifiedATsContent),
738+
expectedIncrementalEmit: [
739+
{ path: expectedADts.path, content: modifiedATsContent },
740+
expectedBDts,
741+
expectedCDts,
742+
expectedIndexDts,
743+
{
744+
path: `${project}/tsconfig.tsbuildinfo`,
745+
content: getBuildInfoText({
746+
program: {
747+
fileInfos: {
748+
[libFilePath]: libFileInfo,
749+
"./c.ts": getFileInfo(cTs.content),
750+
"./b.ts": getFileInfo(bTs.content),
751+
"./a.ts": getFileInfo(modifiedATsContent),
752+
"./index.ts": getFileInfo(indexTs.content)
753+
},
754+
...rest
755+
},
756+
version
757+
})
758+
}
759+
],
760+
expectedIncrementalErrors: emptyArray
761+
};
629762
});
630763
});
631764
});

0 commit comments

Comments
 (0)