@@ -8,10 +8,15 @@ import { CancellationToken, OutputChannel } from 'vscode';
88import { updateResultsFromXmlLogFile , PassCalculationFormulae } from '../common/xUnitParser' ;
99import { run } from '../common/runner' ;
1010import { PythonSettings } from '../../common/configSettings' ;
11+ import * as vscode from 'vscode' ;
12+ import { execPythonFile } from './../../common/utils' ;
13+ import { createDeferred } from './../../common/helpers' ;
14+ import * as os from 'os' ;
15+ import * as path from 'path' ;
1116
1217const pythonSettings = PythonSettings . getInstance ( ) ;
1318
14- export function runTest ( rootDirectory : string , tests : Tests , args : string [ ] , testsToRun ?: TestsToRun , token ?: CancellationToken , outChannel ?: OutputChannel ) : Promise < Tests > {
19+ export function runTest ( rootDirectory : string , tests : Tests , args : string [ ] , testsToRun ?: TestsToRun , token ?: CancellationToken , outChannel ?: OutputChannel , debug ?: boolean ) : Promise < Tests > {
1520 let testPaths = [ ] ;
1621 if ( testsToRun && testsToRun . testFolder ) {
1722 testPaths = testPaths . concat ( testsToRun . testFolder . map ( f => f . nameToRun ) ) ;
@@ -32,12 +37,68 @@ export function runTest(rootDirectory: string, tests: Tests, args: string[], tes
3237 return createTemporaryFile ( '.xml' ) . then ( xmlLogResult => {
3338 xmlLogFile = xmlLogResult . filePath ;
3439 xmlLogFileCleanup = xmlLogResult . cleanupCallback ;
35- if ( testPaths . length > 0 ) {
40+ if ( testPaths . length > 0 ) {
3641 // Ignore the test directories, as we're running a specific test
3742 args = args . filter ( arg => arg . trim ( ) . startsWith ( '-' ) ) ;
3843 }
3944 const testArgs = testPaths . concat ( args , [ `--junitxml=${ xmlLogFile } ` ] ) ;
40- return run ( pythonSettings . unitTest . pyTestPath , testArgs , rootDirectory , token , outChannel ) ;
45+ if ( debug ) {
46+ const def = createDeferred < any > ( ) ;
47+ const launchDef = createDeferred < any > ( ) ;
48+ const testLauncherFile = path . join ( __dirname , '..' , '..' , '..' , '..' , 'pythonFiles' , 'PythonTools' , 'testlauncher.py' ) ;
49+
50+ // start the debug adapter only once we have started the debug process
51+ // pytestlauncherargs
52+ const pytestlauncherargs = [ rootDirectory , 'my_secret' , pythonSettings . unitTest . debugPort . toString ( ) , 'pytest' ] ;
53+ let outputChannelShown = false ;
54+ execPythonFile ( pythonSettings . pythonPath , [ testLauncherFile ] . concat ( pytestlauncherargs ) . concat ( testArgs ) , rootDirectory , true , ( data : string ) => {
55+ if ( data === 'READY' + os . EOL ) {
56+ // debug socket server has started
57+ launchDef . resolve ( ) ;
58+ }
59+ else {
60+ if ( ! outputChannelShown ) {
61+ outputChannelShown = true ;
62+ outChannel . show ( ) ;
63+ }
64+ outChannel . append ( data ) ;
65+ }
66+ } , token ) . catch ( reason => {
67+ if ( ! def . rejected && ! def . resolved ) {
68+ def . reject ( reason ) ;
69+ }
70+ } ) . then ( ( ) => {
71+ if ( ! def . rejected && ! def . resolved ) {
72+ def . resolve ( ) ;
73+ }
74+ } ) . catch ( reason => {
75+ if ( ! def . rejected && ! def . resolved ) {
76+ def . reject ( reason ) ;
77+ }
78+ } ) ;
79+
80+ launchDef . promise . then ( ( ) => {
81+ return vscode . commands . executeCommand ( 'vscode.startDebug' , {
82+ "name" : "Debug Unit Test" ,
83+ "type" : "python" ,
84+ "request" : "attach" ,
85+ "localRoot" : rootDirectory ,
86+ "remoteRoot" : rootDirectory ,
87+ "port" : pythonSettings . unitTest . debugPort ,
88+ "secret" : "my_secret" ,
89+ "host" : "localhost"
90+ } ) ;
91+ } ) . catch ( reason => {
92+ if ( ! def . rejected && ! def . resolved ) {
93+ def . reject ( reason ) ;
94+ }
95+ } ) ;
96+
97+ return def . promise ;
98+ }
99+ else {
100+ return run ( pythonSettings . unitTest . pyTestPath , testArgs , rootDirectory , token , outChannel ) ;
101+ }
41102 } ) . then ( ( ) => {
42103 return updateResultsFromLogFiles ( tests , xmlLogFile ) ;
43104 } ) . then ( result => {
0 commit comments