From a8a74c4d7773470e3d53cb89502b442579d5de84 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 3 Oct 2023 10:27:59 -0700 Subject: [PATCH] testProvider: fix continuous run handling (#917) Fixes #900 --- test-provider-sample/src/extension.ts | 49 ++++++++++---- .../vscode.proposed.testContinuousRun.d.ts | 66 ------------------- 2 files changed, 35 insertions(+), 80 deletions(-) delete mode 100644 test-provider-sample/vscode.proposed.testContinuousRun.d.ts diff --git a/test-provider-sample/src/extension.ts b/test-provider-sample/src/extension.ts index d0c2a292..2bf39f23 100644 --- a/test-provider-sample/src/extension.ts +++ b/test-provider-sample/src/extension.ts @@ -1,25 +1,45 @@ import * as vscode from 'vscode'; -import { getContentFromFilesystem, MarkdownTestData, TestCase, testData, TestFile } from './testTree'; +import { getContentFromFilesystem, TestCase, testData, TestFile } from './testTree'; export async function activate(context: vscode.ExtensionContext) { const ctrl = vscode.tests.createTestController('mathTestController', 'Markdown Math'); context.subscriptions.push(ctrl); const fileChangedEmitter = new vscode.EventEmitter(); - const runHandler = (request: vscode.TestRunRequest2, cancellation: vscode.CancellationToken) => { + const watchingTests = new Map(); + fileChangedEmitter.event(uri => { + if (watchingTests.has('ALL')) { + startTestRun(new vscode.TestRunRequest(undefined, undefined, watchingTests.get('ALL'), true)); + return; + } + + const include: vscode.TestItem[] = []; + let profile: vscode.TestRunProfile | undefined; + for (const [item, thisProfile] of watchingTests) { + const cast = item as vscode.TestItem; + if (cast.uri?.toString() == uri.toString()) { + include.push(cast); + profile = thisProfile; + } + } + + if (include.length) { + startTestRun(new vscode.TestRunRequest(include, undefined, profile, true)); + } + }); + + const runHandler = (request: vscode.TestRunRequest, cancellation: vscode.CancellationToken) => { if (!request.continuous) { return startTestRun(request); } - const l = fileChangedEmitter.event(uri => startTestRun( - new vscode.TestRunRequest2( - [getOrCreateFile(ctrl, uri).file], - undefined, - request.profile, - true - ), - )); - cancellation.onCancellationRequested(() => l.dispose()); + if (request.include === undefined) { + watchingTests.set('ALL', request.profile); + cancellation.onCancellationRequested(() => watchingTests.delete('ALL')); + } else { + request.include.forEach(item => watchingTests.set(item, request.profile)); + cancellation.onCancellationRequested(() => request.include!.forEach(item => watchingTests.delete(item))); + } }; const startTestRun = (request: vscode.TestRunRequest) => { @@ -74,8 +94,9 @@ export async function activate(context: vscode.ExtensionContext) { const lineNo = test.range!.start.line; const fileCoverage = coveredLines.get(test.uri!.toString()); - if (fileCoverage) { - fileCoverage[lineNo]!.executionCount++; + const lineInfo = fileCoverage?.[lineNo]; + if (lineInfo) { + lineInfo.executionCount++; } run.appendOutput(`Completed ${test.id}\r\n`); @@ -183,7 +204,7 @@ async function findInitialFiles(controller: vscode.TestController, pattern: vsco } } -function startWatchingWorkspace(controller: vscode.TestController, fileChangedEmitter: vscode.EventEmitter ) { +function startWatchingWorkspace(controller: vscode.TestController, fileChangedEmitter: vscode.EventEmitter) { return getWorkspaceTestPatterns().map(({ workspaceFolder, pattern }) => { const watcher = vscode.workspace.createFileSystemWatcher(pattern); diff --git a/test-provider-sample/vscode.proposed.testContinuousRun.d.ts b/test-provider-sample/vscode.proposed.testContinuousRun.d.ts deleted file mode 100644 index bb624013..00000000 --- a/test-provider-sample/vscode.proposed.testContinuousRun.d.ts +++ /dev/null @@ -1,66 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module 'vscode' { - export interface TestRunProfile { - /** - * Whether this profile supports continuous running of requests. If so, - * then {@link TestRunRequest.continuous} may be set to `true`. Defaults - * to false. - */ - supportsContinuousRun: boolean; - - /** - * Handler called to start a test run. When invoked, the function should call - * {@link TestController.createTestRun} at least once, and all test runs - * associated with the request should be created before the function returns - * or the returned promise is resolved. - * - * If {@link supportsContinuousRun} is set, then {@link TestRunRequest2.continuous} - * may be `true`. In this case, the profile should observe changes to - * source code and create new test runs by calling {@link TestController.createTestRun}, - * until the cancellation is requested on the `token`. - * - * @param request Request information for the test run. - * @param cancellationToken Token that signals the used asked to abort the - * test run. If cancellation is requested on this token, all {@link TestRun} - * instances associated with the request will be - * automatically cancelled as well. - */ - runHandler: (request: TestRunRequest, token: CancellationToken) => Thenable | void; - } - - export interface TestController { - /** - * Creates a profile used for running tests. Extensions must create - * at least one profile in order for tests to be run. - * @param label A human-readable label for this profile. - * @param kind Configures what kind of execution this profile manages. - * @param runHandler Function called to start a test run. - * @param isDefault Whether this is the default action for its kind. - * @param tag Profile test tag. - * @param supportsContinuousRun Whether the profile supports continuous running. - * @returns An instance of a {@link TestRunProfile}, which is automatically - * associated with this controller. - */ - createRunProfile(label: string, kind: TestRunProfileKind, runHandler: (request: TestRunRequest, token: CancellationToken) => Thenable | void, isDefault?: boolean, tag?: TestTag, supportsContinuousRun?: boolean): TestRunProfile; - } - - export class TestRunRequest2 extends TestRunRequest { - /** - * Whether the profile should run continuously as source code changes. Only - * relevant for profiles that set {@link TestRunProfile.supportsContinuousRun}. - */ - readonly continuous?: boolean; - - /** - * @param tests Array of specific tests to run, or undefined to run all tests - * @param exclude An array of tests to exclude from the run. - * @param profile The run profile used for this request. - * @param continuous Whether to run tests continuously as source changes. - */ - constructor(include?: readonly TestItem[], exclude?: readonly TestItem[], profile?: TestRunProfile, continuous?: boolean); - } -}