mirror of
https://github.com/Microsoft/vscode
synced 2024-08-25 03:56:03 +00:00
testing: implement proposed active profiles api (#197664)
testing: implement proposed active profiles api For https://github.com/microsoft/vscode/issues/193160
This commit is contained in:
parent
273862296b
commit
2749de3808
|
@ -52,6 +52,7 @@
|
||||||
"treeViewReveal",
|
"treeViewReveal",
|
||||||
"workspaceTrust",
|
"workspaceTrust",
|
||||||
"telemetry",
|
"telemetry",
|
||||||
|
"testingActiveProfile",
|
||||||
"windowActivity",
|
"windowActivity",
|
||||||
"interactiveUserActions"
|
"interactiveUserActions"
|
||||||
],
|
],
|
||||||
|
|
|
@ -540,8 +540,8 @@ export namespace Event {
|
||||||
export interface IChainableSythensis<T> {
|
export interface IChainableSythensis<T> {
|
||||||
map<O>(fn: (i: T) => O): IChainableSythensis<O>;
|
map<O>(fn: (i: T) => O): IChainableSythensis<O>;
|
||||||
forEach(fn: (i: T) => void): IChainableSythensis<T>;
|
forEach(fn: (i: T) => void): IChainableSythensis<T>;
|
||||||
|
filter<R extends T>(fn: (e: T) => e is R): IChainableSythensis<R>;
|
||||||
filter(fn: (e: T) => boolean): IChainableSythensis<T>;
|
filter(fn: (e: T) => boolean): IChainableSythensis<T>;
|
||||||
filter<R>(fn: (e: T | R) => e is R): IChainableSythensis<R>;
|
|
||||||
reduce<R>(merge: (last: R, event: T) => R, initial: R): IChainableSythensis<R>;
|
reduce<R>(merge: (last: R, event: T) => R, initial: R): IChainableSythensis<R>;
|
||||||
reduce<R>(merge: (last: R | undefined, event: T) => R): IChainableSythensis<R>;
|
reduce<R>(merge: (last: R | undefined, event: T) => R): IChainableSythensis<R>;
|
||||||
latch(equals?: (a: T, b: T) => boolean): IChainableSythensis<T>;
|
latch(equals?: (a: T, b: T) => boolean): IChainableSythensis<T>;
|
||||||
|
|
|
@ -5,12 +5,13 @@
|
||||||
|
|
||||||
import { VSBuffer } from 'vs/base/common/buffer';
|
import { VSBuffer } from 'vs/base/common/buffer';
|
||||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||||
|
import { Event } from 'vs/base/common/event';
|
||||||
import { Disposable, DisposableStore, IDisposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
import { Disposable, DisposableStore, IDisposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||||
import { revive } from 'vs/base/common/marshalling';
|
import { revive } from 'vs/base/common/marshalling';
|
||||||
import { URI } from 'vs/base/common/uri';
|
import { URI } from 'vs/base/common/uri';
|
||||||
import { Range } from 'vs/editor/common/core/range';
|
import { Range } from 'vs/editor/common/core/range';
|
||||||
import { MutableObservableValue } from 'vs/workbench/contrib/testing/common/observableValue';
|
import { MutableObservableValue } from 'vs/workbench/contrib/testing/common/observableValue';
|
||||||
import { ExtensionRunTestsRequest, IFileCoverage, ITestItem, ITestMessage, ITestRunProfile, ITestRunTask, ResolvedTestRunRequest, TestResultState, TestsDiffOp } from 'vs/workbench/contrib/testing/common/testTypes';
|
import { ExtensionRunTestsRequest, IFileCoverage, ITestItem, ITestMessage, ITestRunProfile, ITestRunTask, ResolvedTestRunRequest, TestResultState, TestRunProfileBitset, TestsDiffOp } from 'vs/workbench/contrib/testing/common/testTypes';
|
||||||
import { TestCoverage } from 'vs/workbench/contrib/testing/common/testCoverage';
|
import { TestCoverage } from 'vs/workbench/contrib/testing/common/testCoverage';
|
||||||
import { ITestProfileService } from 'vs/workbench/contrib/testing/common/testProfileService';
|
import { ITestProfileService } from 'vs/workbench/contrib/testing/common/testProfileService';
|
||||||
import { LiveTestResult } from 'vs/workbench/contrib/testing/common/testResult';
|
import { LiveTestResult } from 'vs/workbench/contrib/testing/common/testResult';
|
||||||
|
@ -45,6 +46,21 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
|
||||||
this.proxy.$cancelExtensionTestRun(runId);
|
this.proxy.$cancelExtensionTestRun(runId);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
this._register(Event.debounce(testProfiles.onDidChange, (_last, e) => e)(() => {
|
||||||
|
const defaults = new Set([
|
||||||
|
...testProfiles.getGroupDefaultProfiles(TestRunProfileBitset.Run),
|
||||||
|
...testProfiles.getGroupDefaultProfiles(TestRunProfileBitset.Debug),
|
||||||
|
...testProfiles.getGroupDefaultProfiles(TestRunProfileBitset.Coverage),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const obj: Record</* controller id */string, /* profile id */ number[]> = {};
|
||||||
|
for (const { controller, profiles } of this.testProfiles.all()) {
|
||||||
|
obj[controller.id] = profiles.filter(p => defaults.has(p)).map(p => p.profileId);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.proxy.$setActiveRunProfiles(obj);
|
||||||
|
}));
|
||||||
|
|
||||||
this._register(resultService.onResultsChanged(evt => {
|
this._register(resultService.onResultsChanged(evt => {
|
||||||
const results = 'completed' in evt ? evt.completed : ('inserted' in evt ? evt.inserted : undefined);
|
const results = 'completed' in evt ? evt.completed : ('inserted' in evt ? evt.inserted : undefined);
|
||||||
const serialized = results?.toJSONWithMessages();
|
const serialized = results?.toJSONWithMessages();
|
||||||
|
|
|
@ -2638,6 +2638,8 @@ export interface ExtHostTestingShape {
|
||||||
$refreshTests(controllerId: string, token: CancellationToken): Promise<void>;
|
$refreshTests(controllerId: string, token: CancellationToken): Promise<void>;
|
||||||
/** Ensures any pending test diffs are flushed */
|
/** Ensures any pending test diffs are flushed */
|
||||||
$syncTests(): Promise<void>;
|
$syncTests(): Promise<void>;
|
||||||
|
/** Sets the active test run profiles */
|
||||||
|
$setActiveRunProfiles(profiles: Record</* controller id */string, /* profile id */ number[]>): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExtHostLocalizationShape {
|
export interface ExtHostLocalizationShape {
|
||||||
|
|
|
@ -29,6 +29,7 @@ import { TestCommandId } from 'vs/workbench/contrib/testing/common/constants';
|
||||||
import { TestId, TestIdPathParts, TestPosition } from 'vs/workbench/contrib/testing/common/testId';
|
import { TestId, TestIdPathParts, TestPosition } from 'vs/workbench/contrib/testing/common/testId';
|
||||||
import { InvalidTestItemError } from 'vs/workbench/contrib/testing/common/testItemCollection';
|
import { InvalidTestItemError } from 'vs/workbench/contrib/testing/common/testItemCollection';
|
||||||
import { AbstractIncrementalTestCollection, CoverageDetails, ICallProfileRunHandler, IFileCoverage, ISerializedTestResults, IStartControllerTests, IStartControllerTestsResult, ITestErrorMessage, ITestItem, ITestItemContext, ITestMessageMenuArgs, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, TestResultState, TestRunProfileBitset, TestsDiff, TestsDiffOp, isStartControllerTests } from 'vs/workbench/contrib/testing/common/testTypes';
|
import { AbstractIncrementalTestCollection, CoverageDetails, ICallProfileRunHandler, IFileCoverage, ISerializedTestResults, IStartControllerTests, IStartControllerTestsResult, ITestErrorMessage, ITestItem, ITestItemContext, ITestMessageMenuArgs, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, TestResultState, TestRunProfileBitset, TestsDiff, TestsDiffOp, isStartControllerTests } from 'vs/workbench/contrib/testing/common/testTypes';
|
||||||
|
import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
|
||||||
import type * as vscode from 'vscode';
|
import type * as vscode from 'vscode';
|
||||||
|
|
||||||
interface ControllerInfo {
|
interface ControllerInfo {
|
||||||
|
@ -36,14 +37,18 @@ interface ControllerInfo {
|
||||||
profiles: Map<number, vscode.TestRunProfile>;
|
profiles: Map<number, vscode.TestRunProfile>;
|
||||||
collection: ExtHostTestItemCollection;
|
collection: ExtHostTestItemCollection;
|
||||||
extension: Readonly<IRelaxedExtensionDescription>;
|
extension: Readonly<IRelaxedExtensionDescription>;
|
||||||
|
activeProfiles: Set<number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ExtHostTesting implements ExtHostTestingShape {
|
type ActiveProfileChangeEvent = Map</* controllerId */ string, Map< /* profileId */number, boolean>>;
|
||||||
private readonly resultsChangedEmitter = new Emitter<void>();
|
|
||||||
private readonly controllers = new Map</* controller ID */ string, ControllerInfo>();
|
export class ExtHostTesting extends Disposable implements ExtHostTestingShape {
|
||||||
|
private readonly resultsChangedEmitter = this._register(new Emitter<void>());
|
||||||
|
protected readonly controllers = new Map</* controller ID */ string, ControllerInfo>();
|
||||||
private readonly proxy: MainThreadTestingShape;
|
private readonly proxy: MainThreadTestingShape;
|
||||||
private readonly runTracker: TestRunCoordinator;
|
private readonly runTracker: TestRunCoordinator;
|
||||||
private readonly observer: TestObservers;
|
private readonly observer: TestObservers;
|
||||||
|
private readonly activeProfilesChangedEmitter = this._register(new Emitter<ActiveProfileChangeEvent>());
|
||||||
|
|
||||||
public onResultsChanged = this.resultsChangedEmitter.event;
|
public onResultsChanged = this.resultsChangedEmitter.event;
|
||||||
public results: ReadonlyArray<vscode.TestRunResult> = [];
|
public results: ReadonlyArray<vscode.TestRunResult> = [];
|
||||||
|
@ -53,6 +58,7 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||||
commands: ExtHostCommands,
|
commands: ExtHostCommands,
|
||||||
private readonly editors: ExtHostDocumentsAndEditors,
|
private readonly editors: ExtHostDocumentsAndEditors,
|
||||||
) {
|
) {
|
||||||
|
super();
|
||||||
this.proxy = rpc.getProxy(MainContext.MainThreadTesting);
|
this.proxy = rpc.getProxy(MainContext.MainThreadTesting);
|
||||||
this.observer = new TestObservers(this.proxy);
|
this.observer = new TestObservers(this.proxy);
|
||||||
this.runTracker = new TestRunCoordinator(this.proxy);
|
this.runTracker = new TestRunCoordinator(this.proxy);
|
||||||
|
@ -110,6 +116,7 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||||
collection.root.label = label;
|
collection.root.label = label;
|
||||||
|
|
||||||
const profiles = new Map<number, vscode.TestRunProfile>();
|
const profiles = new Map<number, vscode.TestRunProfile>();
|
||||||
|
const activeProfiles = new Set<number>();
|
||||||
const proxy = this.proxy;
|
const proxy = this.proxy;
|
||||||
|
|
||||||
const controller: vscode.TestController = {
|
const controller: vscode.TestController = {
|
||||||
|
@ -140,7 +147,7 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||||
profileId++;
|
profileId++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TestRunProfileImpl(this.proxy, profiles, controllerId, profileId, label, group, runHandler, isDefault, tag, supportsContinuousRun);
|
return new TestRunProfileImpl(this.proxy, profiles, extension, activeProfiles, this.activeProfilesChangedEmitter.event, controllerId, profileId, label, group, runHandler, isDefault, tag, supportsContinuousRun);
|
||||||
},
|
},
|
||||||
createTestItem(id, label, uri) {
|
createTestItem(id, label, uri) {
|
||||||
return new TestItemImpl(controllerId, id, label, uri);
|
return new TestItemImpl(controllerId, id, label, uri);
|
||||||
|
@ -170,7 +177,7 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||||
proxy.$registerTestController(controllerId, label, !!refreshHandler);
|
proxy.$registerTestController(controllerId, label, !!refreshHandler);
|
||||||
disposable.add(toDisposable(() => proxy.$unregisterTestController(controllerId)));
|
disposable.add(toDisposable(() => proxy.$unregisterTestController(controllerId)));
|
||||||
|
|
||||||
const info: ControllerInfo = { controller, collection, profiles: profiles, extension };
|
const info: ControllerInfo = { controller, collection, profiles, extension, activeProfiles };
|
||||||
this.controllers.set(controllerId, info);
|
this.controllers.set(controllerId, info);
|
||||||
disposable.add(toDisposable(() => this.controllers.delete(controllerId)));
|
disposable.add(toDisposable(() => this.controllers.delete(controllerId)));
|
||||||
|
|
||||||
|
@ -245,6 +252,33 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||||
this.controllers.get(controllerId)?.profiles.get(profileId)?.configureHandler?.();
|
this.controllers.get(controllerId)?.profiles.get(profileId)?.configureHandler?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
$setActiveRunProfiles(profiles: Record</* controller id */string, /* profile id */ number[]>): void {
|
||||||
|
const evt: ActiveProfileChangeEvent = new Map();
|
||||||
|
for (const [controllerId, profileIds] of Object.entries(profiles)) {
|
||||||
|
const ctrl = this.controllers.get(controllerId);
|
||||||
|
if (!ctrl) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const changes = new Map<number, boolean>();
|
||||||
|
const added = profileIds.filter(id => !ctrl.activeProfiles.has(id));
|
||||||
|
const removed = [...ctrl.activeProfiles].filter(id => !profileIds.includes(id));
|
||||||
|
for (const id of added) {
|
||||||
|
changes.set(id, true);
|
||||||
|
ctrl.activeProfiles.add(id);
|
||||||
|
}
|
||||||
|
for (const id of removed) {
|
||||||
|
changes.set(id, false);
|
||||||
|
ctrl.activeProfiles.delete(id);
|
||||||
|
}
|
||||||
|
if (changes.size) {
|
||||||
|
evt.set(controllerId, changes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.activeProfilesChangedEmitter.fire(evt);
|
||||||
|
}
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
async $refreshTests(controllerId: string, token: CancellationToken) {
|
async $refreshTests(controllerId: string, token: CancellationToken) {
|
||||||
await this.controllers.get(controllerId)?.controller.refreshHandler?.(token);
|
await this.controllers.get(controllerId)?.controller.refreshHandler?.(token);
|
||||||
|
@ -1005,6 +1039,9 @@ class TestObservers {
|
||||||
|
|
||||||
export class TestRunProfileImpl implements vscode.TestRunProfile {
|
export class TestRunProfileImpl implements vscode.TestRunProfile {
|
||||||
readonly #proxy: MainThreadTestingShape;
|
readonly #proxy: MainThreadTestingShape;
|
||||||
|
readonly #extension: IRelaxedExtensionDescription;
|
||||||
|
readonly #activeProfiles: ReadonlySet<number>;
|
||||||
|
readonly #onDidChangeActiveProfiles: Event<ActiveProfileChangeEvent>;
|
||||||
#profiles?: Map<number, vscode.TestRunProfile>;
|
#profiles?: Map<number, vscode.TestRunProfile>;
|
||||||
private _configureHandler?: (() => void);
|
private _configureHandler?: (() => void);
|
||||||
|
|
||||||
|
@ -1065,9 +1102,25 @@ export class TestRunProfileImpl implements vscode.TestRunProfile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get isSelected() {
|
||||||
|
checkProposedApiEnabled(this.#extension, 'testingActiveProfile');
|
||||||
|
return this.#activeProfiles.has(this.profileId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get onDidChangeSelected() {
|
||||||
|
checkProposedApiEnabled(this.#extension, 'testingActiveProfile');
|
||||||
|
return Event.chain(this.#onDidChangeActiveProfiles, $ => $
|
||||||
|
.map(ev => ev.get(this.controllerId)?.get(this.profileId))
|
||||||
|
.filter(isDefined)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
proxy: MainThreadTestingShape,
|
proxy: MainThreadTestingShape,
|
||||||
profiles: Map<number, vscode.TestRunProfile>,
|
profiles: Map<number, vscode.TestRunProfile>,
|
||||||
|
extension: IRelaxedExtensionDescription,
|
||||||
|
activeProfiles: ReadonlySet<number>,
|
||||||
|
onDidChangeActiveProfiles: Event<ActiveProfileChangeEvent>,
|
||||||
public readonly controllerId: string,
|
public readonly controllerId: string,
|
||||||
public readonly profileId: number,
|
public readonly profileId: number,
|
||||||
private _label: string,
|
private _label: string,
|
||||||
|
@ -1079,6 +1132,9 @@ export class TestRunProfileImpl implements vscode.TestRunProfile {
|
||||||
) {
|
) {
|
||||||
this.#proxy = proxy;
|
this.#proxy = proxy;
|
||||||
this.#profiles = profiles;
|
this.#profiles = profiles;
|
||||||
|
this.#extension = extension;
|
||||||
|
this.#activeProfiles = activeProfiles;
|
||||||
|
this.#onDidChangeActiveProfiles = onDidChangeActiveProfiles;
|
||||||
profiles.set(profileId, this);
|
profiles.set(profileId, this);
|
||||||
|
|
||||||
const groupBitset = profileGroupToBitset[kind];
|
const groupBitset = profileGroupToBitset[kind];
|
||||||
|
|
|
@ -7,21 +7,27 @@ import * as assert from 'assert';
|
||||||
import * as sinon from 'sinon';
|
import * as sinon from 'sinon';
|
||||||
import { VSBuffer } from 'vs/base/common/buffer';
|
import { VSBuffer } from 'vs/base/common/buffer';
|
||||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||||
|
import { Event } from 'vs/base/common/event';
|
||||||
import { Iterable } from 'vs/base/common/iterator';
|
import { Iterable } from 'vs/base/common/iterator';
|
||||||
import { URI } from 'vs/base/common/uri';
|
import { URI } from 'vs/base/common/uri';
|
||||||
import { mockObject, MockObject } from 'vs/base/test/common/mock';
|
import { mock, mockObject, MockObject } from 'vs/base/test/common/mock';
|
||||||
import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils';
|
import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils';
|
||||||
import * as editorRange from 'vs/editor/common/core/range';
|
import * as editorRange from 'vs/editor/common/core/range';
|
||||||
import { IRelaxedExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
import { ExtensionIdentifier, IRelaxedExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||||
|
import { NullLogService } from 'vs/platform/log/common/log';
|
||||||
import { MainThreadTestingShape } from 'vs/workbench/api/common/extHost.protocol';
|
import { MainThreadTestingShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||||
|
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||||
import { TestRunCoordinator, TestRunDto, TestRunProfileImpl } from 'vs/workbench/api/common/extHostTesting';
|
import { IExtHostTelemetry } from 'vs/workbench/api/common/extHostTelemetry';
|
||||||
|
import { ExtHostTesting, TestRunCoordinator, TestRunDto, TestRunProfileImpl } from 'vs/workbench/api/common/extHostTesting';
|
||||||
import { ExtHostTestItemCollection, TestItemImpl } from 'vs/workbench/api/common/extHostTestItem';
|
import { ExtHostTestItemCollection, TestItemImpl } from 'vs/workbench/api/common/extHostTestItem';
|
||||||
import * as convert from 'vs/workbench/api/common/extHostTypeConverters';
|
import * as convert from 'vs/workbench/api/common/extHostTypeConverters';
|
||||||
import { Location, Position, Range, TestMessage, TestResultState, TestRunProfileKind, TestRunRequest as TestRunRequestImpl, TestTag } from 'vs/workbench/api/common/extHostTypes';
|
import { Location, Position, Range, TestMessage, TestResultState, TestRunProfileKind, TestRunRequest as TestRunRequestImpl, TestTag } from 'vs/workbench/api/common/extHostTypes';
|
||||||
|
import { AnyCallRPCProtocol } from 'vs/workbench/api/test/common/testRPCProtocol';
|
||||||
import { TestId } from 'vs/workbench/contrib/testing/common/testId';
|
import { TestId } from 'vs/workbench/contrib/testing/common/testId';
|
||||||
import { TestDiffOpType, TestItemExpandState, TestMessageType, TestsDiff } from 'vs/workbench/contrib/testing/common/testTypes';
|
import { TestDiffOpType, TestItemExpandState, TestMessageType, TestsDiff } from 'vs/workbench/contrib/testing/common/testTypes';
|
||||||
import type { TestItem, TestRunRequest } from 'vscode';
|
import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
|
||||||
|
import type { TestController, TestItem, TestRunProfile, TestRunRequest } from 'vscode';
|
||||||
|
|
||||||
const simplify = (item: TestItem) => ({
|
const simplify = (item: TestItem) => ({
|
||||||
id: item.id,
|
id: item.id,
|
||||||
|
@ -604,7 +610,7 @@ suite('ExtHost Testing', () => {
|
||||||
cts = new CancellationTokenSource();
|
cts = new CancellationTokenSource();
|
||||||
c = new TestRunCoordinator(proxy);
|
c = new TestRunCoordinator(proxy);
|
||||||
|
|
||||||
configuration = new TestRunProfileImpl(mockObject<MainThreadTestingShape>()(), new Map(), 'ctrlId', 42, 'Do Run', TestRunProfileKind.Run, () => { }, false);
|
configuration = new TestRunProfileImpl(mockObject<MainThreadTestingShape>()(), new Map(), nullExtensionDescription, new Set(), Event.None, 'ctrlId', 42, 'Do Run', TestRunProfileKind.Run, () => { }, false);
|
||||||
|
|
||||||
await single.expand(single.root.id, Infinity);
|
await single.expand(single.root.id, Infinity);
|
||||||
single.collectDiff();
|
single.collectDiff();
|
||||||
|
@ -867,4 +873,82 @@ suite('ExtHost Testing', () => {
|
||||||
task1.end();
|
task1.end();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
suite('service', () => {
|
||||||
|
let ctrl: TestExtHostTesting;
|
||||||
|
|
||||||
|
class TestExtHostTesting extends ExtHostTesting {
|
||||||
|
public getProfileInternalId(ctrl: TestController, profile: TestRunProfile) {
|
||||||
|
for (const [id, p] of this.controllers.get(ctrl.id)!.profiles) {
|
||||||
|
if (profile === p) {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('profile not found');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setup(() => {
|
||||||
|
const rpcProtocol = AnyCallRPCProtocol();
|
||||||
|
ctrl = ds.add(new TestExtHostTesting(
|
||||||
|
rpcProtocol,
|
||||||
|
new ExtHostCommands(rpcProtocol, new NullLogService(), new class extends mock<IExtHostTelemetry>() {
|
||||||
|
override onExtensionError(): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
new ExtHostDocumentsAndEditors(rpcProtocol, new NullLogService()),
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('exposes active profiles correctly', async () => {
|
||||||
|
const extA = { ...nullExtensionDescription, identifier: new ExtensionIdentifier('ext.a'), enabledApiProposals: ['testingActiveProfile'] };
|
||||||
|
const extB = { ...nullExtensionDescription, identifier: new ExtensionIdentifier('ext.b'), enabledApiProposals: ['testingActiveProfile'] };
|
||||||
|
|
||||||
|
const ctrlA = ds.add(ctrl.createTestController(extA, 'a', 'ctrla'));
|
||||||
|
const profAA = ds.add(ctrlA.createRunProfile('aa', TestRunProfileKind.Run, () => { }));
|
||||||
|
const profAB = ds.add(ctrlA.createRunProfile('ab', TestRunProfileKind.Run, () => { }));
|
||||||
|
|
||||||
|
const ctrlB = ds.add(ctrl.createTestController(extB, 'b', 'ctrlb'));
|
||||||
|
const profBA = ds.add(ctrlB.createRunProfile('ba', TestRunProfileKind.Run, () => { }));
|
||||||
|
const profBB = ds.add(ctrlB.createRunProfile('bb', TestRunProfileKind.Run, () => { }));
|
||||||
|
const neverCalled = sinon.stub();
|
||||||
|
|
||||||
|
// empty default state:
|
||||||
|
assert.deepStrictEqual(profAA.isSelected, false);
|
||||||
|
assert.deepStrictEqual(profBA.isSelected, false);
|
||||||
|
assert.deepStrictEqual(profBB.isSelected, false);
|
||||||
|
|
||||||
|
// fires a change event:
|
||||||
|
const changeA = Event.toPromise(profAA.onDidChangeSelected as Event<boolean>);
|
||||||
|
const changeBA = Event.toPromise(profBA.onDidChangeSelected as Event<boolean>);
|
||||||
|
const changeBB = Event.toPromise(profBB.onDidChangeSelected as Event<boolean>);
|
||||||
|
|
||||||
|
ds.add(profAB.onDidChangeSelected(neverCalled));
|
||||||
|
assert.strictEqual(neverCalled.called, false);
|
||||||
|
|
||||||
|
ctrl.$setActiveRunProfiles({
|
||||||
|
a: [ctrl.getProfileInternalId(ctrlA, profAA)],
|
||||||
|
b: [ctrl.getProfileInternalId(ctrlB, profBA), ctrl.getProfileInternalId(ctrlB, profBB)]
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepStrictEqual(await changeA, true);
|
||||||
|
assert.deepStrictEqual(await changeBA, true);
|
||||||
|
assert.deepStrictEqual(await changeBB, true);
|
||||||
|
|
||||||
|
// updates internal state:
|
||||||
|
assert.deepStrictEqual(profAA.isSelected, true);
|
||||||
|
assert.deepStrictEqual(profBA.isSelected, true);
|
||||||
|
assert.deepStrictEqual(profBB.isSelected, true);
|
||||||
|
assert.deepStrictEqual(profAB.isSelected, false);
|
||||||
|
|
||||||
|
// no-ops if equal
|
||||||
|
ds.add(profAA.onDidChangeSelected(neverCalled));
|
||||||
|
ctrl.$setActiveRunProfiles({
|
||||||
|
a: [ctrl.getProfileInternalId(ctrlA, profAA)],
|
||||||
|
});
|
||||||
|
assert.strictEqual(neverCalled.called, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -28,6 +28,18 @@ export function SingleProxyRPCProtocol(thing: any): IExtHostContext & IExtHostRp
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Makes a fake {@link SingleProxyRPCProtocol} on which any method can be called */
|
||||||
|
export function AnyCallRPCProtocol<T>(useCalls?: { [K in keyof T]: T[K] }) {
|
||||||
|
return SingleProxyRPCProtocol(new Proxy({}, {
|
||||||
|
get(_target, prop: string) {
|
||||||
|
if (useCalls && prop in useCalls) {
|
||||||
|
return (useCalls as any)[prop];
|
||||||
|
}
|
||||||
|
return () => Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
export class TestRPCProtocol implements IExtHostContext, IExtHostRpcService {
|
export class TestRPCProtocol implements IExtHostContext, IExtHostRpcService {
|
||||||
|
|
||||||
public _serviceBrand: undefined;
|
public _serviceBrand: undefined;
|
||||||
|
|
|
@ -97,6 +97,7 @@ export const allApiProposals = Object.freeze({
|
||||||
terminalSelection: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalSelection.d.ts',
|
terminalSelection: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalSelection.d.ts',
|
||||||
testCoverage: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.testCoverage.d.ts',
|
testCoverage: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.testCoverage.d.ts',
|
||||||
testObserver: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.testObserver.d.ts',
|
testObserver: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.testObserver.d.ts',
|
||||||
|
testingActiveProfile: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.testingActiveProfile.d.ts',
|
||||||
textSearchProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.textSearchProvider.d.ts',
|
textSearchProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.textSearchProvider.d.ts',
|
||||||
timeline: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.timeline.d.ts',
|
timeline: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.timeline.d.ts',
|
||||||
tokenInformation: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.tokenInformation.d.ts',
|
tokenInformation: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.tokenInformation.d.ts',
|
||||||
|
|
21
src/vscode-dts/vscode.proposed.testingActiveProfile.d.ts
vendored
Normal file
21
src/vscode-dts/vscode.proposed.testingActiveProfile.d.ts
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
// https://github.com/microsoft/vscode/issues/193160 @connor4312
|
||||||
|
|
||||||
|
declare module 'vscode' {
|
||||||
|
export interface TestRunProfile {
|
||||||
|
/**
|
||||||
|
* Whether this profile is currently selected as a default by the user
|
||||||
|
*/
|
||||||
|
readonly isSelected: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when a user has changed whether this is a selected profile. The
|
||||||
|
* event contains the new value of {@link isSelected}
|
||||||
|
*/
|
||||||
|
onDidChangeSelected: Event<boolean>;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue