Skip to content

Commit

Permalink
Debug diagnostics tests
Browse files Browse the repository at this point in the history
  • Loading branch information
plemarquand committed Nov 13, 2024
1 parent edbf32f commit 524dfc0
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 82 deletions.
2 changes: 1 addition & 1 deletion .vscode-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
const { defineConfig } = require("@vscode/test-cli");
const path = require("path");

const isCIBuild = process.env["CI"] === "1";
const isCIBuild = false; // process.env["CI"] === "1";
const isFastTestRun = process.env["FAST_TEST_RUN"] === "1";

// "env" in launch.json doesn't seem to work with vscode-test
Expand Down
15 changes: 12 additions & 3 deletions src/DiagnosticsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,19 @@ export class DiagnosticsManager implements vscode.Disposable {
.then(map => {
// Clean up old "swiftc" diagnostics
this.removeSwiftcDiagnostics();
map.forEach((diagnostics, uri) =>
map.forEach((diagnostics, uri) => {
console.log(
">>> PROVIDED DIAGNOSTIC!",
uri,
"::",
JSON.stringify(diagnostics)
);
this.handleDiagnostics(
vscode.Uri.file(uri),
DiagnosticsManager.isSwiftc,
diagnostics
)
);
);
});
})
.catch(e =>
context.outputChannel.log(`${e}`, 'Failed to provide "swiftc" diagnostics')
Expand Down Expand Up @@ -319,9 +325,12 @@ export class DiagnosticsManager implements vscode.Disposable {
): ParsedDiagnostic | vscode.DiagnosticRelatedInformation | undefined {
const diagnosticRegex = /^(.*?):(\d+)(?::(\d+))?:\s+(warning|error|note):\s+([^\\[]*)/g;
const match = diagnosticRegex.exec(line);
console.log(">>> CHECK LINE", line);
if (!match) {
console.log(">>> NO MATCH", line);
return;
}
console.log(">>> MATCH", match);
const uri = match[1];
const message = this.capitalize(match[5]).trim();
const range = this.range(match[2], match[3]);
Expand Down
9 changes: 8 additions & 1 deletion src/tasks/SwiftProcess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,20 @@ export class SwiftPtyProcess implements SwiftProcess {
// The pty process hangs on Windows when debugging the extension if we use conpty
// See https://github.com/microsoft/node-pty/issues/640
const useConpty = isWindows && process.env["VSCODE_DEBUG"] === "1" ? false : true;
console.log(
">>> COLS: ",
isWindows,
useConpty,
process.env["VSCODE_DEBUG"],
!isWindows ? undefined : 2147483647
);
this.spawnedProcess = spawn(this.command, this.args, {
cwd: this.options.cwd,
env: { ...process.env, ...this.options.env },
useConpty,
// https://github.com/swiftlang/vscode-swift/issues/1074
// Causing weird truncation issues
cols: !isWindows || useConpty ? undefined : 2147483647, // Max int32
cols: !isWindows ? undefined : 2147483647, // Max int32
});
this.spawnEmitter.fire();
this.spawnedProcess.onData(data => {
Expand Down
195 changes: 118 additions & 77 deletions test/integration-tests/DiagnosticsManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,14 @@ import { Version } from "../../src/utilities/version";
import { folderContextPromise, globalWorkspaceContextPromise } from "./extension.test";
import { Workbench } from "../../src/utilities/commands";

const waitForDiagnostics = (uris: vscode.Uri[], allowEmpty: boolean = true) =>
new Promise<void>(res =>
vscode.languages.onDidChangeDiagnostics(e => {
const paths = e.uris.map(u => u.fsPath);
for (const uri of uris) {
if (!paths.includes(uri.fsPath)) {
return;
}
if (!allowEmpty && !vscode.languages.getDiagnostics(uri).length) {
return;
}
}
res();
})
const isEqual = (d1: vscode.Diagnostic, d2: vscode.Diagnostic) => {
return (
d1.severity === d2.severity &&
d1.source === d2.source &&
d1.message === d2.message &&
d1.range.isEqual(d2.range)
);

const isEqual = (d1: vscode.Diagnostic, d2: vscode.Diagnostic) =>
d1.severity === d2.severity &&
d1.source === d2.source &&
d1.message === d2.message &&
d1.range.isEqual(d2.range);
};

const findDiagnostic = (expected: vscode.Diagnostic) => (d: vscode.Diagnostic) =>
isEqual(d, expected);
Expand All @@ -56,7 +43,7 @@ function assertHasDiagnostic(uri: vscode.Uri, expected: vscode.Diagnostic): vsco
assert.notEqual(
diagnostic,
undefined,
`Could not find diagnostic matching:\n${JSON.stringify(expected)}`
`Could not find diagnostic matching:\n${JSON.stringify(expected)}\nDiagnostics found:\n${JSON.stringify(diagnostics)}`
);
return diagnostic!;
}
Expand Down Expand Up @@ -90,9 +77,51 @@ suite("DiagnosticsManager Test Suite", async function () {
let cUri: vscode.Uri;
let cppUri: vscode.Uri;
let cppHeaderUri: vscode.Uri;
let diagnosticWaiterDisposable: vscode.Disposable | undefined;
let remainingExpectedDiagnostics: {
[uri: string]: vscode.Diagnostic[];
};

// Wait for all the expected diagnostics to be recieved. This may happen over several `onChangeDiagnostics` events.
const waitForDiagnostics = (expectedDiagnostics: { [uri: string]: vscode.Diagnostic[] }) => {
return new Promise<void>(resolve => {
if (diagnosticWaiterDisposable) {
console.warn(
"Wait for diagnostics was called before the previous wait was resolved. Only one waitForDiagnostics should run per test."
);
diagnosticWaiterDisposable?.dispose();
}
// Keep a lookup of diagnostics we haven't encountered yet. When all array values in
// this lookup are empty then we've seen all diagnostics and we can resolve successfully.
const expected = { ...expectedDiagnostics };
diagnosticWaiterDisposable = vscode.languages.onDidChangeDiagnostics(e => {
const matchingPaths = Object.keys(expectedDiagnostics).filter(uri =>
e.uris.some(u => u.fsPath === uri)
);
for (const uri of matchingPaths) {
const actualDiagnostics = vscode.languages.getDiagnostics(vscode.Uri.file(uri));
expected[uri] = expected[uri].filter(expectedDiagnostic => {
return !actualDiagnostics.some(actualDiagnostic =>
isEqual(actualDiagnostic, expectedDiagnostic)
);
});
remainingExpectedDiagnostics = expected;
}

const allDiagnosticsFulfilled = Object.values(expected).every(
diagnostics => diagnostics.length === 0
);

if (allDiagnosticsFulfilled) {
diagnosticWaiterDisposable?.dispose();
resolve();
}
});
});
};

suiteSetup(async function () {
this.timeout(30000);
this.timeout(60000);

workspaceContext = await globalWorkspaceContextPromise;
toolchain = workspaceContext.toolchain;
Expand All @@ -111,6 +140,23 @@ suite("DiagnosticsManager Test Suite", async function () {
);
});

teardown(function () {
diagnosticWaiterDisposable?.dispose();
const allDiagnosticsFulfilled = Object.values(remainingExpectedDiagnostics).every(
diagnostics => diagnostics.length === 0
);
if (!allDiagnosticsFulfilled) {
const title = this.currentTest?.fullTitle() ?? "<unknown test>";
const remainingDiagnostics = Object.entries(remainingExpectedDiagnostics).filter(
([_uri, diagnostics]) => diagnostics.length > 0
);
console.error(
`${title} - Not all diagnostics were fulfilled`,
JSON.stringify(remainingDiagnostics, undefined, " ")
);
}
});

suite("Parse diagnostics", async () => {
suite("Parse from task output", async () => {
const expectedWarningDiagnostic = new vscode.Diagnostic(
Expand Down Expand Up @@ -152,21 +198,19 @@ suite("DiagnosticsManager Test Suite", async function () {
await swiftConfig.update("diagnosticsStyle", undefined);
});

test("default diagnosticsStyle", async () => {
test.only("default diagnosticsStyle", async () => {
await swiftConfig.update("diagnosticsStyle", "default");
const task = createBuildAllTask(folderContext);
// Run actual task
const promise = waitForDiagnostics([mainUri, funcUri]);
await executeTaskAndWaitForResult(task);
await promise;
await waitForNoRunningTasks();

// Should have parsed correct severity
assertHasDiagnostic(mainUri, expectedWarningDiagnostic);
assertHasDiagnostic(mainUri, expectedMainErrorDiagnostic);
// Check parsed for other file
assertHasDiagnostic(funcUri, expectedFuncErrorDiagnostic);
}).timeout(2 * 60 * 1000); // Allow 2 minutes to build
await Promise.all([
executeTaskAndWaitForResult(createBuildAllTask(folderContext)),
waitForDiagnostics({
[mainUri.fsPath]: [expectedWarningDiagnostic, expectedMainErrorDiagnostic], // Should have parsed correct severity
[funcUri.fsPath]: [expectedFuncErrorDiagnostic], // Check parsed for other file
}),
]);

await waitForNoRunningTasks();
});

test("swift diagnosticsStyle", async function () {
// This is only supported in swift versions >=5.10.0
Expand All @@ -176,31 +220,29 @@ suite("DiagnosticsManager Test Suite", async function () {
return;
}
await swiftConfig.update("diagnosticsStyle", "swift");
const task = createBuildAllTask(folderContext);
// Run actual task
const promise = waitForDiagnostics([mainUri, funcUri]);
await executeTaskAndWaitForResult(task);
await promise;
await Promise.all([
executeTaskAndWaitForResult(createBuildAllTask(folderContext)),
waitForDiagnostics({
[mainUri.fsPath]: [expectedWarningDiagnostic, expectedMainErrorDiagnostic], // Should have parsed correct severity
[funcUri.fsPath]: [expectedFuncErrorDiagnostic], // Check parsed for other file
}),
]);
await waitForNoRunningTasks();

// Should have parsed severity
assertHasDiagnostic(mainUri, expectedWarningDiagnostic);
assertHasDiagnostic(mainUri, expectedMainErrorDiagnostic);
// Check parsed for other file
assertHasDiagnostic(funcUri, expectedFuncErrorDiagnostic);
}).timeout(2 * 60 * 1000); // Allow 2 minutes to build
});

test("llvm diagnosticsStyle", async () => {
await swiftConfig.update("diagnosticsStyle", "llvm");
const task = createBuildAllTask(folderContext);
// Run actual task
const promise = waitForDiagnostics([mainUri, funcUri]);
await executeTaskAndWaitForResult(task);
await promise;

await Promise.all([
executeTaskAndWaitForResult(createBuildAllTask(folderContext)),
waitForDiagnostics({
[mainUri.fsPath]: [expectedWarningDiagnostic, expectedMainErrorDiagnostic], // Should have parsed correct severity
[funcUri.fsPath]: [expectedFuncErrorDiagnostic], // Check parsed for other file
}),
]);
await waitForNoRunningTasks();

// Should have parsed severity
assertHasDiagnostic(mainUri, expectedWarningDiagnostic);
const diagnostic = assertHasDiagnostic(mainUri, expectedMainErrorDiagnostic);
// Should have parsed related note
assert.equal(diagnostic.relatedInformation?.length, 1);
Expand All @@ -215,9 +257,7 @@ suite("DiagnosticsManager Test Suite", async function () {
),
true
);
// Check parsed for other file
assertHasDiagnostic(funcUri, expectedFuncErrorDiagnostic);
}).timeout(2 * 60 * 1000); // Allow 2 minutes to build
});

test("Parses C diagnostics", async function () {
const swiftVersion = workspaceContext.toolchain.swiftVersion;
Expand All @@ -228,12 +268,6 @@ suite("DiagnosticsManager Test Suite", async function () {
}

await swiftConfig.update("diagnosticsStyle", "llvm");
const task = createBuildAllTask(cFolderContext);
// Run actual task
const promise = waitForDiagnostics([cUri]);
await executeTaskAndWaitForResult(task);
await promise;
await waitForNoRunningTasks();

// Should have parsed severity
const expectedDiagnostic1 = new vscode.Diagnostic(
Expand All @@ -249,8 +283,13 @@ suite("DiagnosticsManager Test Suite", async function () {
);
expectedDiagnostic2.source = "swiftc";

assertHasDiagnostic(cUri, expectedDiagnostic1);
assertHasDiagnostic(cUri, expectedDiagnostic2);
await Promise.all([
executeTaskAndWaitForResult(createBuildAllTask(cFolderContext)),
waitForDiagnostics({
[cUri.fsPath]: [expectedDiagnostic1, expectedDiagnostic2],
}),
]);
await waitForNoRunningTasks();
});

test("Parses C++ diagnostics", async function () {
Expand All @@ -262,12 +301,6 @@ suite("DiagnosticsManager Test Suite", async function () {
}

await swiftConfig.update("diagnosticsStyle", "llvm");
const task = createBuildAllTask(cppFolderContext);
// Run actual task
const promise = waitForDiagnostics([cppUri]);
await executeTaskAndWaitForResult(task);
await promise;
await waitForNoRunningTasks();

// Should have parsed severity
const expectedDiagnostic1 = new vscode.Diagnostic(
Expand All @@ -276,7 +309,6 @@ suite("DiagnosticsManager Test Suite", async function () {
vscode.DiagnosticSeverity.Error
);
expectedDiagnostic1.source = "swiftc";
assertHasDiagnostic(cppUri, expectedDiagnostic1);

// Should have parsed releated information
const expectedDiagnostic2 = new vscode.Diagnostic(
Expand All @@ -285,6 +317,15 @@ suite("DiagnosticsManager Test Suite", async function () {
vscode.DiagnosticSeverity.Error
);
expectedDiagnostic2.source = "swiftc";

await Promise.all([
executeTaskAndWaitForResult(createBuildAllTask(cppFolderContext)),
waitForDiagnostics({
[cppUri.fsPath]: [expectedDiagnostic1, expectedDiagnostic2],
}),
]);
await waitForNoRunningTasks();

const diagnostic = assertHasDiagnostic(cppUri, expectedDiagnostic2);
assert.equal(
diagnostic.relatedInformation![0].location.uri.fsPath,
Expand Down Expand Up @@ -315,7 +356,7 @@ suite("DiagnosticsManager Test Suite", async function () {
test("Parse partial line", async () => {
const fixture = testSwiftTask("swift", ["build"], workspaceFolder, toolchain);
await vscode.tasks.executeTask(fixture.task);
const diagnosticsPromise = waitForDiagnostics([mainUri]);
const diagnosticsPromise = Promise.resolve(); // waitForDiagnostics([mainUri]);
// Wait to spawn before writing
fixture.process.write(`${mainUri.fsPath}:13:5: err`, "");
fixture.process.write("or: Cannot find 'fo", "");
Expand All @@ -331,7 +372,7 @@ suite("DiagnosticsManager Test Suite", async function () {
test("Ignore duplicates", async () => {
const fixture = testSwiftTask("swift", ["build"], workspaceFolder, toolchain);
await vscode.tasks.executeTask(fixture.task);
const diagnosticsPromise = waitForDiagnostics([mainUri]);
const diagnosticsPromise = Promise.resolve(); // waitForDiagnostics([mainUri]);
// Wait to spawn before writing
const output = `${mainUri.fsPath}:13:5: error: Cannot find 'foo' in scope`;
fixture.process.write(output);
Expand All @@ -349,7 +390,7 @@ suite("DiagnosticsManager Test Suite", async function () {
test("New set of swiftc diagnostics clear old list", async () => {
let fixture = testSwiftTask("swift", ["build"], workspaceFolder, toolchain);
await vscode.tasks.executeTask(fixture.task);
let diagnosticsPromise = waitForDiagnostics([mainUri]);
let diagnosticsPromise = Promise.resolve(); // waitForDiagnostics([mainUri]);
// Wait to spawn before writing
fixture.process.write(`${mainUri.fsPath}:13:5: error: Cannot find 'foo' in scope`);
fixture.process.close(1);
Expand All @@ -363,7 +404,7 @@ suite("DiagnosticsManager Test Suite", async function () {
// Run again but no diagnostics returned
fixture = testSwiftTask("swift", ["build"], workspaceFolder, toolchain);
await vscode.tasks.executeTask(fixture.task);
diagnosticsPromise = waitForDiagnostics([mainUri]);
diagnosticsPromise = Promise.resolve(); // waitForDiagnostics([mainUri]);
fixture.process.close(0);
await waitForNoRunningTasks();
await diagnosticsPromise;
Expand Down Expand Up @@ -920,7 +961,7 @@ suite("DiagnosticsManager Test Suite", async function () {
await executeTaskAndWaitForResult(task);

// Open file
const promise = waitForDiagnostics([mainUri], false);
const promise = Promise.resolve(); // waitForDiagnostics([mainUri], false);
const document = await vscode.workspace.openTextDocument(mainUri);
await vscode.languages.setTextDocumentLanguage(document, "swift");
await vscode.window.showTextDocument(document);
Expand Down Expand Up @@ -961,7 +1002,7 @@ suite("DiagnosticsManager Test Suite", async function () {
await executeTaskAndWaitForResult(task);

// Open file
const promise = waitForDiagnostics([cUri], false);
const promise = Promise.resolve(); // waitForDiagnostics([cUri], false);
const document = await vscode.workspace.openTextDocument(cUri);
await vscode.languages.setTextDocumentLanguage(document, "c");
await vscode.window.showTextDocument(document);
Expand Down

0 comments on commit 524dfc0

Please sign in to comment.