Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use the right version of Python interpreter on Travis when running unit tests #1319

Merged
merged 19 commits into from
Apr 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ before_install: |
yarn global add azure-cli
export TRAVIS_PYTHON_PATH=`which python`
install:
- pip install --upgrade -r requirements.txt
- pip install -t ./pythonFiles/experimental/ptvsd git+https://github.com/Microsoft/ptvsd/
- python -m pip install --upgrade -r requirements.txt
- python -m pip install -t ./pythonFiles/experimental/ptvsd git+https://github.com/Microsoft/ptvsd/
- yarn

script:
Expand Down
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
"files.exclude": {
"out": true, // set this to true to hide the "out" folder with the compiled JS files
"**/*.pyc": true,
"obj": true,
"bin": true,
"**/__pycache__": true,
"node_modules": true,
".vscode-test": true,
Expand All @@ -19,4 +21,4 @@
"python.unitTest.promptToConfigure": false,
"python.workspaceSymbols.enabled": false,
"python.formatting.provider": "none"
}
}
1 change: 1 addition & 0 deletions news/3 Code Health/1318.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Ensure all unit tests run on Travis use the right Python interpreter.
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ pytest
fabric
numba
rope
flask
django
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@ import { ICommandManager, IDocumentManager, IWorkspaceService } from '../../comm
import '../../common/extensions';
import { IFileSystem, IPlatformService } from '../../common/platform/types';
import { ITerminalServiceFactory } from '../../common/terminal/types';
import { IConfigurationService } from '../../common/types';
import { IDisposableRegistry } from '../../common/types';
import { IConfigurationService, IDisposableRegistry } from '../../common/types';
import { DjangoContextInitializer } from './djangoContext';
import { TerminalCodeExecutionProvider } from './terminalCodeExecution';

@injectable()
export class DjangoShellCodeExecutionProvider extends TerminalCodeExecutionProvider {
constructor( @inject(ITerminalServiceFactory) terminalServiceFactory: ITerminalServiceFactory,
constructor(@inject(ITerminalServiceFactory) terminalServiceFactory: ITerminalServiceFactory,
@inject(IConfigurationService) configurationService: IConfigurationService,
@inject(IWorkspaceService) workspace: IWorkspaceService,
@inject(IDocumentManager) documentManager: IDocumentManager,
Expand All @@ -30,10 +29,10 @@ export class DjangoShellCodeExecutionProvider extends TerminalCodeExecutionProvi
this.terminalTitle = 'Django Shell';
disposableRegistry.push(new DjangoContextInitializer(documentManager, workspace, fileSystem, commandManager));
}
public getReplCommandArgs(resource?: Uri): { command: string, args: string[] } {
public getReplCommandArgs(resource?: Uri): { command: string; args: string[] } {
const pythonSettings = this.configurationService.getSettings(resource);
const command = this.platformService.isWindows ? pythonSettings.pythonPath.replace(/\\/g, '/') : pythonSettings.pythonPath;
const args = pythonSettings.terminal.launchArgs.slice();
const args = pythonSettings.terminal!.launchArgs.slice();

const workspaceUri = resource ? this.workspace.getWorkspaceFolder(resource) : undefined;
const defaultWorkspace = Array.isArray(this.workspace.workspaceFolders) && this.workspace.workspaceFolders.length > 0 ? this.workspace.workspaceFolders[0].uri.fsPath : '';
Expand Down
11 changes: 11 additions & 0 deletions src/test/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { IS_MULTI_ROOT_TEST } from './initialize';
const fileInNonRootWorkspace = path.join(__dirname, '..', '..', 'src', 'test', 'pythonFiles', 'dummy.py');
export const rootWorkspaceUri = getWorkspaceRoot();

export const PYTHON_PATH = getPythonPath();

export type PythonSettingKeys = 'workspaceSymbols.enabled' | 'pythonPath' |
'linting.lintOnSave' |
'linting.enabled' | 'linting.pylintEnabled' |
Expand Down Expand Up @@ -118,3 +120,12 @@ const globalPythonPathSetting = workspace.getConfiguration('python').inspect('py
export const clearPythonPathInWorkspaceFolder = async (resource: string | Uri) => retryAsync(setPythonPathInWorkspace)(resource, ConfigurationTarget.WorkspaceFolder);
export const setPythonPathInWorkspaceRoot = async (pythonPath: string) => retryAsync(setPythonPathInWorkspace)(undefined, ConfigurationTarget.Workspace, pythonPath);
export const resetGlobalPythonPathSetting = async () => retryAsync(restoreGlobalPythonPathSetting)();

function getPythonPath(): string {
// tslint:disable-next-line:no-unsafe-any
if (process.env.TRAVIS_PYTHON_PATH && fs.existsSync(process.env.TRAVIS_PYTHON_PATH)) {
// tslint:disable-next-line:no-unsafe-any
return process.env.TRAVIS_PYTHON_PATH;
}
return 'python';
}
4 changes: 2 additions & 2 deletions src/test/common/moduleInstaller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { ITerminalService, ITerminalServiceFactory } from '../../client/common/t
import { IConfigurationService, ICurrentProcess, IInstaller, ILogger, IPathUtils, IPersistentStateFactory, IPythonSettings, IsWindows } from '../../client/common/types';
import { ICondaService, IInterpreterLocatorService, IInterpreterService, INTERPRETER_LOCATOR_SERVICE, InterpreterType, PIPENV_SERVICE, PythonInterpreter } from '../../client/interpreter/contracts';
import { IServiceContainer } from '../../client/ioc/types';
import { rootWorkspaceUri } from '../common';
import { PYTHON_PATH, rootWorkspaceUri } from '../common';
import { MockModuleInstaller } from '../mocks/moduleInstaller';
import { MockProcessService } from '../mocks/proc';
import { UnitTestIocContainer } from '../unittests/serviceRegistry';
Expand Down Expand Up @@ -195,7 +195,7 @@ suite('Module Installer', () => {

const interpreter: PythonInterpreter = {
type: InterpreterType.Unknown,
path: 'python'
path: PYTHON_PATH
};
interpreterService.setup(x => x.getActiveInterpreter(TypeMoq.It.isAny())).returns(() => Promise.resolve(interpreter));

Expand Down
4 changes: 2 additions & 2 deletions src/test/debugger/attach.ptvsd.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ 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 { PYTHON_PATH, sleep } from '../common';
import { initialize, IS_MULTI_ROOT_TEST, TEST_DEBUGGER } from '../initialize';
import { continueDebugging, createDebugAdapter } from './utils';

Expand Down Expand Up @@ -59,7 +59,7 @@ suite('Attach Debugger - Experimental', () => {
// tslint:disable-next-line:no-string-literal
env['PYTHONPATH'] = PTVSD_PATH;
const pythonArgs = ['-m', 'ptvsd', '--server', '--port', `${port}`, '--file', fileToDebug.fileToCommandArgument()];
proc = spawn('python', pythonArgs, { env: env, cwd: path.dirname(fileToDebug) });
proc = spawn(PYTHON_PATH, pythonArgs, { env: env, cwd: path.dirname(fileToDebug) });
await sleep(3000);

// Send initialize, attach
Expand Down
4 changes: 2 additions & 2 deletions src/test/debugger/attach.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { createDeferred } from '../../client/common/helpers';
import { BufferDecoder } from '../../client/common/process/decoder';
import { ProcessService } from '../../client/common/process/proc';
import { AttachRequestArguments } from '../../client/debugger/Common/Contracts';
import { sleep } from '../common';
import { PYTHON_PATH, sleep } from '../common';
import { initialize, IS_APPVEYOR, IS_MULTI_ROOT_TEST, TEST_DEBUGGER } from '../initialize';
import { DEBUGGER_TIMEOUT } from './common/constants';

Expand Down Expand Up @@ -67,7 +67,7 @@ suite('Attach Debugger', () => {
// tslint:disable-next-line:no-string-literal
customEnv['PYTHONPATH'] = ptvsdPath;
const procService = new ProcessService(new BufferDecoder());
const result = procService.execObservable('python', [fileToDebug, port.toString()], { env: customEnv, cwd: path.dirname(fileToDebug) });
const result = procService.execObservable(PYTHON_PATH, [fileToDebug, port.toString()], { env: customEnv, cwd: path.dirname(fileToDebug) });
procToKill = result.proc;

const expectedOutputs = [
Expand Down
3 changes: 2 additions & 1 deletion src/test/debugger/capabilities.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ 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 { PYTHON_PATH } from '../common';
import { IS_MULTI_ROOT_TEST, TEST_DEBUGGER } from '../initialize';

class Request extends Message implements DebugProtocol.InitializeRequest {
Expand Down Expand Up @@ -80,7 +81,7 @@ suite('Debugging - Capabilities', () => {
const port = await getFreePort({ host, port: 3000 });
const env = { ...process.env };
env.PYTHONPATH = PTVSD_PATH;
proc = spawn('python', ['-m', 'ptvsd', '--server', '--port', `${port}`, '--file', fileToDebug], { cwd: path.dirname(fileToDebug), env });
proc = spawn(PYTHON_PATH, ['-m', 'ptvsd', '--server', '--port', `${port}`, '--file', fileToDebug], { cwd: path.dirname(fileToDebug), env });
await sleep(3000);

const connected = createDeferred();
Expand Down
6 changes: 0 additions & 6 deletions src/test/debugger/core/capabilities.test.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/test/debugger/misc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ 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 { PYTHON_PATH, sleep } from '../common';
import { IS_MULTI_ROOT_TEST, TEST_DEBUGGER } from '../initialize';
import { DEBUGGER_TIMEOUT } from './common/constants';
import { DebugClientEx } from './debugClient';
Expand Down Expand Up @@ -78,7 +78,7 @@ let testCounter = 0;
cwd: debugFilesPath,
stopOnEntry,
debugOptions: [DebugOptions.RedirectOutput],
pythonPath: 'python',
pythonPath: PYTHON_PATH,
args: [],
env,
envFile: '',
Expand Down
4 changes: 2 additions & 2 deletions src/test/debugger/module.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ 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 { PYTHON_PATH, sleep } from '../common';
import { IS_MULTI_ROOT_TEST, TEST_DEBUGGER } from '../initialize';
import { createDebugAdapter } from './utils';

Expand Down Expand Up @@ -46,7 +46,7 @@ suite(`Module Debugging - Misc tests: ${debuggerType}`, () => {
program: '',
cwd: workspaceDirectory,
debugOptions: [DebugOptions.RedirectOutput],
pythonPath: 'python',
pythonPath: PYTHON_PATH,
args: [],
env,
envFile: '',
Expand Down
3 changes: 2 additions & 1 deletion src/test/debugger/portAndHost.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as path from 'path';
import { DebugClient } from 'vscode-debugadapter-testsupport';
import { noop } from '../../client/common/core.utils';
import { DebugOptions, LaunchRequestArguments } from '../../client/debugger/Common/Contracts';
import { PYTHON_PATH } from '../common';
import { IS_MULTI_ROOT_TEST, TEST_DEBUGGER } from '../initialize';
import { DEBUGGER_TIMEOUT } from './common/constants';

Expand Down Expand Up @@ -50,7 +51,7 @@ const EXPERIMENTAL_DEBUG_ADAPTER = path.join(__dirname, '..', '..', 'client', 'd
cwd: debugFilesPath,
stopOnEntry,
debugOptions: [DebugOptions.RedirectOutput],
pythonPath: 'python',
pythonPath: PYTHON_PATH,
args: [],
envFile: '',
host, port,
Expand Down
3 changes: 3 additions & 0 deletions src/test/debugger/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ export async function validateVariablesInFrame(debugClient: DebugClient,
export function makeHttpRequest(uri: string): Promise<string> {
return new Promise<string>((resolve, reject) => {
request.get(uri, (error: any, response: request.Response, body: any) => {
if (error) {
return reject(error);
}
if (response.statusCode !== 200) {
reject(new Error(`Status code = ${response.statusCode}`));
} else {
Expand Down
148 changes: 148 additions & 0 deletions src/test/debugger/web.framework.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// 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 no-http-string no-string-literal no-console

import { expect } from 'chai';
import * as getFreePort from 'get-port';
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 { DebugOptions, LaunchRequestArguments } from '../../client/debugger/Common/Contracts';
import { PYTHON_PATH, sleep } from '../common';
import { IS_MULTI_ROOT_TEST, TEST_DEBUGGER } from '../initialize';
import { DEBUGGER_TIMEOUT } from './common/constants';
import { continueDebugging, createDebugAdapter, ExpectedVariable, hitHttpBreakpoint, makeHttpRequest, validateVariablesInFrame } from './utils';

let testCounter = 0;
const debuggerType = 'pythonExperimental';
suite(`Django and Flask Debugging: ${debuggerType}`, () => {
let debugClient: DebugClient;
setup(async function () {
if (!IS_MULTI_ROOT_TEST || !TEST_DEBUGGER) {
this.skip();
}
this.timeout(5 * DEBUGGER_TIMEOUT);
const coverageDirectory = path.join(EXTENSION_ROOT_DIR, `debug_coverage_django_flask${testCounter += 1}`);
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 buildLaunchArgs(workspaceDirectory: string): LaunchRequestArguments {
const env = {};
// tslint:disable-next-line:no-string-literal
env['PYTHONPATH'] = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'experimental', 'ptvsd');

// tslint:disable-next-line:no-unnecessary-local-variable
const options: LaunchRequestArguments = {
cwd: workspaceDirectory,
program: '',
debugOptions: [DebugOptions.RedirectOutput],
pythonPath: PYTHON_PATH,
args: [],
env,
envFile: '',
logToFile: true,
type: debuggerType
};

return options;
}
async function buildFlaskLaunchArgs(workspaceDirectory: string) {
const port = await getFreePort({ host: 'localhost' });
const options = buildLaunchArgs(workspaceDirectory);

options.env!['FLASK_APP'] = path.join(workspaceDirectory, 'run.py');
options.module = 'flask';
options.debugOptions = [DebugOptions.RedirectOutput, DebugOptions.Jinja];
options.args = [
'run',
'--no-debugger',
'--no-reload',
'--port',
`${port}`
];

return { options, port };
}
async function buildDjangoLaunchArgs(workspaceDirectory: string) {
const port = await getFreePort({ host: 'localhost' });
const options = buildLaunchArgs(workspaceDirectory);

options.program = path.join(workspaceDirectory, 'manage.py');
options.debugOptions = [DebugOptions.RedirectOutput, DebugOptions.Django];
options.args = [
'runserver',
'--noreload',
'--nothreading',
`${port}`
];

return { options, port };
}

async function testTemplateDebugging(launchArgs: LaunchRequestArguments, port: number, viewFile: string, viewLine: number, templateFile: string, templateLine: number) {
await Promise.all([
debugClient.configurationSequence(),
debugClient.launch(launchArgs),
debugClient.waitForEvent('initialized'),
debugClient.waitForEvent('process'),
debugClient.waitForEvent('thread')
]);

const httpResult = await makeHttpRequest(`http://localhost:${port}`);

expect(httpResult).to.contain('Hello this_is_a_value_from_server');
expect(httpResult).to.contain('Hello this_is_another_value_from_server');

await hitHttpBreakpoint(debugClient, `http://localhost:${port}`, viewFile, viewLine);

await continueDebugging(debugClient);
await debugClient.setBreakpointsRequest({ breakpoints: [], lines: [], source: { path: viewFile } });

// Template debugging.
const [stackTrace, htmlResultPromise] = await hitHttpBreakpoint(debugClient, `http://localhost:${port}`, templateFile, templateLine);

// Wait for breakpoint to hit
const expectedVariables: ExpectedVariable[] = [
{ name: 'value_from_server', type: 'str', value: '\'this_is_a_value_from_server\'' },
{ name: 'another_value_from_server', type: 'str', value: '\'this_is_another_value_from_server\'' }
];
await validateVariablesInFrame(debugClient, stackTrace, expectedVariables, 1);

await debugClient.setBreakpointsRequest({ breakpoints: [], lines: [], source: { path: templateFile } });
await continueDebugging(debugClient);

const htmlResult = await htmlResultPromise;
expect(htmlResult).to.contain('Hello this_is_a_value_from_server');
expect(htmlResult).to.contain('Hello this_is_another_value_from_server');
}

test('Test Flask Route and Template debugging', async () => {
const workspaceDirectory = path.join(EXTENSION_ROOT_DIR, 'src', 'testMultiRootWkspc', 'workspace5', 'flaskApp');
const { options, port } = await buildFlaskLaunchArgs(workspaceDirectory);

await testTemplateDebugging(options, port,
path.join(workspaceDirectory, 'run.py'), 7,
path.join(workspaceDirectory, 'templates', 'index.html'), 6);
});

test('Test Django Route and Template debugging', async () => {
const workspaceDirectory = path.join(EXTENSION_ROOT_DIR, 'src', 'testMultiRootWkspc', 'workspace5', 'djangoApp');
const { options, port } = await buildDjangoLaunchArgs(workspaceDirectory);

await testTemplateDebugging(options, port,
path.join(workspaceDirectory, 'home', 'views.py'), 10,
path.join(workspaceDirectory, 'home', 'templates', 'index.html'), 6);
});
});
Loading