refactor: update to new testing API

Previously in the testing API, you called `registerTestProvider` with
your own instance of a TestController, and VS Code would request
workspace or document tests. This has been changed: now, you call
`createTestController`, which returns an object, and call
`createTestItem` to insert test nodes under the `controller.root`.

Extensions should generally decide themselves when to publish tests. For
example, when a file is opened in an editor, test extensions will want
to make sure tests for that file are available so that inline
decorations can be shown. This is pretty similar to what the editor
API does in diagnostics.

There is still a `resolveChildrenHandler` on the controller (rather than
the TestItem directly), which you should _set_ if the test extension
supports lazy discovery. Additionally, if you support running tests,
you'll also want a `runHandler` (migrating from the old `runTests` method).

Some of the existing test providers have been updated, you can check
them out here:

- https://github.com/microsoft/vscode-extension-samples/tree/main/test-provider-sample
- https://github.com/microsoft/vscode-selfhost-test-provider

In summary, to update to the new API:

- Call `vscode.test.createTestController` instead of `registerTestController`
- Move the contents of your `runTests` method to `controller.runHandler`
- Move your `TestItem.resolveHandler` to `controller.resolveChildrenHandler`,
  which may involve adding some `instanceof` checks.
- If you lazily discovered tests in `createDocumentTestRoot`, you'll want
  to trigger that logic based on `vscode.workspace.onDidOpenTextDocument`.
- If your test runner can deal with showing locations of unsaved changes,
  listen for `vscode.workspace.onDidChangeTextDocument` to trigger those
  changes in the tree.
This commit is contained in:
Connor Peet
2021-06-17 12:17:55 -07:00
parent 6446b3a8e5
commit 186e565ec0
35 changed files with 1092 additions and 2692 deletions

View File

@@ -5,17 +5,17 @@
import { VSBuffer } from 'vs/base/common/buffer';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { Disposable, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { isDefined } from 'vs/base/common/types';
import { URI, UriComponents } from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
import { Range } from 'vs/editor/common/core/range';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { TestResultState } from 'vs/workbench/api/common/extHostTypes';
import { ExtensionRunTestsRequest, getTestSubscriptionKey, ITestItem, ITestMessage, ITestRunTask, RunTestsRequest, TestDiffOpType, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
import { ExtensionRunTestsRequest, ITestItem, ITestMessage, ITestRunTask, RunTestsRequest, TestDiffOpType, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
import { LiveTestResult } from 'vs/workbench/contrib/testing/common/testResult';
import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService';
import { ITestRootProvider, ITestService } from 'vs/workbench/contrib/testing/common/testService';
import { ExtHostContext, ExtHostTestingResource, ExtHostTestingShape, IExtHostContext, MainContext, MainThreadTestingShape } from '../common/extHost.protocol';
import { ExtHostContext, ExtHostTestingShape, IExtHostContext, MainContext, MainThreadTestingShape } from '../common/extHost.protocol';
const reviveDiff = (diff: TestsDiff) => {
for (const entry of diff) {
@@ -34,6 +34,7 @@ const reviveDiff = (diff: TestsDiff) => {
@extHostNamedCustomer(MainContext.MainThreadTesting)
export class MainThreadTesting extends Disposable implements MainThreadTestingShape, ITestRootProvider {
private readonly proxy: ExtHostTestingShape;
private readonly diffListener = this._register(new MutableDisposable());
private readonly testSubscriptions = new Map<string, IDisposable>();
private readonly testProviderRegistrations = new Map<string, IDisposable>();
@@ -44,15 +45,13 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
) {
super();
this.proxy = extHostContext.getProxy(ExtHostContext.ExtHostTesting);
this._register(this.testService.onShouldSubscribe(args => this.proxy.$subscribeToTests(args.resource, args.uri)));
this._register(this.testService.onShouldUnsubscribe(args => this.proxy.$unsubscribeFromTests(args.resource, args.uri)));
const prevResults = resultService.results.map(r => r.toJSON()).filter(isDefined);
if (prevResults.length) {
this.proxy.$publishTestResults(prevResults);
}
this._register(this.testService.onCancelTestRun(({ runId }) => {
this._register(this.testService.onDidCancelTestRun(({ runId }) => {
this.proxy.$cancelExtensionTestRun(runId);
}));
@@ -63,18 +62,12 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
this.proxy.$publishTestResults([serialized]);
}
}));
this._register(testService.registerRootProvider(this));
for (const { resource, uri } of this.testService.subscriptions) {
this.proxy.$subscribeToTests(resource, uri);
}
}
/**
* @inheritdoc
*/
$addTestsToRun(runId: string, tests: ITestItem[]): void {
$addTestsToRun(controllerId: string, runId: string, tests: ITestItem[]): void {
for (const test of tests) {
test.uri = URI.revive(test.uri);
if (test.range) {
@@ -82,7 +75,7 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
}
}
this.withLiveRun(runId, r => r.addTestChainToRun(tests));
this.withLiveRun(runId, r => r.addTestChainToRun(controllerId, tests));
}
/**
@@ -146,14 +139,13 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
/**
* @inheritdoc
*/
public $registerTestController(id: string) {
const disposable = this.testService.registerTestController(id, {
runTests: (req, token) => this.proxy.$runTestsForProvider(req, token),
lookupTest: test => this.proxy.$lookupTest(test),
public $registerTestController(controllerId: string) {
const disposable = this.testService.registerTestController(controllerId, {
runTests: (req, token) => this.proxy.$runControllerTests(req, token),
expandTest: (src, levels) => this.proxy.$expandTest(src, isFinite(levels) ? levels : -1),
});
this.testProviderRegistrations.set(id, disposable);
this.testProviderRegistrations.set(controllerId, disposable);
}
/**
@@ -167,28 +159,24 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
/**
* @inheritdoc
*/
public $subscribeToDiffs(resource: ExtHostTestingResource, uriComponents: UriComponents): void {
const uri = URI.revive(uriComponents);
const disposable = this.testService.subscribeToDiffs(resource, uri,
diff => this.proxy.$acceptDiff(resource, uriComponents, diff));
this.testSubscriptions.set(getTestSubscriptionKey(resource, uri), disposable);
public $subscribeToDiffs(): void {
this.proxy.$acceptDiff(this.testService.collection.getReviverDiff());
this.diffListener.value = this.testService.onDidProcessDiff(this.proxy.$acceptDiff, this.proxy);
}
/**
* @inheritdoc
*/
public $unsubscribeFromDiffs(resource: ExtHostTestingResource, uriComponents: UriComponents): void {
const key = getTestSubscriptionKey(resource, URI.revive(uriComponents));
this.testSubscriptions.get(key)?.dispose();
this.testSubscriptions.delete(key);
public $unsubscribeFromDiffs(): void {
this.diffListener.clear();
}
/**
* @inheritdoc
*/
public $publishDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void {
public $publishDiff(controllerId: string, diff: TestsDiff): void {
reviveDiff(diff);
this.testService.publishDiff(resource, URI.revive(uri), diff);
this.testService.publishDiff(controllerId, diff);
}
public async $runTests(req: RunTestsRequest, token: CancellationToken): Promise<string> {