@@ -6,12 +6,17 @@ import { updateResults } from '../common/testUtils';
66import { updateResultsFromXmlLogFile , PassCalculationFormulae } from '../common/xUnitParser' ;
77import { run } from '../common/runner' ;
88import { PythonSettings } from '../../common/configSettings' ;
9+ import * as vscode from 'vscode' ;
10+ import { execPythonFile } from './../../common/utils' ;
11+ import { createDeferred } from './../../common/helpers' ;
12+ import * as os from 'os' ;
13+ import * as path from 'path' ;
914
1015const pythonSettings = PythonSettings . getInstance ( ) ;
1116const WITH_XUNIT = '--with-xunit' ;
1217const XUNIT_FILE = '--xunit-file' ;
1318
14- export function runTest ( rootDirectory : string , tests : Tests , args : string [ ] , testsToRun ?: TestsToRun , token ?: CancellationToken , outChannel ?: OutputChannel ) : Promise < any > {
19+ export function runTest ( rootDirectory : string , tests : Tests , args : string [ ] , testsToRun ?: TestsToRun , token ?: CancellationToken , outChannel ?: OutputChannel , debug ?: boolean ) : Promise < any > {
1520 let testPaths = [ ] ;
1621 if ( testsToRun && testsToRun . testFolder ) {
1722 testPaths = testPaths . concat ( testsToRun . testFolder . map ( f => f . nameToRun ) ) ;
@@ -59,7 +64,63 @@ export function runTest(rootDirectory: string, tests: Tests, args: string[], tes
5964 }
6065
6166 return promiseToGetXmlLogFile . then ( ( ) => {
62- return run ( pythonSettings . unitTest . nosetestPath , noseTestArgs . concat ( testPaths ) , rootDirectory , token , outChannel ) ;
67+ if ( debug === true ) {
68+ const def = createDeferred < any > ( ) ;
69+ const launchDef = createDeferred < any > ( ) ;
70+ const testLauncherFile = path . join ( __dirname , '..' , '..' , '..' , '..' , 'pythonFiles' , 'PythonTools' , 'testlauncher.py' ) ;
71+
72+ // start the debug adapter only once we have started the debug process
73+ // pytestlauncherargs
74+ const nosetestlauncherargs = [ rootDirectory , 'my_secret' , pythonSettings . unitTest . debugPort . toString ( ) , 'nose' ] ;
75+ let outputChannelShown = false ;
76+ execPythonFile ( pythonSettings . pythonPath , [ testLauncherFile ] . concat ( nosetestlauncherargs ) . concat ( noseTestArgs . concat ( testPaths ) ) , rootDirectory , true , ( data : string ) => {
77+ if ( data === 'READY' + os . EOL ) {
78+ // debug socket server has started
79+ launchDef . resolve ( ) ;
80+ }
81+ else {
82+ if ( ! outputChannelShown ) {
83+ outputChannelShown = true ;
84+ outChannel . show ( ) ;
85+ }
86+ outChannel . append ( data ) ;
87+ }
88+ } , token ) . catch ( reason => {
89+ if ( ! def . rejected && ! def . resolved ) {
90+ def . reject ( reason ) ;
91+ }
92+ } ) . then ( ( ) => {
93+ if ( ! def . rejected && ! def . resolved ) {
94+ def . resolve ( ) ;
95+ }
96+ } ) . catch ( reason => {
97+ if ( ! def . rejected && ! def . resolved ) {
98+ def . reject ( reason ) ;
99+ }
100+ } ) ;
101+
102+ launchDef . promise . then ( ( ) => {
103+ return vscode . commands . executeCommand ( 'vscode.startDebug' , {
104+ "name" : "Debug Unit Test" ,
105+ "type" : "python" ,
106+ "request" : "attach" ,
107+ "localRoot" : rootDirectory ,
108+ "remoteRoot" : rootDirectory ,
109+ "port" : pythonSettings . unitTest . debugPort ,
110+ "secret" : "my_secret" ,
111+ "host" : "localhost"
112+ } ) ;
113+ } ) . catch ( reason => {
114+ if ( ! def . rejected && ! def . resolved ) {
115+ def . reject ( reason ) ;
116+ }
117+ } ) ;
118+
119+ return def . promise ;
120+ }
121+ else {
122+ return run ( pythonSettings . unitTest . nosetestPath , noseTestArgs . concat ( testPaths ) , rootDirectory , token , outChannel ) ;
123+ }
63124 } ) . then ( ( ) => {
64125 return updateResultsFromLogFiles ( tests , xmlLogFile ) ;
65126 } ) . then ( result => {
0 commit comments