Skip to content

Commit

Permalink
Fix debugging tests (#304)
Browse files Browse the repository at this point in the history
Fixes #89 
- Get a random port if the one prescribed is not available
- Perform handshake with support for data being streamed
  • Loading branch information
DonJayamanne authored Nov 28, 2017
1 parent d324979 commit 2b7dc71
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 26 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1568,6 +1568,7 @@
"diff-match-patch": "^1.0.0",
"fs-extra": "^4.0.2",
"fuzzy": "^0.1.3",
"get-port": "^3.2.0",
"line-by-line": "^0.1.5",
"lodash": "^4.17.4",
"minimatch": "^3.0.3",
Expand All @@ -1591,6 +1592,7 @@
"@types/chai": "^4.0.4",
"@types/chai-as-promised": "^7.1.0",
"@types/fs-extra": "^4.0.2",
"@types/get-port": "^3.2.0",
"@types/jquery": "^1.10.31",
"@types/lodash": "^4.14.74",
"@types/mocha": "^2.2.43",
Expand Down
23 changes: 18 additions & 5 deletions src/client/unittests/common/debugLauncher.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,36 @@
import * as getFreePort from 'get-port';
import * as os from 'os';
import { CancellationToken, debug, OutputChannel, Uri, workspace } from 'vscode';
import { PythonSettings } from '../../common/configSettings';
import { createDeferred } from './../../common/helpers';
import { execPythonFile } from './../../common/utils';
import { ITestDebugLauncher } from './types';

const HAND_SHAKE = `READY${os.EOL}`;

export class DebugLauncher implements ITestDebugLauncher {
public async launchDebugger(rootDirectory: string, testArgs: string[], token?: CancellationToken, outChannel?: OutputChannel) {
public getPort(resource?: Uri): Promise<number> {
const pythonSettings = PythonSettings.getInstance(resource);
const port = pythonSettings.unitTest.debugPort;
return new Promise<number>((resolve, reject) => getFreePort({ host: 'localhost', port }).then(resolve, reject));
}
public async launchDebugger(rootDirectory: string, testArgs: string[], port: number, token?: CancellationToken, outChannel?: OutputChannel) {
const pythonSettings = PythonSettings.getInstance(rootDirectory ? Uri.file(rootDirectory) : undefined);
// tslint:disable-next-line:no-any
const def = createDeferred<any>();
// tslint:disable-next-line:no-any
const launchDef = createDeferred<any>();
let outputChannelShown = false;
let accumulatedData: string = '';
execPythonFile(rootDirectory, pythonSettings.pythonPath, testArgs, rootDirectory, true, (data: string) => {
if (data.startsWith(`READY${os.EOL}`)) {
// debug socket server has started.
if (!launchDef.resolved) {
accumulatedData += data;
if (!accumulatedData.startsWith(HAND_SHAKE)) {
return;
}
// Socket server has started, lets start the debugger.
launchDef.resolve();
data = data.substring((`READY${os.EOL}`).length);
data = accumulatedData.substring(HAND_SHAKE.length);
}

if (!outputChannelShown) {
Expand Down Expand Up @@ -53,7 +66,7 @@ export class DebugLauncher implements ITestDebugLauncher {
request: 'attach',
localRoot: rootDirectory,
remoteRoot: rootDirectory,
port: pythonSettings.unitTest.debugPort,
port,
secret: 'my_secret',
host: 'localhost'
});
Expand Down
3 changes: 2 additions & 1 deletion src/client/unittests/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,5 +148,6 @@ export interface ITestResultsService {
}

export interface ITestDebugLauncher {
launchDebugger(rootDirectory: string, testArgs: string[], token?: CancellationToken, outChannel?: OutputChannel): Promise<Tests>;
getPort(resource?: Uri): Promise<number>;
launchDebugger(rootDirectory: string, testArgs: string[], port: number, token?: CancellationToken, outChannel?: OutputChannel): Promise<Tests>;
}
15 changes: 9 additions & 6 deletions src/client/unittests/nosetest/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,17 @@ export function runTest(testResultsService: ITestResultsService, debugLauncher:
}

return promiseToGetXmlLogFile.then(() => {
const pythonSettings = PythonSettings.getInstance(Uri.file(rootDirectory));
if (debug === true) {
const testLauncherFile = path.join(__dirname, '..', '..', '..', '..', 'pythonFiles', 'PythonTools', 'testlauncher.py');
const nosetestlauncherargs = [rootDirectory, 'my_secret', pythonSettings.unitTest.debugPort.toString(), 'nose'];
const debuggerArgs = [testLauncherFile].concat(nosetestlauncherargs).concat(noseTestArgs.concat(testPaths));
// tslint:disable-next-line:prefer-type-cast no-any
return debugLauncher.launchDebugger(rootDirectory, debuggerArgs, token, outChannel) as Promise<any>;
return debugLauncher.getPort(Uri.file(rootDirectory))
.then(debugPort => {
const testLauncherFile = path.join(__dirname, '..', '..', '..', '..', 'pythonFiles', 'PythonTools', 'testlauncher.py');
const nosetestlauncherargs = [rootDirectory, 'my_secret', debugPort.toString(), 'nose'];
const debuggerArgs = [testLauncherFile].concat(nosetestlauncherargs).concat(noseTestArgs.concat(testPaths));
// tslint:disable-next-line:prefer-type-cast no-any
return debugLauncher.launchDebugger(rootDirectory, debuggerArgs, debugPort, token, outChannel) as Promise<any>;
});
} else {
const pythonSettings = PythonSettings.getInstance(Uri.file(rootDirectory));
// tslint:disable-next-line:prefer-type-cast no-any
return run(pythonSettings.unitTest.nosetestPath, noseTestArgs.concat(testPaths), rootDirectory, token, outChannel) as Promise<any>;
}
Expand Down
15 changes: 9 additions & 6 deletions src/client/unittests/pytest/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,17 @@ export function runTest(testResultsService: ITestResultsService, debugLauncher:
args = args.filter(arg => arg.trim().startsWith('-'));
}
const testArgs = testPaths.concat(args, [`--junitxml=${xmlLogFile}`]);
const pythonSettings = PythonSettings.getInstance(Uri.file(rootDirectory));
if (debug) {
const testLauncherFile = path.join(__dirname, '..', '..', '..', '..', 'pythonFiles', 'PythonTools', 'testlauncher.py');
const pytestlauncherargs = [rootDirectory, 'my_secret', pythonSettings.unitTest.debugPort.toString(), 'pytest'];
const debuggerArgs = [testLauncherFile].concat(pytestlauncherargs).concat(testArgs);
// tslint:disable-next-line:prefer-type-cast no-any
return debugLauncher.launchDebugger(rootDirectory, debuggerArgs, token, outChannel) as Promise<any>;
return debugLauncher.getPort(Uri.file(rootDirectory))
.then(debugPort => {
const testLauncherFile = path.join(__dirname, '..', '..', '..', '..', 'pythonFiles', 'PythonTools', 'testlauncher.py');
const pytestlauncherargs = [rootDirectory, 'my_secret', debugPort.toString(), 'pytest'];
const debuggerArgs = [testLauncherFile].concat(pytestlauncherargs).concat(testArgs);
// tslint:disable-next-line:prefer-type-cast no-any
return debugLauncher.launchDebugger(rootDirectory, debuggerArgs, debugPort, token, outChannel) as Promise<any>;
});
} else {
const pythonSettings = PythonSettings.getInstance(Uri.file(rootDirectory));
// tslint:disable-next-line:prefer-type-cast no-any
return run(pythonSettings.unitTest.pyTestPath, testArgs, rootDirectory, token, outChannel) as Promise<any>;
}
Expand Down
12 changes: 6 additions & 6 deletions src/client/unittests/unittest/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,6 @@ export function runTest(testManager: BaseTestManager, testResultsService: ITestR
testArgs = testArgs.filter(arg => arg !== '--uf');

testArgs.push(`--result-port=${port}`);
if (debug === true) {
const debugPort = PythonSettings.getInstance(Uri.file(rootDirectory)).unitTest.debugPort;
testArgs.push(...['--secret=my_secret', `--port=${debugPort}`]);
}
testArgs.push(`--us=${startTestDiscoveryDirectory}`);
if (testId.length > 0) {
testArgs.push(`-t${testId}`);
Expand All @@ -97,8 +93,12 @@ export function runTest(testManager: BaseTestManager, testResultsService: ITestR
testArgs.push(`--testFile=${testFile}`);
}
if (debug === true) {
// tslint:disable-next-line:prefer-type-cast no-any
return debugLauncher.launchDebugger(rootDirectory, [testLauncherFile].concat(testArgs), token, outChannel);
return debugLauncher.getPort(Uri.file(rootDirectory))
.then(debugPort => {
testArgs.push(...['--secret=my_secret', `--port=${debugPort}`]);
// tslint:disable-next-line:prefer-type-cast no-any
return debugLauncher.launchDebugger(rootDirectory, [testLauncherFile].concat(testArgs), debugPort, token, outChannel);
});
} else {
// tslint:disable-next-line:prefer-type-cast no-any
return run(PythonSettings.getInstance(Uri.file(rootDirectory)).pythonPath, [testLauncherFile].concat(testArgs), rootDirectory, token, outChannel);
Expand Down
7 changes: 5 additions & 2 deletions src/test/unittests/mocks.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CancellationToken, Disposable, OutputChannel } from 'vscode';
import { CancellationToken, Disposable, OutputChannel, Uri } from 'vscode';
import { createDeferred, Deferred } from '../../client/common/helpers';
import { Product } from '../../client/common/installer';
import { BaseTestManager } from '../../client/unittests/common/baseTestManager';
Expand All @@ -25,7 +25,10 @@ export class MockDebugLauncher implements ITestDebugLauncher, Disposable {
constructor() {
this._launched = createDeferred<boolean>();
}
public async launchDebugger(rootDirectory: string, testArgs: string[], token?: CancellationToken, outChannel?: OutputChannel): Promise<Tests> {
public async getPort(resource?: Uri): Promise<number> {
return 0;
}
public async launchDebugger(rootDirectory: string, testArgs: string[], debugPort: number, token?: CancellationToken, outChannel?: OutputChannel): Promise<Tests> {
this._launched.resolve(true);
// tslint:disable-next-line:no-non-null-assertion
this._token = token!;
Expand Down

0 comments on commit 2b7dc71

Please sign in to comment.