未验证 提交 a0e24147 编写于 作者: C Connor Peet

testing: remove duplicate extension and internal IDs

Fixes #117084
上级 513c68ac
......@@ -1048,85 +1048,3 @@ export class LRUCache<K, V> extends LinkedMap<K, V> {
}
}
}
type IndexRecord<V, R> = [fn: (value: V) => R, map: Map<R, V>];
/**
* Map that supports multiple indicies whose keys are derived from the value.
*/
export class IndexedSet<V> implements Set<V> {
public get size() {
return this.source.size;
}
private source = new Set<V>();
private readonly indexes: IndexRecord<V, unknown>[] = [];
/**
* Creates a map that maintains a copy of the items in the set, indexed
* by the given 'indexer' function.
*/
index<R>(indexer: (value: V) => R) {
const map = new Map<R, V>();
for (const value of this.source) {
map.set(indexer(value), value);
}
this.indexes.push([indexer, map]);
return map as ReadonlyMap<R, V>;
}
add(value: V): this {
this.source.add(value);
for (const [index, map] of this.indexes) {
map.set(index(value), value);
}
return this;
}
clear(): void {
this.source.clear();
for (const [, map] of this.indexes) {
map.clear();
}
}
delete(value: V): boolean {
if (!this.source.delete(value)) {
return false;
}
for (const [index, map] of this.indexes) {
map.delete(index(value));
}
return true;
}
forEach(callbackfn: (value: V, value2: V, set: Set<V>) => void, thisArg?: any): void {
this.source.forEach(callbackfn, thisArg);
}
has(value: V): boolean {
return this.source.has(value);
}
[Symbol.toStringTag]: string;
values(): IterableIterator<V> {
return this.source.values();
}
entries(): IterableIterator<[V, V]> {
return this.source.entries();
}
keys(): IterableIterator<V> {
return this.source.keys();
}
[Symbol.iterator](): IterableIterator<V> {
return this.source.values();
}
}
......@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { ConfigKeysIterator, IndexedSet, LinkedMap, LRUCache, PathIterator, ResourceMap, StringIterator, TernarySearchTree, Touch, UriIterator } from 'vs/base/common/map';
import { ConfigKeysIterator, LinkedMap, LRUCache, PathIterator, ResourceMap, StringIterator, TernarySearchTree, Touch, UriIterator } from 'vs/base/common/map';
import { extUriIgnorePathCase } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
......@@ -1020,30 +1020,4 @@ suite('Map', () => {
assert.strictEqual(map.get(windowsFile), 'true');
assert.strictEqual(map.get(uncFile), 'true');
});
test('IndexedSet - add', () => {
const i = new IndexedSet<number>();
i.add(2);
const map = i.index(v => v ** 2);
assert.deepStrictEqual(map, new Map([[4, 2]]));
i.add(3);
assert.deepStrictEqual(map, new Map([[9, 3], [4, 2]]));
});
test('IndexedSet - delete', () => {
const i = new IndexedSet<number>();
const map = i.index(v => v ** 2);
i.add(2);
i.delete(2);
assert.deepStrictEqual(map, new Map([]));
});
test('IndexedSet - clear', () => {
const i = new IndexedSet<number>();
const map = i.index(v => v ** 2);
i.add(2);
i.clear();
assert.deepStrictEqual(map, new Map([]));
});
});
......@@ -2426,19 +2426,16 @@ declare module 'vscode' {
*/
export interface TestItem {
/**
* Display name describing the test case.
* Unique identifier for the TestItem. This is used to correlate
* test results and tests in the document with those in the workspace
* (test explorer). This must not change for the lifetime of a test item.
*/
label: string;
readonly id: string;
/**
* Optional unique identifier for the TestItem. This is used to correlate
* test results and tests in the document with those in the workspace
* (test explorer). This must not change for the lifetime of a test item.
*
* If the ID is not provided, it defaults to the concatenation of the
* item's label and its parent's ID, if any.
* Display name describing the test case.
*/
readonly id?: string;
label: string;
/**
* Optional description that appears next to the label.
......@@ -2589,12 +2586,6 @@ declare module 'vscode' {
* provided in {@link TestResult} interfaces.
*/
export interface TestItemWithResults extends TestItem {
/**
* ID of the test result, this is required in order to correlate the result
* with the live test item.
*/
readonly id: string;
/**
* Current result of the test.
*/
......
......@@ -96,7 +96,7 @@ export class ExtHostTesting implements ExtHostTestingShape {
// the workspace because it's less ephemeral.
.map(this.getInternalTestForReference, this)
.filter(isDefined)
.map(item => ({ providerId: item.providerId, testId: item.id })),
.map(t => ({ providerId: t.providerId, testId: t.item.extId })),
debug: req.debug
}, token);
}
......@@ -256,7 +256,7 @@ export class ExtHostTesting implements ExtHostTestingShape {
const internal = this.getInternalTestForReference(test);
if (internal) {
this.flushCollectionDiffs();
this.proxy.$updateTestStateInRun(req.runId, internal.id, TestState.from(state));
this.proxy.$updateTestStateInRun(req.runId, internal.item.extId, TestState.from(state));
}
}, tests, debug: req.debug
}, cancellation);
......@@ -389,6 +389,10 @@ export class TestItemFilteredWrapper implements vscode.TestItem {
return w;
}
public get id() {
return this.actual.id;
}
public get label() {
return this.actual.label;
}
......@@ -506,7 +510,7 @@ class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollect
this.updated.delete(node);
if (node.parent && this.alreadyRemoved.has(node.parent)) {
this.alreadyRemoved.add(node.id);
this.alreadyRemoved.add(node.item.extId);
return;
}
......@@ -646,8 +650,7 @@ export class MirroredTestCollection extends AbstractIncrementalTestCollection<Mi
* If the test item is a mirrored test item, returns its underlying ID.
*/
public getMirroredTestDataByReference(item: vscode.TestItem) {
const id = getMirroredItemId(item);
return id ? this.items.get(id) : undefined;
return this.items.get(item.id);
}
/**
......@@ -676,12 +679,6 @@ export class MirroredTestCollection extends AbstractIncrementalTestCollection<Mi
}
}
const getMirroredItemId = (item: vscode.TestItem) => {
return (item as any)[MirroredItemId] as string | undefined;
};
const MirroredItemId = Symbol('MirroredItemId');
class TestItemFromMirror implements vscode.RequiredTestItem {
readonly #internal: MirroredCollectionTestItem;
readonly #collection: MirroredTestCollection;
......@@ -696,8 +693,6 @@ class TestItemFromMirror implements vscode.RequiredTestItem {
return this.#collection.getAllAsTestItem(this.#internal.children);
}
get [MirroredItemId]() { return this.#internal.id; }
constructor(internal: MirroredCollectionTestItem, collection: MirroredTestCollection) {
this.#internal = internal;
this.#collection = collection;
......@@ -714,7 +709,7 @@ class TestItemFromMirror implements vscode.RequiredTestItem {
children: this.children.map(c => (c as TestItemFromMirror).toJSON()),
providerId: this.#internal.providerId,
testId: this.#internal.id,
testId: this.id,
};
return serialized;
......
......@@ -1625,9 +1625,9 @@ export namespace TestState {
}
export namespace TestItem {
export function from(item: vscode.TestItem, parentExtId?: string): ITestItem {
export function from(item: vscode.TestItem): ITestItem {
return {
extId: item.id ?? (parentExtId ? `${parentExtId}\0${item.label}` : item.label),
extId: item.id,
label: item.label,
location: item.location ? location.from(item.location) as any : undefined,
debuggable: item.debuggable ?? false,
......@@ -1663,18 +1663,16 @@ export namespace TestResults {
[null, results.results],
];
let counter = 0;
while (queue.length) {
const [parent, children] = queue.pop()!;
for (const item of children) {
const serializedItem: SerializedTestResultItem = {
children: item.children?.map(c => c.id) ?? [],
computedState: item.result.state,
id: `${id}-${counter++}`,
item: TestItem.from(item, parent?.item.extId),
item: TestItem.from(item),
state: TestState.from(item.result),
retired: undefined,
parent: parent?.id ?? null,
parent: parent?.item.extId ?? null,
providerId: '',
direct: !parent,
};
......@@ -1702,7 +1700,7 @@ export namespace TestResults {
const roots: SerializedTestResultItem[] = [];
const byInternalId = new Map<string, SerializedTestResultItem>();
for (const item of serialized.items) {
byInternalId.set(item.id, item);
byInternalId.set(item.item.extId, item);
if (item.direct) {
roots.push(item);
}
......
......@@ -7,7 +7,6 @@ import { ObjectTree } from 'vs/base/browser/ui/tree/objectTree';
import { Emitter } from 'vs/base/common/event';
import { FuzzyScore } from 'vs/base/common/filters';
import { Disposable } from 'vs/base/common/lifecycle';
import { IndexedSet } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';
import { Position } from 'vs/editor/common/core/position';
import { IWorkspaceFolder, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace';
......@@ -40,13 +39,11 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes
private readonly updateEmitter = new Emitter<void>();
private readonly changes = new NodeChangeList<HierarchicalElement | HierarchicalFolder>();
private readonly locations = new TestLocationStore<HierarchicalElement>();
private readonly itemSource = new IndexedSet<HierarchicalElement>();
public readonly itemsByExtId = this.itemSource.index(v => v.test.item.extId);
/**
* Map of item IDs to test item objects.
* Map of test IDs to test item objects.
*/
protected readonly items = this.itemSource.index(v => v.test.id);
protected readonly items = new Map<string, HierarchicalElement>();
/**
* Root folders
......@@ -70,7 +67,7 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes
}
for (const inTree of [...this.items.values()].sort((a, b) => b.depth - a.depth)) {
const lookup = this.results.getStateByExtId(inTree.test.item.extId)?.[1];
const lookup = this.results.getStateById(inTree.test.item.extId)?.[1];
inTree.ownState = lookup?.state.state ?? TestRunState.Unset;
const computed = lookup?.computedState ?? TestRunState.Unset;
if (computed !== inTree.state) {
......@@ -85,7 +82,7 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes
// when test states change, reflect in the tree
// todo: optimize this to avoid needing to iterate
this._register(results.onTestChanged(({ item: result }) => {
const item = this.itemsByExtId.get(result.item.extId);
const item = this.items.get(result.item.extId);
if (item) {
item.ownState = result.state.state;
item.retired = result.retired;
......@@ -106,6 +103,13 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes
}
}
/**
* @inheritdoc
*/
public getElementByTestId(testId: string): ITestTreeElement | undefined {
return this.items.get(testId);
}
private applyFolderChange(evt: IWorkspaceFoldersChangeEvent) {
for (const folder of evt.removed) {
const existing = this.folders.get(folder.uri.toString());
......@@ -145,15 +149,15 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes
}
case TestDiffOpType.Update: {
const item = op[1];
const existing = this.items.get(item.id);
const internalTest = op[1];
const existing = this.items.get(internalTest.item.extId);
if (!existing) {
break;
}
const locationChanged = !locationsEqual(existing.location, item.item.location);
const locationChanged = !locationsEqual(existing.location, internalTest.item.location);
if (locationChanged) { this.locations.remove(existing); }
existing.update(item);
existing.update(internalTest);
if (locationChanged) { this.locations.add(existing); }
this.addUpdated(existing);
break;
......@@ -218,23 +222,23 @@ export class HierarchicalByLocationProjection extends Disposable implements ITes
return { element: node, incompressible: true, children: recurse(node.children) };
};
protected unstoreItem(item: HierarchicalElement) {
item.parentItem.children.delete(item);
this.itemSource.delete(item);
this.locations.remove(item);
return item.children;
protected unstoreItem(treeElement: HierarchicalElement) {
treeElement.parentItem.children.delete(treeElement);
this.items.delete(treeElement.test.item.extId);
this.locations.remove(treeElement);
return treeElement.children;
}
protected storeItem(item: HierarchicalElement) {
item.parentItem.children.add(item);
this.itemSource.add(item);
this.locations.add(item);
protected storeItem(treeElement: HierarchicalElement) {
treeElement.parentItem.children.add(treeElement);
this.items.set(treeElement.test.item.extId, treeElement);
this.locations.add(treeElement);
const prevState = this.results.getStateByExtId(item.test.item.extId)?.[1];
const prevState = this.results.getStateById(treeElement.test.item.extId)?.[1];
if (prevState) {
item.ownState = prevState.state.state;
item.retired = prevState.retired;
refreshComputedState(computedStateAccessor, item, this.addUpdated, prevState.computedState);
treeElement.ownState = prevState.state.state;
treeElement.retired = prevState.retired;
refreshComputedState(computedStateAccessor, treeElement, this.addUpdated, prevState.computedState);
}
}
}
......@@ -45,7 +45,7 @@ export class HierarchicalByNameElement extends HierarchicalElement {
}
public get testId() {
return `hintest:${this.test.id}`;
return `hintest:${this.test.item.extId}`;
}
/**
......
......@@ -17,7 +17,7 @@ export class HierarchicalElement implements ITestTreeElement {
public readonly depth: number = this.parentItem.depth + 1;
public get treeId() {
return `hitest:${this.test.id}`;
return `hitest:${this.test.item.extId}`;
}
public get label() {
......@@ -30,13 +30,13 @@ export class HierarchicalElement implements ITestTreeElement {
public get runnable(): Iterable<TestIdWithProvider> {
return this.test.item.runnable
? [{ providerId: this.test.providerId, testId: this.test.id }]
? [{ providerId: this.test.providerId, testId: this.test.item.extId }]
: Iterable.empty();
}
public get debuggable() {
return this.test.item.debuggable
? [{ providerId: this.test.providerId, testId: this.test.id }]
? [{ providerId: this.test.providerId, testId: this.test.item.extId }]
: Iterable.empty();
}
......
......@@ -27,9 +27,9 @@ export interface ITestTreeProjection extends IDisposable {
onUpdate: Event<void>;
/**
* Items indexed by their extension ID.
* Gets an element by its extension-assigned ID.
*/
itemsByExtId: ReadonlyMap<string, ITestTreeElement>;
getElementByTestId(testId: string): ITestTreeElement | undefined;
/**
* Gets the test at the given position in th editor. Should be fast,
......
......@@ -131,14 +131,14 @@ abstract class RunOrDebugSelectedAction extends ViewAction<TestingExplorerView>
for (const folder of testCollection.workspaceFolders()) {
for (const child of folder.getChildren()) {
if (this.filter(child)) {
tests.push({ testId: child.id, providerId: child.providerId });
tests.push({ testId: child.item.extId, providerId: child.providerId });
}
}
}
} else {
for (const item of selected) {
if (item?.test && this.filter(item.test)) {
tests.push({ testId: item.test.id, providerId: item.test.providerId });
for (const treeElement of selected) {
if (treeElement?.test && this.filter(treeElement.test)) {
tests.push({ testId: treeElement.test.item.extId, providerId: treeElement.test.providerId });
}
}
}
......@@ -233,7 +233,7 @@ abstract class RunOrDebugAllAllAction extends Action2 {
for (const root of ref.object.rootIds) {
const node = ref.object.getNodeById(root);
if (node && (this.debug ? node.item.debuggable : node.item.runnable)) {
tests.push({ testId: node.id, providerId: node.providerId });
tests.push({ testId: node.item.extId, providerId: node.providerId });
}
}
} finally {
......@@ -588,8 +588,11 @@ export class RunAtCursor extends RunOrDebugAtCursor {
return node.item.runnable;
}
protected runTest(service: ITestService, node: InternalTestItem): Promise<ITestResult> {
return service.runTests({ debug: false, tests: [{ testId: node.id, providerId: node.providerId }] });
protected runTest(service: ITestService, internalTest: InternalTestItem): Promise<ITestResult> {
return service.runTests({
debug: false,
tests: [{ testId: internalTest.item.extId, providerId: internalTest.providerId }],
});
}
}
......@@ -607,8 +610,11 @@ export class DebugAtCursor extends RunOrDebugAtCursor {
return node.item.debuggable;
}
protected runTest(service: ITestService, node: InternalTestItem): Promise<ITestResult> {
return service.runTests({ debug: true, tests: [{ testId: node.id, providerId: node.providerId }] });
protected runTest(service: ITestService, internalTest: InternalTestItem): Promise<ITestResult> {
return service.runTests({
debug: true,
tests: [{ testId: internalTest.item.extId, providerId: internalTest.providerId }],
});
}
}
......@@ -662,8 +668,11 @@ export class RunCurrentFile extends RunOrDebugCurrentFile {
return node.item.runnable;
}
protected runTest(service: ITestService, nodes: InternalTestItem[]): Promise<ITestResult> {
return service.runTests({ debug: false, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) });
protected runTest(service: ITestService, internalTests: InternalTestItem[]): Promise<ITestResult> {
return service.runTests({
debug: false,
tests: internalTests.map(t => ({ testId: t.item.extId, providerId: t.providerId })),
});
}
}
......@@ -681,8 +690,11 @@ export class DebugCurrentFile extends RunOrDebugCurrentFile {
return node.item.debuggable;
}
protected runTest(service: ITestService, nodes: InternalTestItem[]): Promise<ITestResult> {
return service.runTests({ debug: true, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) });
protected runTest(service: ITestService, internalTests: InternalTestItem[]): Promise<ITestResult> {
return service.runTests({
debug: true,
tests: internalTests.map(t => ({ testId: t.item.extId, providerId: t.providerId }))
});
}
}
......@@ -784,8 +796,11 @@ export class ReRunFailedTests extends RunOrDebugFailedTests {
return node.item.runnable;
}
protected runTest(service: ITestService, nodes: InternalTestItem[]): Promise<ITestResult> {
return service.runTests({ debug: false, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) });
protected runTest(service: ITestService, internalTests: InternalTestItem[]): Promise<ITestResult> {
return service.runTests({
debug: false,
tests: internalTests.map(t => ({ testId: t.item.extId, providerId: t.providerId })),
});
}
}
......@@ -803,8 +818,11 @@ export class DebugFailedTests extends RunOrDebugFailedTests {
return node.item.debuggable;
}
protected runTest(service: ITestService, nodes: InternalTestItem[]): Promise<ITestResult> {
return service.runTests({ debug: true, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) });
protected runTest(service: ITestService, internalTests: InternalTestItem[]): Promise<ITestResult> {
return service.runTests({
debug: true,
tests: internalTests.map(t => ({ testId: t.item.extId, providerId: t.providerId })),
});
}
}
......@@ -822,8 +840,11 @@ export class ReRunLastRun extends RunOrDebugLastRun {
return node.item.runnable;
}
protected runTest(service: ITestService, nodes: InternalTestItem[]): Promise<ITestResult> {
return service.runTests({ debug: false, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) });
protected runTest(service: ITestService, internalTests: InternalTestItem[]): Promise<ITestResult> {
return service.runTests({
debug: false,
tests: internalTests.map(t => ({ testId: t.item.extId, providerId: t.providerId })),
});
}
}
......@@ -841,8 +862,11 @@ export class DebugLastRun extends RunOrDebugLastRun {
return node.item.debuggable;
}
protected runTest(service: ITestService, nodes: InternalTestItem[]): Promise<ITestResult> {
return service.runTests({ debug: true, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) });
protected runTest(service: ITestService, internalTests: InternalTestItem[]): Promise<ITestResult> {
return service.runTests({
debug: true,
tests: internalTests.map(t => ({ testId: t.item.extId, providerId: t.providerId })),
});
}
}
......
......@@ -139,7 +139,7 @@ CommandsRegistry.registerCommand({
CommandsRegistry.registerCommand({
id: 'vscode.peekTestError',
handler: async (accessor: ServicesAccessor, extId: string) => {
const lookup = accessor.get(ITestResultService).getStateByExtId(extId);
const lookup = accessor.get(ITestResultService).getStateById(extId);
if (lookup) {
accessor.get(ITestingPeekOpener).tryPeekFirstError(lookup[0], lookup[1]);
}
......
......@@ -137,7 +137,7 @@ export class TestingDecorations extends Disposable implements IEditorContributio
this.editor.changeDecorations(accessor => {
const newDecorations: ITestDecoration[] = [];
for (const test of ref.object.all) {
const stateLookup = this.results.getStateByExtId(test.item.extId);
const stateLookup = this.results.getStateById(test.item.extId);
if (hasValidLocation(uri, test.item)) {
newDecorations.push(this.instantiationService.createInstance(
RunTestDecoration, test, test.item.location, this.editor, stateLookup?.[1]));
......@@ -275,7 +275,10 @@ class RunTestDecoration extends Disposable implements ITestDecoration {
});
} else {
// todo: customize click behavior
this.testService.runTests({ tests: [{ testId: this.test.id, providerId: this.test.providerId }], debug: false });
this.testService.runTests({
tests: [{ testId: this.test.item.extId, providerId: this.test.providerId }],
debug: false,
});
}
return true;
......@@ -295,14 +298,14 @@ class RunTestDecoration extends Disposable implements ITestDecoration {
if (this.test.item.runnable) {
testActions.push(new Action('testing.run', localize('run test', 'Run Test'), undefined, undefined, () => this.testService.runTests({
debug: false,
tests: [{ providerId: this.test.providerId, testId: this.test.id }],
tests: [{ providerId: this.test.providerId, testId: this.test.item.extId }],
})));
}
if (this.test.item.debuggable) {
testActions.push(new Action('testing.debug', localize('debug test', 'Debug Test'), undefined, undefined, () => this.testService.runTests({
debug: true,
tests: [{ providerId: this.test.providerId, testId: this.test.id }],
tests: [{ providerId: this.test.providerId, testId: this.test.item.extId }],
})));
}
......
......@@ -426,7 +426,7 @@ export class TestingExplorerViewModel extends Disposable {
return;
}
const item = testExtId && this.projection?.itemsByExtId.get(testExtId);
const item = testExtId && this.projection?.getElementByTestId(testExtId);
if (!item) {
this.hasPendingReveal = true;
return;
......@@ -478,7 +478,7 @@ export class TestingExplorerViewModel extends Disposable {
* Tries to peek the first test error, if the item is in a failed state.
*/
private async tryPeekError(item: ITestTreeElement) {
const lookup = item.test && this.testResults.getStateByExtId(item.test.item.extId);
const lookup = item.test && this.testResults.getStateById(item.test.item.extId);
return lookup && isFailedState(lookup[1].state.state)
? this.peekOpener.tryPeekFirstError(lookup[0], lookup[1], { preserveFocus: true })
: false;
......@@ -515,7 +515,10 @@ export class TestingExplorerViewModel extends Disposable {
.filter(e => e.item.runnable);
if (toRun.length) {
this.testService.runTests({ debug: false, tests: toRun.map(t => ({ providerId: t.providerId, testId: t.id })) });
this.testService.runTests({
debug: false,
tests: toRun.map(t => ({ providerId: t.providerId, testId: t.item.extId })),
});
}
}
......
......@@ -254,7 +254,7 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo
return undefined;
}
const test = this.testResults.getResult(parts.resultId)?.getStateByExtId(parts.testExtId);
const test = this.testResults.getResult(parts.resultId)?.getStateById(parts.testExtId);
return test && {
test: test.item,
state: test.state,
......
......@@ -3,10 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { mapFind } from 'vs/base/common/arrays';
import { RunOnceScheduler } from 'vs/base/common/async';
import { throttle } from 'vs/base/common/decorators';
import { IDisposable } from 'vs/base/common/lifecycle';
import { generateUuid } from 'vs/base/common/uuid';
import { IDisposable, IReference } from 'vs/base/common/lifecycle';
import { TestItem } from 'vs/workbench/api/common/extHostTypeConverters';
import { RequiredTestItem, TestItem as ApiTestItem } from 'vs/workbench/api/common/extHostTypes';
import { InternalTestItem, TestDiffOpType, TestsDiff, TestsDiffOp } from 'vs/workbench/contrib/testing/common/testCollection';
......@@ -15,14 +15,14 @@ import { InternalTestItem, TestDiffOpType, TestsDiff, TestsDiffOp } from 'vs/wor
* @private
*/
export class OwnedTestCollection {
protected readonly testIdToInternal = new Map<string, OwnedCollectionTestItem>();
protected readonly testIdsToInternal = new Set<Map<string, OwnedCollectionTestItem>>();
/**
* Gets test information by ID, if it was defined and still exists in this
* extension host.
*/
public getTestById(id: string) {
return this.testIdToInternal.get(id);
return mapFind(this.testIdsToInternal, m => m.get(id));
}
/**
......@@ -30,7 +30,13 @@ export class OwnedTestCollection {
* or document observation.
*/
public createForHierarchy(publishDiff: (diff: TestsDiff) => void = () => undefined) {
return new SingleUseTestCollection(this.testIdToInternal, publishDiff);
return new SingleUseTestCollection(this.createIdMap(), publishDiff);
}
protected createIdMap(): IReference<Map<string, OwnedCollectionTestItem>> {
const map = new Map<string, OwnedCollectionTestItem>();
this.testIdsToInternal.add(map);
return { object: map, dispose: () => this.testIdsToInternal.delete(map) };
}
}
/**
......@@ -60,7 +66,10 @@ export class SingleUseTestCollection implements IDisposable {
*/
private readonly debounceSendDiff = new RunOnceScheduler(() => this.throttleSendDiff(), 2);
constructor(private readonly testIdToInternal: Map<string, OwnedCollectionTestItem>, private readonly publishDiff: (diff: TestsDiff) => void) { }
constructor(
private readonly testIdToInternal: IReference<Map<string, OwnedCollectionTestItem>>,
private readonly publishDiff: (diff: TestsDiff) => void,
) { }
/**
* Adds a new root node to the collection.
......@@ -112,39 +121,34 @@ export class SingleUseTestCollection implements IDisposable {
}
public dispose() {
for (const item of this.testItemToInternal.values()) {
this.testIdToInternal.delete(item.id);
}
this.testIdToInternal.dispose();
this.diff = [];
this.disposed = true;
}
protected getId(): string {
return generateUuid();
}
private addItem(actual: ApiTestItem, providerId: string, parent: string | null) {
let internal = this.testItemToInternal.get(actual);
const parentItem = parent ? this.testIdToInternal.get(parent) : null;
if (!internal) {
if (this.testIdToInternal.object.has(actual.id)) {
throw new Error(`Attempted to insert a duplicate test item ID ${actual.id}`);
}
internal = {
actual,
id: this.getId(),
parent,
item: TestItem.from(actual, parentItem?.item.extId),
item: TestItem.from(actual),
providerId,
previousChildren: new Set(),
previousEquals: itemEqualityComparator(actual),
};
this.testItemToInternal.set(actual, internal);
this.testIdToInternal.set(internal.id, internal);
this.diff.push([TestDiffOpType.Add, { id: internal.id, parent, providerId, item: internal.item }]);
this.testIdToInternal.object.set(actual.id, internal);
this.diff.push([TestDiffOpType.Add, { parent, providerId, item: internal.item }]);
} else if (!internal.previousEquals(actual)) {
internal.item = TestItem.from(actual, parentItem?.item.extId);
internal.item = TestItem.from(actual);
internal.previousEquals = itemEqualityComparator(actual);
this.diff.push([TestDiffOpType.Update, { id: internal.id, parent, providerId, item: internal.item }]);
this.diff.push([TestDiffOpType.Update, { parent, providerId, item: internal.item }]);
}
// If there are children, track which ones are deleted
......@@ -153,9 +157,9 @@ export class SingleUseTestCollection implements IDisposable {
const deletedChildren = internal.previousChildren;
const currentChildren = new Set<string>();
for (const child of actual.children) {
const c = this.addItem(child, providerId, internal.id);
deletedChildren.delete(c.id);
currentChildren.add(c.id);
const c = this.addItem(child, providerId, internal.item.extId);
deletedChildren.delete(c.item.extId);
currentChildren.add(c.item.extId);
}
for (const child of deletedChildren) {
......@@ -172,17 +176,17 @@ export class SingleUseTestCollection implements IDisposable {
private removeItembyId(id: string) {
this.diff.push([TestDiffOpType.Remove, id]);
const queue = [this.testIdToInternal.get(id)];
const queue = [this.testIdToInternal.object.get(id)];
while (queue.length) {
const item = queue.pop();
if (!item) {
continue;
}
this.testIdToInternal.delete(item.id);
this.testIdToInternal.object.delete(item.item.extId);
this.testItemToInternal.delete(item.actual);
for (const child of item.previousChildren) {
queue.push(this.testIdToInternal.get(child));
queue.push(this.testIdToInternal.object.get(child));
}
}
}
......
......@@ -73,7 +73,6 @@ export interface ITestItem {
* TestItem-like shape, butm with an ID and children as strings.
*/
export interface InternalTestItem {
id: string;
providerId: string;
parent: string | null;
item: ITestItem;
......@@ -197,27 +196,27 @@ export abstract class AbstractIncrementalTestCollection<T extends IncrementalTes
for (const op of diff) {
switch (op[0]) {
case TestDiffOpType.Add: {
const item = op[1];
if (!item.parent) {
this.roots.add(item.id);
const created = this.createItem(item);
this.items.set(item.id, created);
const internalTest = op[1];
if (!internalTest.parent) {
this.roots.add(internalTest.item.extId);
const created = this.createItem(internalTest);
this.items.set(internalTest.item.extId, created);
changes.add(created);
} else if (this.items.has(item.parent)) {
const parent = this.items.get(item.parent)!;
parent.children.add(item.id);
const created = this.createItem(item, parent);
this.items.set(item.id, created);
} else if (this.items.has(internalTest.parent)) {
const parent = this.items.get(internalTest.parent)!;
parent.children.add(internalTest.item.extId);
const created = this.createItem(internalTest, parent);
this.items.set(internalTest.item.extId, created);
changes.add(created);
}
break;
}
case TestDiffOpType.Update: {
const item = op[1];
const existing = this.items.get(item.id);
const internalTest = op[1];
const existing = this.items.get(internalTest.item.extId);
if (existing) {
Object.assign(existing.item, item.item);
Object.assign(existing.item, internalTest.item);
changes.update(existing);
}
break;
......@@ -231,9 +230,9 @@ export abstract class AbstractIncrementalTestCollection<T extends IncrementalTes
if (toRemove.parent) {
const parent = this.items.get(toRemove.parent)!;
parent.children.delete(toRemove.id);
parent.children.delete(toRemove.item.extId);
} else {
this.roots.delete(toRemove.id);
this.roots.delete(toRemove.item.extId);
}
const queue: Iterable<string>[] = [[op[1]]];
......
......@@ -70,7 +70,7 @@ export interface ITestResult {
/**
* Gets the state of the test by its extension-assigned ID.
*/
getStateByExtId(testExtId: string): TestResultItem | undefined;
getStateById(testExtId: string): TestResultItem | undefined;
/**
* Serializes the test result. Used to save and restore results
......@@ -114,7 +114,6 @@ const unsetState: ITestState = {
const itemToNode = (
item: IncrementalTestCollectionItem,
byExtId: Map<string, TestResultItem>,
byInternalId: Map<string, TestResultItem>,
): TestResultItem => {
const n: TestResultItem = {
...item,
......@@ -126,7 +125,6 @@ const itemToNode = (
};
byExtId.set(n.item.extId, n);
byInternalId.set(n.id, n);
return n;
};
......@@ -135,37 +133,35 @@ const makeParents = (
collection: IMainThreadTestCollection,
child: IncrementalTestCollectionItem,
byExtId: Map<string, TestResultItem>,
byInternalId: Map<string, TestResultItem>,
) => {
const parent = child.parent && collection.getNodeById(child.parent);
if (!parent) {
return;
}
let parentResultItem = byInternalId.get(parent.id);
let parentResultItem = byExtId.get(parent.item.extId);
if (parentResultItem) {
parentResultItem.children.add(child.id);
parentResultItem.children.add(child.item.extId);
return; // no need to recurse, all parents already in result
}
parentResultItem = itemToNode(parent, byExtId, byInternalId);
parentResultItem.children = new Set([child.id]);
makeParents(collection, parent, byExtId, byInternalId);
parentResultItem = itemToNode(parent, byExtId);
parentResultItem.children = new Set([child.item.extId]);
makeParents(collection, parent, byExtId);
};
const makeNodeAndChildren = (
collection: IMainThreadTestCollection,
test: IncrementalTestCollectionItem,
byExtId: Map<string, TestResultItem>,
byInternalId: Map<string, TestResultItem>,
isExecutedDirectly = true,
): TestResultItem => {
const existing = byInternalId.get(test.id);
const existing = byExtId.get(test.item.extId);
if (existing) {
return existing;
}
const mapped = itemToNode(test, byExtId, byInternalId);
const mapped = itemToNode(test, byExtId);
if (isExecutedDirectly) {
mapped.direct = true;
}
......@@ -173,7 +169,7 @@ const makeNodeAndChildren = (
for (const childId of test.children) {
const child = collection.getNodeById(childId);
if (child) {
makeNodeAndChildren(collection, child, byExtId, byInternalId, false);
makeNodeAndChildren(collection, child, byExtId, false);
}
}
......@@ -194,7 +190,6 @@ export class LiveTestResult implements ITestResult {
req: RunTestsRequest,
) {
const testByExtId = new Map<string, TestResultItem>();
const testByInternalId = new Map<string, TestResultItem>();
for (const test of req.tests) {
for (const collection of collections) {
const node = collection.getNodeById(test.testId);
......@@ -202,12 +197,12 @@ export class LiveTestResult implements ITestResult {
continue;
}
makeNodeAndChildren(collection, node, testByExtId, testByInternalId);
makeParents(collection, node, testByExtId, testByInternalId);
makeNodeAndChildren(collection, node, testByExtId);
makeParents(collection, node, testByExtId);
}
}
return new LiveTestResult(collections, testByExtId, testByInternalId, !!req.isAutoRun);
return new LiveTestResult(collections, testByExtId, !!req.isAutoRun);
}
private readonly completeEmitter = new Emitter<void>();
......@@ -238,7 +233,7 @@ export class LiveTestResult implements ITestResult {
* @inheritdoc
*/
public get tests() {
return this.testByInternalId.values();
return this.testById.values();
}
private readonly computedStateAccessor: IComputedStateAccessor<TestResultItem> = {
......@@ -246,10 +241,10 @@ export class LiveTestResult implements ITestResult {
getCurrentComputedState: i => i.computedState,
setComputedState: (i, s) => i.computedState = s,
getChildren: i => {
const { testByInternalId } = this;
const { testById: testByExtId } = this;
return (function* () {
for (const childId of i.children) {
const child = testByInternalId.get(childId);
const child = testByExtId.get(childId);
if (child) {
yield child;
}
......@@ -257,10 +252,10 @@ export class LiveTestResult implements ITestResult {
})();
},
getParents: i => {
const { testByInternalId } = this;
const { testById: testByExtId } = this;
return (function* () {
for (let parentId = i.parent; parentId;) {
const parent = testByInternalId.get(parentId);
const parent = testByExtId.get(parentId);
if (!parent) {
break;
}
......@@ -274,25 +269,24 @@ export class LiveTestResult implements ITestResult {
constructor(
private readonly collections: ReadonlyArray<IMainThreadTestCollection>,
private readonly testByExtId: Map<string, TestResultItem>,
private readonly testByInternalId: Map<string, TestResultItem>,
private readonly testById: Map<string, TestResultItem>,
public readonly isAutoRun: boolean,
) {
this.counts[TestRunState.Unset] = testByInternalId.size;
this.counts[TestRunState.Unset] = testById.size;
}
/**
* @inheritdoc
*/
public getStateByExtId(extTestId: string) {
return this.testByExtId.get(extTestId);
public getStateById(extTestId: string) {
return this.testById.get(extTestId);
}
/**
* Updates all tests in the collection to the given state.
*/
public setAllToState(state: ITestState, when: (_t: TestResultItem) => boolean) {
for (const test of this.testByInternalId.values()) {
for (const test of this.testById.values()) {
if (when(test)) {
this.fireUpdateAndRefresh(test, state);
}
......@@ -303,7 +297,7 @@ export class LiveTestResult implements ITestResult {
* Updates the state of the test by its internal ID.
*/
public updateState(testId: string, state: ITestState) {
let entry = this.testByInternalId.get(testId);
let entry = this.testById.get(testId);
if (!entry) {
entry = this.addTestToRun(testId);
}
......@@ -332,16 +326,16 @@ export class LiveTestResult implements ITestResult {
/**
* Marks a test as retired. This can trigger it to be re-run in live mode.
*/
public retire(extId: string) {
const root = this.testByExtId.get(extId);
public retire(testId: string) {
const root = this.testById.get(testId);
if (!root || root.retired) {
return;
}
const queue: Iterable<string>[] = [[root.id]];
const queue: Iterable<string>[] = [[root.item.extId]];
while (queue.length) {
for (const id of queue.pop()!) {
const entry = this.testByInternalId.get(id);
const entry = this.testById.get(id);
if (entry && !entry.retired) {
entry.retired = true;
queue.push(entry.children);
......@@ -366,10 +360,10 @@ export class LiveTestResult implements ITestResult {
for (const collection of this.collections) {
let test = collection.getNodeById(testId);
if (test) {
const originalSize = this.testByExtId.size;
makeParents(collection, test, this.testByExtId, this.testByInternalId);
const node = makeNodeAndChildren(collection, test, this.testByExtId, this.testByInternalId);
this.counts[TestRunState.Unset] += this.testByExtId.size - originalSize;
const originalSize = this.testById.size;
makeParents(collection, test, this.testById);
const node = makeNodeAndChildren(collection, test, this.testById);
this.counts[TestRunState.Unset] += this.testById.size - originalSize;
return node;
}
}
......@@ -401,7 +395,7 @@ export class LiveTestResult implements ITestResult {
private readonly doSerialize = new Lazy((): ISerializedTestResults => ({
id: this.id,
completedAt: this.completedAt!,
items: [...this.testByExtId.values()].map(entry => ({
items: [...this.testById.values()].map(entry => ({
...entry,
retired: undefined,
children: [...entry.children],
......@@ -432,10 +426,10 @@ export class HydratedTestResult implements ITestResult {
* @inheritdoc
*/
public get tests() {
return this.byExtId.values();
return this.testById.values();
}
private readonly byExtId = new Map<string, TestResultItem>();
private readonly testById = new Map<string, TestResultItem>();
constructor(private readonly serialized: ISerializedTestResults, private readonly persist = true) {
this.id = serialized.id;
......@@ -456,15 +450,15 @@ export class HydratedTestResult implements ITestResult {
}
this.counts[item.state.state]++;
this.byExtId.set(item.item.extId, cast);
this.testById.set(item.item.extId, cast);
}
}
/**
* @inheritdoc
*/
public getStateByExtId(extTestId: string) {
return this.byExtId.get(extTestId);
public getStateById(extTestId: string) {
return this.testById.get(extTestId);
}
/**
......@@ -516,7 +510,7 @@ export interface ITestResultService {
/**
* Looks up a test's most recent state, by its extension-assigned ID.
*/
getStateByExtId(extId: string): [results: ITestResult, item: TestResultItem] | undefined;
getStateById(extId: string): [results: ITestResult, item: TestResultItem] | undefined;
}
export const ITestResultService = createDecorator<ITestResultService>('testResultService');
......@@ -577,9 +571,9 @@ export class TestResultService implements ITestResultService {
/**
* @inheritdoc
*/
public getStateByExtId(extId: string): [results: ITestResult, item: TestResultItem] | undefined {
public getStateById(extId: string): [results: ITestResult, item: TestResultItem] | undefined {
for (const result of this.results) {
const lookup = result.getStateByExtId(extId);
const lookup = result.getStateById(extId);
if (lookup && lookup.computedState !== TestRunState.Unset) {
return [result, lookup];
}
......@@ -625,7 +619,7 @@ export class TestResultService implements ITestResultService {
if (otherResult === result) {
this.testChangeEmitter.fire({ item, result, reason: TestResultItemChangeReason.ComputedStateChange });
break;
} else if (otherResult.getStateByExtId(item.item.extId) !== undefined) {
} else if (otherResult.getStateById(item.item.extId) !== undefined) {
break;
}
}
......
......@@ -322,7 +322,7 @@ export class MainThreadTestCollection extends AbstractIncrementalTestCollection<
while (queue.length) {
for (const child of queue.pop()!) {
const item = this.items.get(child)!;
ops.push([TestDiffOpType.Add, { id: item.id, providerId: item.providerId, item: item.item, parent: item.parent }]);
ops.push([TestDiffOpType.Add, { providerId: item.providerId, item: item.item, parent: item.parent }]);
queue.push(item.children);
}
}
......
......@@ -5,7 +5,8 @@
import { TestItem, TestRunState } from 'vs/workbench/api/common/extHostTypes';
export const stubTest = (label: string): TestItem => ({
export const stubTest = (label: string, idPrefix = 'id-'): TestItem => ({
id: idPrefix + label,
label,
location: undefined,
debuggable: true,
......@@ -15,11 +16,11 @@ export const stubTest = (label: string): TestItem => ({
export const testStubs = {
test: stubTest,
nested: () => ({
...stubTest('root'),
nested: (idPrefix = 'id-') => ({
...stubTest('root', idPrefix),
children: [
{ ...stubTest('a'), children: [stubTest('aa'), stubTest('ab')] },
stubTest('b'),
{ ...stubTest('a', idPrefix), children: [stubTest('aa', idPrefix), stubTest('ab', idPrefix)] },
stubTest('b', idPrefix),
],
}),
};
......
......@@ -3,9 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { mapFind } from 'vs/base/common/arrays';
import { RunOnceScheduler } from 'vs/base/common/async';
import { Iterable } from 'vs/base/common/iterator';
import { Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
......@@ -15,7 +13,6 @@ import { TestIdWithProvider } from 'vs/workbench/contrib/testing/common/testColl
import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys';
import { ITestResultService, TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResultService';
import { ITestService } from 'vs/workbench/contrib/testing/common/testService';
import { IWorkspaceTestCollectionService } from 'vs/workbench/contrib/testing/common/workspaceTestCollectionService';
export interface ITestingAutoRun {
/**
......@@ -32,7 +29,6 @@ export class TestingAutoRun extends Disposable implements ITestingAutoRun {
constructor(
@IContextKeyService contextKeyService: IContextKeyService,
@IWorkspaceTestCollectionService private readonly workspaceTests: IWorkspaceTestCollectionService,
@ITestService private readonly testService: ITestService,
@ITestResultService private readonly results: ITestResultService,
@IConfigurationService private readonly configuration: IConfigurationService,
......@@ -75,8 +71,6 @@ export class TestingAutoRun extends Disposable implements ITestingAutoRun {
delay = getTestingConfiguration(this.configuration, TestingConfigKeys.AutoRunDelay);
}));
const workspaceTests = store.add(this.workspaceTests.subscribeToWorkspaceTests());
const scheduler = store.add(new RunOnceScheduler(async () => {
const tests = [...rerunIds.values()];
......@@ -96,11 +90,7 @@ export class TestingAutoRun extends Disposable implements ITestingAutoRun {
}
const { extId } = evt.item.item;
const workspaceTest = mapFind(workspaceTests.workspaceFolderCollections,
([, c]) => c.getNodeById(evt.item.id) ?? Iterable.find(c.all, t => t.item.extId === extId));
const subject = workspaceTest ?? evt.item;
rerunIds.set(subject.id, ({ testId: subject.id, providerId: subject.providerId }));
rerunIds.set(`${extId}/${evt.item.providerId}`, ({ testId: extId, providerId: evt.item.providerId }));
if (!isRunning) {
scheduler.schedule(delay);
......
......@@ -38,7 +38,7 @@ export class TestingContentProvider implements IWorkbenchContribution, ITextMode
return null;
}
const test = this.resultService.getResult(parsed.resultId)?.getStateByExtId(parsed.testExtId);
const test = this.resultService.getResult(parsed.resultId)?.getStateById(parsed.testExtId);
if (!test) {
return null;
......
......@@ -16,7 +16,7 @@ suite('Workbench - Testing Explorer Hierarchal by Location Projection', () => {
harness = new TestTreeTestHarness(l => new HierarchicalByLocationProjection(l, {
onResultsChanged: () => undefined,
onTestChanged: () => undefined,
getStateByExtId: () => ({ state: { state: 0 }, computedState: 0 }),
getStateById: () => ({ state: { state: 0 }, computedState: 0 }),
} as any));
});
......@@ -32,9 +32,9 @@ suite('Workbench - Testing Explorer Hierarchal by Location Projection', () => {
});
test('updates render if a second folder is added', () => {
harness.c.addRoot(testStubs.nested(), 'a');
harness.c.addRoot(testStubs.nested('id1-'), 'a');
harness.flush(folder1);
harness.c.addRoot(testStubs.nested(), 'a');
harness.c.addRoot(testStubs.nested('id2-'), 'a');
harness.flush(folder2);
assert.deepStrictEqual(harness.flush(folder1), [
{ e: 'f1', children: [{ e: 'a', children: [{ e: 'aa' }, { e: 'ab' }] }, { e: 'b' }] },
......@@ -43,9 +43,9 @@ suite('Workbench - Testing Explorer Hierarchal by Location Projection', () => {
});
test('updates render if second folder is removed', () => {
harness.c.addRoot(testStubs.nested(), 'a');
harness.c.addRoot(testStubs.nested('id1-'), 'a');
harness.flush(folder1);
harness.c.addRoot(testStubs.nested(), 'a');
harness.c.addRoot(testStubs.nested('id2-'), 'a');
harness.flush(folder2);
harness.onFolderChange.fire({ added: [], changed: [], removed: [folder1] });
assert.deepStrictEqual(harness.flush(folder1), [
......
......@@ -16,7 +16,7 @@ suite('Workbench - Testing Explorer Hierarchal by Name Projection', () => {
harness = new TestTreeTestHarness(l => new HierarchicalByNameProjection(l, {
onResultsChanged: () => undefined,
onTestChanged: () => undefined,
getStateByExtId: () => ({ state: { state: 0 }, computedState: 0 }),
getStateById: () => ({ state: { state: 0 }, computedState: 0 }),
} as any));
});
......@@ -32,9 +32,9 @@ suite('Workbench - Testing Explorer Hierarchal by Name Projection', () => {
});
test('updates render if a second folder is added', () => {
harness.c.addRoot(testStubs.nested(), 'a');
harness.c.addRoot(testStubs.nested('id1-'), 'a');
harness.flush(folder1);
harness.c.addRoot(testStubs.nested(), 'a');
harness.c.addRoot(testStubs.nested('id2-'), 'a');
harness.flush(folder2);
assert.deepStrictEqual(harness.flush(folder1), [
{ e: 'f1', children: [{ e: 'aa' }, { e: 'ab' }, { e: 'b' }] },
......@@ -43,9 +43,9 @@ suite('Workbench - Testing Explorer Hierarchal by Name Projection', () => {
});
test('updates render if second folder is removed', () => {
harness.c.addRoot(testStubs.nested(), 'a');
harness.c.addRoot(testStubs.nested('id1-'), 'a');
harness.flush(folder1);
harness.c.addRoot(testStubs.nested(), 'a');
harness.c.addRoot(testStubs.nested('id2-'), 'a');
harness.flush(folder2);
harness.onFolderChange.fire({ added: [], changed: [], removed: [folder1] });
assert.deepStrictEqual(harness.flush(folder1), [
......
......@@ -3,14 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Iterable } from 'vs/base/common/iterator';
import { OwnedTestCollection, SingleUseTestCollection } from 'vs/workbench/contrib/testing/common/ownedTestCollection';
import { TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
import { MainThreadTestCollection } from 'vs/workbench/contrib/testing/common/testServiceImpl';
import { testStubs } from 'vs/workbench/contrib/testing/common/testStubs';
export class TestSingleUseCollection extends SingleUseTestCollection {
private idCounter = 0;
public get itemToInternal() {
return this.testItemToInternal;
}
......@@ -19,10 +18,6 @@ export class TestSingleUseCollection extends SingleUseTestCollection {
return this.diff;
}
protected getId() {
return String(this.idCounter++);
}
public setDiff(diff: TestsDiff) {
this.diff = diff;
}
......@@ -30,11 +25,11 @@ export class TestSingleUseCollection extends SingleUseTestCollection {
export class TestOwnedTestCollection extends OwnedTestCollection {
public get idToInternal() {
return this.testIdToInternal;
return Iterable.first(this.testIdsToInternal)!;
}
public createForHierarchy(publishDiff: (diff: TestsDiff) => void = () => undefined) {
return new TestSingleUseCollection(this.testIdToInternal, publishDiff);
return new TestSingleUseCollection(this.createIdMap(), publishDiff);
}
}
......@@ -44,7 +39,7 @@ export class TestOwnedTestCollection extends OwnedTestCollection {
*/
export const getInitializedMainTestCollection = (root = testStubs.nested()) => {
const c = new MainThreadTestCollection(0);
const singleUse = new TestSingleUseCollection(new Map(), () => undefined);
const singleUse = new TestSingleUseCollection({ object: new Map(), dispose: () => undefined }, () => undefined);
singleUse.addRoot(root, 'provider');
c.apply(singleUse.collectDiff());
return c;
......
......@@ -24,7 +24,7 @@ suite('Workbench - Test Results Service', () => {
changed = new Set();
r = LiveTestResult.from(
[getInitializedMainTestCollection()],
{ tests: [{ providerId: 'provider', testId: '1' }], debug: false }
{ tests: [{ providerId: 'provider', testId: 'id-a' }], debug: false }
);
r.onChange(e => changed.add(e));
......@@ -59,7 +59,7 @@ suite('Workbench - Test Results Service', () => {
[TestRunState.Queued]: 3,
});
assert.deepStrictEqual(r.getStateByExtId('root\0a')?.state.state, TestRunState.Queued);
assert.deepStrictEqual(r.getStateById('id-a')?.state.state, TestRunState.Queued);
assert.deepStrictEqual(getChangeSummary(), [
{ label: 'a', reason: TestResultItemChangeReason.OwnStateChange },
{ label: 'aa', reason: TestResultItemChangeReason.OwnStateChange },
......@@ -69,15 +69,15 @@ suite('Workbench - Test Results Service', () => {
});
test('updateState', () => {
r.updateState('1', { state: TestRunState.Running, duration: 0, messages: [] });
r.updateState('id-a', { state: TestRunState.Running, duration: 0, messages: [] });
assert.deepStrictEqual(r.counts, {
...makeEmptyCounts(),
[TestRunState.Running]: 1,
[TestRunState.Unset]: 3,
});
assert.deepStrictEqual(r.getStateByExtId('root\0a')?.state.state, TestRunState.Running);
assert.deepStrictEqual(r.getStateById('id-a')?.state.state, TestRunState.Running);
// update computed state:
assert.deepStrictEqual(r.getStateByExtId('root')?.computedState, TestRunState.Running);
assert.deepStrictEqual(r.getStateById('id-root')?.computedState, TestRunState.Running);
assert.deepStrictEqual(getChangeSummary(), [
{ label: 'a', reason: TestResultItemChangeReason.OwnStateChange },
{ label: 'root', reason: TestResultItemChangeReason.ComputedStateChange },
......@@ -85,7 +85,7 @@ suite('Workbench - Test Results Service', () => {
});
test('retire', () => {
r.retire('root\0a');
r.retire('id-a');
assert.deepStrictEqual(getChangeSummary(), [
{ label: 'a', reason: TestResultItemChangeReason.Retired },
{ label: 'aa', reason: TestResultItemChangeReason.ParentRetired },
......@@ -93,25 +93,25 @@ suite('Workbench - Test Results Service', () => {
]);
changed.clear();
r.retire('root\0a');
r.retire('id-a');
assert.strictEqual(changed.size, 0);
});
test('addTestToRun', () => {
r.updateState('4', { state: TestRunState.Running, duration: 0, messages: [] });
r.updateState('id-b', { state: TestRunState.Running, duration: 0, messages: [] });
assert.deepStrictEqual(r.counts, {
...makeEmptyCounts(),
[TestRunState.Running]: 1,
[TestRunState.Unset]: 4,
});
assert.deepStrictEqual(r.getStateByExtId('root\0b')?.state.state, TestRunState.Running);
assert.deepStrictEqual(r.getStateById('id-b')?.state.state, TestRunState.Running);
// update computed state:
assert.deepStrictEqual(r.getStateByExtId('root')?.computedState, TestRunState.Running);
assert.deepStrictEqual(r.getStateById('id-root')?.computedState, TestRunState.Running);
});
test('markComplete', () => {
r.setAllToState({ state: TestRunState.Queued, duration: 0, messages: [] }, t => true);
r.updateState('2', { state: TestRunState.Passed, duration: 0, messages: [] });
r.updateState('id-aa', { state: TestRunState.Passed, duration: 0, messages: [] });
changed.clear();
r.markComplete();
......@@ -122,8 +122,8 @@ suite('Workbench - Test Results Service', () => {
[TestRunState.Unset]: 3,
});
assert.deepStrictEqual(r.getStateByExtId('root')?.state.state, TestRunState.Unset);
assert.deepStrictEqual(r.getStateByExtId('root\0a\0aa')?.state.state, TestRunState.Passed);
assert.deepStrictEqual(r.getStateById('id-root')?.state.state, TestRunState.Unset);
assert.deepStrictEqual(r.getStateById('id-aa')?.state.state, TestRunState.Passed);
});
});
......@@ -146,7 +146,7 @@ suite('Workbench - Test Results Service', () => {
test('serializes and re-hydrates', () => {
results.push(r);
r.updateState('2', { state: TestRunState.Passed, duration: 0, messages: [] });
r.updateState('id-aa', { state: TestRunState.Passed, duration: 0, messages: [] });
r.markComplete();
results = new TestResultService(
......@@ -154,8 +154,8 @@ suite('Workbench - Test Results Service', () => {
storage,
);
const [rehydrated, actual] = results.getStateByExtId('root')!;
const expected = r.getStateByExtId('root')!;
const [rehydrated, actual] = results.getStateById('id-root')!;
const expected = r.getStateById('id-root')!;
delete expected.state.duration; // delete undefined props that don't survive serialization
delete expected.item.location;
......@@ -195,7 +195,7 @@ suite('Workbench - Test Results Service', () => {
completedAt,
id: 'some-id',
items: [{
...getInitializedMainTestCollection().getNodeById('2')!,
...getInitializedMainTestCollection().getNodeById('id-a')!,
state: { state, duration: 0, messages: [] },
computedState: state,
retired: undefined,
......
......@@ -17,7 +17,6 @@ import { Range } from 'vs/editor/common/core/range';
const simplify = (item: TestItem) => {
if ('toJSON' in item) {
item = (item as any).toJSON();
delete (item as any).id;
delete (item as any).providerId;
delete (item as any).testId;
}
......@@ -70,11 +69,11 @@ suite('ExtHost Testing', () => {
const tests = testStubs.nested();
single.addRoot(tests, 'pid');
assert.deepStrictEqual(single.collectDiff(), [
[TestDiffOpType.Add, { id: '0', providerId: 'pid', parent: null, item: convert.TestItem.from(stubTest('root')) }],
[TestDiffOpType.Add, { id: '1', providerId: 'pid', parent: '0', item: convert.TestItem.from(stubTest('a'), 'root') }],
[TestDiffOpType.Add, { id: '2', providerId: 'pid', parent: '1', item: convert.TestItem.from(stubTest('aa'), 'root\0a') }],
[TestDiffOpType.Add, { id: '3', providerId: 'pid', parent: '1', item: convert.TestItem.from(stubTest('ab'), 'root\0a') }],
[TestDiffOpType.Add, { id: '4', providerId: 'pid', parent: '0', item: convert.TestItem.from(stubTest('b'), 'root') }],
[TestDiffOpType.Add, { providerId: 'pid', parent: null, item: convert.TestItem.from(stubTest('root')) }],
[TestDiffOpType.Add, { providerId: 'pid', parent: 'id-root', item: convert.TestItem.from(stubTest('a')) }],
[TestDiffOpType.Add, { providerId: 'pid', parent: 'id-a', item: convert.TestItem.from(stubTest('aa')) }],
[TestDiffOpType.Add, { providerId: 'pid', parent: 'id-a', item: convert.TestItem.from(stubTest('ab')) }],
[TestDiffOpType.Add, { providerId: 'pid', parent: 'id-root', item: convert.TestItem.from(stubTest('b')) }],
]);
});
......@@ -92,7 +91,7 @@ suite('ExtHost Testing', () => {
tests.children![0].description = 'Hello world'; /* item a */
single.onItemChange(tests, 'pid');
assert.deepStrictEqual(single.collectDiff(), [
[TestDiffOpType.Update, { id: '1', parent: '0', providerId: 'pid', item: convert.TestItem.from({ ...stubTest('a'), description: 'Hello world' }, 'root') }],
[TestDiffOpType.Update, { parent: 'id-root', providerId: 'pid', item: convert.TestItem.from({ ...stubTest('a'), description: 'Hello world' }) }],
]);
single.onItemChange(tests, 'pid');
......@@ -107,9 +106,9 @@ suite('ExtHost Testing', () => {
single.onItemChange(tests, 'pid');
assert.deepStrictEqual(single.collectDiff(), [
[TestDiffOpType.Remove, '1'],
[TestDiffOpType.Remove, 'id-a'],
]);
assert.deepStrictEqual([...owned.idToInternal.keys()].sort(), ['0', '4']);
assert.deepStrictEqual([...owned.idToInternal.keys()].sort(), ['id-b', 'id-root']);
assert.strictEqual(single.itemToInternal.size, 2);
});
......@@ -122,9 +121,12 @@ suite('ExtHost Testing', () => {
single.onItemChange(tests, 'pid');
assert.deepStrictEqual(single.collectDiff(), [
[TestDiffOpType.Add, { id: '5', providerId: 'pid', parent: '1', item: convert.TestItem.from(child, 'root\0a') }],
[TestDiffOpType.Add, { providerId: 'pid', parent: 'id-a', item: convert.TestItem.from(child) }],
]);
assert.deepStrictEqual([...owned.idToInternal.keys()].sort(), ['0', '1', '2', '3', '4', '5']);
assert.deepStrictEqual(
[...owned.idToInternal.keys()].sort(),
['id-a', 'id-aa', 'id-ab', 'id-ac', 'id-b', 'id-root'],
);
assert.strictEqual(single.itemToInternal.size, 6);
});
});
......@@ -137,7 +139,7 @@ suite('ExtHost Testing', () => {
const tests = testStubs.nested();
single.addRoot(tests, 'pid');
m.apply(single.collectDiff());
assertTreesEqual(m.rootTestItems[0], owned.getTestById('0')!.actual);
assertTreesEqual(m.rootTestItems[0], owned.getTestById('id-root')!.actual);
assert.strictEqual(m.length, single.itemToInternal.size);
});
......@@ -149,7 +151,7 @@ suite('ExtHost Testing', () => {
single.onItemChange(tests, 'pid');
m.apply(single.collectDiff());
assertTreesEqual(m.rootTestItems[0], owned.getTestById('0')!.actual);
assertTreesEqual(m.rootTestItems[0], owned.getTestById('id-root')!.actual);
assert.strictEqual(m.length, single.itemToInternal.size);
});
......@@ -161,7 +163,7 @@ suite('ExtHost Testing', () => {
single.onItemChange(tests, 'pid');
m.apply(single.collectDiff());
assertTreesEqual(m.rootTestItems[0], owned.getTestById('0')!.actual);
assertTreesEqual(m.rootTestItems[0], owned.getTestById('id-root')!.actual);
assert.strictEqual(m.length, single.itemToInternal.size);
});
......@@ -173,7 +175,7 @@ suite('ExtHost Testing', () => {
single.onItemChange(tests, 'pid');
m.apply(single.collectDiff());
assertTreesEqual(m.rootTestItems[0], owned.getTestById('0')!.actual);
assertTreesEqual(m.rootTestItems[0], owned.getTestById('id-root')!.actual);
});
suite('MirroredChangeCollector', () => {
......@@ -224,7 +226,7 @@ suite('ExtHost Testing', () => {
});
test('is a no-op if a node is added and removed', () => {
const nested = testStubs.nested();
const nested = testStubs.nested('id2-');
tests.children.push(nested);
single.onItemChange(tests, 'pid');
tests.children.pop();
......
{
"name": "vscode-automation",
"version": "1.53.0",
"version": "1.54.0",
"description": "VS Code UI automation driver",
"author": {
"name": "Microsoft Corporation"
......@@ -35,4 +35,4 @@
"vscode-uri": "^2.0.3",
"watch": "^1.0.2"
}
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册