From a6a4a26509adc3b5ae6613847a0a8e31342f5a7e Mon Sep 17 00:00:00 2001 From: Janaka Abeywardhana Date: Wed, 23 May 2018 00:28:44 +0100 Subject: [PATCH 01/13] Add vsTestPlatform code and get it working --- package-lock.json | 114 +- package.json | 8 +- src/extension.ts | 8 + src/testCommands.ts | 12 + src/vsTestPlatform/README.md | 7 + .../vsCode/commands/testDiscoveryCommands.ts | 19 + .../vsCode/commands/testExecutionCommands.ts | 32 + src/vsTestPlatform/vsCode/config.ts | 23 + .../vsCode/console/testOutputChannel.ts | 9 + .../testTreeDataProvider/groupByFilter.ts | 68 + .../testTreeDataProvider.ts | 507 ++++ src/vsTestPlatform/vsCode/utils.ts | 13 + .../vsCode/vsTest/vsTestManager.ts | 92 + .../vsCode/vsTest/vsTestServiceIDE.ts | 47 + .../vsTest/base/common/callbackList.ts | 86 + .../vsTest/base/common/event.ts | 526 +++++ .../vsTest/base/common/eventEmitter.ts | 299 +++ .../vsTest/base/common/functional.ts | 28 + .../vsTest/base/common/lifecycle.ts | 124 + .../vsTest/base/common/objects.ts | 0 .../vsTest/base/common/platform.ts | 14 + src/vsTestPlatform/vsTest/base/common/uri.ts | 404 ++++ .../vsTest/base/common/winjs.base.d.ts | 143 ++ .../vsTest/base/common/winjs.base.js | 13 + .../vsTest/base/common/winjs.base.raw.js | 2071 +++++++++++++++++ .../vsTest/base/node/stdFork.ts | 156 ++ .../vsTest/base/node/stdForkStart.js | 197 ++ .../vsTest/binary/binaryReader.ts | 119 + .../vsTest/binary/binaryWriter.ts | 81 + .../vsTest/rawProtocolSession.ts | 174 ++ src/vsTestPlatform/vsTest/vsTestConfig.ts | 18 + src/vsTestPlatform/vsTest/vsTestModel.ts | 494 ++++ src/vsTestPlatform/vsTest/vsTestProtocol.d.ts | 177 ++ src/vsTestPlatform/vsTest/vsTestService.ts | 253 ++ src/vsTestPlatform/vsTest/vsTestSession.ts | 238 ++ .../vsTestDotNet/vsTestDotNetModel.ts | 49 + test/mstest/.vscode/settings.json | 3 + tslint.json | 7 +- 38 files changed, 6602 insertions(+), 31 deletions(-) create mode 100644 src/vsTestPlatform/README.md create mode 100644 src/vsTestPlatform/vsCode/commands/testDiscoveryCommands.ts create mode 100644 src/vsTestPlatform/vsCode/commands/testExecutionCommands.ts create mode 100644 src/vsTestPlatform/vsCode/config.ts create mode 100644 src/vsTestPlatform/vsCode/console/testOutputChannel.ts create mode 100644 src/vsTestPlatform/vsCode/testTreeDataProvider/groupByFilter.ts create mode 100644 src/vsTestPlatform/vsCode/testTreeDataProvider/testTreeDataProvider.ts create mode 100644 src/vsTestPlatform/vsCode/utils.ts create mode 100644 src/vsTestPlatform/vsCode/vsTest/vsTestManager.ts create mode 100644 src/vsTestPlatform/vsCode/vsTest/vsTestServiceIDE.ts create mode 100644 src/vsTestPlatform/vsTest/base/common/callbackList.ts create mode 100644 src/vsTestPlatform/vsTest/base/common/event.ts create mode 100644 src/vsTestPlatform/vsTest/base/common/eventEmitter.ts create mode 100644 src/vsTestPlatform/vsTest/base/common/functional.ts create mode 100644 src/vsTestPlatform/vsTest/base/common/lifecycle.ts create mode 100644 src/vsTestPlatform/vsTest/base/common/objects.ts create mode 100644 src/vsTestPlatform/vsTest/base/common/platform.ts create mode 100644 src/vsTestPlatform/vsTest/base/common/uri.ts create mode 100644 src/vsTestPlatform/vsTest/base/common/winjs.base.d.ts create mode 100644 src/vsTestPlatform/vsTest/base/common/winjs.base.js create mode 100644 src/vsTestPlatform/vsTest/base/common/winjs.base.raw.js create mode 100644 src/vsTestPlatform/vsTest/base/node/stdFork.ts create mode 100644 src/vsTestPlatform/vsTest/base/node/stdForkStart.js create mode 100644 src/vsTestPlatform/vsTest/binary/binaryReader.ts create mode 100644 src/vsTestPlatform/vsTest/binary/binaryWriter.ts create mode 100644 src/vsTestPlatform/vsTest/rawProtocolSession.ts create mode 100644 src/vsTestPlatform/vsTest/vsTestConfig.ts create mode 100644 src/vsTestPlatform/vsTest/vsTestModel.ts create mode 100644 src/vsTestPlatform/vsTest/vsTestProtocol.d.ts create mode 100644 src/vsTestPlatform/vsTest/vsTestService.ts create mode 100644 src/vsTestPlatform/vsTest/vsTestSession.ts create mode 100644 src/vsTestPlatform/vsTestDotNet/vsTestDotNetModel.ts create mode 100644 test/mstest/.vscode/settings.json diff --git a/package-lock.json b/package-lock.json index 6a6d15b..fe78123 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "dotnet-test-explorer", - "version": "0.2.1", + "version": "0.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -13,6 +13,34 @@ "samsam": "1.3.0" } }, + "@types/events": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", + "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==" + }, + "@types/fs-extra": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-3.0.3.tgz", + "integrity": "sha512-o2qkg/J2LWK+sr007+KFBBOrxzxpr9kiP0gMFC75gQJXhUn/E3pQA0kSVdxrQ3lf+rOwsRnuH0wnR5MNTotEKg==", + "requires": { + "@types/node": "6.0.87" + } + }, + "@types/glob": { + "version": "5.0.35", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-5.0.35.tgz", + "integrity": "sha512-wc+VveszMLyMWFvXLkloixT4n0harUIVZjnpzztaZ0nKLuul7Z32iMt2fUFGAaZ4y1XWjFRMtCI5ewvyh4aIeg==", + "requires": { + "@types/events": "1.2.0", + "@types/minimatch": "3.0.3", + "@types/node": "6.0.87" + } + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" + }, "@types/mocha": { "version": "2.2.42", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.42.tgz", @@ -22,8 +50,7 @@ "@types/node": { "version": "6.0.87", "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.87.tgz", - "integrity": "sha512-Xo0pYENOBaGtJUhi50KH6gdBNQmZQQxAwBArsJpBd15ncoz+LZD5Ev14vuezcw62CsQ1q6bM++7jA6jfwaAbfQ==", - "dev": true + "integrity": "sha512-Xo0pYENOBaGtJUhi50KH6gdBNQmZQQxAwBArsJpBd15ncoz+LZD5Ev14vuezcw62CsQ1q6bM++7jA6jfwaAbfQ==" }, "@types/sinon": { "version": "4.3.0", @@ -41,6 +68,11 @@ "json-stable-stringify": "1.0.1" } }, + "amd-loader": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/amd-loader/-/amd-loader-0.0.8.tgz", + "integrity": "sha1-ECKSgEDlZ+jmpvtYvaBS+BkYkgY=" + }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -155,8 +187,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "bcrypt-pbkdf": { "version": "1.0.1", @@ -196,7 +227,6 @@ "version": "1.1.8", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", - "dev": true, "requires": { "balanced-match": "1.0.0", "concat-map": "0.0.1" @@ -311,8 +341,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "convert-source-map": { "version": "1.5.0", @@ -632,6 +661,11 @@ "mime-types": "2.1.16" } }, + "freeport": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/freeport/-/freeport-1.0.5.tgz", + "integrity": "sha1-JV6KuEFwwzuoXZkOghrl9KGpvF0=" + }, "from": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", @@ -641,8 +675,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fstream": { "version": "1.0.11", @@ -689,13 +722,16 @@ } }, "glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", - "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", - "dev": true, + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", "inherits": "2.0.3", - "minimatch": "0.3.0" + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "glob-base": { @@ -1243,7 +1279,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "1.4.0", "wrappy": "1.0.2" @@ -1252,8 +1287,7 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "is": { "version": "3.2.1", @@ -1752,13 +1786,11 @@ } }, "minimatch": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", - "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", - "dev": true, + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "requires": { - "lru-cache": "2.7.3", - "sigmund": "1.0.1" + "brace-expansion": "1.1.8" } }, "minimist": { @@ -1792,6 +1824,28 @@ "mkdirp": "0.5.1", "supports-color": "1.2.0", "to-iso-string": "0.0.2" + }, + "dependencies": { + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "minimatch": "0.3.0" + } + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "dev": true, + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } + } } }, "ms": { @@ -1889,7 +1943,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1.0.2" } @@ -1942,8 +1995,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-parse": { "version": "1.0.5", @@ -2622,6 +2674,11 @@ "integrity": "sha1-+DlfhdRZJ2BnyYiqQYN6j4KHCEQ=", "dev": true }, + "typescript-collections": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/typescript-collections/-/typescript-collections-1.3.2.tgz", + "integrity": "sha512-Frfvtwym0VebbueXWEJlVkGiWjKEFStsRwusuzjh8lX8OEJ9ZbFqpYLNfPvZcxw/+nqW0cRNBeBq6SVoTjymcQ==" + }, "unique-stream": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.1.tgz", @@ -2926,8 +2983,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "xmldom": { "version": "0.1.27", diff --git a/package.json b/package.json index f144750..0273ebd 100644 --- a/package.json +++ b/package.json @@ -230,6 +230,12 @@ }, "dependencies": { "applicationinsights": "^0.21.0", - "xmldom": "^0.1.27" + "xmldom": "^0.1.27", + "@types/fs-extra": "^3.0.1", + "@types/glob": "^5.0.30", + "amd-loader": "0.0.8", + "freeport": "^1.0.5", + "glob": "^7.1.2", + "typescript-collections": "^1.2.3" } } diff --git a/src/extension.ts b/src/extension.ts index 553101d..860adbc 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -14,6 +14,7 @@ import { TestNode } from "./testNode"; import { TestResultsFile } from "./testResultsFile"; import { TestStatusCodeLensProvider } from "./testStatusCodeLensProvider"; import { Utility } from "./utility"; +import { TestManager } from "./vsTestPlatform/vsCode/vsTest/vsTestManager"; export function activate(context: vscode.ExtensionContext) { const testResults = new TestResultsFile(); @@ -44,6 +45,13 @@ export function activate(context: vscode.ExtensionContext) { testCommands.discoverTests(); + TestManager.initialize(this.context, vscode.workspace.rootPath).then(() => { + // this.isTestExplorerInitialized = true; + // this._onDidChangeTreeData.fire(); + const testService = TestManager.getInstance().getTestService(); + testCommands.vsDiscoverTests(testService); + }); + const codeLensProvider = new TestStatusCodeLensProvider(testResults); context.subscriptions.push(codeLensProvider); context.subscriptions.push(vscode.languages.registerCodeLensProvider( diff --git a/src/testCommands.ts b/src/testCommands.ts index 9abdae3..d3f4672 100644 --- a/src/testCommands.ts +++ b/src/testCommands.ts @@ -10,6 +10,7 @@ import { discoverTests } from "./testDiscovery"; import { TestNode } from "./testNode"; import { TestResultsFile } from "./testResultsFile"; import { Utility } from "./utility"; +import { VSTestServiceIDE } from "./vsTestPlatform/vsCode/vsTest/vsTestServiceIDE"; export class TestCommands { private onNewTestDiscoveryEmitter = new EventEmitter(); @@ -76,6 +77,17 @@ export class TestCommands { }); } + public vsDiscoverTests(testService: VSTestServiceIDE) { + testService.discoveryTests(vscode.workspace.rootPath).then((result) => { + if (result) { + // this._onDidChangeTreeData.fire(); + } else { + // tslint:disable-next-line:no-console + console.info("vsTestPlatform: No tests discovered."); + } + }); + } + public get onNewTestDiscovery(): Event { return this.onNewTestDiscoveryEmitter.event; } diff --git a/src/vsTestPlatform/README.md b/src/vsTestPlatform/README.md new file mode 100644 index 0000000..f3e001e --- /dev/null +++ b/src/vsTestPlatform/README.md @@ -0,0 +1,7 @@ +# TODO + +- [] Get vstest platform code minimally working + - [] convert spawn to macOS + - [] vstest server spawns + - [] can make version request and get response successfully +- [] Add license fron source to all files \ No newline at end of file diff --git a/src/vsTestPlatform/vsCode/commands/testDiscoveryCommands.ts b/src/vsTestPlatform/vsCode/commands/testDiscoveryCommands.ts new file mode 100644 index 0000000..7e3882b --- /dev/null +++ b/src/vsTestPlatform/vsCode/commands/testDiscoveryCommands.ts @@ -0,0 +1,19 @@ +import * as vscode from "vscode"; +import { VSTestServiceIDE } from "../vsTest/vsTestServiceIDE" +import { TestManager } from "../vsTest/vsTestManager"; +import { TestModel, Test, TestResult, TestOutcome } from "../../vsTest/vsTestModel" + +export function RegisterTestDiscoveryCommands(context: vscode.ExtensionContext) { + new TestDiscoveryCommands(context); +} + +export class TestDiscoveryCommands { + private testService: VSTestServiceIDE; + constructor(private context: vscode.ExtensionContext) { + //this.testService = TestManager.getInstance().getTestService(); + + //const runCommand = vscode.commands.registerCommand("vstest.execution.run", + // test => this.runTests([test])); + //context.subscriptions.push(runCommand); + } +} \ No newline at end of file diff --git a/src/vsTestPlatform/vsCode/commands/testExecutionCommands.ts b/src/vsTestPlatform/vsCode/commands/testExecutionCommands.ts new file mode 100644 index 0000000..c1aa1d9 --- /dev/null +++ b/src/vsTestPlatform/vsCode/commands/testExecutionCommands.ts @@ -0,0 +1,32 @@ +import * as vscode from "vscode"; +import { VSTestServiceIDE } from "../vsTest/vsTestServiceIDE" +import { TestManager } from "../vsTest/vsTestManager"; +import { TestModel, Test, TestResult, TestOutcome } from "../../vsTest/vsTestModel" + +export function RegisterTestExecutionCommands(context: vscode.ExtensionContext) { + new TestExecutionCommands(context); +} + +export class TestExecutionCommands { + private testService: VSTestServiceIDE; + constructor(private context: vscode.ExtensionContext) { + //this.testService = TestManager.getInstance().getTestService(); + + + + //const runAllCommand = vscode.commands.registerCommand("vstest.execution.runAll", + // test => this.runTests(this.testService.getModel().getTests())); + //context.subscriptions.push(runAllCommand); + + //const debugAllCommand = vscode.commands.registerCommand("vstest.execution.debugAll", + // test => this.runTests(this.testService.getModel().getTests(), true)); + //context.subscriptions.push(debugAllCommand); + } + + + runTests(test: Array, debuggingEnabled: boolean = false): void { + this.testService.runTests(test, debuggingEnabled); + } + + +} \ No newline at end of file diff --git a/src/vsTestPlatform/vsCode/config.ts b/src/vsTestPlatform/vsCode/config.ts new file mode 100644 index 0000000..3cbd3a1 --- /dev/null +++ b/src/vsTestPlatform/vsCode/config.ts @@ -0,0 +1,23 @@ +import * as vscode from "vscode"; +import { IVSTestConfig } from "../vsTest/vsTestConfig"; + +export function getCurrentAdapterName() { + //return vscode.workspace.getConfiguration("vstest.adapterName"); + return "dotnet"; +} + +export function getConfigurationForAdatper(): IVSTestConfig { + return vscode.workspace.getConfiguration(`vstest.${getCurrentAdapterName()}`) as any; +} + +export function isExtensionEnabled(): boolean { + const configuration = vscode.workspace.getConfiguration(`vstest`); + const value = configuration.get("enable"); + return value == true || !value; +} + +export function isAutoInitializeEnabled(): boolean { + const configuration = vscode.workspace.getConfiguration(`vstest`); + const value = configuration.get("autoInitialize"); + return value == true; +} \ No newline at end of file diff --git a/src/vsTestPlatform/vsCode/console/testOutputChannel.ts b/src/vsTestPlatform/vsCode/console/testOutputChannel.ts new file mode 100644 index 0000000..337f02f --- /dev/null +++ b/src/vsTestPlatform/vsCode/console/testOutputChannel.ts @@ -0,0 +1,9 @@ +import * as vscode from "vscode"; + +export class TestOutputChannel { + private outputChannel = vscode.window.createOutputChannel('vsTest'); + + public appendData(value : string) { + this.outputChannel.appendLine(value); + } +} \ No newline at end of file diff --git a/src/vsTestPlatform/vsCode/testTreeDataProvider/groupByFilter.ts b/src/vsTestPlatform/vsCode/testTreeDataProvider/groupByFilter.ts new file mode 100644 index 0000000..aa4a19d --- /dev/null +++ b/src/vsTestPlatform/vsCode/testTreeDataProvider/groupByFilter.ts @@ -0,0 +1,68 @@ +import * as vscode from "vscode"; + +export enum GroupByQuickPickItemType { + Duration, + Outcome, + Class, +} + +class GroupByQuickPickItem implements vscode.QuickPickItem { + type: GroupByQuickPickItemType; + /** + * A human readable string which is rendered prominent. + */ + label: string; + + /** + * A human readable string which is rendered less prominent. + */ + description: string; + + /** + * A human readable string which is rendered less prominent. + */ + detail?: string; + + constructor(type, label, description, detail?) { + this.type = type; + this.label = label; + this.description = description; + this.detail = detail; + } +} + +export class GroupByFilter { + private items: Array = new Array(); + + private selected: GroupByQuickPickItem; + + constructor() { + const groupByOutcome = new GroupByQuickPickItem(GroupByQuickPickItemType.Outcome, "Outcome", "Groups tests by execution results: Failed Tests, Skipped Tests, Passed Tests.") + this.items.push(groupByOutcome); + this.items.push(new GroupByQuickPickItem(GroupByQuickPickItemType.Duration, "Duration", "Groups test by execution time: Fast, Medium, and Slow.")); + this.items.push(new GroupByQuickPickItem(GroupByQuickPickItemType.Class, "Class", "Groups tests by method class")); + + this.selected = groupByOutcome; + } + + getQuickPickOptions(): vscode.QuickPickOptions { + return { placeHolder: "Select how you would like to group you test files" }; + } + + show(): Promise { + + return new Promise((resolve, reject) => { + vscode.window.showQuickPick(this.items, this.getQuickPickOptions()).then((value: GroupByQuickPickItem) => { + if (value != null) { + this.selected = value; + } + return resolve(this.selected); + }); + }); + } + + getSelected(): GroupByQuickPickItem { + return this.selected; + } + +} \ No newline at end of file diff --git a/src/vsTestPlatform/vsCode/testTreeDataProvider/testTreeDataProvider.ts b/src/vsTestPlatform/vsCode/testTreeDataProvider/testTreeDataProvider.ts new file mode 100644 index 0000000..42cd65d --- /dev/null +++ b/src/vsTestPlatform/vsCode/testTreeDataProvider/testTreeDataProvider.ts @@ -0,0 +1,507 @@ +import * as vscode from "vscode"; +import * as Utils from "../Utils"; +import * as Collections from "typescript-collections"; +import { TestManager } from "../vsTest/vsTestManager"; +import { TestModel, Test, TestResult, TestOutcome } from "../../vsTest/vsTestModel" +import { VSTestServiceIDE } from "../vsTest/vsTestServiceIDE" +import { VSTestServiceStatus } from "../../vsTest/vsTestService" +import { getConfigurationForAdatper, getCurrentAdapterName, isAutoInitializeEnabled } from "../config"; +import { GroupByFilter, GroupByQuickPickItemType } from "./groupByFilter" + +export function RegisterVSTestTreeProvider(context: vscode.ExtensionContext) { + let testTreeDataProvider: TestTreeDataProvider; + testTreeDataProvider = new TestTreeDataProvider(context); + vscode.window.registerTreeDataProvider("vstest.explorer.vsTestTree", testTreeDataProvider); +} + +/** + * A class to handle the group lables of the tests + */ +class Label { + private tests: Array; + + private displayName: string; + + private outcome: TestOutcome; + + + + constructor(displayName: string, outcome: TestOutcome, tests: Array = null) { + this.displayName = displayName; + this.tests = tests; + this.outcome = outcome; + } + + public getChildrenLenght(): number { + return this.getChildren() ? this.getChildren().length : 0; + } + + public getDisplayName() { + return `${this.displayName} (${this.getChildrenLenght()})`; + } + + public setTests(tests: Array) { + this.tests = tests; + } + + public getChildren(): Array { + return this.tests; + } + + public getOutcome(): TestOutcome { + return this.outcome; + } + + public getId(): string { + return this.displayName; + } +} + +/** + * Type that the tree provider handles + */ +type TestTreeType = Label | Test; + +/** + * Additional data to help the tree data provider + */ +class TestAdditionalData { + collapsibleState: vscode.TreeItemCollapsibleState; +} + +export class TestTreeDataProvider implements vscode.TreeDataProvider { + public _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); + readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; + + private groupByFilter: GroupByFilter = new GroupByFilter(); + + private testService: VSTestServiceIDE; + + private testsAdditionalData: Collections.Dictionary = new Collections.Dictionary(); + + private selectedItem: TestTreeType = null; + + + + private isTestExplorerInitialized = false; + + constructor(private context: vscode.ExtensionContext) { + + const disposable = vscode.commands.registerCommand("vstest.explorer.open", event => this.goToTestLocation(event)); + context.subscriptions.push(disposable); + + const runCommand = vscode.commands.registerCommand("vstest.execution.runSelected", + (item) => { + if (item) { + this.runTests(item); + } + else { + this.runTests(this.selectedItem); + } + }); + context.subscriptions.push(runCommand); + + const debugCommand = vscode.commands.registerCommand("vstest.execution.debugSelected", + (item) => { + if (item) { + this.debugTests(item); + } + else { + this.debugTests(this.selectedItem); + } + }); + context.subscriptions.push(debugCommand); + + const restartExplorerCommand = vscode.commands.registerCommand("vstest.explorer.restart", + () => this.restart()); + context.subscriptions.push(restartExplorerCommand); + + const groupByExplorerCommand = vscode.commands.registerCommand("vstest.explorer.groupBy", + () => this.selectGroupBy()); + context.subscriptions.push(groupByExplorerCommand); + + const refreshExplorerCommand = vscode.commands.registerCommand("vstest.explorer.refresh", + () => this.discoveryTests()); + context.subscriptions.push(refreshExplorerCommand); + + const runAllTestCommand = vscode.commands.registerCommand("vstest.execution.runAll", + () => this.runAllTests()); + context.subscriptions.push(runAllTestCommand); + + const showTestResult = vscode.commands.registerCommand("vstest.explorer.showResult", event => this.showTestResult(event)); + context.subscriptions.push(showTestResult); + + const initializeTestExplorer = vscode.commands.registerCommand("vstest.explorer.initialize", event => this.initialize()); + context.subscriptions.push(showTestResult); + + vscode.workspace.onDidChangeConfiguration(() => { + this.testService.updateConfiguration(getCurrentAdapterName(), getConfigurationForAdatper()); + this.registerTestModelListeners(); + this.refrehTestExplorer(null); + this.discoveryTests(); + }); + + if (isAutoInitializeEnabled()) { + this.initialize(); + } + } + + private restart(): void { + this.isTestExplorerInitialized = false; + TestManager.getInstance().restart(vscode.workspace.rootPath).then(() => { + this.testService = TestManager.getInstance().getTestService(); + this.isTestExplorerInitialized = true; + this.registerTestServiceListeners(); + this.registerTestModelListeners(); + this.refrehTestExplorer(null); + this.discoveryTests(); + }); + } + + private discoveryTests() { + this.testService.discoveryTests(vscode.workspace.rootPath).then((result) => { + if (result) { + this._onDidChangeTreeData.fire(); + } + else { + } + }); + } + + private _createLaunchConfiguration(program: string, args: string, cwd: string, debuggerEventsPipeName: string) { + let debugOptions = vscode.workspace.getConfiguration('csharp').get('unitTestDebuggingOptions'); + + // Get the initial set of options from the workspace setting + let result: any; + if (typeof debugOptions === "object") { + // clone the options object to avoid changing it + result = JSON.parse(JSON.stringify(debugOptions)); + } else { + result = {}; + } + + if (!result.type) { + result.type = "coreclr"; + } + + // Now fill in the rest of the options + result.name = ".NET Test Launch"; + result.request = "launch"; + result.debuggerEventsPipeName = debuggerEventsPipeName; + result.program = program; + result.args = args; + result.cwd = cwd; + + return result; + } + + private registerTestServiceListeners() { + this.testService = TestManager.getInstance().getTestService(); + + /*this.testService.onDidTestServiceStatusChanged((status) => { + if (status == VSTestServiceStatus.Connected) { + this.isTestExplorerInitialized = true; + this._onDidChangeTreeData.fire(); + //this.discoveryTests(); + } + });*/ + + this.testService.onDidTestServiceDebugLaunchRequest((event) => { + const response = event.Payload; + const config = this._createLaunchConfiguration(response.FileName, response.Arguments, response.WorkingDirectory, null); + vscode.commands.executeCommand('vscode.startDebug', config) + }); + + + } + + private registerTestModelListeners() { + this.testService.getModel().onDidTestChanged((test: Test) => { + this._onDidChangeTreeData.fire(); + //this.refrehTestExplorer(null); + }); + } + + private initialize() { + // only allow to initialize if not initialized yet + if (this.isTestExplorerInitialized) { + return; + } + // initilize the test manager + TestManager.initialize(this.context, vscode.workspace.rootPath).then(() => { + this.isTestExplorerInitialized = true; + this._onDidChangeTreeData.fire(); + this.discoveryTests(); + }); + + this.registerTestServiceListeners(); + this.registerTestModelListeners(); + } + + private runTests(test: TestTreeType, debuggingEnabled: boolean = false): void { + if (!test) { + vscode.window.showWarningMessage("You need to select a test or a test group first."); + } + if (test instanceof Test) { + this.testService.runTests([test], debuggingEnabled); + } + else if (test instanceof Label) { + this.testService.runTests((