Skip to content

Commit 0418d92

Browse files
authored
Add TreeView data model and hook up existing test data (#4304)
* Add workspace test discovery data to the Python Test tree view. For #4273 & #4274 - Make Python Test View Provider injectable (testable) - Maps `Tests` object to TreeViewItem hierarchy - Adds a Event (subscription) model to the TestStorageService Initial PR, more to follow...
1 parent 7601808 commit 0418d92

File tree

11 files changed

+343
-174
lines changed

11 files changed

+343
-174
lines changed

src/client/extension.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ import { ReplProvider } from './providers/replProvider';
9393
import { registerTypes as providersRegisterTypes } from './providers/serviceRegistry';
9494
import { activateSimplePythonRefactorProvider } from './providers/simpleRefactorProvider';
9595
import { TerminalProvider } from './providers/terminalProvider';
96-
import { PythonTestTreeViewProvider } from './providers/testTreeViewProvider';
9796
import { ISortImportsEditingProvider } from './providers/types';
9897
import { activateUpdateSparkLibraryProvider } from './providers/updateSparkLibraryProvider';
9998
import { sendTelemetryEvent } from './telemetry';
@@ -208,8 +207,6 @@ async function activateUnsafe(context: ExtensionContext): Promise<IExtensionApi>
208207

209208
context.subscriptions.push(languages.registerCodeActionsProvider(PYTHON, new PythonCodeActionProvider(), { providedCodeActionKinds: [CodeActionKind.SourceOrganizeImports] }));
210209

211-
context.subscriptions.push(window.registerTreeDataProvider('python_tests', new PythonTestTreeViewProvider()));
212-
213210
serviceContainer.getAll<DebugConfigurationProvider>(IDebugConfigurationService).forEach(debugConfigProvider => {
214211
context.subscriptions.push(debug.registerDebugConfigurationProvider(DebuggerTypeName, debugConfigProvider));
215212
});

src/client/providers/testTreeViewItem.ts

Lines changed: 0 additions & 46 deletions
This file was deleted.

src/client/providers/testTreeViewProvider.ts

Lines changed: 0 additions & 105 deletions
This file was deleted.

src/client/providers/types.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,22 @@
33

44
'use strict';
55

6-
import { CancellationToken, Uri, WorkspaceEdit } from 'vscode';
6+
import {
7+
CancellationToken, Event, ProviderResult,
8+
TreeDataProvider, Uri, WorkspaceEdit
9+
} from 'vscode';
10+
import { TestTreeItem } from '../unittests/providers/testTreeViewItem';
711

812
export const ISortImportsEditingProvider = Symbol('ISortImportsEditingProvider');
913
export interface ISortImportsEditingProvider {
1014
provideDocumentSortImportsEdits(uri: Uri, token?: CancellationToken): Promise<WorkspaceEdit | undefined>;
1115
sortImports(uri?: Uri): Promise<void>;
1216
registerCommands(): void;
1317
}
18+
19+
export const ITestTreeViewProvider = Symbol('ITestTreeViewProvider');
20+
export interface ITestTreeViewProvider extends TreeDataProvider<TestTreeItem> {
21+
onDidChangeTreeData: Event<TestTreeItem | undefined>;
22+
getTreeItem(element: TestTreeItem): Promise<TestTreeItem>;
23+
getChildren(element?: TestTreeItem): ProviderResult<TestTreeItem[]>;
24+
}

src/client/unittests/common/services/storageService.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
import { inject, injectable } from 'inversify';
2-
import { Disposable, Uri, workspace } from 'vscode';
2+
import {
3+
Disposable, Event, EventEmitter,
4+
Uri, workspace
5+
} from 'vscode';
36
import { IDisposableRegistry } from '../../../common/types';
47
import { FlattenedTestFunction, FlattenedTestSuite, ITestCollectionStorageService, TestFunction, Tests, TestSuite } from './../types';
58

69
@injectable()
710
export class TestCollectionStorageService implements ITestCollectionStorageService {
11+
public readonly onUpdated: Event<Uri>;
12+
813
private testsIndexedByWorkspaceUri = new Map<string, Tests | undefined>();
14+
private _onTestStoreUpdated: EventEmitter<Uri> = new EventEmitter<Uri>();
15+
916
constructor(@inject(IDisposableRegistry) disposables: Disposable[]) {
1017
disposables.push(this);
18+
this.onUpdated = this._onTestStoreUpdated.event;
1119
}
1220
public getTests(wkspace: Uri): Tests | undefined {
1321
const workspaceFolder = this.getWorkspaceFolderPath(wkspace) || '';
@@ -16,6 +24,7 @@ export class TestCollectionStorageService implements ITestCollectionStorageServi
1624
public storeTests(wkspace: Uri, tests: Tests | undefined): void {
1725
const workspaceFolder = this.getWorkspaceFolderPath(wkspace) || '';
1826
this.testsIndexedByWorkspaceUri.set(workspaceFolder, tests);
27+
this._onTestStoreUpdated.fire(wkspace);
1928
}
2029
public findFlattendTestFunction(resource: Uri, func: TestFunction): FlattenedTestFunction | undefined {
2130
const tests = this.getTests(resource);
@@ -33,6 +42,7 @@ export class TestCollectionStorageService implements ITestCollectionStorageServi
3342
}
3443
public dispose() {
3544
this.testsIndexedByWorkspaceUri.clear();
45+
this._onTestStoreUpdated.dispose();
3646
}
3747
private getWorkspaceFolderPath(resource: Uri): string | undefined {
3848
const folder = workspace.getWorkspaceFolder(resource);

src/client/unittests/common/types.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { CancellationToken, DiagnosticCollection, Disposable, OutputChannel, Uri } from 'vscode';
1+
import {
2+
CancellationToken, DiagnosticCollection, Disposable,
3+
Event, OutputChannel, Uri
4+
} from 'vscode';
25
import { IUnitTestSettings, Product } from '../../common/types';
36
import { IPythonUnitTestMessage } from '../types';
47
import { CommandSource } from './constants';
@@ -181,6 +184,7 @@ export interface ITestVisitor {
181184
export const ITestCollectionStorageService = Symbol('ITestCollectionStorageService');
182185

183186
export interface ITestCollectionStorageService extends Disposable {
187+
onUpdated: Event<Uri>;
184188
getTests(wkspace: Uri): Tests | undefined;
185189
storeTests(wkspace: Uri, tests: Tests | null | undefined): void;
186190
findFlattendTestFunction(resource: Uri, func: TestFunction): FlattenedTestFunction | undefined;

src/client/unittests/main.ts

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,50 @@
33
// tslint:disable:no-duplicate-imports no-unnecessary-callback-wrapper
44

55
import { inject, injectable } from 'inversify';
6-
import { ConfigurationChangeEvent, Disposable, OutputChannel, TextDocument, Uri } from 'vscode';
7-
import * as vscode from 'vscode';
8-
import { ICommandManager, IDocumentManager, IWorkspaceService } from '../common/application/types';
6+
import {
7+
ConfigurationChangeEvent, Disposable,
8+
DocumentSymbolProvider, EventEmitter,
9+
OutputChannel, TextDocument, Uri, window
10+
} from 'vscode';
11+
import {
12+
ICommandManager, IDocumentManager, IWorkspaceService
13+
} from '../common/application/types';
914
import * as constants from '../common/constants';
1015
import '../common/extensions';
11-
import { IConfigurationService, IDisposableRegistry, ILogger, IOutputChannel } from '../common/types';
16+
import {
17+
IConfigurationService, IDisposableRegistry,
18+
ILogger, IOutputChannel
19+
} from '../common/types';
1220
import { IServiceContainer } from '../ioc/types';
21+
import { ITestTreeViewProvider } from '../providers/types';
1322
import { EventName } from '../telemetry/constants';
1423
import { sendTelemetryEvent } from '../telemetry/index';
1524
import { activateCodeLenses } from './codeLenses/main';
16-
import { CANCELLATION_REASON, CommandSource, TEST_OUTPUT_CHANNEL } from './common/constants';
25+
import {
26+
CANCELLATION_REASON, CommandSource, TEST_OUTPUT_CHANNEL
27+
} from './common/constants';
1728
import { selectTestWorkspace } from './common/testUtils';
18-
import { ITestCollectionStorageService, ITestManager, IWorkspaceTestManagerService, TestFile, TestFunction, TestStatus, TestsToRun } from './common/types';
19-
import { ITestDisplay, ITestResultDisplay, IUnitTestConfigurationService, IUnitTestManagementService } from './types';
29+
import {
30+
ITestCollectionStorageService, ITestManager,
31+
IWorkspaceTestManagerService, TestFile,
32+
TestFunction, TestStatus, TestsToRun
33+
} from './common/types';
34+
import {
35+
ITestDisplay, ITestResultDisplay,
36+
IUnitTestConfigurationService, IUnitTestManagementService
37+
} from './types';
2038

2139
@injectable()
2240
export class UnitTestManagementService implements IUnitTestManagementService, Disposable {
23-
private readonly outputChannel: vscode.OutputChannel;
41+
private readonly outputChannel: OutputChannel;
2442
private readonly disposableRegistry: Disposable[];
2543
private workspaceTestManagerService?: IWorkspaceTestManagerService;
2644
private documentManager: IDocumentManager;
2745
private workspaceService: IWorkspaceService;
2846
private testResultDisplay?: ITestResultDisplay;
2947
private autoDiscoverTimer?: NodeJS.Timer;
3048
private configChangedTimer?: NodeJS.Timer;
31-
private readonly onDidChange: vscode.EventEmitter<void> = new vscode.EventEmitter<void>();
49+
private readonly onDidChange: EventEmitter<void> = new EventEmitter<void>();
3250

3351
constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer) {
3452
this.disposableRegistry = serviceContainer.get<Disposable[]>(IDisposableRegistry);
@@ -43,15 +61,28 @@ export class UnitTestManagementService implements IUnitTestManagementService, Di
4361
this.workspaceTestManagerService.dispose();
4462
}
4563
}
46-
public async activate(symboldProvider: vscode.DocumentSymbolProvider): Promise<void> {
64+
public async activate(symbolProvider: DocumentSymbolProvider): Promise<void> {
4765
this.workspaceTestManagerService = this.serviceContainer.get<IWorkspaceTestManagerService>(IWorkspaceTestManagerService);
66+
const disposablesRegistry = this.serviceContainer.get<Disposable[]>(IDisposableRegistry);
4867

4968
this.registerHandlers();
5069
this.registerCommands();
70+
71+
// register provider...
72+
const testViewProvider = this.serviceContainer.get<ITestTreeViewProvider>(ITestTreeViewProvider);
73+
const disposable = window.registerTreeDataProvider('python_tests', testViewProvider);
74+
disposablesRegistry.push(disposable);
75+
5176
this.autoDiscoverTests()
5277
.catch(ex => this.serviceContainer.get<ILogger>(ILogger).logError('Failed to auto discover tests upon activation', ex));
53-
await this.registerSymbolProvider(symboldProvider);
78+
await this.registerSymbolProvider(symbolProvider);
5479
}
80+
81+
public async activateCodeLenses(symbolProvider: DocumentSymbolProvider): Promise<void> {
82+
const testCollectionStorage = this.serviceContainer.get<ITestCollectionStorageService>(ITestCollectionStorageService);
83+
this.disposableRegistry.push(activateCodeLenses(this.onDidChange, symbolProvider, testCollectionStorage));
84+
}
85+
5586
public async getTestManager(displayTestNotConfiguredMessage: boolean, resource?: Uri): Promise<ITestManager | undefined | void> {
5687
let wkspace: Uri | undefined;
5788
if (resource) {
@@ -278,9 +309,9 @@ export class UnitTestManagementService implements IUnitTestManagementService, Di
278309
this.testResultDisplay.displayProgressStatus(promise, debug);
279310
await promise;
280311
}
281-
private async registerSymbolProvider(symboldProvider: vscode.DocumentSymbolProvider): Promise<void> {
312+
private async registerSymbolProvider(symbolProvider: DocumentSymbolProvider): Promise<void> {
282313
const testCollectionStorage = this.serviceContainer.get<ITestCollectionStorageService>(ITestCollectionStorageService);
283-
this.disposableRegistry.push(activateCodeLenses(this.onDidChange, symboldProvider, testCollectionStorage));
314+
this.disposableRegistry.push(activateCodeLenses(this.onDidChange, symbolProvider, testCollectionStorage));
284315
}
285316
private registerCommands(): void {
286317
const disposablesRegistry = this.serviceContainer.get<Disposable[]>(IDisposableRegistry);

0 commit comments

Comments
 (0)