@@ -36,6 +36,14 @@ export default class TestManager extends AbstractProvider {
3636 'dotnet.test.debug' ,
3737 ( testMethod , fileName , testFrameworkName ) => this . _debugDotnetTest ( testMethod , fileName , testFrameworkName ) ) ;
3838
39+ let d4 = vscode . commands . registerCommand (
40+ 'dotnet.classTests.run' ,
41+ ( methodsInClass , fileName , testFrameworkName ) => this . _runDotnetTestsInClass ( methodsInClass , fileName , testFrameworkName ) ) ;
42+
43+ let d5 = vscode . commands . registerCommand (
44+ 'dotnet.classTests.debug' ,
45+ ( methodsInClass , fileName , testFrameworkName ) => this . _debugDotnetTestsInClass ( methodsInClass , fileName , testFrameworkName ) ) ;
46+
3947 this . _telemetryIntervalId = setInterval ( ( ) =>
4048 this . _reportTelemetry ( ) , TelemetryReportingDelay ) ;
4149
@@ -48,7 +56,7 @@ export default class TestManager extends AbstractProvider {
4856 }
4957 } ) ;
5058
51- this . addDisposables ( d1 , d2 , d3 ) ;
59+ this . addDisposables ( d1 , d2 , d3 , d4 , d5 ) ;
5260 }
5361
5462 private _getOutputChannel ( ) : vscode . OutputChannel {
@@ -126,9 +134,11 @@ export default class TestManager extends AbstractProvider {
126134
127135 private _reportResults ( results : protocol . V2 . DotNetTestResult [ ] ) : Promise < void > {
128136 const totalTests = results . length ;
137+ const output = this . _getOutputChannel ( ) ;
129138
130139 let totalPassed = 0 , totalFailed = 0 , totalSkipped = 0 ;
131140 for ( let result of results ) {
141+ output . appendLine ( `${ result . MethodName } : ${ result . Outcome } ` ) ;
132142 switch ( result . Outcome ) {
133143 case protocol . V2 . TestOutcomes . Failed :
134144 totalFailed += 1 ;
@@ -142,15 +152,35 @@ export default class TestManager extends AbstractProvider {
142152 }
143153 }
144154
145- const output = this . _getOutputChannel ( ) ;
146155 output . appendLine ( '' ) ;
147156 output . appendLine ( `Total tests: ${ totalTests } . Passed: ${ totalPassed } . Failed: ${ totalFailed } . Skipped: ${ totalSkipped } ` ) ;
148157 output . appendLine ( '' ) ;
149158
150159 return Promise . resolve ( ) ;
151160 }
152161
153- private _runDotnetTest ( testMethod : string , fileName : string , testFrameworkName : string ) {
162+ private async _recordRunAndGetFrameworkVersion ( fileName : string , testFrameworkName : string ) {
163+
164+ await this . _saveDirtyFiles ( ) ;
165+ this . _recordRunRequest ( testFrameworkName ) ;
166+ let projectInfo = await serverUtils . requestProjectInformation ( this . _server , { FileName : fileName } ) ;
167+
168+ let targetFrameworkVersion : string ;
169+
170+ if ( projectInfo . DotNetProject ) {
171+ targetFrameworkVersion = undefined ;
172+ }
173+ else if ( projectInfo . MsBuildProject ) {
174+ targetFrameworkVersion = projectInfo . MsBuildProject . TargetFramework ;
175+ }
176+ else {
177+ throw new Error ( 'Expected project.json or .csproj project.' ) ;
178+ }
179+
180+ return targetFrameworkVersion ;
181+ }
182+
183+ private async _runDotnetTest ( testMethod : string , fileName : string , testFrameworkName : string ) {
154184 const output = this . _getOutputChannel ( ) ;
155185
156186 output . show ( ) ;
@@ -161,25 +191,9 @@ export default class TestManager extends AbstractProvider {
161191 output . appendLine ( e . Message ) ;
162192 } ) ;
163193
164- this . _saveDirtyFiles ( )
165- . then ( _ => this . _recordRunRequest ( testFrameworkName ) )
166- . then ( _ => serverUtils . requestProjectInformation ( this . _server , { FileName : fileName } ) )
167- . then ( projectInfo =>
168- {
169- let targetFrameworkVersion : string ;
170-
171- if ( projectInfo . DotNetProject ) {
172- targetFrameworkVersion = undefined ;
173- }
174- else if ( projectInfo . MsBuildProject ) {
175- targetFrameworkVersion = projectInfo . MsBuildProject . TargetFramework ;
176- }
177- else {
178- throw new Error ( 'Expected project.json or .csproj project.' ) ;
179- }
194+ let targetFrameworkVersion = await this . _recordRunAndGetFrameworkVersion ( fileName , testFrameworkName ) ;
180195
181- return this . _runTest ( fileName , testMethod , testFrameworkName , targetFrameworkVersion ) ;
182- } )
196+ return this . _runTest ( fileName , testMethod , testFrameworkName , targetFrameworkVersion )
183197 . then ( results => this . _reportResults ( results ) )
184198 . then ( ( ) => listener . dispose ( ) )
185199 . catch ( reason => {
@@ -188,6 +202,37 @@ export default class TestManager extends AbstractProvider {
188202 } ) ;
189203 }
190204
205+ private async _runDotnetTestsInClass ( methodsInClass : string [ ] , fileName : string , testFrameworkName : string ) {
206+ const output = this . _getOutputChannel ( ) ;
207+
208+ output . show ( ) ;
209+ const listener = this . _server . onTestMessage ( e => {
210+ output . appendLine ( e . Message ) ;
211+ } ) ;
212+
213+ let targetFrameworkVersion = await this . _recordRunAndGetFrameworkVersion ( fileName , testFrameworkName ) ;
214+
215+ return this . _runTestsInClass ( fileName , testFrameworkName , targetFrameworkVersion , methodsInClass )
216+ . then ( results => this . _reportResults ( results ) )
217+ . then ( ( ) => listener . dispose ( ) )
218+ . catch ( reason => {
219+ listener . dispose ( ) ;
220+ vscode . window . showErrorMessage ( `Failed to run tests because ${ reason } .` ) ;
221+ } ) ;
222+ }
223+
224+ private _runTestsInClass ( fileName : string , testFrameworkName : string , targetFrameworkVersion : string , methodsToRun : string [ ] ) : Promise < protocol . V2 . DotNetTestResult [ ] > {
225+ const request : protocol . V2 . RunTestsInClassRequest = {
226+ FileName : fileName ,
227+ TestFrameworkName : testFrameworkName ,
228+ TargetFrameworkVersion : targetFrameworkVersion ,
229+ MethodNames : methodsToRun
230+ } ;
231+
232+ return serverUtils . runTestsInClass ( this . _server , request )
233+ . then ( response => response . Results ) ;
234+ }
235+
191236 private _createLaunchConfiguration ( program : string , args : string , cwd : string , debuggerEventsPipeName : string ) {
192237 let debugOptions = vscode . workspace . getConfiguration ( 'csharp' ) . get ( 'unitTestDebuggingOptions' ) ;
193238
@@ -271,39 +316,64 @@ export default class TestManager extends AbstractProvider {
271316 }
272317 }
273318
274- private _debugDotnetTest ( testMethod : string , fileName : string , testFrameworkName : string ) {
275- // We support to styles of 'dotnet test' for debugging: The legacy 'project.json' testing, and the newer csproj support
276- // using VS Test. These require a different level of communication.
319+ private async _recordDebugAndGetDebugValues ( fileName : string , testFrameworkName : string , output : vscode . OutputChannel ) {
320+ await this . _saveDirtyFiles ( ) ;
321+ this . _recordDebugRequest ( testFrameworkName ) ;
322+ let projectInfo = await serverUtils . requestProjectInformation ( this . _server , { FileName : fileName } ) ;
323+
277324 let debugType : string ;
278325 let debugEventListener : DebugEventListener = null ;
279326 let targetFrameworkVersion : string ;
280327
328+ if ( projectInfo . DotNetProject ) {
329+ debugType = 'legacy' ;
330+ targetFrameworkVersion = '' ;
331+ }
332+ else if ( projectInfo . MsBuildProject ) {
333+ debugType = 'vstest' ;
334+ targetFrameworkVersion = projectInfo . MsBuildProject . TargetFramework ;
335+ debugEventListener = new DebugEventListener ( fileName , this . _server , output ) ;
336+ debugEventListener . start ( ) ;
337+ }
338+ else {
339+ throw new Error ( 'Expected project.json or .csproj project.' ) ;
340+ }
341+
342+ return { debugType, debugEventListener, targetFrameworkVersion } ;
343+ }
344+
345+ private async _debugDotnetTest ( testMethod : string , fileName : string , testFrameworkName : string ) {
346+ // We support to styles of 'dotnet test' for debugging: The legacy 'project.json' testing, and the newer csproj support
347+ // using VS Test. These require a different level of communication.
348+
281349 const output = this . _getOutputChannel ( ) ;
282350
283351 output . show ( ) ;
284352 output . appendLine ( `Debugging method '${ testMethod } '...` ) ;
285353 output . appendLine ( '' ) ;
286354
287- return this . _saveDirtyFiles ( )
288- . then ( _ => this . _recordDebugRequest ( testFrameworkName ) )
289- . then ( _ => serverUtils . requestProjectInformation ( this . _server , { FileName : fileName } ) )
290- . then ( projectInfo => {
291- if ( projectInfo . DotNetProject ) {
292- debugType = 'legacy' ;
293- targetFrameworkVersion = '' ;
294- return Promise . resolve ( ) ;
295- }
296- else if ( projectInfo . MsBuildProject ) {
297- debugType = 'vstest' ;
298- targetFrameworkVersion = projectInfo . MsBuildProject . TargetFramework ;
299- debugEventListener = new DebugEventListener ( fileName , this . _server , output ) ;
300- return debugEventListener . start ( ) ;
301- }
302- else {
303- throw new Error ( 'Expected project.json or .csproj project.' ) ;
304- }
355+ let { debugType, debugEventListener, targetFrameworkVersion } = await this . _recordDebugAndGetDebugValues ( fileName , testFrameworkName , output ) ;
356+
357+ return this . _getLaunchConfiguration ( debugType , fileName , testMethod , testFrameworkName , targetFrameworkVersion , debugEventListener )
358+ . then ( config => {
359+ const workspaceFolder = vscode . workspace . getWorkspaceFolder ( vscode . Uri . file ( fileName ) ) ;
360+ return vscode . debug . startDebugging ( workspaceFolder , config ) ;
305361 } )
306- . then ( ( ) => this . _getLaunchConfiguration ( debugType , fileName , testMethod , testFrameworkName , targetFrameworkVersion , debugEventListener ) )
362+ . catch ( reason => {
363+ vscode . window . showErrorMessage ( `Failed to start debugger: ${ reason } ` ) ;
364+ if ( debugEventListener != null ) {
365+ debugEventListener . close ( ) ;
366+ }
367+ } ) ;
368+ }
369+
370+ private async _debugDotnetTestsInClass ( methodsToRun : string [ ] , fileName : string , testFrameworkName : string ) {
371+
372+ const output = this . _getOutputChannel ( ) ;
373+
374+ let { debugType, debugEventListener, targetFrameworkVersion } = await this . _recordDebugAndGetDebugValues ( fileName , testFrameworkName , output ) ;
375+
376+ return await this . _getLaunchConfigurationForClass ( debugType , fileName , methodsToRun , testFrameworkName , targetFrameworkVersion , debugEventListener )
307377 . then ( config => {
308378 const workspaceFolder = vscode . workspace . getWorkspaceFolder ( vscode . Uri . file ( fileName ) ) ;
309379 return vscode . debug . startDebugging ( workspaceFolder , config ) ;
@@ -315,6 +385,34 @@ export default class TestManager extends AbstractProvider {
315385 }
316386 } ) ;
317387 }
388+
389+ private _getLaunchConfigurationForClass ( debugType : string , fileName : string , methodsToRun : string [ ] , testFrameworkName : string , targetFrameworkVersion : string , debugEventListener : DebugEventListener ) : Promise < any > {
390+ if ( debugType == 'vstest' ) {
391+ return this . _getLaunchConfigurationForVSTestClass ( fileName , methodsToRun , testFrameworkName , targetFrameworkVersion , debugEventListener ) ;
392+ }
393+ throw new Error ( `Unexpected debug type: ${ debugType } ` ) ;
394+ }
395+
396+ private _getLaunchConfigurationForVSTestClass ( fileName : string , methodsToRun : string [ ] , testFrameworkName : string , targetFrameworkVersion : string , debugEventListener : DebugEventListener ) : Promise < any > {
397+ const output = this . _getOutputChannel ( ) ;
398+
399+ const listener = this . _server . onTestMessage ( e => {
400+ output . appendLine ( e . Message ) ;
401+ } ) ;
402+
403+ const request : protocol . V2 . DebugTestClassGetStartInfoRequest = {
404+ FileName : fileName ,
405+ MethodNames : methodsToRun ,
406+ TestFrameworkName : testFrameworkName ,
407+ TargetFrameworkVersion : targetFrameworkVersion
408+ } ;
409+
410+ return serverUtils . debugTestClassGetStartInfo ( this . _server , request )
411+ . then ( response => {
412+ listener . dispose ( ) ;
413+ return this . _createLaunchConfiguration ( response . FileName , response . Arguments , response . WorkingDirectory , debugEventListener . pipePath ( ) ) ;
414+ } ) ;
415+ }
318416}
319417
320418class DebugEventListener {
0 commit comments