Skip to content

Add test debugging tests #1111

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .vscode-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ module.exports = defineConfig({
invert: isFastTestRun,
slow: 10000,
},
installExtensions: ["vadimcn.vscode-lldb"],
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is redundant due to the extensionDependencies defined in package.json

reuseMachineInstall: !isCIBuild,
},
{
Expand Down
4 changes: 3 additions & 1 deletion assets/test/.vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@
"swift.additionalTestArguments": [
"-Xswiftc",
"-DTEST_ARGUMENT_SET_VIA_TEST_BUILD_ARGUMENTS_SETTING"
]
],
"lldb.verboseLogging": true

}
8 changes: 1 addition & 7 deletions assets/test/defaultPackage/[email protected]
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ let package = Package(
platforms: [
.macOS(.v13)
],
dependencies: [
.package(url: "https://github.com/apple/swift-testing.git", branch: "main")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
Expand All @@ -24,10 +21,7 @@ let package = Package(
),
.testTarget(
name: "PackageTests",
dependencies: [
"PackageLib",
.product(name: "Testing", package: "swift-testing")
]
dependencies: ["PackageLib"]
),
]
)
2 changes: 1 addition & 1 deletion src/WorkspaceContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ export class WorkspaceContext implements vscode.Disposable {
// find context with root folder
const index = this.folders.findIndex(context => context.folder.fsPath === folder.fsPath);
if (index !== -1) {
console.error(`Adding package folder ${folder} twice`);
this.outputChannel.log(`Adding package folder ${folder} twice`, "WARN");
return this.folders[index];
}
const folderContext = await FolderContext.create(folder, workspaceFolder, this);
Expand Down
5 changes: 4 additions & 1 deletion src/debugger/buildConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,10 @@ export class TestingConfigurationFactory {
program: await this.testExecutableOutputPath(),
args: await this.debuggingTestExecutableArgs(),
env: {
...swiftRuntimeEnv(),
...swiftRuntimeEnv(
process.env,
this.ctx.workspaceContext.toolchain.runtimePath ?? configuration.runtimePath
),
...configuration.folder(this.ctx.workspaceFolder).testEnvironmentVariables,
},
};
Expand Down
26 changes: 15 additions & 11 deletions src/debugger/debugAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import configuration from "../configuration";
import contextKeys from "../contextKeys";
import { fileExists } from "../utilities/filesystem";
import { Version } from "../utilities/version";
import { WorkspaceContext } from "../WorkspaceContext";
import { SwiftToolchain } from "../toolchain/toolchain";
import { SwiftOutputChannel } from "../ui/SwiftOutputChannel";

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

/** Return the path to the debug adapter */
/**
* Return the path to the debug adapter.
*
* @param toolchain The Swift toolchain to use
* @returns A path to the debug adapter for the user's toolchain and configuration
**/
public static async debugAdapterPath(toolchain: SwiftToolchain): Promise<string> {
const customDebugAdapterPath = configuration.debugger.customDebugAdapterPath;
if (customDebugAdapterPath.length > 0) {
Expand Down Expand Up @@ -74,27 +79,26 @@ export class DebugAdapter {
* @returns Whether or not the debug adapter exists
*/
public static async verifyDebugAdapterExists(
workspace: WorkspaceContext,
toolchain: SwiftToolchain,
outputChannel: SwiftOutputChannel,
quiet = false
): Promise<boolean> {
const lldbDebugAdapterPath = await this.debugAdapterPath(workspace.toolchain).catch(
error => {
workspace.outputChannel.log(error);
return undefined;
}
);
const lldbDebugAdapterPath = await this.debugAdapterPath(toolchain).catch(error => {
outputChannel.log(error);
return undefined;
});

if (!lldbDebugAdapterPath || !(await fileExists(lldbDebugAdapterPath))) {
if (!quiet) {
const debugAdapterName = this.getLaunchConfigType(workspace.toolchain.swiftVersion);
const debugAdapterName = this.getLaunchConfigType(toolchain.swiftVersion);
vscode.window.showErrorMessage(
configuration.debugger.customDebugAdapterPath.length > 0
? `Cannot find ${debugAdapterName} debug adapter specified in setting Swift.Debugger.Path.`
: `Cannot find ${debugAdapterName} debug adapter in your Swift toolchain.`
);
}
if (lldbDebugAdapterPath) {
workspace.outputChannel.log(`Failed to find ${lldbDebugAdapterPath}`);
outputChannel.log(`Failed to find ${lldbDebugAdapterPath}`);
}
contextKeys.lldbVSCodeAvailable = false;
return false;
Expand Down
88 changes: 72 additions & 16 deletions src/debugger/debugAdapterFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,76 @@ import * as path from "path";
import { WorkspaceContext } from "../WorkspaceContext";
import { DebugAdapter, LaunchConfigType } from "./debugAdapter";
import { Version } from "../utilities/version";
import { registerLoggingDebugAdapterTracker } from "./logTracker";
import { SwiftToolchain } from "../toolchain/toolchain";
import { SwiftOutputChannel } from "../ui/SwiftOutputChannel";

export function registerLLDBDebugAdapter(workspaceContext: WorkspaceContext): vscode.Disposable {
/**
* Registers the active debugger with the extension, and reregisters it
* when the debugger settings change.
* @param workspaceContext The workspace context
* @returns A disposable to be disposed when the extension is deactivated
*/
export function registerDebugger(workspaceContext: WorkspaceContext): vscode.Disposable {
let subscriptions: vscode.Disposable[] = [];
const register = async () => {
subscriptions.map(sub => sub.dispose());
subscriptions = [
registerLoggingDebugAdapterTracker(workspaceContext.toolchain.swiftVersion),
registerLLDBDebugAdapter(workspaceContext.toolchain, workspaceContext.outputChannel),
];

await workspaceContext.setLLDBVersion();

// Verify that the adapter exists, but only after registration. This async method
// is basically an unstructured task so we don't want to run it before the adapter
// registration above as it could cause code executing immediately after register()
// to use the incorrect adapter.
DebugAdapter.verifyDebugAdapterExists(
workspaceContext.toolchain,
workspaceContext.outputChannel,
true
).catch(error => {
workspaceContext.outputChannel.log(error);
});
};

const changeMonitor = vscode.workspace.onDidChangeConfiguration(event => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the change from the user perspective? This seems to do more than just add new tests, I guess mostly just enabling us to switch debuggers during a test run?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes a bug. Changing the debug adapter didn't require a restart of the extension. When you changed the debug adapter nothing re-registered the adapter/loggingDebugAdapter.

Copy link
Contributor Author

@plemarquand plemarquand Oct 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was uncovered by the tests since they did need to switch the debug adapter without restarting the extension

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing the debug adapter didn't require a restart of the extension

do you mean changing did require a restart?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@award999 I mean it did require a restart, but there was no dialog that popped up prompting a restart.

if (event.affectsConfiguration("swift.debugger.useDebugAdapterFromToolchain")) {
register();
}
});

// Perform the initial registration, then reregister every time the settings change.
register();

return {
dispose: () => {
changeMonitor.dispose();
subscriptions.map(sub => sub.dispose());
},
};
}

/**
* Registers the LLDB debug adapter with the VS Code debug adapter descriptor factory.
* @param workspaceContext The workspace context
* @returns A disposable to be disposed when the extension is deactivated
*/
function registerLLDBDebugAdapter(
toolchain: SwiftToolchain,
outputChannel: SwiftOutputChannel
): vscode.Disposable {
const debugAdpaterFactory = vscode.debug.registerDebugAdapterDescriptorFactory(
LaunchConfigType.SWIFT_EXTENSION,
new LLDBDebugAdapterExecutableFactory(workspaceContext)
new LLDBDebugAdapterExecutableFactory(toolchain, outputChannel)
);

const debugConfigProvider = vscode.debug.registerDebugConfigurationProvider(
LaunchConfigType.SWIFT_EXTENSION,
new LLDBDebugConfigurationProvider(
process.platform,
workspaceContext.toolchain.swiftVersion
)
new LLDBDebugConfigurationProvider(process.platform, toolchain.swiftVersion)
);

return {
dispose: () => {
debugConfigProvider.dispose();
Expand All @@ -56,19 +113,18 @@ export function registerLLDBDebugAdapter(workspaceContext: WorkspaceContext): vs
* @implements {vscode.DebugAdapterDescriptorFactory}
*/
export class LLDBDebugAdapterExecutableFactory implements vscode.DebugAdapterDescriptorFactory {
private workspaceContext: WorkspaceContext;
private toolchain: SwiftToolchain;
private outputChannel: SwiftOutputChannel;

constructor(workspaceContext: WorkspaceContext) {
this.workspaceContext = workspaceContext;
constructor(toolchain: SwiftToolchain, outputChannel: SwiftOutputChannel) {
this.toolchain = toolchain;
this.outputChannel = outputChannel;
}

createDebugAdapterDescriptor(): vscode.ProviderResult<vscode.DebugAdapterDescriptor> {
// Use the stored workspaceContext
return DebugAdapter.debugAdapterPath(this.workspaceContext.toolchain)
.then(path =>
DebugAdapter.verifyDebugAdapterExists(this.workspaceContext).then(() => path)
)
.then(path => new vscode.DebugAdapterExecutable(path, [], {}));
async createDebugAdapterDescriptor(): Promise<vscode.DebugAdapterDescriptor> {
const path = await DebugAdapter.debugAdapterPath(this.toolchain);
await DebugAdapter.verifyDebugAdapterExists(this.toolchain, this.outputChannel);
return new vscode.DebugAdapterExecutable(path, [], {});
}
}

Expand Down
37 changes: 31 additions & 6 deletions src/debugger/logTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import * as vscode from "vscode";
import { DebugAdapter } from "./debugAdapter";
import { WorkspaceContext } from "../WorkspaceContext";
import { Version } from "../utilities/version";

/**
* Factory class for building LoggingDebugAdapterTracker
Expand All @@ -39,11 +39,34 @@ interface DebugMessage {
body: OutputEventBody;
}

export function registerLoggingDebugAdapterTracker(ctx: WorkspaceContext): vscode.Disposable {
return vscode.debug.registerDebugAdapterTrackerFactory(
DebugAdapter.getLaunchConfigType(ctx.swiftVersion),
new LoggingDebugAdapterTrackerFactory()
);
/**
* Register the LoggingDebugAdapterTrackerFactory with the VS Code debug adapter tracker
* @returns A disposable to be disposed when the extension is deactivated
*/
export function registerLoggingDebugAdapterTracker(swiftVersion: Version): vscode.Disposable {
const register = () =>
vscode.debug.registerDebugAdapterTrackerFactory(
DebugAdapter.getLaunchConfigType(swiftVersion),
new LoggingDebugAdapterTrackerFactory()
);

// Maintains the disposable for the last registered debug adapter.
let debugAdapterDisposable = register();
const changeMonitor = vscode.workspace.onDidChangeConfiguration(event => {
if (event.affectsConfiguration("swift.debugger.useDebugAdapterFromToolchain")) {
// Dispose the old adapter and reconfigure with the new settings.
debugAdapterDisposable.dispose();
debugAdapterDisposable = register();
}
});

// Return a disposable that cleans everything up.
return {
dispose() {
changeMonitor.dispose();
debugAdapterDisposable.dispose();
},
};
}

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

Expand Down
30 changes: 3 additions & 27 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ import { SwiftPluginTaskProvider } from "./tasks/SwiftPluginTaskProvider";
import configuration from "./configuration";
import { Version } from "./utilities/version";
import { getReadOnlyDocumentProvider } from "./ui/ReadOnlyDocumentProvider";
import { registerLoggingDebugAdapterTracker } from "./debugger/logTracker";
import { registerLLDBDebugAdapter } from "./debugger/debugAdapterFactory";
import { DebugAdapter } from "./debugger/debugAdapter";
import { registerDebugger } from "./debugger/debugAdapterFactory";
import contextKeys from "./contextKeys";
import { showToolchainError } from "./ui/ToolchainSelection";
import { SwiftToolchain } from "./toolchain/toolchain";
Expand Down Expand Up @@ -83,6 +81,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<Api |
});

context.subscriptions.push(...commands.registerToolchainCommands(toolchain));

context.subscriptions.push(
vscode.workspace.onDidChangeConfiguration(event => {
// on toolchain config change, reload window
Expand Down Expand Up @@ -112,14 +111,8 @@ export async function activate(context: vscode.ExtensionContext): Promise<Api |

const workspaceContext = await WorkspaceContext.create(outputChannel, toolchain);
context.subscriptions.push(...commands.register(workspaceContext));

context.subscriptions.push(workspaceContext);

// setup swift version of LLDB. Don't await on this as it can run in the background
DebugAdapter.verifyDebugAdapterExists(workspaceContext, true).catch(error => {
outputChannel.log(error);
});
workspaceContext.setLLDBVersion();
context.subscriptions.push(registerDebugger(workspaceContext));

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

const testExplorerObserver = TestExplorer.observeFolders(workspaceContext);

// Register swift-lldb debug provider
const lldbDebugAdapter = registerLLDBDebugAdapter(workspaceContext);
context.subscriptions.push(lldbDebugAdapter);

const loggingDebugAdapter = registerLoggingDebugAdapterTracker(workspaceContext);

// setup workspace context with initial workspace folders
workspaceContext.addWorkspaceFolders();

// Register any disposables for cleanup when the extension deactivates.
context.subscriptions.push(
loggingDebugAdapter,
resolvePackageObserver,
testExplorerObserver,
swiftModuleDocumentProvider,
Expand All @@ -266,13 +252,3 @@ export async function activate(context: vscode.ExtensionContext): Promise<Api |
throw error;
}
}

/**
* Deactivate the extension.
*
* Any disposables registered in `context.subscriptions` will be automatically
* disposed of, so there's nothing left to do here.
*/
export function deactivate() {
return;
}
6 changes: 3 additions & 3 deletions src/utilities/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ import { SwiftToolchain } from "../toolchain/toolchain";
* @returns minimal required environment for Swift product
*/
export function swiftRuntimeEnv(
base: NodeJS.ProcessEnv | boolean = process.env
base: NodeJS.ProcessEnv | boolean = process.env,
runtimePath: string = configuration.runtimePath
): { [key: string]: string } | undefined {
if (configuration.runtimePath === "") {
if (runtimePath === "") {
return undefined;
}
const runtimePath = configuration.runtimePath;
const key = swiftLibraryPathKey();
const separator = process.platform === "win32" ? ";" : ":";
switch (base) {
Expand Down
6 changes: 1 addition & 5 deletions test/integration-tests/extension.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,6 @@ suite("Extension Test Suite", () => {
suite("Workspace", () => {
/** Verify tasks.json is being loaded */
test("Tasks.json", async () => {
// Skip if running CI as it takes too long
if (process.env.CI) {
return;
}
const folder = workspaceContext.folders.find(f => f.name === "test/defaultPackage");
assert(folder);
const buildAllTask = await getBuildAllTask(folder);
Expand All @@ -86,6 +82,6 @@ suite("Extension Test Suite", () => {
for (const arg of ["build", "--build-tests", "--verbose"]) {
assert(execution?.args.find(item => item === arg));
}
}).timeout(10000);
});
});
}).timeout(15000);
Loading