Merge pull request #3232 from github/dbartol/new-test-api

Switch to built-in VS Code test UI unconditionally
This commit is contained in:
Dave Bartolomeo
2024-01-11 16:48:56 -05:00
committed by GitHub
14 changed files with 107 additions and 571 deletions

View File

@@ -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.

View File

@@ -3,6 +3,9 @@
## [UNRELEASED]
- If you run a query without having selected a database, we show a more intuitive prompt to help you select a database. [#3214](https://github.com/github/vscode-codeql/pull/3214)
- The UI for browsing and running CodeQL tests has moved to use VS Code's built-in test UI. This makes the CodeQL test UI more consistent with the test UIs for other languages.
This change means that this extension no longer depends on the "Test Explorer UI" and "Test Adapter Converter" extensions. You can uninstall those two extensions if they are
not being used by any other extensions you may have installed.
## 1.12.0 - 11 January 2024

View File

@@ -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).

View File

@@ -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"
},
@@ -31964,31 +31962,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",

View File

@@ -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"
},

View File

@@ -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 = {

View File

@@ -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() ?? {};

View File

@@ -1,273 +0,0 @@
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.
* @param testPath The full path to the test file.
*/
export function getExpectedFile(testPath: string): string {
return getTestOutputFile(testPath, ".expected");
}
/**
* Get the full path of the `.actual` file for the specified QL test.
* @param testPath The full path to the test file.
*/
export function getActualFile(testPath: string): string {
return getTestOutputFile(testPath, ".actual");
}
/**
* Gets the the full path to a particular output file of the specified QL test.
* @param testPath The full path to the QL test.
* @param extension The file extension of the output file.
*/
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.
* @param ext The new extension, including the `.`.
*/
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,
})),
});
}
}

View File

@@ -1,75 +0,0 @@
import { copy, createFile, lstat, pathExists } from "fs-extra";
import type { TestUICommands } from "../common/commands";
import { DisposableObject } from "../common/disposable-object";
import { getActualFile, getExpectedFile } from "./test-adapter";
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;
/**
* Base class for both the legacy and new test services. Implements commands that are common to
* both.
*/
export abstract class TestManagerBase extends DisposableObject {
protected constructor(private readonly app: App) {
super();
}
public getCommands(): TestUICommands {
return {
"codeQLTests.showOutputDifferences":
this.showOutputDifferences.bind(this),
"codeQLTests.acceptOutput": this.acceptOutput.bind(this),
"codeQLTests.acceptOutputContextTestItem": this.acceptOutput.bind(this),
};
}
/** Override to compute the path of the test file from the selected node. */
protected abstract getTestPath(node: TestNode): string;
private async acceptOutput(node: TestNode): Promise<void> {
const testPath = this.getTestPath(node);
const stat = await lstat(testPath);
if (stat.isFile()) {
const expectedPath = getExpectedFile(testPath);
const actualPath = getActualFile(testPath);
await copy(actualPath, expectedPath, { overwrite: true });
}
}
private async showOutputDifferences(node: TestNode): Promise<void> {
const testId = this.getTestPath(node);
const stat = await lstat(testId);
if (stat.isFile()) {
const expectedPath = getExpectedFile(testId);
const expectedUri = Uri.file(expectedPath);
const actualPath = getActualFile(testId);
const options: TextDocumentShowOptions = {
preserveFocus: true,
preview: true,
};
if (!(await pathExists(expectedPath))) {
// Just create a new file.
await createFile(expectedPath);
}
if (await pathExists(actualPath)) {
const actualUri = Uri.file(actualPath);
await this.app.commands.execute(
"vscode.diff",
expectedUri,
actualUri,
`Expected vs. Actual for ${basename(testId)}`,
options,
);
} else {
await window.showTextDocument(expectedUri, options);
}
}
}
}

View File

@@ -1,10 +1,11 @@
import { readFile } from "fs-extra";
import { copy, createFile, lstat, pathExists, readFile } from "fs-extra";
import type {
CancellationToken,
TestController,
TestItem,
TestRun,
TestRunRequest,
TextDocumentShowOptions,
WorkspaceFolder,
WorkspaceFoldersChangeEvent,
} from "vscode";
@@ -15,6 +16,7 @@ import {
TestRunProfileKind,
Uri,
tests,
window,
workspace,
} from "vscode";
import { DisposableObject } from "../common/disposable-object";
@@ -23,11 +25,46 @@ import type { CodeQLCliServer } from "../codeql-cli/cli";
import { getErrorMessage } from "../common/helpers-pure";
import type { BaseLogger, LogOptions } from "../common/logging";
import type { TestRunner } from "./test-runner";
import { TestManagerBase } from "./test-manager-base";
import type { App } from "../common/app";
import { isWorkspaceFolderOnDisk } from "../common/vscode/workspace-folders";
import type { FileTreeNode } from "../common/file-tree-nodes";
import { FileTreeDirectory, FileTreeLeaf } from "../common/file-tree-nodes";
import type { TestUICommands } from "../common/commands";
import { basename, extname } from "path";
/**
* Get the full path of the `.expected` file for the specified QL test.
* @param testPath The full path to the test file.
*/
function getExpectedFile(testPath: string): string {
return getTestOutputFile(testPath, ".expected");
}
/**
* Get the full path of the `.actual` file for the specified QL test.
* @param testPath The full path to the test file.
*/
function getActualFile(testPath: string): string {
return getTestOutputFile(testPath, ".actual");
}
/**
* Gets the the full path to a particular output file of the specified QL test.
* @param testPath The full path to the QL test.
* @param extension The file extension of the output file.
*/
function getTestOutputFile(testPath: string, extension: string): string {
return changeExtension(testPath, extension);
}
/**
* Change the file extension of the specified path.
* @param p The original file path.
* @param ext The new extension, including the `.`.
*/
function changeExtension(p: string, ext: string): string {
return p.slice(0, -extname(p).length) + ext;
}
/**
* Returns the complete text content of the specified file. If there is an error reading the file,
@@ -108,7 +145,7 @@ class WorkspaceFolderHandler extends DisposableObject {
* Service that populates the VS Code "Test Explorer" panel for CodeQL, and handles running and
* debugging of tests.
*/
export class TestManager extends TestManagerBase {
export class TestManager extends DisposableObject {
/**
* Maps from each workspace folder being tracked to the `WorkspaceFolderHandler` responsible for
* tracking it.
@@ -119,7 +156,7 @@ export class TestManager extends TestManagerBase {
>();
public constructor(
app: App,
private readonly app: App,
private readonly testRunner: TestRunner,
private readonly cliServer: CodeQLCliServer,
// Having this as a parameter with a default value makes passing in a mock easier.
@@ -128,7 +165,7 @@ export class TestManager extends TestManagerBase {
"CodeQL Tests",
),
) {
super(app);
super();
this.testController.createRunProfile(
"Run",
@@ -151,6 +188,15 @@ export class TestManager extends TestManagerBase {
super.dispose();
}
public getCommands(): TestUICommands {
return {
"codeQLTests.showOutputDifferences":
this.showOutputDifferences.bind(this),
"codeQLTests.acceptOutput": this.acceptOutput.bind(this),
"codeQLTests.acceptOutputContextTestItem": this.acceptOutput.bind(this),
};
}
protected getTestPath(node: TestItem): string {
if (node.uri === undefined || node.uri.scheme !== "file") {
throw new Error("Selected test is not a CodeQL test.");
@@ -158,6 +204,48 @@ export class TestManager extends TestManagerBase {
return node.uri.fsPath;
}
private async acceptOutput(node: TestItem): Promise<void> {
const testPath = this.getTestPath(node);
const stat = await lstat(testPath);
if (stat.isFile()) {
const expectedPath = getExpectedFile(testPath);
const actualPath = getActualFile(testPath);
await copy(actualPath, expectedPath, { overwrite: true });
}
}
private async showOutputDifferences(node: TestItem): Promise<void> {
const testId = this.getTestPath(node);
const stat = await lstat(testId);
if (stat.isFile()) {
const expectedPath = getExpectedFile(testId);
const expectedUri = Uri.file(expectedPath);
const actualPath = getActualFile(testId);
const options: TextDocumentShowOptions = {
preserveFocus: true,
preview: true,
};
if (!(await pathExists(expectedPath))) {
// Just create a new file.
await createFile(expectedPath);
}
if (await pathExists(actualPath)) {
const actualUri = Uri.file(actualPath);
await this.app.commands.execute(
"vscode.diff",
expectedUri,
actualUri,
`Expected vs. Actual for ${basename(testId)}`,
options,
);
} else {
await window.showTextDocument(expectedUri, options);
}
}
}
/** Start tracking tests in the specified workspace folders. */
private startTrackingWorkspaceFolders(
workspaceFolders: readonly WorkspaceFolder[],

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -1,9 +1,4 @@
import type {
TestItem,
TestItemCollection,
TestRun,
WorkspaceFolder,
} from "vscode";
import type { TestItem, TestItemCollection, TestRun } from "vscode";
import {
CancellationTokenSource,
Range,
@@ -12,7 +7,6 @@ import {
tests,
} from "vscode";
import { QLTestAdapter } from "../../../../src/query-testing/test-adapter";
import type { CodeQLCliServer } from "../../../../src/codeql-cli/cli";
import type { DatabaseManager } from "../../../../src/databases/local-databases";
import { mockedObject } from "../../utils/mocking.helpers";
@@ -41,50 +35,6 @@ describe("test-adapter", () => {
testRunner = new TestRunner(fakeDatabaseManager, fakeCliServer);
});
it("legacy test adapter should run some tests", async () => {
const adapter = new QLTestAdapter(
mockedObject<WorkspaceFolder>({
name: "ABC",
uri: Uri.parse("file:/ab/c"),
}),
testRunner,
fakeCliServer,
);
const listenerSpy = jest.fn();
adapter.testStates(listenerSpy);
await adapter.run([mockTestsInfo.testsPath]);
expect(listenerSpy).toBeCalledTimes(5);
expect(listenerSpy).toHaveBeenNthCalledWith(1, {
type: "started",
tests: [mockTestsInfo.testsPath],
});
expect(listenerSpy).toHaveBeenNthCalledWith(2, {
type: "test",
state: "passed",
test: mockTestsInfo.dPath,
message: undefined,
decorations: [],
});
expect(listenerSpy).toHaveBeenNthCalledWith(3, {
type: "test",
state: "errored",
test: mockTestsInfo.gPath,
message: `\ncompilation error: ${mockTestsInfo.gPath}\nERROR: abc\n`,
decorations: [{ line: 1, message: "abc" }],
});
expect(listenerSpy).toHaveBeenNthCalledWith(4, {
type: "test",
state: "failed",
test: mockTestsInfo.hPath,
message: `\nfailed: ${mockTestsInfo.hPath}\njkh\ntuv\n`,
decorations: [],
});
expect(listenerSpy).toHaveBeenNthCalledWith(5, { type: "finished" });
});
it("native test manager should run some tests", async () => {
const enqueuedSpy = jest.fn();
const passedSpy = jest.fn();