From a4890aacd843f2b2521ecfc996c244c1be1dfa40 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 4 Apr 2018 12:51:32 -0700 Subject: [PATCH 1/7] :white_check_mark: unit tests for debugging module --- .vscode/settings.json | 4 +- package.json | 2 +- src/client/common/helpers.ts | 8 +-- src/client/debugger/Common/constants.ts | 9 +++ .../debugger/DebugClients/LocalDebugClient.ts | 5 +- src/test/debugger/attach.ptvsd.test.ts | 4 +- src/test/debugger/capabilities.test.ts | 5 +- src/test/debugger/misc.test.ts | 9 +-- src/test/debugger/module.test.ts | 70 +++++++++++++++++++ .../workspace5/mymod/__init__.py | 0 .../workspace5/mymod/main.py | 1 + 11 files changed, 96 insertions(+), 21 deletions(-) create mode 100644 src/client/debugger/Common/constants.ts create mode 100644 src/test/debugger/module.test.ts create mode 100644 src/testMultiRootWkspc/workspace5/mymod/__init__.py create mode 100644 src/testMultiRootWkspc/workspace5/mymod/main.py diff --git a/.vscode/settings.json b/.vscode/settings.json index 311167344456..be66f967c5c4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,9 +14,9 @@ "coverage": true }, "typescript.tsdk": "./node_modules/typescript/lib", // we want to use the TS server from our node_modules folder to control its version - "tslint.enable": true, // We will run our own linting in gulp (& git commit hooks), else tslint extension just complains about unmodified files + "tslint.enable": true, "python.linting.enabled": false, "python.unitTest.promptToConfigure": false, "python.workspaceSymbols.enabled": false, "python.formatting.provider": "none" -} +} \ No newline at end of file diff --git a/package.json b/package.json index 9a589d160676..7925bb4a33f9 100644 --- a/package.json +++ b/package.json @@ -1036,7 +1036,7 @@ { "name": "Python Experimental: Attach", "type": "pythonExperimental", - "request": "pythonExperimental", + "request": "attach", "localRoot": "${workspaceFolder}", "remoteRoot": "${workspaceFolder}", "port": 3000, diff --git a/src/client/common/helpers.ts b/src/client/common/helpers.ts index 82d59e9308c4..ce1824291bd7 100644 --- a/src/client/common/helpers.ts +++ b/src/client/common/helpers.ts @@ -29,9 +29,9 @@ export interface Deferred { } class DeferredImpl implements Deferred { - private _resolve: (value?: T | PromiseLike) => void; + private _resolve!: (value?: T | PromiseLike) => void; // tslint:disable-next-line:no-any - private _reject: (reason?: any) => void; + private _reject!: (reason?: any) => void; private _resolved: boolean = false; private _rejected: boolean = false; private _promise: Promise; @@ -70,14 +70,14 @@ export function createDeferred(scope: any = null): Deferred { return new DeferredImpl(scope); } -export function createTemporaryFile(extension: string, temporaryDirectory?: string): Promise<{ filePath: string, cleanupCallback: Function }> { +export function createTemporaryFile(extension: string, temporaryDirectory?: string): Promise<{ filePath: string; cleanupCallback: Function }> { // tslint:disable-next-line:no-any const options: any = { postfix: extension }; if (temporaryDirectory) { options.dir = temporaryDirectory; } - return new Promise<{ filePath: string, cleanupCallback: Function }>((resolve, reject) => { + return new Promise<{ filePath: string; cleanupCallback: Function }>((resolve, reject) => { tmp.file(options, (err, tmpFile, fd, cleanupCallback) => { if (err) { return reject(err); diff --git a/src/client/debugger/Common/constants.ts b/src/client/debugger/Common/constants.ts new file mode 100644 index 000000000000..e24fb1b790e5 --- /dev/null +++ b/src/client/debugger/Common/constants.ts @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import * as path from 'path'; +import { EXTENSION_ROOT_DIR } from '../../common/constants'; + +export const PTVSD_PATH = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'experimental', 'ptvsd'); diff --git a/src/client/debugger/DebugClients/LocalDebugClient.ts b/src/client/debugger/DebugClients/LocalDebugClient.ts index 5697c3978a59..84903584b616 100644 --- a/src/client/debugger/DebugClients/LocalDebugClient.ts +++ b/src/client/debugger/DebugClients/LocalDebugClient.ts @@ -2,12 +2,12 @@ import { ChildProcess, spawn } from 'child_process'; import * as path from 'path'; import { DebugSession, OutputEvent } from 'vscode-debugadapter'; import { DebugProtocol } from 'vscode-debugprotocol'; -import { EXTENSION_ROOT_DIR } from '../../common/constants'; import { open } from '../../common/open'; import { PathUtils } from '../../common/platform/pathUtils'; import { CurrentProcess } from '../../common/process/currentProcess'; import { EnvironmentVariablesService } from '../../common/variables/environment'; import { IServiceContainer } from '../../ioc/types'; +import { PTVSD_PATH } from '../Common/constants'; import { DebugOptions, IDebugServer, IPythonProcess, LaunchRequestArguments } from '../Common/Contracts'; import { IS_WINDOWS } from '../Common/Utils'; import { BaseDebugServer } from '../DebugServers/BaseDebugServer'; @@ -88,8 +88,7 @@ export class LocalDebugClient extends DebugClient { const environmentVariables = await helper.getEnvironmentVariables(this.args); if (this.args.type === 'pythonExperimental') { // Import the PTVSD debugger, allowing users to use their own latest copies. - const experimentalPTVSDPath = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'experimental', 'ptvsd'); - environmentVariablesService.appendPythonPath(environmentVariables, experimentalPTVSDPath); + environmentVariablesService.appendPythonPath(environmentVariables, PTVSD_PATH); } // tslint:disable-next-line:max-func-body-length cyclomatic-complexity no-any return new Promise((resolve, reject) => { diff --git a/src/test/debugger/attach.ptvsd.test.ts b/src/test/debugger/attach.ptvsd.test.ts index 8dbe6df09152..76ccd4944034 100644 --- a/src/test/debugger/attach.ptvsd.test.ts +++ b/src/test/debugger/attach.ptvsd.test.ts @@ -11,13 +11,13 @@ import * as path from 'path'; import { DebugClient } from 'vscode-debugadapter-testsupport'; import { EXTENSION_ROOT_DIR } from '../../client/common/constants'; import '../../client/common/extensions'; +import { PTVSD_PATH } from '../../client/debugger/Common/constants'; import { DebugOptions } from '../../client/debugger/Common/Contracts'; import { sleep } from '../common'; import { initialize, IS_APPVEYOR, 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'); -const ptvsdPath = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'experimental', 'ptvsd'); suite('Attach Debugger - Experimental', () => { let debugClient: DebugClient; @@ -55,7 +55,7 @@ suite('Attach Debugger - Experimental', () => { // Set the path for PTVSD to be picked up. // tslint:disable-next-line:no-string-literal - customEnv['PYTHONPATH'] = ptvsdPath; + customEnv['PYTHONPATH'] = PTVSD_PATH; const pythonArgs = ['-m', 'ptvsd', '--server', '--port', `${port}`, '--file', fileToDebug.fileToCommandArgument()]; procToKill = spawn('python', pythonArgs, { env: customEnv, cwd: path.dirname(fileToDebug) }); diff --git a/src/test/debugger/capabilities.test.ts b/src/test/debugger/capabilities.test.ts index 8052b5f6f2dc..0bc4005ee512 100644 --- a/src/test/debugger/capabilities.test.ts +++ b/src/test/debugger/capabilities.test.ts @@ -9,12 +9,11 @@ import { expect } from 'chai'; import { ChildProcess, spawn } from 'child_process'; import * as getFreePort from 'get-port'; import { connect, 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 { 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'; @@ -75,7 +74,7 @@ suite('Debugging - Capabilities', () => { const host = 'localhost'; const port = await getFreePort({ host }); const env = { ...process.env }; - env.PYTHONPATH = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'experimental', 'ptvsd'); + 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. diff --git a/src/test/debugger/misc.test.ts b/src/test/debugger/misc.test.ts index 05b67f77898f..0e17bc9a05c9 100644 --- a/src/test/debugger/misc.test.ts +++ b/src/test/debugger/misc.test.ts @@ -13,6 +13,7 @@ import { noop } from '../../client/common/core.utils'; import { IS_WINDOWS } from '../../client/common/platform/constants'; import { FileSystem } from '../../client/common/platform/fileSystem'; import { PlatformService } from '../../client/common/platform/platformService'; +import { PTVSD_PATH } from '../../client/debugger/Common/constants'; import { DebugOptions, LaunchRequestArguments } from '../../client/debugger/Common/Contracts'; import { sleep } from '../common'; import { IS_MULTI_ROOT_TEST, TEST_DEBUGGER } from '../initialize'; @@ -69,8 +70,9 @@ let testCounter = 0; const env = {}; if (debuggerType === 'pythonExperimental') { // tslint:disable-next-line:no-string-literal - env['PYTHONPATH'] = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'experimental', 'ptvsd'); + env['PYTHONPATH'] = PTVSD_PATH; } + // tslint:disable-next-line:no-unnecessary-local-variable const options: LaunchRequestArguments = { program: path.join(debugFilesPath, pythonFile), cwd: debugFilesPath, @@ -84,11 +86,6 @@ let testCounter = 0; type: debuggerType }; - // Custom experimental debugger options (filled in by DebugConfigurationProvider). - if (debuggerType === 'pythonExperimental') { - (options as any).redirectOutput = true; - } - return options; } diff --git a/src/test/debugger/module.test.ts b/src/test/debugger/module.test.ts new file mode 100644 index 000000000000..cdf88f27a68a --- /dev/null +++ b/src/test/debugger/module.test.ts @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +// tslint:disable:no-suspicious-comment max-func-body-length no-invalid-this no-var-requires no-require-imports no-any + +import * as path from 'path'; +import { DebugClient } from 'vscode-debugadapter-testsupport'; +import { EXTENSION_ROOT_DIR } from '../../client/common/constants'; +import { noop } from '../../client/common/core.utils'; +import { PTVSD_PATH } from '../../client/debugger/Common/constants'; +import { DebugOptions, LaunchRequestArguments } from '../../client/debugger/Common/Contracts'; +import { sleep } from '../common'; +import { IS_MULTI_ROOT_TEST, TEST_DEBUGGER } from '../initialize'; +import { createDebugAdapter } from './utils'; + +const workspaceDirectory = path.join(EXTENSION_ROOT_DIR, 'src', 'testMultiRootWkspc', 'workspace5'); +const debuggerType = 'pythonExperimental'; +suite(`Module Debugging - Misc tests: ${debuggerType}`, () => { + let debugClient: DebugClient; + setup(async function () { + if (!IS_MULTI_ROOT_TEST || !TEST_DEBUGGER) { + this.skip(); + } + const coverageDirectory = path.join(EXTENSION_ROOT_DIR, 'debug_coverage_module'); + debugClient = await createDebugAdapter(coverageDirectory); + }); + teardown(async () => { + // Wait for a second before starting another test (sometimes, sockets take a while to get closed). + await sleep(1000); + try { + await debugClient.stop().catch(noop); + // tslint:disable-next-line:no-empty + } catch (ex) { } + await sleep(1000); + }); + function buildLauncArgs(): LaunchRequestArguments { + const env = {}; + // tslint:disable-next-line:no-string-literal + env['PYTHONPATH'] = `.${path.delimiter}${PTVSD_PATH}`; + + // tslint:disable-next-line:no-unnecessary-local-variable + const options: LaunchRequestArguments = { + module: 'mymod', + program: '', + cwd: workspaceDirectory, + debugOptions: [DebugOptions.RedirectOutput], + pythonPath: 'python', + args: [], + env, + envFile: '', + logToFile: false, + type: debuggerType + }; + + return options; + } + + test('Test stdout output', async () => { + await Promise.all([ + debugClient.configurationSequence(), + debugClient.launch(buildLauncArgs()), + debugClient.waitForEvent('initialized'), + debugClient.assertOutput('stdout', 'Hello world!'), + debugClient.waitForEvent('exited'), + debugClient.waitForEvent('terminated') + ]); + }); +}); diff --git a/src/testMultiRootWkspc/workspace5/mymod/__init__.py b/src/testMultiRootWkspc/workspace5/mymod/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/testMultiRootWkspc/workspace5/mymod/main.py b/src/testMultiRootWkspc/workspace5/mymod/main.py new file mode 100644 index 000000000000..442659b88b30 --- /dev/null +++ b/src/testMultiRootWkspc/workspace5/mymod/main.py @@ -0,0 +1 @@ +print("Hello world!") \ No newline at end of file From f84ed17b762705f2b3b19dd6f0bcea09d10f1d66 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 4 Apr 2018 12:53:02 -0700 Subject: [PATCH 2/7] add blank line --- src/testMultiRootWkspc/workspace5/mymod/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/testMultiRootWkspc/workspace5/mymod/main.py b/src/testMultiRootWkspc/workspace5/mymod/main.py index 442659b88b30..f1a18139c84a 100644 --- a/src/testMultiRootWkspc/workspace5/mymod/main.py +++ b/src/testMultiRootWkspc/workspace5/mymod/main.py @@ -1 +1 @@ -print("Hello world!") \ No newline at end of file +print("Hello world!") From c2fb6f36735aa7f18014667b6c4c9cce08816516 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 4 Apr 2018 14:18:26 -0700 Subject: [PATCH 3/7] add a minor delay for output to be sent --- src/test/pythonFiles/debugging/stdErrOutput.py | 3 +++ src/test/pythonFiles/debugging/stdOutOutput.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/test/pythonFiles/debugging/stdErrOutput.py b/src/test/pythonFiles/debugging/stdErrOutput.py index 1e55c2ffd4ba..e648596ae29e 100644 --- a/src/test/pythonFiles/debugging/stdErrOutput.py +++ b/src/test/pythonFiles/debugging/stdErrOutput.py @@ -1,3 +1,6 @@ import sys +import time +time.sleep(1) sys.stderr.write('error output') sys.stderr.flush() +time.sleep(1) diff --git a/src/test/pythonFiles/debugging/stdOutOutput.py b/src/test/pythonFiles/debugging/stdOutOutput.py index 9d1994322597..21153234f1b0 100644 --- a/src/test/pythonFiles/debugging/stdOutOutput.py +++ b/src/test/pythonFiles/debugging/stdOutOutput.py @@ -1,3 +1,6 @@ import sys +import time +time.sleep(1) sys.stdout.write('normal output') sys.stdout.flush() +time.sleep(1) From 5784d59255ea63e8a233f66d6b8e93f7b36ce96a Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 4 Apr 2018 14:41:05 -0700 Subject: [PATCH 4/7] logging --- src/test/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/index.ts b/src/test/index.ts index 135ca5d8b6c1..0d6bb1fe6955 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -13,7 +13,7 @@ process.env.IS_MULTI_ROOT_TEST = IS_MULTI_ROOT_TEST.toString(); // If running on CI server and we're running the debugger tests, then ensure we only run debug tests. // We do this to ensure we only run debugger test, as debugger tests are very flaky on CI. // So the solution is to run them separately and first on CI. -const grep = IS_CI_SERVER && IS_CI_SERVER_TEST_DEBUGGER ? 'Debug' : undefined; +const grep = IS_CI_SERVER && IS_CI_SERVER_TEST_DEBUGGER ? 'Standard Debugging - Misc tests' : 'Standard Debugging - Misc tests'; // You can directly control Mocha options by uncommenting the following lines. // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info. From 923c6f1d80ef6cf3c6247ba35caa254a1484c1a4 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 4 Apr 2018 14:46:51 -0700 Subject: [PATCH 5/7] undo changes --- src/test/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/index.ts b/src/test/index.ts index 0d6bb1fe6955..135ca5d8b6c1 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -13,7 +13,7 @@ process.env.IS_MULTI_ROOT_TEST = IS_MULTI_ROOT_TEST.toString(); // If running on CI server and we're running the debugger tests, then ensure we only run debug tests. // We do this to ensure we only run debugger test, as debugger tests are very flaky on CI. // So the solution is to run them separately and first on CI. -const grep = IS_CI_SERVER && IS_CI_SERVER_TEST_DEBUGGER ? 'Standard Debugging - Misc tests' : 'Standard Debugging - Misc tests'; +const grep = IS_CI_SERVER && IS_CI_SERVER_TEST_DEBUGGER ? 'Debug' : undefined; // You can directly control Mocha options by uncommenting the following lines. // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info. From 6d8f6ecb9c8f9659e8791aa54cb59d7edac1ae30 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 4 Apr 2018 15:36:25 -0700 Subject: [PATCH 6/7] revert unnecessary change --- src/test/pythonFiles/debugging/stdErrOutput.py | 2 -- src/test/pythonFiles/debugging/stdOutOutput.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/test/pythonFiles/debugging/stdErrOutput.py b/src/test/pythonFiles/debugging/stdErrOutput.py index e648596ae29e..ef576d80d8a8 100644 --- a/src/test/pythonFiles/debugging/stdErrOutput.py +++ b/src/test/pythonFiles/debugging/stdErrOutput.py @@ -1,6 +1,4 @@ import sys import time -time.sleep(1) sys.stderr.write('error output') sys.stderr.flush() -time.sleep(1) diff --git a/src/test/pythonFiles/debugging/stdOutOutput.py b/src/test/pythonFiles/debugging/stdOutOutput.py index 21153234f1b0..e750f3c1fcbe 100644 --- a/src/test/pythonFiles/debugging/stdOutOutput.py +++ b/src/test/pythonFiles/debugging/stdOutOutput.py @@ -1,6 +1,4 @@ import sys import time -time.sleep(1) sys.stdout.write('normal output') sys.stdout.flush() -time.sleep(1) From 1af9f2a62e41355df15eea63d6256b3829a240ac Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 4 Apr 2018 15:36:32 -0700 Subject: [PATCH 7/7] rename file --- src/testMultiRootWkspc/workspace5/mymod/{main.py => __main__.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/testMultiRootWkspc/workspace5/mymod/{main.py => __main__.py} (100%) diff --git a/src/testMultiRootWkspc/workspace5/mymod/main.py b/src/testMultiRootWkspc/workspace5/mymod/__main__.py similarity index 100% rename from src/testMultiRootWkspc/workspace5/mymod/main.py rename to src/testMultiRootWkspc/workspace5/mymod/__main__.py