Skip to content

Commit 06194c5

Browse files
committed
Try to fix some intermittent nightly failures
- Clear task cache each activation - Capture terminate promise before start debug test session - gatherTests does not need to be async - Make sure CodeLLDB is setup - Try external terminal for LLDB - Better reseting of diagnostics collection
1 parent 3de9e1b commit 06194c5

File tree

10 files changed

+82
-63
lines changed

10 files changed

+82
-63
lines changed

.vscode-test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ module.exports = defineConfig({
7878
grep: isFastTestRun ? "@slow" : undefined,
7979
invert: isFastTestRun,
8080
slow: 10000,
81+
retries: 1,
8182
reporter: path.join(__dirname, ".mocha-reporter.js"),
8283
reporterOptions: {
8384
jsonReporterOptions: {

assets/test/.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@
88
"-DTEST_ARGUMENT_SET_VIA_TEST_BUILD_ARGUMENTS_SETTING"
99
],
1010
"lldb.verboseLogging": true,
11+
"lldb.launch.terminal": "external", // May prevent CodeLLDB hanging https://github.com/vadimcn/codelldb/issues/1040
1112
"swift.sourcekit-lsp.backgroundIndexing": "off"
1213
}

scripts/test.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ set -ex
1717

1818
current_directory=$(pwd)
1919

20-
mkdir /tmp/code
20+
mkdir -p /tmp/code
2121
# Add the -v flag to see what is getting copied in to the working folder
2222
rsync -a --exclude "node_modules" \
2323
--exclude "out" \

src/TestExplorer/TestRunner.ts

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,6 +1027,23 @@ export class TestRunner {
10271027
});
10281028
subscriptions.push(startSession);
10291029

1030+
const terminateSession = vscode.debug.onDidTerminateDebugSession(e => {
1031+
if (e.name !== config.name) {
1032+
return;
1033+
}
1034+
this.workspaceContext.outputChannel.logDiagnostic(
1035+
"Stop Test Debugging",
1036+
this.folderContext.name
1037+
);
1038+
// dispose terminate debug handler
1039+
subscriptions.forEach(sub => sub.dispose());
1040+
1041+
void vscode.commands
1042+
.executeCommand("workbench.view.extension.test")
1043+
.then(() => resolve());
1044+
});
1045+
subscriptions.push(terminateSession);
1046+
10301047
vscode.debug
10311048
.startDebugging(this.folderContext.workspaceFolder, config)
10321049
.then(
@@ -1040,21 +1057,6 @@ export class TestRunner {
10401057
runState
10411058
);
10421059
}
1043-
1044-
const terminateSession =
1045-
vscode.debug.onDidTerminateDebugSession(() => {
1046-
this.workspaceContext.outputChannel.logDiagnostic(
1047-
"Stop Test Debugging",
1048-
this.folderContext.name
1049-
);
1050-
// dispose terminate debug handler
1051-
subscriptions.forEach(sub => sub.dispose());
1052-
1053-
void vscode.commands
1054-
.executeCommand("workbench.view.extension.test")
1055-
.then(() => resolve());
1056-
});
1057-
subscriptions.push(terminateSession);
10581060
} else {
10591061
subscriptions.forEach(sub => sub.dispose());
10601062
reject("Debugger not started");

src/WorkspaceContext.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { TestKind } from "./TestExplorer/TestKind";
3636
import { isValidWorkspaceFolder, searchForPackages } from "./utilities/workspace";
3737
import { SwiftPluginTaskProvider } from "./tasks/SwiftPluginTaskProvider";
3838
import { SwiftTaskProvider } from "./tasks/SwiftTaskProvider";
39+
import { LLDBDebugConfigurationProvider } from "./debugger/debugAdapterFactory";
3940

4041
/**
4142
* Context for whole workspace. Holds array of contexts for each workspace folder
@@ -52,6 +53,7 @@ export class WorkspaceContext implements vscode.Disposable {
5253
public diagnostics: DiagnosticsManager;
5354
public taskProvider: SwiftTaskProvider;
5455
public pluginProvider: SwiftPluginTaskProvider;
56+
public launchProvider: LLDBDebugConfigurationProvider;
5557
public subscriptions: vscode.Disposable[];
5658
public commentCompletionProvider: CommentCompletionProviders;
5759
public documentation: DocumentationManager;
@@ -82,6 +84,11 @@ export class WorkspaceContext implements vscode.Disposable {
8284
this.diagnostics = new DiagnosticsManager(this);
8385
this.taskProvider = new SwiftTaskProvider(this);
8486
this.pluginProvider = new SwiftPluginTaskProvider(this);
87+
this.launchProvider = new LLDBDebugConfigurationProvider(
88+
process.platform,
89+
this,
90+
outputChannel
91+
);
8592
this.documentation = new DocumentationManager(extensionContext, this);
8693
this.currentDocument = null;
8794
this.commentCompletionProvider = new CommentCompletionProviders();

src/debugger/debugAdapterFactory.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,7 @@ export function registerDebugger(workspaceContext: WorkspaceContext): vscode.Dis
4747

4848
function register() {
4949
subscriptions.push(registerLoggingDebugAdapterTracker());
50-
subscriptions.push(
51-
registerLLDBDebugAdapter(workspaceContext, workspaceContext.outputChannel)
52-
);
50+
subscriptions.push(registerLLDBDebugAdapter(workspaceContext));
5351
}
5452

5553
if (!configuration.debugger.disable) {
@@ -69,13 +67,10 @@ export function registerDebugger(workspaceContext: WorkspaceContext): vscode.Dis
6967
* @param workspaceContext The workspace context
7068
* @returns A disposable to be disposed when the extension is deactivated
7169
*/
72-
function registerLLDBDebugAdapter(
73-
workspaceContext: WorkspaceContext,
74-
outputChannel: SwiftOutputChannel
75-
): vscode.Disposable {
70+
function registerLLDBDebugAdapter(workspaceContext: WorkspaceContext): vscode.Disposable {
7671
return vscode.debug.registerDebugConfigurationProvider(
7772
SWIFT_LAUNCH_CONFIG_TYPE,
78-
new LLDBDebugConfigurationProvider(process.platform, workspaceContext, outputChannel)
73+
workspaceContext.launchProvider
7974
);
8075
}
8176

@@ -203,7 +198,7 @@ export class LLDBDebugConfigurationProvider implements vscode.DebugConfiguration
203198
}
204199
}
205200

206-
private async promptForCodeLldbSettings(toolchain: SwiftToolchain): Promise<boolean> {
201+
async promptForCodeLldbSettings(toolchain: SwiftToolchain): Promise<boolean> {
207202
const libLldbPathResult = await getLLDBLibPath(toolchain);
208203
if (!libLldbPathResult.success) {
209204
const errorMessage = `Error: ${getErrorDescription(libLldbPathResult.failure)}`;

test/integration-tests/DiagnosticsManager.test.ts

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { SwiftToolchain } from "../../src/toolchain/toolchain";
1818
import { executeTaskAndWaitForResult, waitForNoRunningTasks } from "../utilities/tasks";
1919
import { WorkspaceContext } from "../../src/WorkspaceContext";
2020
import { testAssetUri, testSwiftTask } from "../fixtures";
21-
import { createBuildAllTask } from "../../src/tasks/SwiftTaskProvider";
21+
import { createBuildAllTask, resetBuildAllTaskCache } from "../../src/tasks/SwiftTaskProvider";
2222
import { DiagnosticsManager } from "../../src/DiagnosticsManager";
2323
import { FolderContext } from "../../src/FolderContext";
2424
import { Version } from "../../src/utilities/version";
@@ -63,8 +63,7 @@ function assertWithoutDiagnostic(uri: vscode.Uri, expected: vscode.Diagnostic) {
6363
}
6464

6565
suite("DiagnosticsManager Test Suite", function () {
66-
// Was hitting a timeout in suiteSetup during CI build once in a while
67-
this.timeout(15000);
66+
this.timeout(60 * 1000 * 5); // Allow up to 5 minutes for build
6867

6968
let workspaceContext: WorkspaceContext;
7069
let folderContext: FolderContext;
@@ -78,11 +77,9 @@ suite("DiagnosticsManager Test Suite", function () {
7877
let cppUri: vscode.Uri;
7978
let cppHeaderUri: vscode.Uri;
8079
let diagnosticWaiterDisposable: vscode.Disposable | undefined;
81-
let remainingExpectedDiagnostics:
82-
| {
83-
[uri: string]: vscode.Diagnostic[];
84-
}
85-
| undefined;
80+
let remainingExpectedDiagnostics: {
81+
[uri: string]: vscode.Diagnostic[];
82+
};
8683

8784
// Wait for all the expected diagnostics to be recieved. This may happen over several `onChangeDiagnostics` events.
8885
type ExpectedDiagnostics = { [uri: string]: vscode.Diagnostic[] };
@@ -96,22 +93,23 @@ suite("DiagnosticsManager Test Suite", function () {
9693
}
9794
// Keep a lookup of diagnostics we haven't encountered yet. When all array values in
9895
// this lookup are empty then we've seen all diagnostics and we can resolve successfully.
99-
const expected = { ...expectedDiagnostics };
96+
remainingExpectedDiagnostics = { ...expectedDiagnostics };
10097
diagnosticWaiterDisposable = vscode.languages.onDidChangeDiagnostics(e => {
10198
const matchingPaths = Object.keys(expectedDiagnostics).filter(uri =>
10299
e.uris.some(u => u.fsPath === uri)
103100
);
104101
for (const uri of matchingPaths) {
105102
const actualDiagnostics = vscode.languages.getDiagnostics(vscode.Uri.file(uri));
106-
expected[uri] = expected[uri].filter(expectedDiagnostic => {
107-
return !actualDiagnostics.some(actualDiagnostic =>
108-
isEqual(actualDiagnostic, expectedDiagnostic)
109-
);
110-
});
111-
remainingExpectedDiagnostics = expected;
103+
for (const actualDiagnostic of actualDiagnostics) {
104+
remainingExpectedDiagnostics[uri] = remainingExpectedDiagnostics[
105+
uri
106+
].filter(expectedDiagnostic => {
107+
return !isEqual(actualDiagnostic, expectedDiagnostic);
108+
});
109+
}
112110
}
113111

114-
const allDiagnosticsFulfilled = Object.values(expected).every(
112+
const allDiagnosticsFulfilled = Object.values(remainingExpectedDiagnostics).every(
115113
diagnostics => diagnostics.length === 0
116114
);
117115

@@ -126,8 +124,6 @@ suite("DiagnosticsManager Test Suite", function () {
126124

127125
activateExtensionForSuite({
128126
async setup(ctx) {
129-
this.timeout(60000 * 5);
130-
131127
workspaceContext = ctx;
132128
toolchain = workspaceContext.globalToolchain;
133129
folderContext = await folderInRootWorkspace("diagnostics", workspaceContext);
@@ -160,8 +156,6 @@ suite("DiagnosticsManager Test Suite", function () {
160156
});
161157

162158
suite("Parse diagnostics", function () {
163-
this.timeout(60000 * 2);
164-
165159
suite("Parse from task output", () => {
166160
const expectedWarningDiagnostic = new vscode.Diagnostic(
167161
new vscode.Range(new vscode.Position(1, 8), new vscode.Position(1, 8)),
@@ -228,7 +222,7 @@ suite("DiagnosticsManager Test Suite", function () {
228222
suiteSetup(async function () {
229223
// Swift 5.10 and 6.0 on Windows have a bug where the
230224
// diagnostics are not emitted on their own line.
231-
const swiftVersion = workspaceContext.globalToolchain.swiftVersion;
225+
const swiftVersion = folderContext.toolchain.swiftVersion;
232226
if (
233227
swiftVersion.isLessThan(new Version(5, 10, 0)) ||
234228
(process.platform === "win32" &&
@@ -237,25 +231,36 @@ suite("DiagnosticsManager Test Suite", function () {
237231
) {
238232
this.skip();
239233
}
240-
this.timeout(5 * 60 * 1000); // Allow 5 minutes to build
234+
235+
resetSettings = await updateSettings({ "swift.diagnosticsStyle": style });
241236

242237
// Clean up any lingering diagnostics
243-
workspaceContext.diagnostics.clear();
244-
await workspaceContext.focusFolder(null);
238+
if (vscode.languages.getDiagnostics(mainUri).length > 0) {
239+
const clearPromise = new Promise<void>(resolve => {
240+
const diagnosticDisposable =
241+
vscode.languages.onDidChangeDiagnostics(() => {
242+
if (vscode.languages.getDiagnostics(mainUri).length === 0) {
243+
diagnosticDisposable?.dispose();
244+
resolve();
245+
}
246+
});
247+
});
248+
workspaceContext.diagnostics.clear();
249+
await clearPromise;
250+
}
251+
});
245252

246-
resetSettings = await updateSettings({ "swift.diagnosticsStyle": style });
253+
setup(() => {
254+
resetBuildAllTaskCache();
247255
});
248256

249257
test("succeeds", async function () {
250258
await Promise.all([
251259
waitForDiagnostics(expected()),
252260
createBuildAllTask(folderContext).then(task =>
253-
executeTaskAndWaitForResult(task).catch(() => {
254-
/* Ignore */
255-
})
261+
executeTaskAndWaitForResult(task)
256262
),
257263
]);
258-
await waitForNoRunningTasks();
259264
});
260265

261266
callback && callback();

test/integration-tests/testexplorer/TestExplorerIntegration.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ import { FolderContext } from "../../../src/FolderContext";
5252
import { lineBreakRegex } from "../../../src/utilities/tasks";
5353

5454
suite("Test Explorer Suite", function () {
55-
const MAX_TEST_RUN_TIME_MINUTES = 5;
55+
const MAX_TEST_RUN_TIME_MINUTES = 6;
5656

5757
this.timeout(1000 * 60 * MAX_TEST_RUN_TIME_MINUTES);
5858

@@ -427,7 +427,7 @@ suite("Test Explorer Suite", function () {
427427
this.timeout(1000 * 60 * MAX_TEST_RUN_TIME_MINUTES * 5);
428428

429429
test("@slow runs an swift-testing test multiple times", async function () {
430-
const testItems = await gatherTests(
430+
const testItems = gatherTests(
431431
testExplorer.controller,
432432
"PackageTests.MixedXCTestSuite/testPassing"
433433
);
@@ -495,7 +495,7 @@ suite("Test Explorer Suite", function () {
495495
if (!targetProfile) {
496496
throw new Error(`Unable to find run profile named ${TestKind.standard}`);
497497
}
498-
const testItems = await gatherTests(
498+
const testItems = gatherTests(
499499
testExplorer.controller,
500500
"PackageTests.DuplicateSuffixTests/testPassing"
501501
);
@@ -560,7 +560,7 @@ suite("Test Explorer Suite", function () {
560560
this.timeout(1000 * 60 * MAX_TEST_RUN_TIME_MINUTES * 5);
561561

562562
test("@slow runs an XCTest multiple times", async function () {
563-
const testItems = await gatherTests(
563+
const testItems = gatherTests(
564564
testExplorer.controller,
565565
"PackageTests.PassingXCTestSuite/testPassing"
566566
);

test/integration-tests/testexplorer/utilities.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -247,10 +247,10 @@ function getTestItem(
247247
* @param tests A list of test IDs
248248
* @returns A collection of resolved `vscode.TestItem`s
249249
*/
250-
export async function gatherTests(
250+
export function gatherTests(
251251
controller: vscode.TestController,
252252
...tests: string[]
253-
): Promise<vscode.TestItem[]> {
253+
): vscode.TestItem[] {
254254
const testItems = tests.map(test => {
255255
const testItem = getTestItem(controller, test);
256256
if (!testItem) {
@@ -295,7 +295,7 @@ export async function runTest(
295295
if (!targetProfile) {
296296
throw new Error(`Unable to find run profile named ${runProfile}`);
297297
}
298-
const testItems = await gatherTests(testExplorer.controller, ...tests);
298+
const testItems = gatherTests(testExplorer.controller, ...tests);
299299
const request = new vscode.TestRunRequest(testItems);
300300

301301
// The first promise is the return value, the second promise builds and runs

test/integration-tests/utilities/testutilities.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,17 @@ const extensionBootstrapper = (() => {
9393
restoreSettings = await updateSettings({
9494
"swift.debugger.setupCodeLLDB": "never",
9595
});
96+
} else if (requiresDebugger) {
97+
await workspaceContext.launchProvider.promptForCodeLldbSettings(
98+
workspaceContext.globalToolchain
99+
);
96100
}
101+
102+
// Make sure no running tasks before setting up
103+
await waitForNoRunningTasks({ timeout: 10000 });
104+
// Clear build all cache before starting suite
105+
resetBuildAllTaskCache();
106+
97107
if (!setup) {
98108
return;
99109
}
@@ -141,8 +151,6 @@ const extensionBootstrapper = (() => {
141151
if (autoTeardown) {
142152
await autoTeardown();
143153
}
144-
await waitForNoRunningTasks();
145-
resetBuildAllTaskCache();
146154
} catch (error) {
147155
if (workspaceContext) {
148156
printLogs(workspaceContext.outputChannel, "Error during test/suite teardown");

0 commit comments

Comments
 (0)