From 2fd7b695a9b5e283173ed2898f2dccbb57874ed6 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 4 Apr 2018 18:48:29 -0700 Subject: [PATCH] Unit tests for debugging module (#1297) Fixes #1188 --- .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 +++++++++++++++++++ .../pythonFiles/debugging/stdErrOutput.py | 1 + .../pythonFiles/debugging/stdOutOutput.py | 1 + .../workspace5/mymod/__init__.py | 0 .../workspace5/mymod/__main__.py | 1 + 13 files changed, 98 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/test/pythonFiles/debugging/stdErrOutput.py b/src/test/pythonFiles/debugging/stdErrOutput.py index 1e55c2ffd4ba..ef576d80d8a8 100644 --- a/src/test/pythonFiles/debugging/stdErrOutput.py +++ b/src/test/pythonFiles/debugging/stdErrOutput.py @@ -1,3 +1,4 @@ import sys +import time sys.stderr.write('error output') sys.stderr.flush() diff --git a/src/test/pythonFiles/debugging/stdOutOutput.py b/src/test/pythonFiles/debugging/stdOutOutput.py index 9d1994322597..e750f3c1fcbe 100644 --- a/src/test/pythonFiles/debugging/stdOutOutput.py +++ b/src/test/pythonFiles/debugging/stdOutOutput.py @@ -1,3 +1,4 @@ import sys +import time sys.stdout.write('normal output') sys.stdout.flush() 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..f1a18139c84a --- /dev/null +++ b/src/testMultiRootWkspc/workspace5/mymod/__main__.py @@ -0,0 +1 @@ +print("Hello world!")