From 1dc6111fbad17e7a5724a0231ce608cb07080bce Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 11 Jan 2024 10:47:48 -0500 Subject: [PATCH 1/4] Switch to built-in VS Code test UI unconditionally --- README.md | 7 - extensions/ql-vscode/README.md | 2 - extensions/ql-vscode/package-lock.json | 27 -- extensions/ql-vscode/package.json | 3 - extensions/ql-vscode/src/common/commands.ts | 11 +- extensions/ql-vscode/src/extension.ts | 29 +-- .../src/query-testing/test-adapter.ts | 238 ------------------ .../src/query-testing/test-manager-base.ts | 3 +- .../src/query-testing/test-tree-node.ts | 9 - .../ql-vscode/src/query-testing/test-ui.ts | 71 ------ .../jest-runner-installed-extensions.ts | 18 +- 11 files changed, 11 insertions(+), 407 deletions(-) delete mode 100644 extensions/ql-vscode/src/query-testing/test-tree-node.ts delete mode 100644 extensions/ql-vscode/src/query-testing/test-ui.ts diff --git a/README.md b/README.md index f5dc1b9e84f..03b564f5ace 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/extensions/ql-vscode/README.md b/extensions/ql-vscode/README.md index 7558d27cf46..91c0016fc73 100644 --- a/extensions/ql-vscode/README.md +++ b/extensions/ql-vscode/README.md @@ -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). diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index fcc53e58243..e79cf467c9b 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/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", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 1fa60675886..b905707fe21 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -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" }, diff --git a/extensions/ql-vscode/src/common/commands.ts b/extensions/ql-vscode/src/common/commands.ts index dbd640b3ba3..57c471ec72f 100644 --- a/extensions/ql-vscode/src/common/commands.ts +++ b/extensions/ql-vscode/src/common/commands.ts @@ -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; - "codeQLTests.acceptOutput": (node: TestTreeNode) => Promise; - "codeQLTests.acceptOutputContextTestItem": ( - node: TestTreeNode, - ) => Promise; + "codeQLTests.showOutputDifferences": (node: TestItem) => Promise; + "codeQLTests.acceptOutput": (node: TestItem) => Promise; + "codeQLTests.acceptOutputContextTestItem": (node: TestItem) => Promise; }; export type MockGitHubApiServerCommands = { diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index 8a2ce6a6c42..58be99fad87 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -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( - 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() ?? {}; diff --git a/extensions/ql-vscode/src/query-testing/test-adapter.ts b/extensions/ql-vscode/src/query-testing/test-adapter.ts index eef9a267298..afb9b47b7e1 100644 --- a/extensions/ql-vscode/src/query-testing/test-adapter.ts +++ b/extensions/ql-vscode/src/query-testing/test-adapter.ts @@ -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(), - ); - private readonly _testStates = this.push( - new EventEmitter< - TestRunStartedEvent | TestRunFinishedEvent | TestSuiteEvent | TestEvent - >(), - ); - private readonly _autorun = this.push(new EventEmitter()); - 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 { - return this._tests.event; - } - - public get testStates(): Event< - TestRunStartedEvent | TestRunFinishedEvent | TestSuiteEvent | TestEvent - > { - return this._testStates.event; - } - - public get autorun(): Event | undefined { - return this._autorun.event; - } - - private static createTestOrSuiteInfos( - testNodes: readonly FileTreeNode[], - ): Array { - 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 { - 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 { - 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 { - 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, - })), - }); - } -} diff --git a/extensions/ql-vscode/src/query-testing/test-manager-base.ts b/extensions/ql-vscode/src/query-testing/test-manager-base.ts index 75d92d4f488..e7f7e18a3e4 100644 --- a/extensions/ql-vscode/src/query-testing/test-manager-base.ts +++ b/extensions/ql-vscode/src/query-testing/test-manager-base.ts @@ -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 diff --git a/extensions/ql-vscode/src/query-testing/test-tree-node.ts b/extensions/ql-vscode/src/query-testing/test-tree-node.ts deleted file mode 100644 index 0cd6a6d543a..00000000000 --- a/extensions/ql-vscode/src/query-testing/test-tree-node.ts +++ /dev/null @@ -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; -} diff --git a/extensions/ql-vscode/src/query-testing/test-ui.ts b/extensions/ql-vscode/src/query-testing/test-ui.ts deleted file mode 100644 index c45fc01a9d3..00000000000 --- a/extensions/ql-vscode/src/query-testing/test-ui.ts +++ /dev/null @@ -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 = 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; - } -} diff --git a/extensions/ql-vscode/test/vscode-tests/jest-runner-installed-extensions.ts b/extensions/ql-vscode/test/vscode-tests/jest-runner-installed-extensions.ts index 40bca3e6fb3..869ce06ba56 100644 --- a/extensions/ql-vscode/test/vscode-tests/jest-runner-installed-extensions.ts +++ b/extensions/ql-vscode/test/vscode-tests/jest-runner-installed-extensions.ts @@ -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); } From d8968db1b9464d8e275a8426ba357e121221d7d0 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 11 Jan 2024 11:34:35 -0500 Subject: [PATCH 2/4] Changelog entry --- extensions/ql-vscode/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 2631c77dd1a..1ce3a9f6d63 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -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 From 2ba23ceead55208e616587be12696bcc9a28be66 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 11 Jan 2024 11:48:54 -0500 Subject: [PATCH 3/4] Fold `TestManagerBase` and related functions into `TestManager` --- .../src/query-testing/test-adapter.ts | 35 ------- .../src/query-testing/test-manager-base.ts | 74 -------------- .../src/query-testing/test-manager.ts | 98 ++++++++++++++++++- 3 files changed, 93 insertions(+), 114 deletions(-) delete mode 100644 extensions/ql-vscode/src/query-testing/test-adapter.ts delete mode 100644 extensions/ql-vscode/src/query-testing/test-manager-base.ts diff --git a/extensions/ql-vscode/src/query-testing/test-adapter.ts b/extensions/ql-vscode/src/query-testing/test-adapter.ts deleted file mode 100644 index afb9b47b7e1..00000000000 --- a/extensions/ql-vscode/src/query-testing/test-adapter.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { 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. - */ -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); -} - -/** - * 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; -} diff --git a/extensions/ql-vscode/src/query-testing/test-manager-base.ts b/extensions/ql-vscode/src/query-testing/test-manager-base.ts deleted file mode 100644 index e7f7e18a3e4..00000000000 --- a/extensions/ql-vscode/src/query-testing/test-manager-base.ts +++ /dev/null @@ -1,74 +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"; - -type TestNode = 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 { - 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 { - 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); - } - } - } -} diff --git a/extensions/ql-vscode/src/query-testing/test-manager.ts b/extensions/ql-vscode/src/query-testing/test-manager.ts index 12381c6d98b..ff1a91d88e3 100644 --- a/extensions/ql-vscode/src/query-testing/test-manager.ts +++ b/extensions/ql-vscode/src/query-testing/test-manager.ts @@ -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 { + 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 { + 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[], From 1186026315dbfb37b0f57e9bb9fe8af00d9c6d1f Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 11 Jan 2024 11:56:51 -0500 Subject: [PATCH 4/4] Remove old unit tests --- .../query-testing/test-adapter.test.ts | 52 +------------------ 1 file changed, 1 insertion(+), 51 deletions(-) diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-adapter.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-adapter.test.ts index 038f0635357..e293bbf1e52 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-adapter.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-adapter.test.ts @@ -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({ - 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();