Skip to content

Commit 23739e8

Browse files
author
easonLiangWorldedtech
committed
fix(diffview): tighten post-save diagnostics tail per review
Address four CodeRabbit threads on #1403 in-tree: - add mixed-severity fixtures pinning the tail's Error-only payload filter (root 4503929448) - restore the platform and console.warn spies in finally blocks so an assertion failure cannot leak them into later tests (4503929454) - re-check the abort signal after the awaited settings read: a tail cancelled inside the settings read now stops before the formatting pass instead of running it (4503929461) - assert the tail releases its controller in both the success test and the say-rejected test, so removing the finally cleanup now fails a test (4503929476) Threads 4503929457 (older same-file tail), 4503929466 (say-start persistence, streamed-partial split) and 4503929472 (pending approval controls) are dispositioned as no-change with in-tree evidence in their review threads.
1 parent 5cc9e1a commit 23739e8

2 files changed

Lines changed: 72 additions & 14 deletions

File tree

‎src/integrations/editor/DiffViewProvider.ts‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,6 +1276,13 @@ export class DiffViewProvider {
12761276
const includeDiagnosticMessages = state?.includeDiagnosticMessages ?? true
12771277
const maxDiagnosticMessages = state?.maxDiagnosticMessages ?? 50
12781278

1279+
// The settings read is an await too, so disposal can land inside it and the
1280+
// await still returns normally. Stopping here keeps a gone task from paying for
1281+
// the formatting pass before the emit guard below.
1282+
if (controller.signal.aborted) {
1283+
return
1284+
}
1285+
12791286
const newProblems = await diagnosticsToProblemsString(
12801287
getNewDiagnostics(preDiagnostics, postDiagnostics),
12811288
[vscode.DiagnosticSeverity.Error],

‎src/integrations/editor/__tests__/DiffViewProvider.spec.ts‎

Lines changed: 65 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,9 @@ describe("DiffViewProvider", () => {
936936
// Only the interactivity flag differs: the channel and the text are unchanged.
937937
expect(mockTask.say.mock.calls[0]?.[0]).toBe("error")
938938
expect(mockTask.say.mock.calls[0]?.[1]).toContain("New problems detected after saving file: test.ts")
939+
// The tail releases its controller when it completes - including the failing tail
940+
// below: without the finally cleanup, completed tails accumulate until disposal.
941+
expect(diffViewProvider["postSaveTails"].size).toBe(0)
939942
})
940943

941944
it("does not start a tail when cancellation lands while the save is awaiting the write", async () => {
@@ -1078,7 +1081,43 @@ describe("DiffViewProvider", () => {
10781081

10791082
expect(mockTask.say).toHaveBeenCalledTimes(1)
10801083
expect(mockTask.say.mock.calls[0]?.[1]).toContain("own-problem")
1081-
expect(mockTask.say.mock.calls[0]?.[1]).not.toContain("other-file-problem")
1084+
})
1085+
1086+
it("narrows the tail payload to Error-severity diagnostics", async () => {
1087+
// The tail formats with an explicit Error-only severity list. Mixed fixtures
1088+
// are what make a widened severity list fail this test instead of passing silently.
1089+
const ownWarning: vscode.Diagnostic = {
1090+
severity: vscode.DiagnosticSeverity.Warning,
1091+
range: new vscode.Range(0, 0, 0, 1),
1092+
message: "own-warning",
1093+
}
1094+
const ownError: vscode.Diagnostic = {
1095+
severity: vscode.DiagnosticSeverity.Error,
1096+
range: new vscode.Range(0, 0, 0, 1),
1097+
message: "own-error",
1098+
}
1099+
const mixedDiagnostics: [vscode.Uri, vscode.Diagnostic[]][] = [
1100+
[makeUri(`${mockCwd}/test.ts`), [ownWarning, ownError]],
1101+
]
1102+
vi.mocked(vscode.languages.getDiagnostics).mockReturnValueOnce([]).mockReturnValue(mixedDiagnostics)
1103+
1104+
await diffViewProvider.saveDirectly("test.ts", "new content", true, true, 100)
1105+
await new Promise((resolve) => setTimeout(resolve, 0))
1106+
1107+
expect(mockTask.say).toHaveBeenCalledTimes(1)
1108+
expect(mockTask.say.mock.calls[0]?.[1]).toContain("own-error")
1109+
expect(mockTask.say.mock.calls[0]?.[1]).not.toContain("own-warning")
1110+
expect(diffViewProvider["postSaveTails"].size).toBe(0)
1111+
1112+
// Warning-only diagnostics must not emit at all.
1113+
mockTask.say.mockClear()
1114+
const warningOnly: [vscode.Uri, vscode.Diagnostic[]][] = [[makeUri(`${mockCwd}/test.ts`), [ownWarning]]]
1115+
vi.mocked(vscode.languages.getDiagnostics).mockReturnValueOnce([]).mockReturnValue(warningOnly)
1116+
1117+
await diffViewProvider.saveDirectly("test.ts", "more content", true, true, 100)
1118+
await new Promise((resolve) => setTimeout(resolve, 0))
1119+
1120+
expect(mockTask.say).not.toHaveBeenCalled()
10821121
})
10831122

10841123
it("stops a post-save tail that is cancelled while the diagnostic settings are read", async () => {
@@ -1146,14 +1185,19 @@ describe("DiffViewProvider", () => {
11461185
const postDiagnostics: [vscode.Uri, vscode.Diagnostic[]][] = [[makeUri(`${mockCwd}/Test.ts`), [newDiag]]]
11471186
vi.mocked(vscode.languages.getDiagnostics).mockReturnValueOnce([]).mockReturnValue(postDiagnostics)
11481187

1149-
await diffViewProvider.saveDirectly("test.ts", "new content", true, true, 100)
1188+
try {
1189+
await diffViewProvider.saveDirectly("test.ts", "new content", true, true, 100)
11501190

1151-
// Flush the fire-and-forget tail.
1152-
await new Promise((resolve) => setTimeout(resolve, 0))
1191+
// Flush the fire-and-forget tail.
1192+
await new Promise((resolve) => setTimeout(resolve, 0))
11531193

1154-
expect(mockTask.say).toHaveBeenCalledTimes(1)
1155-
expect(mockTask.say.mock.calls[0]?.[1]).toContain("case-mismatch-problem")
1156-
platformSpy.mockRestore()
1194+
expect(mockTask.say).toHaveBeenCalledTimes(1)
1195+
expect(mockTask.say.mock.calls[0]?.[1]).toContain("case-mismatch-problem")
1196+
} finally {
1197+
// Restore on assertion failure too: a leaked win32 override would apply Windows
1198+
// path rules to later tests' comparisons.
1199+
platformSpy.mockRestore()
1200+
}
11571201
})
11581202

11591203
it("does not block the save on a diagnostics settle delay when diagnostics are disabled", async () => {
@@ -1214,14 +1258,21 @@ describe("DiffViewProvider", () => {
12141258
// The task is aborted while the diagnostics tail is emitting: say() rejects.
12151259
mockTask.say.mockRejectedValueOnce(new Error("aborted"))
12161260

1217-
await diffViewProvider.saveDirectly("test.ts", "new content", true, true, 0)
1218-
// Flush the fire-and-forget tail.
1219-
await new Promise((resolve) => setTimeout(resolve, 0))
1220-
1221-
// The method's outer catch swallows the rejection with a warning.
1222-
expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining("Post-save diagnostics emit failed:"))
1261+
try {
1262+
await diffViewProvider.saveDirectly("test.ts", "new content", true, true, 0)
1263+
// Flush the fire-and-forget tail.
1264+
await new Promise((resolve) => setTimeout(resolve, 0))
12231265

1224-
consoleWarnSpy.mockRestore()
1266+
// The method's outer catch swallows the rejection with a warning.
1267+
expect(consoleWarnSpy).toHaveBeenCalledWith(
1268+
expect.stringContaining("Post-save diagnostics emit failed:"),
1269+
)
1270+
// Even the failing tail releases its controller.
1271+
expect(diffViewProvider["postSaveTails"].size).toBe(0)
1272+
} finally {
1273+
// Restore on assertion failure too: a leaked spy would swallow later tests' warnings.
1274+
consoleWarnSpy.mockRestore()
1275+
}
12251276
})
12261277
})
12271278

0 commit comments

Comments
 (0)