Switch to built-in VS Code test UI unconditionally

This commit is contained in:
Dave Bartolomeo 2024-01-11 10:47:48 -05:00
Родитель 1b737678ba
Коммит 1dc6111fba
11 изменённых файлов: 11 добавлений и 407 удалений

Просмотреть файл

@ -21,13 +21,6 @@ To see what has changed in the last few versions of the extension, see the [Chan
This project will track new feature development in CodeQL and, whenever appropriate, bring that functionality to the Visual Studio Code experience.
## Dependencies
This extension depends on the following two extensions for required functionality. They will be installed automatically when you install VS Code CodeQL.
- [Test Adapter Converter](https://marketplace.visualstudio.com/items?itemName=ms-vscode.test-adapter-converter)
- [Test Explorer UI](https://marketplace.visualstudio.com/items?itemName=hbenl.vscode-test-explorer)
## Contributing
This project welcomes contributions. See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to build, install, and contribute.

Просмотреть файл

@ -17,8 +17,6 @@ For information about other configurations, see the separate [CodeQL help](https
### Quick start: Installing and configuring the extension
1. [Install the extension](#installing-the-extension).
*Note: vscode-codeql installs the following dependencies for required functionality: [Test Adapter Converter](https://marketplace.visualstudio.com/items?itemName=ms-vscode.test-adapter-converter), [Test Explorer UI](https://marketplace.visualstudio.com/items?itemName=hbenl.vscode-test-explorer).*
1. [Check access to the CodeQL CLI](#checking-access-to-the-codeql-cli).
1. [Clone the CodeQL starter workspace](#cloning-the-codeql-starter-workspace).

27
extensions/ql-vscode/package-lock.json сгенерированный
Просмотреть файл

@ -40,8 +40,6 @@
"vscode-extension-telemetry": "^0.1.6",
"vscode-jsonrpc": "^8.0.2",
"vscode-languageclient": "^8.0.2",
"vscode-test-adapter-api": "^1.7.0",
"vscode-test-adapter-util": "^0.7.0",
"yauzl": "^2.10.0",
"zip-a-folder": "^3.1.3"
},
@ -31984,31 +31982,6 @@
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz",
"integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA=="
},
"node_modules/vscode-test-adapter-api": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/vscode-test-adapter-api/-/vscode-test-adapter-api-1.9.0.tgz",
"integrity": "sha512-lltjehUP0J9H3R/HBctjlqeUCwn2t9Lbhj2Y500ib+j5Y4H3hw+hVTzuSsfw16LtxY37knlU39QIlasa7svzOQ==",
"engines": {
"vscode": "^1.23.0"
}
},
"node_modules/vscode-test-adapter-util": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/vscode-test-adapter-util/-/vscode-test-adapter-util-0.7.1.tgz",
"integrity": "sha512-OZZvLDDNhayVVISyTmgUntOhMzl6j9/wVGfNqI2zuR5bQIziTQlDs9W29dFXDTGXZOxazS6uiHkrr86BKDzYUA==",
"dependencies": {
"tslib": "^1.11.1",
"vscode-test-adapter-api": "^1.8.0"
},
"engines": {
"vscode": "^1.24.0"
}
},
"node_modules/vscode-test-adapter-util/node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/w3c-xmlserializer": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",

Просмотреть файл

@ -21,7 +21,6 @@
"Programming Languages"
],
"extensionDependencies": [
"hbenl.vscode-test-explorer",
"vscode.git"
],
"capabilities": {
@ -1938,8 +1937,6 @@
"vscode-extension-telemetry": "^0.1.6",
"vscode-jsonrpc": "^8.0.2",
"vscode-languageclient": "^8.0.2",
"vscode-test-adapter-api": "^1.7.0",
"vscode-test-adapter-util": "^0.7.0",
"yauzl": "^2.10.0",
"zip-a-folder": "^3.1.3"
},

Просмотреть файл

@ -1,10 +1,9 @@
import type { CommandManager } from "../packages/commands";
import type { Uri, Range, TextDocumentShowOptions } from "vscode";
import type { Uri, Range, TextDocumentShowOptions, TestItem } from "vscode";
import type { AstItem } from "../language-support";
import type { DbTreeViewItem } from "../databases/ui/db-tree-view-item";
import type { DatabaseItem } from "../databases/local-databases";
import type { QueryHistoryInfo } from "../query-history/query-history-info";
import type { TestTreeNode } from "../query-testing/test-tree-node";
import type {
VariantAnalysis,
VariantAnalysisScannedRepository,
@ -334,11 +333,9 @@ export type SummaryLanguageSupportCommands = {
};
export type TestUICommands = {
"codeQLTests.showOutputDifferences": (node: TestTreeNode) => Promise<void>;
"codeQLTests.acceptOutput": (node: TestTreeNode) => Promise<void>;
"codeQLTests.acceptOutputContextTestItem": (
node: TestTreeNode,
) => Promise<void>;
"codeQLTests.showOutputDifferences": (node: TestItem) => Promise<void>;
"codeQLTests.acceptOutput": (node: TestItem) => Promise<void>;
"codeQLTests.acceptOutputContextTestItem": (node: TestItem) => Promise<void>;
};
export type MockGitHubApiServerCommands = {

Просмотреть файл

@ -15,8 +15,6 @@ import { arch, homedir, platform } from "os";
import { ensureDir } from "fs-extra";
import { join } from "path";
import { dirSync } from "tmp-promise";
import type { TestHub } from "vscode-test-adapter-api";
import { testExplorerExtensionId } from "vscode-test-adapter-api";
import { lt, parse } from "semver";
import { watch } from "chokidar";
import {
@ -28,7 +26,6 @@ import {
CliConfigListener,
DistributionConfigListener,
GitHubDatabaseConfigListener,
isCanary,
joinOrderWarningThreshold,
QueryHistoryConfigListener,
QueryServerConfigListener,
@ -90,8 +87,6 @@ import {
} from "./common/logging/vscode";
import { QueryHistoryManager } from "./query-history/query-history-manager";
import type { CompletedLocalQueryInfo } from "./query-results";
import { QLTestAdapterFactory } from "./query-testing/test-adapter";
import { TestUIService } from "./query-testing/test-ui";
import { CompareView } from "./compare/compare-view";
import {
initializeTelemetry,
@ -130,7 +125,6 @@ import { DebuggerUI } from "./debugger/debugger-ui";
import { ModelEditorModule } from "./model-editor/model-editor-module";
import { TestManager } from "./query-testing/test-manager";
import { TestRunner } from "./query-testing/test-runner";
import type { TestManagerBase } from "./query-testing/test-manager-base";
import { QueryRunner, QueryServerClient } from "./query-server";
import { QueriesModule } from "./queries-panel/queries-module";
import { OpenReferencedFileCodeLensProvider } from "./local-queries/open-referenced-file-code-lens-provider";
@ -977,27 +971,8 @@ async function activateWithInstalledDistribution(
const testRunner = new TestRunner(dbm, cliServer);
ctx.subscriptions.push(testRunner);
let testManager: TestManagerBase | undefined = undefined;
if (isCanary()) {
testManager = new TestManager(app, testRunner, cliServer);
ctx.subscriptions.push(testManager);
} else {
const testExplorerExtension = extensions.getExtension<TestHub>(
testExplorerExtensionId,
);
if (testExplorerExtension) {
const testHub = testExplorerExtension.exports;
const testAdapterFactory = new QLTestAdapterFactory(
testHub,
testRunner,
cliServer,
);
ctx.subscriptions.push(testAdapterFactory);
testManager = new TestUIService(app, testHub);
ctx.subscriptions.push(testManager);
}
}
const testManager = new TestManager(app, testRunner, cliServer);
ctx.subscriptions.push(testManager);
const testUiCommands = testManager?.getCommands() ?? {};

Просмотреть файл

@ -1,26 +1,4 @@
import { extname } from "path";
import type { Event, WorkspaceFolder } from "vscode";
import { CancellationTokenSource, EventEmitter } from "vscode";
import type {
TestAdapter,
TestEvent,
TestHub,
TestInfo,
TestLoadFinishedEvent,
TestLoadStartedEvent,
TestRunFinishedEvent,
TestRunStartedEvent,
TestSuiteEvent,
TestSuiteInfo,
} from "vscode-test-adapter-api";
import { TestAdapterRegistrar } from "vscode-test-adapter-util";
import { QLTestDiscovery } from "./qltest-discovery";
import { DisposableObject } from "../common/disposable-object";
import type { CodeQLCliServer, TestCompleted } from "../codeql-cli/cli";
import { testLogger } from "../common/logging/vscode";
import type { TestRunner } from "./test-runner";
import type { FileTreeNode } from "../common/file-tree-nodes";
import { FileTreeDirectory, FileTreeLeaf } from "../common/file-tree-nodes";
/**
* Get the full path of the `.expected` file for the specified QL test.
@ -47,28 +25,6 @@ function getTestOutputFile(testPath: string, extension: string): string {
return changeExtension(testPath, extension);
}
/**
* A factory service that creates `QLTestAdapter` objects for workspace folders on demand.
*/
export class QLTestAdapterFactory extends DisposableObject {
constructor(
testHub: TestHub,
testRunner: TestRunner,
cliServer: CodeQLCliServer,
) {
super();
// this will register a QLTestAdapter for each WorkspaceFolder
this.push(
new TestAdapterRegistrar(
testHub,
(workspaceFolder) =>
new QLTestAdapter(workspaceFolder, testRunner, cliServer),
),
);
}
}
/**
* Change the file extension of the specified path.
* @param p The original file path.
@ -77,197 +33,3 @@ export class QLTestAdapterFactory extends DisposableObject {
function changeExtension(p: string, ext: string): string {
return p.slice(0, -extname(p).length) + ext;
}
/**
* Test adapter for QL tests.
*/
export class QLTestAdapter extends DisposableObject implements TestAdapter {
private readonly qlTestDiscovery: QLTestDiscovery;
private readonly _tests = this.push(
new EventEmitter<TestLoadStartedEvent | TestLoadFinishedEvent>(),
);
private readonly _testStates = this.push(
new EventEmitter<
TestRunStartedEvent | TestRunFinishedEvent | TestSuiteEvent | TestEvent
>(),
);
private readonly _autorun = this.push(new EventEmitter<void>());
private runningTask?: CancellationTokenSource = undefined;
constructor(
public readonly workspaceFolder: WorkspaceFolder,
private readonly testRunner: TestRunner,
cliServer: CodeQLCliServer,
) {
super();
this.qlTestDiscovery = this.push(
new QLTestDiscovery(workspaceFolder, cliServer),
);
void this.qlTestDiscovery.refresh();
this.push(this.qlTestDiscovery.onDidChangeTests(this.discoverTests, this));
}
public get tests(): Event<TestLoadStartedEvent | TestLoadFinishedEvent> {
return this._tests.event;
}
public get testStates(): Event<
TestRunStartedEvent | TestRunFinishedEvent | TestSuiteEvent | TestEvent
> {
return this._testStates.event;
}
public get autorun(): Event<void> | undefined {
return this._autorun.event;
}
private static createTestOrSuiteInfos(
testNodes: readonly FileTreeNode[],
): Array<TestSuiteInfo | TestInfo> {
return testNodes.map((childNode) => {
return QLTestAdapter.createTestOrSuiteInfo(childNode);
});
}
private static createTestOrSuiteInfo(
testNode: FileTreeNode,
): TestSuiteInfo | TestInfo {
if (testNode instanceof FileTreeLeaf) {
return QLTestAdapter.createTestInfo(testNode);
} else if (testNode instanceof FileTreeDirectory) {
return QLTestAdapter.createTestSuiteInfo(testNode, testNode.name);
} else {
throw new Error("Unexpected test type.");
}
}
private static createTestInfo(testFile: FileTreeLeaf): TestInfo {
return {
type: "test",
id: testFile.path,
label: testFile.name,
tooltip: testFile.path,
file: testFile.path,
};
}
private static createTestSuiteInfo(
testDirectory: FileTreeDirectory,
label: string,
): TestSuiteInfo {
return {
type: "suite",
id: testDirectory.path,
label,
children: QLTestAdapter.createTestOrSuiteInfos(testDirectory.children),
tooltip: testDirectory.path,
};
}
public async load(): Promise<void> {
this.discoverTests();
}
private discoverTests(): void {
this._tests.fire({ type: "started" } as TestLoadStartedEvent);
const testDirectory = this.qlTestDiscovery.testDirectory;
let testSuite: TestSuiteInfo | undefined;
if (testDirectory?.children.length) {
const children = QLTestAdapter.createTestOrSuiteInfos(
testDirectory.children,
);
testSuite = {
type: "suite",
label: "CodeQL",
id: testDirectory.path,
children,
};
}
this._tests.fire({
type: "finished",
suite: testSuite,
} as TestLoadFinishedEvent);
}
public async run(tests: string[]): Promise<void> {
if (this.runningTask !== undefined) {
throw new Error("Tests already running.");
}
testLogger.outputChannel.clear();
testLogger.outputChannel.show(true);
this.runningTask = this.track(new CancellationTokenSource());
const token = this.runningTask.token;
this._testStates.fire({
type: "started",
tests,
} as TestRunStartedEvent);
await this.testRunner.run(tests, testLogger, token, (event) =>
this.processTestEvent(event),
);
this._testStates.fire({ type: "finished" } as TestRunFinishedEvent);
this.clearTask();
}
private clearTask(): void {
if (this.runningTask !== undefined) {
const runningTask = this.runningTask;
this.runningTask = undefined;
this.disposeAndStopTracking(runningTask);
}
}
public cancel(): void {
if (this.runningTask !== undefined) {
void testLogger.log("Cancelling test run...");
this.runningTask.cancel();
this.clearTask();
}
}
private async processTestEvent(event: TestCompleted): Promise<void> {
const state = event.pass
? "passed"
: event.messages?.length
? "errored"
: "failed";
let message: string | undefined;
if (event.failureDescription || event.diff?.length) {
message =
event.failureStage === "RESULT"
? [
"",
`${state}: ${event.test}`,
event.failureDescription || event.diff?.join("\n"),
"",
].join("\n")
: [
"",
`${event.failureStage?.toLowerCase() ?? "unknown stage"} error: ${
event.test
}`,
event.failureDescription ||
`${event.messages[0].severity}: ${event.messages[0].message}`,
"",
].join("\n");
void testLogger.log(message);
}
this._testStates.fire({
type: "test",
state,
test: event.test,
message,
decorations: event.messages?.map((msg) => ({
line: msg.position.line,
message: msg.message,
})),
});
}
}

Просмотреть файл

@ -6,9 +6,8 @@ import type { TestItem, TextDocumentShowOptions } from "vscode";
import { Uri, window } from "vscode";
import { basename } from "path";
import type { App } from "../common/app";
import type { TestTreeNode } from "./test-tree-node";
type TestNode = TestTreeNode | TestItem;
type TestNode = TestItem;
/**
* Base class for both the legacy and new test services. Implements commands that are common to

Просмотреть файл

@ -1,9 +0,0 @@
import type { TestSuiteInfo, TestInfo } from "vscode-test-adapter-api";
/**
* Tree view node for a test, suite, or collection. This object is passed as the argument to the
* command handler of a context menu item for a tree view item.
*/
export interface TestTreeNode {
readonly info: TestSuiteInfo | TestInfo;
}

Просмотреть файл

@ -1,71 +0,0 @@
import type {
TestHub,
TestController,
TestAdapter,
TestRunStartedEvent,
TestRunFinishedEvent,
TestEvent,
TestSuiteEvent,
} from "vscode-test-adapter-api";
import type { TestTreeNode } from "./test-tree-node";
import { DisposableObject } from "../common/disposable-object";
import { QLTestAdapter } from "./test-adapter";
import type { App } from "../common/app";
import { TestManagerBase } from "./test-manager-base";
type VSCodeTestEvent =
| TestRunStartedEvent
| TestRunFinishedEvent
| TestSuiteEvent
| TestEvent;
/**
* Test event listener. Currently unused, but left in to keep the plumbing hooked up for future use.
*/
class QLTestListener extends DisposableObject {
constructor(adapter: TestAdapter) {
super();
this.push(adapter.testStates(this.onTestStatesEvent, this));
}
private onTestStatesEvent(_e: VSCodeTestEvent): void {
/**/
}
}
/**
* Service that implements all UI and commands for QL tests.
*/
export class TestUIService extends TestManagerBase implements TestController {
private readonly listeners: Map<TestAdapter, QLTestListener> = new Map();
public constructor(
app: App,
private readonly testHub: TestHub,
) {
super(app);
testHub.registerTestController(this);
}
public dispose(): void {
this.testHub.unregisterTestController(this);
super.dispose();
}
public registerTestAdapter(adapter: TestAdapter): void {
this.listeners.set(adapter, new QLTestListener(adapter));
}
public unregisterTestAdapter(adapter: TestAdapter): void {
if (adapter instanceof QLTestAdapter) {
this.listeners.delete(adapter);
}
}
protected getTestPath(node: TestTreeNode): string {
return node.info.id;
}
}

Просмотреть файл

@ -50,20 +50,10 @@ export default class JestRunnerInstalledExtensions extends VSCodeTestRunner {
const [cli, ...args] =
resolveCliArgsFromVSCodeExecutablePath(vscodeExecutablePath);
spawnSync(
cli,
[
...args,
"--install-extension",
"hbenl.vscode-test-explorer",
"--install-extension",
"ms-vscode.test-adapter-converter",
],
{
encoding: "utf-8",
stdio: "inherit",
},
);
spawnSync(cli, args, {
encoding: "utf-8",
stdio: "inherit",
});
installedOnVsCodeVersions.add(versionKey);
}