Skip to content

Commit ae6ecd5

Browse files
authored
Add test debugging tests (#1111)
* Add test debugging tests Adds two new tests that test using the debug adapter in the toolchain to run a test, as well as a test that uses CodeLLDB. This required some new test utilities to set application settings at the start of a test and then set them back to their original value after each test. - Fixes up an issue where the LD_LIBRARY_PATH was not set for lldb on linux. - Fixes up an issue where when switching the useDebugAdapterInToolchain setting the loggingDebugAdapter wasn't reconfigured
1 parent 8d3618a commit ae6ecd5

17 files changed

+949
-640
lines changed

.vscode-test.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ module.exports = defineConfig({
4545
invert: isFastTestRun,
4646
slow: 10000,
4747
},
48-
installExtensions: ["vadimcn.vscode-lldb"],
4948
reuseMachineInstall: !isCIBuild,
5049
},
5150
{

assets/test/.vscode/settings.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,7 @@
55
"swift.additionalTestArguments": [
66
"-Xswiftc",
77
"-DTEST_ARGUMENT_SET_VIA_TEST_BUILD_ARGUMENTS_SETTING"
8-
]
8+
],
9+
"lldb.verboseLogging": true
10+
911
}

assets/test/defaultPackage/[email protected]

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@ let package = Package(
88
platforms: [
99
.macOS(.v13)
1010
],
11-
dependencies: [
12-
.package(url: "https://github.com/apple/swift-testing.git", branch: "main")
13-
],
1411
targets: [
1512
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
1613
// Targets can depend on other targets in this package, and on products in packages this package depends on.
@@ -24,10 +21,7 @@ let package = Package(
2421
),
2522
.testTarget(
2623
name: "PackageTests",
27-
dependencies: [
28-
"PackageLib",
29-
.product(name: "Testing", package: "swift-testing")
30-
]
24+
dependencies: ["PackageLib"]
3125
),
3226
]
3327
)

src/WorkspaceContext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ export class WorkspaceContext implements vscode.Disposable {
372372
// find context with root folder
373373
const index = this.folders.findIndex(context => context.folder.fsPath === folder.fsPath);
374374
if (index !== -1) {
375-
console.error(`Adding package folder ${folder} twice`);
375+
this.outputChannel.log(`Adding package folder ${folder} twice`, "WARN");
376376
return this.folders[index];
377377
}
378378
const folderContext = await FolderContext.create(folder, workspaceFolder, this);

src/debugger/buildConfig.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,10 @@ export class TestingConfigurationFactory {
223223
program: await this.testExecutableOutputPath(),
224224
args: await this.debuggingTestExecutableArgs(),
225225
env: {
226-
...swiftRuntimeEnv(),
226+
...swiftRuntimeEnv(
227+
process.env,
228+
this.ctx.workspaceContext.toolchain.runtimePath ?? configuration.runtimePath
229+
),
227230
...configuration.folder(this.ctx.workspaceFolder).testEnvironmentVariables,
228231
},
229232
};

src/debugger/debugAdapter.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ import configuration from "../configuration";
1717
import contextKeys from "../contextKeys";
1818
import { fileExists } from "../utilities/filesystem";
1919
import { Version } from "../utilities/version";
20-
import { WorkspaceContext } from "../WorkspaceContext";
2120
import { SwiftToolchain } from "../toolchain/toolchain";
21+
import { SwiftOutputChannel } from "../ui/SwiftOutputChannel";
2222

2323
/**
2424
* The supported {@link vscode.DebugConfiguration.type Debug Configuration Type} for auto-generation of launch configurations
@@ -46,7 +46,12 @@ export class DebugAdapter {
4646
: LaunchConfigType.CODE_LLDB;
4747
}
4848

49-
/** Return the path to the debug adapter */
49+
/**
50+
* Return the path to the debug adapter.
51+
*
52+
* @param toolchain The Swift toolchain to use
53+
* @returns A path to the debug adapter for the user's toolchain and configuration
54+
**/
5055
public static async debugAdapterPath(toolchain: SwiftToolchain): Promise<string> {
5156
const customDebugAdapterPath = configuration.debugger.customDebugAdapterPath;
5257
if (customDebugAdapterPath.length > 0) {
@@ -74,27 +79,26 @@ export class DebugAdapter {
7479
* @returns Whether or not the debug adapter exists
7580
*/
7681
public static async verifyDebugAdapterExists(
77-
workspace: WorkspaceContext,
82+
toolchain: SwiftToolchain,
83+
outputChannel: SwiftOutputChannel,
7884
quiet = false
7985
): Promise<boolean> {
80-
const lldbDebugAdapterPath = await this.debugAdapterPath(workspace.toolchain).catch(
81-
error => {
82-
workspace.outputChannel.log(error);
83-
return undefined;
84-
}
85-
);
86+
const lldbDebugAdapterPath = await this.debugAdapterPath(toolchain).catch(error => {
87+
outputChannel.log(error);
88+
return undefined;
89+
});
8690

8791
if (!lldbDebugAdapterPath || !(await fileExists(lldbDebugAdapterPath))) {
8892
if (!quiet) {
89-
const debugAdapterName = this.getLaunchConfigType(workspace.toolchain.swiftVersion);
93+
const debugAdapterName = this.getLaunchConfigType(toolchain.swiftVersion);
9094
vscode.window.showErrorMessage(
9195
configuration.debugger.customDebugAdapterPath.length > 0
9296
? `Cannot find ${debugAdapterName} debug adapter specified in setting Swift.Debugger.Path.`
9397
: `Cannot find ${debugAdapterName} debug adapter in your Swift toolchain.`
9498
);
9599
}
96100
if (lldbDebugAdapterPath) {
97-
workspace.outputChannel.log(`Failed to find ${lldbDebugAdapterPath}`);
101+
outputChannel.log(`Failed to find ${lldbDebugAdapterPath}`);
98102
}
99103
contextKeys.lldbVSCodeAvailable = false;
100104
return false;

src/debugger/debugAdapterFactory.ts

Lines changed: 72 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,76 @@ import * as path from "path";
1717
import { WorkspaceContext } from "../WorkspaceContext";
1818
import { DebugAdapter, LaunchConfigType } from "./debugAdapter";
1919
import { Version } from "../utilities/version";
20+
import { registerLoggingDebugAdapterTracker } from "./logTracker";
21+
import { SwiftToolchain } from "../toolchain/toolchain";
22+
import { SwiftOutputChannel } from "../ui/SwiftOutputChannel";
2023

21-
export function registerLLDBDebugAdapter(workspaceContext: WorkspaceContext): vscode.Disposable {
24+
/**
25+
* Registers the active debugger with the extension, and reregisters it
26+
* when the debugger settings change.
27+
* @param workspaceContext The workspace context
28+
* @returns A disposable to be disposed when the extension is deactivated
29+
*/
30+
export function registerDebugger(workspaceContext: WorkspaceContext): vscode.Disposable {
31+
let subscriptions: vscode.Disposable[] = [];
32+
const register = async () => {
33+
subscriptions.map(sub => sub.dispose());
34+
subscriptions = [
35+
registerLoggingDebugAdapterTracker(workspaceContext.toolchain.swiftVersion),
36+
registerLLDBDebugAdapter(workspaceContext.toolchain, workspaceContext.outputChannel),
37+
];
38+
39+
await workspaceContext.setLLDBVersion();
40+
41+
// Verify that the adapter exists, but only after registration. This async method
42+
// is basically an unstructured task so we don't want to run it before the adapter
43+
// registration above as it could cause code executing immediately after register()
44+
// to use the incorrect adapter.
45+
DebugAdapter.verifyDebugAdapterExists(
46+
workspaceContext.toolchain,
47+
workspaceContext.outputChannel,
48+
true
49+
).catch(error => {
50+
workspaceContext.outputChannel.log(error);
51+
});
52+
};
53+
54+
const changeMonitor = vscode.workspace.onDidChangeConfiguration(event => {
55+
if (event.affectsConfiguration("swift.debugger.useDebugAdapterFromToolchain")) {
56+
register();
57+
}
58+
});
59+
60+
// Perform the initial registration, then reregister every time the settings change.
61+
register();
62+
63+
return {
64+
dispose: () => {
65+
changeMonitor.dispose();
66+
subscriptions.map(sub => sub.dispose());
67+
},
68+
};
69+
}
70+
71+
/**
72+
* Registers the LLDB debug adapter with the VS Code debug adapter descriptor factory.
73+
* @param workspaceContext The workspace context
74+
* @returns A disposable to be disposed when the extension is deactivated
75+
*/
76+
function registerLLDBDebugAdapter(
77+
toolchain: SwiftToolchain,
78+
outputChannel: SwiftOutputChannel
79+
): vscode.Disposable {
2280
const debugAdpaterFactory = vscode.debug.registerDebugAdapterDescriptorFactory(
2381
LaunchConfigType.SWIFT_EXTENSION,
24-
new LLDBDebugAdapterExecutableFactory(workspaceContext)
82+
new LLDBDebugAdapterExecutableFactory(toolchain, outputChannel)
2583
);
84+
2685
const debugConfigProvider = vscode.debug.registerDebugConfigurationProvider(
2786
LaunchConfigType.SWIFT_EXTENSION,
28-
new LLDBDebugConfigurationProvider(
29-
process.platform,
30-
workspaceContext.toolchain.swiftVersion
31-
)
87+
new LLDBDebugConfigurationProvider(process.platform, toolchain.swiftVersion)
3288
);
89+
3390
return {
3491
dispose: () => {
3592
debugConfigProvider.dispose();
@@ -56,19 +113,18 @@ export function registerLLDBDebugAdapter(workspaceContext: WorkspaceContext): vs
56113
* @implements {vscode.DebugAdapterDescriptorFactory}
57114
*/
58115
export class LLDBDebugAdapterExecutableFactory implements vscode.DebugAdapterDescriptorFactory {
59-
private workspaceContext: WorkspaceContext;
116+
private toolchain: SwiftToolchain;
117+
private outputChannel: SwiftOutputChannel;
60118

61-
constructor(workspaceContext: WorkspaceContext) {
62-
this.workspaceContext = workspaceContext;
119+
constructor(toolchain: SwiftToolchain, outputChannel: SwiftOutputChannel) {
120+
this.toolchain = toolchain;
121+
this.outputChannel = outputChannel;
63122
}
64123

65-
createDebugAdapterDescriptor(): vscode.ProviderResult<vscode.DebugAdapterDescriptor> {
66-
// Use the stored workspaceContext
67-
return DebugAdapter.debugAdapterPath(this.workspaceContext.toolchain)
68-
.then(path =>
69-
DebugAdapter.verifyDebugAdapterExists(this.workspaceContext).then(() => path)
70-
)
71-
.then(path => new vscode.DebugAdapterExecutable(path, [], {}));
124+
async createDebugAdapterDescriptor(): Promise<vscode.DebugAdapterDescriptor> {
125+
const path = await DebugAdapter.debugAdapterPath(this.toolchain);
126+
await DebugAdapter.verifyDebugAdapterExists(this.toolchain, this.outputChannel);
127+
return new vscode.DebugAdapterExecutable(path, [], {});
72128
}
73129
}
74130

src/debugger/logTracker.ts

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
import * as vscode from "vscode";
1616
import { DebugAdapter } from "./debugAdapter";
17-
import { WorkspaceContext } from "../WorkspaceContext";
17+
import { Version } from "../utilities/version";
1818

1919
/**
2020
* Factory class for building LoggingDebugAdapterTracker
@@ -39,11 +39,34 @@ interface DebugMessage {
3939
body: OutputEventBody;
4040
}
4141

42-
export function registerLoggingDebugAdapterTracker(ctx: WorkspaceContext): vscode.Disposable {
43-
return vscode.debug.registerDebugAdapterTrackerFactory(
44-
DebugAdapter.getLaunchConfigType(ctx.swiftVersion),
45-
new LoggingDebugAdapterTrackerFactory()
46-
);
42+
/**
43+
* Register the LoggingDebugAdapterTrackerFactory with the VS Code debug adapter tracker
44+
* @returns A disposable to be disposed when the extension is deactivated
45+
*/
46+
export function registerLoggingDebugAdapterTracker(swiftVersion: Version): vscode.Disposable {
47+
const register = () =>
48+
vscode.debug.registerDebugAdapterTrackerFactory(
49+
DebugAdapter.getLaunchConfigType(swiftVersion),
50+
new LoggingDebugAdapterTrackerFactory()
51+
);
52+
53+
// Maintains the disposable for the last registered debug adapter.
54+
let debugAdapterDisposable = register();
55+
const changeMonitor = vscode.workspace.onDidChangeConfiguration(event => {
56+
if (event.affectsConfiguration("swift.debugger.useDebugAdapterFromToolchain")) {
57+
// Dispose the old adapter and reconfigure with the new settings.
58+
debugAdapterDisposable.dispose();
59+
debugAdapterDisposable = register();
60+
}
61+
});
62+
63+
// Return a disposable that cleans everything up.
64+
return {
65+
dispose() {
66+
changeMonitor.dispose();
67+
debugAdapterDisposable.dispose();
68+
},
69+
};
4770
}
4871

4972
/**
@@ -63,6 +86,8 @@ export class LoggingDebugAdapterTracker implements vscode.DebugAdapterTracker {
6386
const loggingDebugAdapter = this.debugSessionIdMap[session.id];
6487
if (loggingDebugAdapter) {
6588
loggingDebugAdapter.cb = cb;
89+
} else {
90+
console.error("Could not find debug adapter for session:", session.id);
6691
}
6792
}
6893

src/extension.ts

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,7 @@ import { SwiftPluginTaskProvider } from "./tasks/SwiftPluginTaskProvider";
2929
import configuration from "./configuration";
3030
import { Version } from "./utilities/version";
3131
import { getReadOnlyDocumentProvider } from "./ui/ReadOnlyDocumentProvider";
32-
import { registerLoggingDebugAdapterTracker } from "./debugger/logTracker";
33-
import { registerLLDBDebugAdapter } from "./debugger/debugAdapterFactory";
34-
import { DebugAdapter } from "./debugger/debugAdapter";
32+
import { registerDebugger } from "./debugger/debugAdapterFactory";
3533
import contextKeys from "./contextKeys";
3634
import { showToolchainError } from "./ui/ToolchainSelection";
3735
import { SwiftToolchain } from "./toolchain/toolchain";
@@ -83,6 +81,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<Api |
8381
});
8482

8583
context.subscriptions.push(...commands.registerToolchainCommands(toolchain));
84+
8685
context.subscriptions.push(
8786
vscode.workspace.onDidChangeConfiguration(event => {
8887
// on toolchain config change, reload window
@@ -112,14 +111,8 @@ export async function activate(context: vscode.ExtensionContext): Promise<Api |
112111

113112
const workspaceContext = await WorkspaceContext.create(outputChannel, toolchain);
114113
context.subscriptions.push(...commands.register(workspaceContext));
115-
116114
context.subscriptions.push(workspaceContext);
117-
118-
// setup swift version of LLDB. Don't await on this as it can run in the background
119-
DebugAdapter.verifyDebugAdapterExists(workspaceContext, true).catch(error => {
120-
outputChannel.log(error);
121-
});
122-
workspaceContext.setLLDBVersion();
115+
context.subscriptions.push(registerDebugger(workspaceContext));
123116

124117
// listen for workspace folder changes and active text editor changes
125118
workspaceContext.setupEventListeners();
@@ -231,18 +224,11 @@ export async function activate(context: vscode.ExtensionContext): Promise<Api |
231224

232225
const testExplorerObserver = TestExplorer.observeFolders(workspaceContext);
233226

234-
// Register swift-lldb debug provider
235-
const lldbDebugAdapter = registerLLDBDebugAdapter(workspaceContext);
236-
context.subscriptions.push(lldbDebugAdapter);
237-
238-
const loggingDebugAdapter = registerLoggingDebugAdapterTracker(workspaceContext);
239-
240227
// setup workspace context with initial workspace folders
241228
workspaceContext.addWorkspaceFolders();
242229

243230
// Register any disposables for cleanup when the extension deactivates.
244231
context.subscriptions.push(
245-
loggingDebugAdapter,
246232
resolvePackageObserver,
247233
testExplorerObserver,
248234
swiftModuleDocumentProvider,
@@ -266,13 +252,3 @@ export async function activate(context: vscode.ExtensionContext): Promise<Api |
266252
throw error;
267253
}
268254
}
269-
270-
/**
271-
* Deactivate the extension.
272-
*
273-
* Any disposables registered in `context.subscriptions` will be automatically
274-
* disposed of, so there's nothing left to do here.
275-
*/
276-
export function deactivate() {
277-
return;
278-
}

src/utilities/utilities.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ import { SwiftToolchain } from "../toolchain/toolchain";
2727
* @returns minimal required environment for Swift product
2828
*/
2929
export function swiftRuntimeEnv(
30-
base: NodeJS.ProcessEnv | boolean = process.env
30+
base: NodeJS.ProcessEnv | boolean = process.env,
31+
runtimePath: string = configuration.runtimePath
3132
): { [key: string]: string } | undefined {
32-
if (configuration.runtimePath === "") {
33+
if (runtimePath === "") {
3334
return undefined;
3435
}
35-
const runtimePath = configuration.runtimePath;
3636
const key = swiftLibraryPathKey();
3737
const separator = process.platform === "win32" ? ";" : ":";
3838
switch (base) {

test/integration-tests/extension.test.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,6 @@ suite("Extension Test Suite", () => {
7373
suite("Workspace", () => {
7474
/** Verify tasks.json is being loaded */
7575
test("Tasks.json", async () => {
76-
// Skip if running CI as it takes too long
77-
if (process.env.CI) {
78-
return;
79-
}
8076
const folder = workspaceContext.folders.find(f => f.name === "test/defaultPackage");
8177
assert(folder);
8278
const buildAllTask = await getBuildAllTask(folder);
@@ -86,6 +82,6 @@ suite("Extension Test Suite", () => {
8682
for (const arg of ["build", "--build-tests", "--verbose"]) {
8783
assert(execution?.args.find(item => item === arg));
8884
}
89-
}).timeout(10000);
85+
});
9086
});
9187
}).timeout(15000);

0 commit comments

Comments
 (0)