diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index 685cbaa95e4d8757ef5429ff7f543e80e179cf84..0e08876e537dbefba045010bfc8f7f73278ee2d3 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -1048,85 +1048,3 @@ export class LRUCache extends LinkedMap { } } } - -type IndexRecord = [fn: (value: V) => R, map: Map]; - -/** - * Map that supports multiple indicies whose keys are derived from the value. - */ -export class IndexedSet implements Set { - public get size() { - return this.source.size; - } - - private source = new Set(); - private readonly indexes: IndexRecord[] = []; - - /** - * Creates a map that maintains a copy of the items in the set, indexed - * by the given 'indexer' function. - */ - index(indexer: (value: V) => R) { - const map = new Map(); - for (const value of this.source) { - map.set(indexer(value), value); - } - - this.indexes.push([indexer, map]); - return map as ReadonlyMap; - } - - 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) => void, thisArg?: any): void { - this.source.forEach(callbackfn, thisArg); - } - - has(value: V): boolean { - return this.source.has(value); - } - - [Symbol.toStringTag]: string; - - values(): IterableIterator { - return this.source.values(); - } - - entries(): IterableIterator<[V, V]> { - return this.source.entries(); - } - - keys(): IterableIterator { - return this.source.keys(); - } - - [Symbol.iterator](): IterableIterator { - return this.source.values(); - } -} diff --git a/src/vs/base/test/common/map.test.ts b/src/vs/base/test/common/map.test.ts index 0c9fc51e0974b0b1733c029d20a859d207d587fb..2e67001c17b2d18d94dcf30a242b82061a469e7b 100644 --- a/src/vs/base/test/common/map.test.ts +++ b/src/vs/base/test/common/map.test.ts @@ -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(); - 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(); - 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(); - const map = i.index(v => v ** 2); - i.add(2); - i.clear(); - assert.deepStrictEqual(map, new Map([])); - }); }); diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 3d74824e6a2213c4c6345779ef376113436a8842..87cf1eee7a16c5b0b14def42eb32dde43436db05 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -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. */ diff --git a/src/vs/workbench/api/common/extHostTesting.ts b/src/vs/workbench/api/common/extHostTesting.ts index 78f4d26bc8e014e4dae204a49f6c802e751f1a3d..a865d6f5c855bfae0723ba6f536ba31b4536a2a4 100644 --- a/src/vs/workbench/api/common/extHostTesting.ts +++ b/src/vs/workbench/api/common/extHostTesting.ts @@ -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 { - 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; diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index fd1f8a8f1780954a79d9cf53766282c7d4d0e24d..60afcd721faef35fbafa4c4b707c83ba4bfaf489 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -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(); for (const item of serialized.items) { - byInternalId.set(item.id, item); + byInternalId.set(item.item.extId, item); if (item.direct) { roots.push(item); } diff --git a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts b/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts index 87fec52daab8a1fc737bf565e15d540fa7ed97e9..287e561c0fc97876821feee9507d0d5e3f94bac9 100644 --- a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts +++ b/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByLocation.ts @@ -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(); private readonly changes = new NodeChangeList(); private readonly locations = new TestLocationStore(); - private readonly itemSource = new IndexedSet(); - 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(); /** * 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); } } } diff --git a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByName.ts b/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByName.ts index 8775e2454d0d3ea36868f2e026887b164c3bed49..8ff8d568112df28159195120a0c2caf9b0d422f4 100644 --- a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByName.ts +++ b/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalByName.ts @@ -45,7 +45,7 @@ export class HierarchicalByNameElement extends HierarchicalElement { } public get testId() { - return `hintest:${this.test.id}`; + return `hintest:${this.test.item.extId}`; } /** diff --git a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalNodes.ts b/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalNodes.ts index 4e4dc956bddede92e5eb56ae5ce02d0952e21230..148bc267c77fccaf213ddf2e42754074217b9c00 100644 --- a/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalNodes.ts +++ b/src/vs/workbench/contrib/testing/browser/explorerProjections/hierarchalNodes.ts @@ -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 { 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(); } diff --git a/src/vs/workbench/contrib/testing/browser/explorerProjections/index.ts b/src/vs/workbench/contrib/testing/browser/explorerProjections/index.ts index 4b18e253369b46af30dfec9a32f5f00ae4bc0b94..94238523144f9b6ece3922bb3d770ddf77067c12 100644 --- a/src/vs/workbench/contrib/testing/browser/explorerProjections/index.ts +++ b/src/vs/workbench/contrib/testing/browser/explorerProjections/index.ts @@ -27,9 +27,9 @@ export interface ITestTreeProjection extends IDisposable { onUpdate: Event; /** - * Items indexed by their extension ID. + * Gets an element by its extension-assigned ID. */ - itemsByExtId: ReadonlyMap; + getElementByTestId(testId: string): ITestTreeElement | undefined; /** * Gets the test at the given position in th editor. Should be fast, diff --git a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts index 8772613e971eb4ee24fb4e0b83f027c0c2842843..2584e2e3c0d46c11298219968cbb9934adfc449a 100644 --- a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts +++ b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts @@ -131,14 +131,14 @@ abstract class RunOrDebugSelectedAction extends ViewAction 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 { - return service.runTests({ debug: false, tests: [{ testId: node.id, providerId: node.providerId }] }); + protected runTest(service: ITestService, internalTest: InternalTestItem): Promise { + 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 { - return service.runTests({ debug: true, tests: [{ testId: node.id, providerId: node.providerId }] }); + protected runTest(service: ITestService, internalTest: InternalTestItem): Promise { + 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 { - return service.runTests({ debug: false, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) }); + protected runTest(service: ITestService, internalTests: InternalTestItem[]): Promise { + 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 { - return service.runTests({ debug: true, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) }); + protected runTest(service: ITestService, internalTests: InternalTestItem[]): Promise { + 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 { - return service.runTests({ debug: false, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) }); + protected runTest(service: ITestService, internalTests: InternalTestItem[]): Promise { + 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 { - return service.runTests({ debug: true, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) }); + protected runTest(service: ITestService, internalTests: InternalTestItem[]): Promise { + 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 { - return service.runTests({ debug: false, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) }); + protected runTest(service: ITestService, internalTests: InternalTestItem[]): Promise { + 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 { - return service.runTests({ debug: true, tests: nodes.map(node => ({ testId: node.id, providerId: node.providerId })) }); + protected runTest(service: ITestService, internalTests: InternalTestItem[]): Promise { + return service.runTests({ + debug: true, + tests: internalTests.map(t => ({ testId: t.item.extId, providerId: t.providerId })), + }); } } diff --git a/src/vs/workbench/contrib/testing/browser/testing.contribution.ts b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts index 3919c1f150012a95cd9daf4d5a0806a7dd00e92b..836437c9dead4209be8454617932a4ab80df0265 100644 --- a/src/vs/workbench/contrib/testing/browser/testing.contribution.ts +++ b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts @@ -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]); } diff --git a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts index 4f8b104bcdf41c3b84c9b9223e949116d1f791a1..7d02714c4b60f6e370d8835e92ee1d27040e6114 100644 --- a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts +++ b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts @@ -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 }], }))); } diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index 898a07d1b56c1636ee4da0a80eba868dd1c8b973..fec3e232249b2d8d0d3826bbc7ace5c33d71dd48 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -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 })), + }); } } diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index f5f863f32551cbd801eefaaad310ea7d7e0e3221..0433c9aabe9818ee0ce4176d47b693b2e5b18e89 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -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, diff --git a/src/vs/workbench/contrib/testing/common/ownedTestCollection.ts b/src/vs/workbench/contrib/testing/common/ownedTestCollection.ts index c997e237d7a461e2f53b3e29358e1ad2d9e8e150..795c4a919d24e474f0f08cebf9a24f4aeb1f8f35 100644 --- a/src/vs/workbench/contrib/testing/common/ownedTestCollection.ts +++ b/src/vs/workbench/contrib/testing/common/ownedTestCollection.ts @@ -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(); + protected readonly testIdsToInternal = new Set>(); /** * 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> { + const map = new Map(); + 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, private readonly publishDiff: (diff: TestsDiff) => void) { } + constructor( + private readonly testIdToInternal: IReference>, + 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(); 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)); } } } diff --git a/src/vs/workbench/contrib/testing/common/testCollection.ts b/src/vs/workbench/contrib/testing/common/testCollection.ts index 7d4ede8fc714df004b238ad634346f0673abfefb..8d619a5cf49d0cc29220ae2f574923a5cf7385d5 100644 --- a/src/vs/workbench/contrib/testing/common/testCollection.ts +++ b/src/vs/workbench/contrib/testing/common/testCollection.ts @@ -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[] = [[op[1]]]; diff --git a/src/vs/workbench/contrib/testing/common/testResultService.ts b/src/vs/workbench/contrib/testing/common/testResultService.ts index 85bd7cd44b7110f28ee951b97974cba0ae095a3c..849809a135f1edc6ae80d76322dd39545113c9da 100644 --- a/src/vs/workbench/contrib/testing/common/testResultService.ts +++ b/src/vs/workbench/contrib/testing/common/testResultService.ts @@ -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, - byInternalId: Map, ): 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, - byInternalId: Map, ) => { 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, - byInternalId: Map, 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(); - const testByInternalId = new Map(); 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(); @@ -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 = { @@ -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, - private readonly testByExtId: Map, - private readonly testByInternalId: Map, + private readonly testById: Map, 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[] = [[root.id]]; + const queue: Iterable[] = [[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(); + private readonly testById = new Map(); 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('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; } } diff --git a/src/vs/workbench/contrib/testing/common/testServiceImpl.ts b/src/vs/workbench/contrib/testing/common/testServiceImpl.ts index 8b65c2c83ff3d5d54cee4f1687038cc6de0c7f18..2050aa313fd38c243551fbef1c2c8bfa21584c17 100644 --- a/src/vs/workbench/contrib/testing/common/testServiceImpl.ts +++ b/src/vs/workbench/contrib/testing/common/testServiceImpl.ts @@ -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); } } diff --git a/src/vs/workbench/contrib/testing/common/testStubs.ts b/src/vs/workbench/contrib/testing/common/testStubs.ts index e1d61278a1f70a4b6705945f552d7bcec78e5fda..c3a67e574b0a92a53c0bb54eb05beec67721df33 100644 --- a/src/vs/workbench/contrib/testing/common/testStubs.ts +++ b/src/vs/workbench/contrib/testing/common/testStubs.ts @@ -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), ], }), }; diff --git a/src/vs/workbench/contrib/testing/common/testingAutoRun.ts b/src/vs/workbench/contrib/testing/common/testingAutoRun.ts index a1eae07dd53e694cf84e74cbee49622d3d144bf5..d095a8480ddc025f399ca38d125ff161f82221b3 100644 --- a/src/vs/workbench/contrib/testing/common/testingAutoRun.ts +++ b/src/vs/workbench/contrib/testing/common/testingAutoRun.ts @@ -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); diff --git a/src/vs/workbench/contrib/testing/common/testingContentProvider.ts b/src/vs/workbench/contrib/testing/common/testingContentProvider.ts index b3e91103ce41eff758b7859bfe7ff13f443c5af1..49a8b21e2ec025bc001ae5bc232a9b4758c661cd 100644 --- a/src/vs/workbench/contrib/testing/common/testingContentProvider.ts +++ b/src/vs/workbench/contrib/testing/common/testingContentProvider.ts @@ -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; diff --git a/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByLocation.test.ts b/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByLocation.test.ts index 343ac3cdecf9f7c594c7260b30ef8d7893e7cacc..76a2065f0595a388a5b72293b2230da0d136811b 100644 --- a/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByLocation.test.ts +++ b/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByLocation.test.ts @@ -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), [ diff --git a/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByName.test.ts b/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByName.test.ts index c08d3eaf637feaef759b45b4aad93b2cf7ee8f50..8095598d999edf2b3b0b7595d3bfd15dd9961018 100644 --- a/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByName.test.ts +++ b/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByName.test.ts @@ -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), [ diff --git a/src/vs/workbench/contrib/testing/test/common/ownedTestCollection.ts b/src/vs/workbench/contrib/testing/test/common/ownedTestCollection.ts index 8df8bb684ff79c208f16cdd335217363204c2255..988dd4ab4ef32dc7e220ff93a49c38d0a1ca4efa 100644 --- a/src/vs/workbench/contrib/testing/test/common/ownedTestCollection.ts +++ b/src/vs/workbench/contrib/testing/test/common/ownedTestCollection.ts @@ -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; diff --git a/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts b/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts index 031eaa9c24dfcb14fd540640d1c63c942f98f6db..301e1636daeaea899bdc2742e3b6c8189f67ade1 100644 --- a/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts +++ b/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts @@ -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, diff --git a/src/vs/workbench/test/browser/api/extHostTesting.test.ts b/src/vs/workbench/test/browser/api/extHostTesting.test.ts index a60900d2ea877dd797467ddf0aa6842088736f31..bacf94f3cca6fc93f8af760d44dbf7356394db07 100644 --- a/src/vs/workbench/test/browser/api/extHostTesting.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTesting.test.ts @@ -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(); diff --git a/test/automation/package.json b/test/automation/package.json index 78c3bce9f56db917936922759afdd2e3c37f5ab1..47f249d9e6d787b135f7024479d7afb6573dece9 100644 --- a/test/automation/package.json +++ b/test/automation/package.json @@ -1,6 +1,6 @@ { "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