From ec65dd0b90737da842d40558ff0cbafff5e82711 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 4 Apr 2018 16:08:33 -0700 Subject: [PATCH 01/10] :sparkles: remote path mapping --- package.json | 25 +++++++++ src/client/debugger/Common/Contracts.ts | 2 + .../configProviders/pythonV2Provider.ts | 8 +++ src/test/debugger/attach.ptvsd.test.ts | 54 ++++++++++++++----- 4 files changed, 76 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 7925bb4a33f9..c4a700a4060e 100644 --- a/package.json +++ b/package.json @@ -1017,6 +1017,31 @@ }, "default": [] }, + "pathMappings": { + "type": "array", + "label": "Additional path mappings.", + "items": { + "type": "object", + "label": "Path mapping", + "required": [ + "localRoot", + "remoteRoot" + ], + "properties": { + "localRoot": { + "type": "string", + "label": "Local source root.", + "default": "" + }, + "remoteRoot": { + "type": "string", + "label": "Remote source root.", + "default": "" + } + } + }, + "default": [] + }, "logToFile": { "type": "boolean", "description": "Enable logging of debugger events to a log file.", diff --git a/src/client/debugger/Common/Contracts.ts b/src/client/debugger/Common/Contracts.ts index 09dd275528a6..cd32d1304dfd 100644 --- a/src/client/debugger/Common/Contracts.ts +++ b/src/client/debugger/Common/Contracts.ts @@ -80,6 +80,8 @@ export interface AttachRequestArguments extends DebugProtocol.AttachRequestArgum host?: string; secret?: string; logToFile?: boolean; + pathMappings?: { localRoot: string; remoteRoot: string }[]; + debugOptions?: DebugOptions[]; } export interface IDebugServer { diff --git a/src/client/debugger/configProviders/pythonV2Provider.ts b/src/client/debugger/configProviders/pythonV2Provider.ts index d356901f45ab..417822d81e8f 100644 --- a/src/client/debugger/configProviders/pythonV2Provider.ts +++ b/src/client/debugger/configProviders/pythonV2Provider.ts @@ -39,5 +39,13 @@ export class PythonV2DebugConfigurationProvider extends BaseConfigurationProvide if (this.serviceContainer.get(IPlatformService).isWindows) { debugConfiguration.debugOptions.push(DebugOptions.FixFilePathCase); } + + if (!debugConfiguration.pathMappings) { + debugConfiguration.pathMappings = []; + } + debugConfiguration.pathMappings!.push({ + localRoot: debugConfiguration.localRoot, + remoteRoot: debugConfiguration.remoteRoot + }); } } diff --git a/src/test/debugger/attach.ptvsd.test.ts b/src/test/debugger/attach.ptvsd.test.ts index 76ccd4944034..a95eed099201 100644 --- a/src/test/debugger/attach.ptvsd.test.ts +++ b/src/test/debugger/attach.ptvsd.test.ts @@ -8,6 +8,8 @@ import { ChildProcess, spawn } from 'child_process'; import * as getFreePort from 'get-port'; import * as path from 'path'; +import * as TypeMoq from 'typemoq'; +import { DebugConfiguration, Uri } from 'vscode'; import { DebugClient } from 'vscode-debugadapter-testsupport'; import { EXTENSION_ROOT_DIR } from '../../client/common/constants'; import '../../client/common/extensions'; @@ -43,13 +45,7 @@ suite('Attach Debugger - Experimental', () => { } catch { } } }); - test('Confirm we are able to attach to a running program', async function () { - this.timeout(20000); - // Lets skip this test on AppVeyor (very flaky on AppVeyor). - if (IS_APPVEYOR) { - return; - } - + async function testAttachingToRemoteProcess(localRoot: string, remoteRoot: string, pathSeparator: string) { const port = await getFreePort({ host: 'localhost', port: 3000 }); const customEnv = { ...process.env }; @@ -58,6 +54,8 @@ suite('Attach Debugger - Experimental', () => { customEnv['PYTHONPATH'] = PTVSD_PATH; const pythonArgs = ['-m', 'ptvsd', '--server', '--port', `${port}`, '--file', fileToDebug.fileToCommandArgument()]; procToKill = spawn('python', pythonArgs, { env: customEnv, cwd: path.dirname(fileToDebug) }); + // wait for remote socket to start + await sleep(1000); // Send initialize, attach const initializePromise = debugClient.initializeRequest({ @@ -69,15 +67,23 @@ suite('Attach Debugger - Experimental', () => { supportsVariableType: true, supportsVariablePaging: true }); - const attachPromise = debugClient.attachRequest({ - localRoot: path.dirname(fileToDebug), - remoteRoot: path.dirname(fileToDebug), + const options: AttachRequestArguments & DebugConfiguration = { + name: 'attach', + request: 'attach', + localRoot, + remoteRoot, type: 'pythonExperimental', port: port, host: 'localhost', - logToFile: false, + logToFile: true, debugOptions: [DebugOptions.RedirectOutput] - }); + }; + const serviceContainer = TypeMoq.Mock.ofType(); + serviceContainer.setup(c => c.get(IPlatformService, TypeMoq.It.isAny())).returns(() => new PlatformService()); + const configProvider = new PythonV2DebugConfigurationProvider(serviceContainer.object); + + const launchArgs = await configProvider.resolveDebugConfiguration({ index: 0, name: 'root', uri: Uri.file(localRoot) }, options); + const attachPromise = debugClient.attachRequest(launchArgs); await Promise.all([ initializePromise, @@ -90,7 +96,9 @@ suite('Attach Debugger - Experimental', () => { const stdOutPromise = debugClient.assertOutput('stdout', 'this is stdout'); const stdErrPromise = debugClient.assertOutput('stderr', 'this is stderr'); - const breakpointLocation = { path: fileToDebug, column: 1, line: 12 }; + // Don't use path utils, as we're building the paths manually (mimic windows paths on unix test servers and vice versa). + const localFileName = `${localRoot}${pathSeparator}${path.basename(fileToDebug)}`; + const breakpointLocation = { path: localFileName, column: 1, line: 12 }; const breakpointPromise = debugClient.setBreakpointsRequest({ lines: [breakpointLocation.line], breakpoints: [{ line: breakpointLocation.line, column: breakpointLocation.column }], @@ -111,5 +119,25 @@ suite('Attach Debugger - Experimental', () => { debugClient.waitForEvent('exited'), debugClient.waitForEvent('terminated') ]); + } + test('Confirm we are able to attach to a running program', async function () { + this.timeout(20000); + // Lets skip this test on AppVeyor (very flaky on AppVeyor). + if (IS_APPVEYOR) { + return; + } + + await testAttachingToRemoteProcess(path.dirname(fileToDebug), path.dirname(fileToDebug), path.sep); + }); + test('Confirm localpath translations are done correctly', async function () { + this.timeout(20000); + // Lets skip this test on AppVeyor (very flaky on AppVeyor). + if (IS_APPVEYOR) { + return; + } + + const localWorkspace = IS_WINDOWS ? '/home/user/Desktop/project/src' : 'C:\\Project\\src'; + const pathSeparator = IS_WINDOWS ? '\\' : '/'; + await testAttachingToRemoteProcess(localWorkspace, path.dirname(fileToDebug), pathSeparator); }); }); From a10769d02005117f82d6f864891b27338a798b24 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 4 Apr 2018 16:15:15 -0700 Subject: [PATCH 02/10] :sparkles: remote path mapping --- src/test/debugger/attach.ptvsd.test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/test/debugger/attach.ptvsd.test.ts b/src/test/debugger/attach.ptvsd.test.ts index a95eed099201..c252a14c4e5d 100644 --- a/src/test/debugger/attach.ptvsd.test.ts +++ b/src/test/debugger/attach.ptvsd.test.ts @@ -111,7 +111,9 @@ suite('Attach Debugger - Experimental', () => { stdOutPromise, stdErrPromise ]); - await debugClient.assertStoppedLocation('breakpoint', breakpointLocation); + // Unfortunately PTVSD will be running on the current OS, hence paths will be concatenated using current OS path separator. + const expectedBreapointFile = path.join(localRoot, path.basename(fileToDebug)); + await debugClient.assertStoppedLocation('breakpoint', { path: expectedBreapointFile, column: 1, line: 12 }); await Promise.all([ continueDebugging(debugClient), @@ -131,6 +133,7 @@ suite('Attach Debugger - Experimental', () => { }); test('Confirm localpath translations are done correctly', async function () { this.timeout(20000); + this.retries(0); // Lets skip this test on AppVeyor (very flaky on AppVeyor). if (IS_APPVEYOR) { return; From fad9dbe6eea179eb56fbaff660cedb112251dc58 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 4 Apr 2018 16:19:43 -0700 Subject: [PATCH 03/10] :sparkles: remote path mapping --- src/test/debugger/attach.ptvsd.test.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/test/debugger/attach.ptvsd.test.ts b/src/test/debugger/attach.ptvsd.test.ts index c252a14c4e5d..110cea725501 100644 --- a/src/test/debugger/attach.ptvsd.test.ts +++ b/src/test/debugger/attach.ptvsd.test.ts @@ -111,9 +111,7 @@ suite('Attach Debugger - Experimental', () => { stdOutPromise, stdErrPromise ]); - // Unfortunately PTVSD will be running on the current OS, hence paths will be concatenated using current OS path separator. - const expectedBreapointFile = path.join(localRoot, path.basename(fileToDebug)); - await debugClient.assertStoppedLocation('breakpoint', { path: expectedBreapointFile, column: 1, line: 12 }); + await debugClient.assertStoppedLocation('breakpoint', breakpointLocation); await Promise.all([ continueDebugging(debugClient), @@ -131,7 +129,7 @@ suite('Attach Debugger - Experimental', () => { await testAttachingToRemoteProcess(path.dirname(fileToDebug), path.dirname(fileToDebug), path.sep); }); - test('Confirm localpath translations are done correctly', async function () { + test('Confirm local and remote paths are translated', async function () { this.timeout(20000); this.retries(0); // Lets skip this test on AppVeyor (very flaky on AppVeyor). @@ -140,7 +138,7 @@ suite('Attach Debugger - Experimental', () => { } const localWorkspace = IS_WINDOWS ? '/home/user/Desktop/project/src' : 'C:\\Project\\src'; - const pathSeparator = IS_WINDOWS ? '\\' : '/'; + const pathSeparator = IS_WINDOWS ? '/' : '\\'; await testAttachingToRemoteProcess(localWorkspace, path.dirname(fileToDebug), pathSeparator); }); }); From fd29890c927d9c58fa2b6ac6d3f26d55c6f59b76 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 4 Apr 2018 16:31:52 -0700 Subject: [PATCH 04/10] :hammer: additional fixes --- src/test/debugger/attach.ptvsd.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/debugger/attach.ptvsd.test.ts b/src/test/debugger/attach.ptvsd.test.ts index 110cea725501..e2443d0df6bc 100644 --- a/src/test/debugger/attach.ptvsd.test.ts +++ b/src/test/debugger/attach.ptvsd.test.ts @@ -75,15 +75,15 @@ suite('Attach Debugger - Experimental', () => { type: 'pythonExperimental', port: port, host: 'localhost', - logToFile: true, + logToFile: false, debugOptions: [DebugOptions.RedirectOutput] }; const serviceContainer = TypeMoq.Mock.ofType(); serviceContainer.setup(c => c.get(IPlatformService, TypeMoq.It.isAny())).returns(() => new PlatformService()); const configProvider = new PythonV2DebugConfigurationProvider(serviceContainer.object); - const launchArgs = await configProvider.resolveDebugConfiguration({ index: 0, name: 'root', uri: Uri.file(localRoot) }, options); - const attachPromise = debugClient.attachRequest(launchArgs); + await configProvider.resolveDebugConfiguration({ index: 0, name: 'root', uri: Uri.file(localRoot) }, options); + const attachPromise = debugClient.attachRequest(options); await Promise.all([ initializePromise, @@ -131,12 +131,12 @@ suite('Attach Debugger - Experimental', () => { }); test('Confirm local and remote paths are translated', async function () { this.timeout(20000); - this.retries(0); // Lets skip this test on AppVeyor (very flaky on AppVeyor). if (IS_APPVEYOR) { return; } + // If tests are running on windows, then treat debug client as a unix client and remote process as current OS. const localWorkspace = IS_WINDOWS ? '/home/user/Desktop/project/src' : 'C:\\Project\\src'; const pathSeparator = IS_WINDOWS ? '/' : '\\'; await testAttachingToRemoteProcess(localWorkspace, path.dirname(fileToDebug), pathSeparator); From ffe44c219b555c9d3e54ea53ccbdf645426c9e7b Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 4 Apr 2018 22:32:56 -0700 Subject: [PATCH 05/10] increase timeouts in an attempt fix #1301 --- src/test/autocomplete/base.test.ts | 5 ++++- src/test/initialize.ts | 14 ++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/test/autocomplete/base.test.ts b/src/test/autocomplete/base.test.ts index 5219e4ababd9..d67d322dbb7b 100644 --- a/src/test/autocomplete/base.test.ts +++ b/src/test/autocomplete/base.test.ts @@ -26,7 +26,10 @@ suite('Autocomplete', () => { let isPython2: boolean; let ioc: UnitTestIocContainer; - suiteSetup(async () => { + suiteSetup(async function () { + // Attempt to fix #1301 + // tslint:disable-next-line:no-invalid-this + this.timeout(60000); await initialize(); initializeDI(); isPython2 = await ioc.getPythonMajorVersion(rootWorkspaceUri) === 2; diff --git a/src/test/initialize.ts b/src/test/initialize.ts index 5914d38185e9..0a428faa4dbd 100644 --- a/src/test/initialize.ts +++ b/src/test/initialize.ts @@ -42,11 +42,17 @@ export async function initializeTest(): Promise { // Dispose any cached python settings (used only in test env). PythonSettings.dispose(); } - export async function closeActiveWindows(): Promise { - return new Promise((resolve, reject) => vscode.commands.executeCommand('workbench.action.closeAllEditors') - // tslint:disable-next-line:no-unnecessary-callback-wrapper - .then(() => resolve(), reject)); + return new Promise((resolve, reject) => { + vscode.commands.executeCommand('workbench.action.closeAllEditors') + // tslint:disable-next-line:no-unnecessary-callback-wrapper + .then(() => resolve(), reject); + // Attempt to fix #1301. + // Lets not waste too much time. + setTimeout(() => { + reject(new Error('Command \'workbench.action.closeAllEditors\' timedout')); + }, 15000); + }); } function getPythonPath(): string { From 4ee99daf96007cd4733f27eac63593066eeadaf4 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 4 Apr 2018 22:36:48 -0700 Subject: [PATCH 06/10] fix merge issues --- src/test/debugger/attach.ptvsd.test.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/test/debugger/attach.ptvsd.test.ts b/src/test/debugger/attach.ptvsd.test.ts index e2443d0df6bc..11f3ba2f72d8 100644 --- a/src/test/debugger/attach.ptvsd.test.ts +++ b/src/test/debugger/attach.ptvsd.test.ts @@ -13,8 +13,13 @@ import { DebugConfiguration, Uri } from 'vscode'; import { DebugClient } from 'vscode-debugadapter-testsupport'; import { EXTENSION_ROOT_DIR } from '../../client/common/constants'; import '../../client/common/extensions'; +import { IS_WINDOWS } from '../../client/common/platform/constants'; +import { PlatformService } from '../../client/common/platform/platformService'; +import { IPlatformService } from '../../client/common/platform/types'; +import { PythonV2DebugConfigurationProvider } from '../../client/debugger'; import { PTVSD_PATH } from '../../client/debugger/Common/constants'; -import { DebugOptions } from '../../client/debugger/Common/Contracts'; +import { AttachRequestArguments, DebugOptions } from '../../client/debugger/Common/Contracts'; +import { IServiceContainer } from '../../client/ioc/types'; import { sleep } from '../common'; import { initialize, IS_APPVEYOR, IS_MULTI_ROOT_TEST, TEST_DEBUGGER } from '../initialize'; import { continueDebugging, createDebugAdapter } from './utils'; From b13c066ee355132f7ebd4a67224e0395a28219ee Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 5 Apr 2018 00:03:45 -0700 Subject: [PATCH 07/10] increase timeout --- src/test/autocomplete/base.test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/test/autocomplete/base.test.ts b/src/test/autocomplete/base.test.ts index d67d322dbb7b..f3289e359dfd 100644 --- a/src/test/autocomplete/base.test.ts +++ b/src/test/autocomplete/base.test.ts @@ -22,7 +22,10 @@ const fileEncodingUsed = path.join(autoCompPath, 'five.py'); const fileSuppress = path.join(autoCompPath, 'suppress.py'); // tslint:disable-next-line:max-func-body-length -suite('Autocomplete', () => { +suite('Autocomplete', function () { + // Attempt to fix #1301 + // tslint:disable-next-line:no-invalid-this + this.timeout(60000); let isPython2: boolean; let ioc: UnitTestIocContainer; From d9e12f40dfbd44a65a21d836ffbd46a8faeb67d4 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 5 Apr 2018 12:40:25 -0700 Subject: [PATCH 08/10] refactor to enable tests on appveyor --- src/client/common/net/socket/socketServer.ts | 10 ++-- .../DebugServers/RemoteDebugServerv2.ts | 15 ++--- .../configProviders/pythonV2Provider.ts | 6 +- src/test/debugger/attach.ptvsd.test.ts | 55 ++++++++----------- src/test/debugger/capabilities.test.ts | 37 ++++++------- 5 files changed, 58 insertions(+), 65 deletions(-) diff --git a/src/client/common/net/socket/socketServer.ts b/src/client/common/net/socket/socketServer.ts index 24e7b2713740..74099fd7cffe 100644 --- a/src/client/common/net/socket/socketServer.ts +++ b/src/client/common/net/socket/socketServer.ts @@ -28,22 +28,22 @@ export class SocketServer extends EventEmitter implements ISocketServer { this.socketServer = undefined; } - public Start(options: { port?: number, host?: string } = {}): Promise { + public Start(options: { port?: number; host?: string } = {}): Promise { const def = createDeferred(); this.socketServer = net.createServer(this.connectionListener.bind(this)); const port = typeof options.port === 'number' ? options.port! : 0; const host = typeof options.host === 'string' ? options.host! : 'localhost'; - this.socketServer!.listen({ port, host }, () => { - def.resolve(this.socketServer!.address().port); - }); - this.socketServer!.on('error', ex => { console.error('Error in Socket Server', ex); const msg = `Failed to start the socket server. (Error: ${ex.message})`; def.reject(msg); }); + this.socketServer!.listen({ port, host }, () => { + def.resolve(this.socketServer!.address().port); + }); + return def.promise; } diff --git a/src/client/debugger/DebugServers/RemoteDebugServerv2.ts b/src/client/debugger/DebugServers/RemoteDebugServerv2.ts index be400c12c5ea..ff6ff06f9a40 100644 --- a/src/client/debugger/DebugServers/RemoteDebugServerv2.ts +++ b/src/client/debugger/DebugServers/RemoteDebugServerv2.ts @@ -3,7 +3,7 @@ 'use strict'; -import { connect, Socket } from 'net'; +import { Socket } from 'net'; import { DebugSession } from 'vscode-debugadapter'; import { AttachRequestArguments, IDebugServer, IPythonProcess } from '../Common/Contracts'; import { BaseDebugServer } from './BaseDebugServer'; @@ -31,18 +31,19 @@ export class RemoteDebugServerV2 extends BaseDebugServer { } try { let connected = false; - const socket = connect(options, () => { - connected = true; - this.socket = socket; - this.clientSocket.resolve(socket); - resolve(options); - }); + const socket = new Socket(); socket.on('error', ex => { if (connected) { return; } reject(ex); }); + socket.connect(options, () => { + connected = true; + this.socket = socket; + this.clientSocket.resolve(socket); + resolve(options); + }); } catch (ex) { reject(ex); } diff --git a/src/client/debugger/configProviders/pythonV2Provider.ts b/src/client/debugger/configProviders/pythonV2Provider.ts index 417822d81e8f..4c6b74d7faac 100644 --- a/src/client/debugger/configProviders/pythonV2Provider.ts +++ b/src/client/debugger/configProviders/pythonV2Provider.ts @@ -35,8 +35,10 @@ export class PythonV2DebugConfigurationProvider extends BaseConfigurationProvide debugConfiguration.debugOptions = Array.isArray(debugConfiguration.debugOptions) ? debugConfiguration.debugOptions : []; - // Add PTVSD specific flags. - if (this.serviceContainer.get(IPlatformService).isWindows) { + // We'll need paths to be fixed only in the case where local and remote hosts are the same + // I.e. only if hostName === 'localhost' or '127.0.0.1' or '' + const isLocalHost = !debugConfiguration.host || debugConfiguration.host === 'localhost' || debugConfiguration.host === '127.0.0.1'; + if (this.serviceContainer.get(IPlatformService).isWindows && isLocalHost) { debugConfiguration.debugOptions.push(DebugOptions.FixFilePathCase); } diff --git a/src/test/debugger/attach.ptvsd.test.ts b/src/test/debugger/attach.ptvsd.test.ts index 11f3ba2f72d8..4966c024f5e2 100644 --- a/src/test/debugger/attach.ptvsd.test.ts +++ b/src/test/debugger/attach.ptvsd.test.ts @@ -14,27 +14,27 @@ import { DebugClient } from 'vscode-debugadapter-testsupport'; import { EXTENSION_ROOT_DIR } from '../../client/common/constants'; import '../../client/common/extensions'; import { IS_WINDOWS } from '../../client/common/platform/constants'; -import { PlatformService } from '../../client/common/platform/platformService'; import { IPlatformService } from '../../client/common/platform/types'; import { PythonV2DebugConfigurationProvider } from '../../client/debugger'; import { PTVSD_PATH } from '../../client/debugger/Common/constants'; import { AttachRequestArguments, DebugOptions } from '../../client/debugger/Common/Contracts'; import { IServiceContainer } from '../../client/ioc/types'; import { sleep } from '../common'; -import { initialize, IS_APPVEYOR, IS_MULTI_ROOT_TEST, TEST_DEBUGGER } from '../initialize'; +import { initialize, IS_MULTI_ROOT_TEST, TEST_DEBUGGER } from '../initialize'; import { continueDebugging, createDebugAdapter } from './utils'; const fileToDebug = path.join(EXTENSION_ROOT_DIR, 'src', 'testMultiRootWkspc', 'workspace5', 'remoteDebugger-start-with-ptvsd.py'); suite('Attach Debugger - Experimental', () => { let debugClient: DebugClient; - let procToKill: ChildProcess; + let proc: ChildProcess; suiteSetup(initialize); setup(async function () { if (!IS_MULTI_ROOT_TEST || !TEST_DEBUGGER) { this.skip(); } + this.timeout(30000); const coverageDirectory = path.join(EXTENSION_ROOT_DIR, 'debug_coverage_attach_ptvsd'); debugClient = await createDebugAdapter(coverageDirectory); }); @@ -44,23 +44,24 @@ suite('Attach Debugger - Experimental', () => { try { await debugClient.stop().catch(() => { }); } catch (ex) { } - if (procToKill) { + if (proc) { try { - procToKill.kill(); + proc.kill(); } catch { } } }); - async function testAttachingToRemoteProcess(localRoot: string, remoteRoot: string, pathSeparator: string) { + async function testAttachingToRemoteProcess(localRoot: string, remoteRoot: string, isLocalHostWindows: boolean) { + const localHostPathSeparator = isLocalHostWindows ? '\\' : '/'; const port = await getFreePort({ host: 'localhost', port: 3000 }); - const customEnv = { ...process.env }; + const env = { ...process.env }; // Set the path for PTVSD to be picked up. // tslint:disable-next-line:no-string-literal - customEnv['PYTHONPATH'] = PTVSD_PATH; + env['PYTHONPATH'] = PTVSD_PATH; + env.PYTHONPATH = 'C:\\Development\\VSCode\\ptvsd'; const pythonArgs = ['-m', 'ptvsd', '--server', '--port', `${port}`, '--file', fileToDebug.fileToCommandArgument()]; - procToKill = spawn('python', pythonArgs, { env: customEnv, cwd: path.dirname(fileToDebug) }); - // wait for remote socket to start - await sleep(1000); + proc = spawn('python', pythonArgs, { env: env, cwd: path.dirname(fileToDebug) }); + await sleep(3000); // Send initialize, attach const initializePromise = debugClient.initializeRequest({ @@ -80,11 +81,13 @@ suite('Attach Debugger - Experimental', () => { type: 'pythonExperimental', port: port, host: 'localhost', - logToFile: false, + logToFile: true, debugOptions: [DebugOptions.RedirectOutput] }; + const platformService = TypeMoq.Mock.ofType(); + platformService.setup(p => p.isWindows).returns(() => isLocalHostWindows); const serviceContainer = TypeMoq.Mock.ofType(); - serviceContainer.setup(c => c.get(IPlatformService, TypeMoq.It.isAny())).returns(() => new PlatformService()); + serviceContainer.setup(c => c.get(IPlatformService, TypeMoq.It.isAny())).returns(() => platformService.object); const configProvider = new PythonV2DebugConfigurationProvider(serviceContainer.object); await configProvider.resolveDebugConfiguration({ index: 0, name: 'root', uri: Uri.file(localRoot) }, options); @@ -102,7 +105,7 @@ suite('Attach Debugger - Experimental', () => { const stdErrPromise = debugClient.assertOutput('stderr', 'this is stderr'); // Don't use path utils, as we're building the paths manually (mimic windows paths on unix test servers and vice versa). - const localFileName = `${localRoot}${pathSeparator}${path.basename(fileToDebug)}`; + const localFileName = `${localRoot}${localHostPathSeparator}${path.basename(fileToDebug)}`; const breakpointLocation = { path: localFileName, column: 1, line: 12 }; const breakpointPromise = debugClient.setBreakpointsRequest({ lines: [breakpointLocation.line], @@ -125,25 +128,13 @@ suite('Attach Debugger - Experimental', () => { debugClient.waitForEvent('terminated') ]); } - test('Confirm we are able to attach to a running program', async function () { - this.timeout(20000); - // Lets skip this test on AppVeyor (very flaky on AppVeyor). - if (IS_APPVEYOR) { - return; - } - - await testAttachingToRemoteProcess(path.dirname(fileToDebug), path.dirname(fileToDebug), path.sep); + test('Confirm we are able to attach to a running program', async () => { + await testAttachingToRemoteProcess(path.dirname(fileToDebug), path.dirname(fileToDebug), IS_WINDOWS); }); - test('Confirm local and remote paths are translated', async function () { - this.timeout(20000); - // Lets skip this test on AppVeyor (very flaky on AppVeyor). - if (IS_APPVEYOR) { - return; - } - + test('Confirm local and remote paths are translated', async () => { // If tests are running on windows, then treat debug client as a unix client and remote process as current OS. - const localWorkspace = IS_WINDOWS ? '/home/user/Desktop/project/src' : 'C:\\Project\\src'; - const pathSeparator = IS_WINDOWS ? '/' : '\\'; - await testAttachingToRemoteProcess(localWorkspace, path.dirname(fileToDebug), pathSeparator); + const isLocalHostWindows = !IS_WINDOWS; + const localWorkspace = isLocalHostWindows ? 'C:\\Project\\src' : '/home/user/Desktop/project/src'; + await testAttachingToRemoteProcess(localWorkspace, path.dirname(fileToDebug), isLocalHostWindows); }); }); diff --git a/src/test/debugger/capabilities.test.ts b/src/test/debugger/capabilities.test.ts index 0bc4005ee512..44c97d7128b6 100644 --- a/src/test/debugger/capabilities.test.ts +++ b/src/test/debugger/capabilities.test.ts @@ -8,16 +8,18 @@ import { expect } from 'chai'; import { ChildProcess, spawn } from 'child_process'; import * as getFreePort from 'get-port'; -import { connect, Socket } from 'net'; +import { Socket } from 'net'; +import * as path from 'path'; import { PassThrough } from 'stream'; import { Message } from 'vscode-debugadapter/lib/messages'; import { DebugProtocol } from 'vscode-debugprotocol'; +import { EXTENSION_ROOT_DIR } from '../../client/common/constants'; +import { sleep } from '../../client/common/core.utils'; import { createDeferred } from '../../client/common/helpers'; import { PTVSD_PATH } from '../../client/debugger/Common/constants'; import { ProtocolParser } from '../../client/debugger/Common/protocolParser'; import { ProtocolMessageWriter } from '../../client/debugger/Common/protocolWriter'; import { PythonDebugger } from '../../client/debugger/mainV2'; -import { sleep } from '../common'; import { IS_MULTI_ROOT_TEST, TEST_DEBUGGER } from '../initialize'; class Request extends Message implements DebugProtocol.InitializeRequest { @@ -29,13 +31,16 @@ class Request extends Message implements DebugProtocol.InitializeRequest { } } -suite('Debugging - Capabilities', () => { +const fileToDebug = path.join(EXTENSION_ROOT_DIR, 'src', 'testMultiRootWkspc', 'workspace5', 'remoteDebugger-start-with-ptvsd.py'); + +suite('Attach Debugger - Experimental', () => { let disposables: { dispose?: Function; destroy?: Function }[]; let proc: ChildProcess; setup(async function () { if (!IS_MULTI_ROOT_TEST || !TEST_DEBUGGER) { this.skip(); } + this.timeout(30000); disposables = []; }); teardown(() => { @@ -72,24 +77,18 @@ suite('Debugging - Capabilities', () => { const expectedResponse = await expectedResponsePromise; const host = 'localhost'; - const port = await getFreePort({ host }); + const port = await getFreePort({ host, port: 3000 }); const env = { ...process.env }; env.PYTHONPATH = PTVSD_PATH; - proc = spawn('python', ['-m', 'ptvsd', '--server', '--port', `${port}`, '--file', 'someFile.py'], { cwd: __dirname, env }); - // Wait for the socket server to start. - // Keep trying till we timeout. - let socket: Socket | undefined; - for (let index = 0; index < 1000; index += 1) { - try { - const connected = createDeferred(); - socket = connect({ port, host }, () => connected.resolve(socket)); - socket.on('error', connected.reject.bind(connected)); - await connected.promise; - break; - } catch { - await sleep(500); - } - } + env.PYTHONPATH = 'C:\\Development\\VSCode\\ptvsd'; + proc = spawn('python', ['-m', 'ptvsd', '--server', '--port', `${port}`, '--file', fileToDebug], { cwd: path.dirname(fileToDebug), env }); + await sleep(3000); + + const connected = createDeferred(); + const socket = new Socket(); + socket.on('error', connected.reject.bind(connected)); + socket.connect({ port, host }, () => connected.resolve(socket)); + await connected.promise; const protocolParser = new ProtocolParser(); protocolParser.connect(socket!); disposables.push(protocolParser); From 73db9cb4084b1db1bd84c9c222667e4191d7cb4e Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 5 Apr 2018 12:40:57 -0700 Subject: [PATCH 09/10] oopsy --- src/test/debugger/capabilities.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/debugger/capabilities.test.ts b/src/test/debugger/capabilities.test.ts index 44c97d7128b6..6a9bdae102dd 100644 --- a/src/test/debugger/capabilities.test.ts +++ b/src/test/debugger/capabilities.test.ts @@ -33,7 +33,7 @@ class Request extends Message implements DebugProtocol.InitializeRequest { const fileToDebug = path.join(EXTENSION_ROOT_DIR, 'src', 'testMultiRootWkspc', 'workspace5', 'remoteDebugger-start-with-ptvsd.py'); -suite('Attach Debugger - Experimental', () => { +suite('Debugging - Capabilities', () => { let disposables: { dispose?: Function; destroy?: Function }[]; let proc: ChildProcess; setup(async function () { From 3f61104d80959628ebb4fd1b608c9744f8b38e4f Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 5 Apr 2018 12:42:28 -0700 Subject: [PATCH 10/10] remove test code --- src/test/debugger/attach.ptvsd.test.ts | 1 - src/test/debugger/capabilities.test.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/test/debugger/attach.ptvsd.test.ts b/src/test/debugger/attach.ptvsd.test.ts index 4966c024f5e2..87d7e77de17d 100644 --- a/src/test/debugger/attach.ptvsd.test.ts +++ b/src/test/debugger/attach.ptvsd.test.ts @@ -58,7 +58,6 @@ suite('Attach Debugger - Experimental', () => { // Set the path for PTVSD to be picked up. // tslint:disable-next-line:no-string-literal env['PYTHONPATH'] = PTVSD_PATH; - env.PYTHONPATH = 'C:\\Development\\VSCode\\ptvsd'; const pythonArgs = ['-m', 'ptvsd', '--server', '--port', `${port}`, '--file', fileToDebug.fileToCommandArgument()]; proc = spawn('python', pythonArgs, { env: env, cwd: path.dirname(fileToDebug) }); await sleep(3000); diff --git a/src/test/debugger/capabilities.test.ts b/src/test/debugger/capabilities.test.ts index 6a9bdae102dd..5f7f6f53c159 100644 --- a/src/test/debugger/capabilities.test.ts +++ b/src/test/debugger/capabilities.test.ts @@ -80,7 +80,6 @@ suite('Debugging - Capabilities', () => { const port = await getFreePort({ host, port: 3000 }); const env = { ...process.env }; env.PYTHONPATH = PTVSD_PATH; - env.PYTHONPATH = 'C:\\Development\\VSCode\\ptvsd'; proc = spawn('python', ['-m', 'ptvsd', '--server', '--port', `${port}`, '--file', fileToDebug], { cwd: path.dirname(fileToDebug), env }); await sleep(3000);