@@ -11,12 +11,14 @@ export async function commonRunTestsHandler(controller: vscode.TestController, r
1111 // For each authority (i.e. server:namespace) accumulate a map of the class-level Test nodes in the tree.
1212 // We don't yet support running only some TestXXX methods in a testclass
1313 const mapAuthorities = new Map < string , Map < string , vscode . TestItem > > ( ) ;
14+ const runIndices : number [ ] = [ ] ;
1415 const queue : vscode . TestItem [ ] = [ ] ;
1516
1617 // Loop through all included tests, or all known tests, and add them to our queue
1718 if ( request . include ) {
1819 request . include . forEach ( test => queue . push ( test ) ) ;
1920 } else {
21+ // Run was launched from controller's root level
2022 controller . items . forEach ( test => queue . push ( test ) ) ;
2123 }
2224
@@ -25,7 +27,7 @@ export async function commonRunTestsHandler(controller: vscode.TestController, r
2527 const test = queue . pop ( ) ! ;
2628
2729 // Skip tests the user asked to exclude
28- if ( request . exclude ?. includes ( test ) ) {
30+ if ( request . exclude && request . exclude . filter ( ( excludedTest ) => excludedTest . id === test . id ) . length > 0 ) {
2931 continue ;
3032 }
3133
@@ -58,15 +60,26 @@ export async function commonRunTestsHandler(controller: vscode.TestController, r
5860 test . children . forEach ( test => queue . push ( test ) ) ;
5961 }
6062
63+ // Cancelled while building our structures?
64+ if ( cancellation . isCancellationRequested ) {
65+ return ;
66+ }
67+
6168 if ( mapAuthorities . size === 0 ) {
6269 // Nothing included
6370 vscode . window . showWarningMessage ( `Empty test run` ) ;
6471 return ;
6572 }
6673
67- if ( cancellation . isCancellationRequested ) {
68- // TODO what?
69- }
74+ // Stop debugging sessions we started
75+ cancellation . onCancellationRequested ( ( ) => {
76+ runIndices . forEach ( ( runIndex ) => {
77+ const session = allTestRuns [ runIndex ] ?. debugSession ;
78+ if ( session ) {
79+ vscode . debug . stopDebugging ( session ) ;
80+ }
81+ } ) ;
82+ } ) ;
7083
7184 for await ( const mapInstance of mapAuthorities ) {
7285
@@ -135,20 +148,43 @@ export async function commonRunTestsHandler(controller: vscode.TestController, r
135148 const runQualifiers = controller . id === `${ extensionId } -Local` ? "" : "/noload/nodelete" ;
136149 // Run tests through the debugger but only stop at breakpoints etc if user chose "Debug Test" instead of "Run Test"
137150 const runIndex = allTestRuns . push ( run ) - 1 ;
138- const configuration : vscode . DebugConfiguration = {
151+ runIndices . push ( runIndex ) ;
152+
153+ // Compute the testspec argument for %UnitTest.Manager.RunTest() call.
154+ // Typically it is a testsuite, the subfolder where we copied all the testclasses,
155+ // but if only a single method of a single class is being tested we will also specify testcase and testmethod.
156+ let testSpec = serverSpec . username ;
157+ if ( request . include ?. length === 1 ) {
158+ const idParts = request . include [ 0 ] . id . split ( ":" ) ;
159+ if ( idParts . length === 4 ) {
160+ testSpec = `${ serverSpec . username } :${ idParts [ 2 ] } :${ idParts [ 3 ] } ` ;
161+ }
162+ }
163+
164+ const configuration = {
139165 "type" : "objectscript" ,
140166 "request" : "launch" ,
141167 "name" : `${ controller . id . split ( "-" ) . pop ( ) } Tests:${ serverSpec . name } :${ namespace } :${ serverSpec . username } ` ,
142- "program" : `##class(%UnitTest.Manager).RunTest("${ serverSpec . username } ","${ runQualifiers } ")` ,
143- "testRunIndex" : runIndex ,
144- "testIdBase" : firstClassTestItem . id . split ( ":" , 2 ) . join ( ":" )
168+ "program" : `##class(%UnitTest.Manager).RunTest("${ testSpec } ","${ runQualifiers } ")` ,
169+
170+ // Extra properties needed by our DebugAdapterTracker
171+ "testingRunIndex" : runIndex ,
172+ "testingIdBase" : firstClassTestItem . id . split ( ":" , 2 ) . join ( ":" )
145173 } ;
146174 const sessionOptions : vscode . DebugSessionOptions = {
147175 noDebug : request . profile ?. kind !== vscode . TestRunProfileKind . Debug ,
148176 suppressDebugToolbar : request . profile ?. kind !== vscode . TestRunProfileKind . Debug
149177 } ;
150- if ( ! await vscode . debug . startDebugging ( folder , configuration , sessionOptions ) ) {
151- await vscode . window . showErrorMessage ( `Failed to launch testing` , { modal : true } ) ;
178+
179+ // ObjectScript debugger's initializeRequest handler needs to identify target server and namespace
180+ // and does this from current active document, so here we make sure there's a suitable one.
181+ vscode . commands . executeCommand ( "vscode.open" , oneUri , { preserveFocus : true } ) ;
182+
183+ // Start the debugger unless cancelled
184+ if ( cancellation . isCancellationRequested || ! await vscode . debug . startDebugging ( folder , configuration , sessionOptions ) ) {
185+ if ( ! cancellation . isCancellationRequested ) {
186+ await vscode . window . showErrorMessage ( `Failed to launch testing` , { modal : true } ) ;
187+ }
152188 run . end ( ) ;
153189 allTestRuns [ runIndex ] = undefined ;
154190 return ;
0 commit comments