Skip to content

Commit a45565d

Browse files
committed
fixed ability to run individual nosetests
1 parent 6429d5f commit a45565d

File tree

13 files changed

+131
-132
lines changed

13 files changed

+131
-132
lines changed

src/client/common/constants.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export var Command_Set_Interpreter = "python.setInterpreter";
2+
export var Command_Tests_View_UI = "python.viewTests";
3+
export var Command_Tests_Discover = "python.discoverTests";
4+
export var Command_Tests_Run_Failed = "python.runFailedTests";
5+
export var Command_Sort_Imports = "python.sortImports";
6+
export var Command_Tests_Run = "python.runtests";
7+
export var Command_Tests_Stop = "python.stopUnitTests";
8+
export var Command_Tests_ViewOutput = "python.viewTestOutput";
9+
export var Command_Refactor_Extract_Variable = "python.refactorExtractVariable";
10+
export var Command_Refaactor_Extract_Method = "python.refactorExtractMethod";
11+
12+
export var Button_Text_Tests_View_Output = 'View Output';

src/client/unittest/contracts.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ export const CANCELLATION_REASON = 'cancelled_user_request';
33
export interface TestFolder extends TestResult {
44
name: string;
55
testFiles: TestFile[];
6-
rawName: string;
6+
nameToRun: string;
77
status?: TestStatus;
88
folders: TestFolder[];
99
}
1010
export interface TestFile extends TestResult {
1111
name: string;
1212
functions: TestFunction[];
1313
suites: TestSuite[];
14-
rawName: string;
14+
nameToRun: string;
1515
xmlName: string;
1616
status?: TestStatus;
1717
errorsWhenDiscovering?: string;
@@ -22,13 +22,13 @@ export interface TestSuite extends TestResult {
2222
suites: TestSuite[];
2323
isUnitTest: Boolean;
2424
isInstance: Boolean;
25-
rawName: string;
25+
nameToRun: string;
2626
xmlName: string;
2727
status?: TestStatus;
2828
}
2929
export interface TestFunction extends TestResult {
3030
name: string;
31-
rawName: string;
31+
nameToRun: string;
3232
status?: TestStatus;
3333
}
3434
export interface TestResult extends Node {

src/client/unittest/display/main.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import * as vscode from 'vscode';
33
import {Tests, TestsToRun, TestFolder, TestFile, TestStatus, TestSuite, TestFunction, CANCELLATION_REASON} from '../contracts';
44
import {PythonSettings} from '../../common/configSettings';
5+
import * as constants from '../../common/constants';
6+
import {displayTestErrorMessage} from '../testUtils';
57

68
const settings = PythonSettings.getInstance();
79

@@ -14,7 +16,7 @@ export class TestResultDisplay {
1416
this.statusBar.dispose();
1517
}
1618
public DisplayProgressStatus(tests: Promise<Tests>) {
17-
this.displayProgress('Running Tests', 'Running Python Unit Tests (Click to Stop)', 'python.stopUnitTests');
19+
this.displayProgress('Running Tests', `Running Tests (Click to Stop)`, constants.Command_Tests_Stop);
1820
tests
1921
.then(this.updateTestRunWithSuccess.bind(this))
2022
.catch(this.updateTestRunWithFailure.bind(this))
@@ -44,26 +46,24 @@ export class TestResultDisplay {
4446
statusText.push(`$(circle-slash) ${tests.summary.skipped}`);
4547
toolTip.push(`${tests.summary.skipped} Skipped`);
4648
}
47-
this.statusBar.tooltip = toolTip.length === 0 ? 'No Python Unit Tests Ran' : toolTip.join(', ') + ' (Python Unit Tests)';
49+
this.statusBar.tooltip = toolTip.length === 0 ? 'No Tests Ran' : toolTip.join(', ') + ' (Tests)';
4850
this.statusBar.text = statusText.length === 0 ? 'No Tests Ran' : statusText.join(' ');
49-
this.statusBar.command = 'python.viewTests';
50-
this.statusBar.show();
51+
this.statusBar.command = constants.Command_Tests_View_UI;
5152
return tests;
5253
}
5354

5455
private updateTestRunWithFailure(reason: any): Promise<any> {
5556
this.clearProgressTicker();
56-
this.statusBar.command = 'python.viewTests';
57+
this.statusBar.command = constants.Command_Tests_View_UI;
5758
if (reason === CANCELLATION_REASON) {
5859
this.statusBar.text = '$(triangle-right) Run Tests';
59-
this.statusBar.tooltip = 'Run Python Unit Tests';
60+
this.statusBar.tooltip = 'Run Tests';
6061
}
6162
else {
6263
this.statusBar.text = `$(octicon-alert) Tests Failed`;
63-
this.statusBar.tooltip = 'Running Python Unit Tests Failed';
64-
vscode.window.showErrorMessage('There was an error in running the unit tests.');
64+
this.statusBar.tooltip = 'Running Tests Failed';
65+
displayTestErrorMessage('There was an error in running the tests.');
6566
}
66-
this.statusBar.show();
6767
return Promise.reject(reason);
6868
}
6969

@@ -93,7 +93,7 @@ export class TestResultDisplay {
9393
}
9494

9595
public DisplayDiscoverStatus(tests: Promise<Tests>) {
96-
this.displayProgress('Discovering Tests', 'Discovering Python Unit Tests (Click to Stop)', 'python.stopUnitTests');
96+
this.displayProgress('Discovering Tests', 'Discovering Tests (Click to Stop)', constants.Command_Tests_Stop);
9797
return tests.then(tests => {
9898
this.updateWithDiscoverSuccess(tests);
9999
return tests;
@@ -106,20 +106,20 @@ export class TestResultDisplay {
106106
private updateWithDiscoverSuccess(tests: Tests) {
107107
this.clearProgressTicker();
108108
const haveTests = tests && (tests.testFunctions.length > 0);
109-
this.statusBar.text = haveTests ? '$(triangle-right) Run Tests' : 'No Unit Tests';
110-
this.statusBar.tooltip = haveTests ? 'Run Python Unit Tests' : 'No Python Unit Tests discovered';
111-
this.statusBar.command = haveTests ? 'python.viewTests' : 'python.discoverTests';
109+
this.statusBar.text = haveTests ? '$(triangle-right) Run Tests' : 'No Tests';
110+
this.statusBar.tooltip = haveTests ? 'Run Tests' : 'No Tests discovered';
111+
this.statusBar.command = haveTests ? 'python.viewTests' : constants.Command_Tests_Discover;
112112
this.statusBar.show();
113113
}
114114

115115
private updateWithDiscoverFailure(reason: any) {
116116
this.clearProgressTicker();
117117
this.statusBar.text = `$(triangle-right) Discover Tests`;
118-
this.statusBar.tooltip = 'Discover Python Unit Tests';
119-
this.statusBar.command = 'python.discoverTests';
118+
this.statusBar.tooltip = 'Discover Tests';
119+
this.statusBar.command = constants.Command_Tests_Discover;
120120
this.statusBar.show();
121121
if (reason !== CANCELLATION_REASON) {
122-
vscode.window.showErrorMessage('There was an error in discovering unit tests');
122+
vscode.window.showErrorMessage('There was an error in discovering tests');
123123
}
124124
}
125125
}

src/client/unittest/display/picker.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {QuickPickItem, window} from 'vscode';
22
import * as vscode from 'vscode';
33
import {Tests, TestsToRun, TestFolder, TestFile, TestFunction, TestSuite, FlattenedTestFunction} from '../contracts';
44
import {getDiscoveredTests} from '../testUtils';
5+
import * as constants from '../../common/constants';
56

67
export class TestDisplay {
78
constructor() {
@@ -22,16 +23,18 @@ enum Type {
2223
RunFolder = 3,
2324
RunFile = 4,
2425
RunClass = 5,
25-
RunMethod = 6
26+
RunMethod = 6,
27+
ViewTestOutput = 7
2628
}
2729
interface TestItem extends QuickPickItem {
2830
type: Type;
2931
fn?: FlattenedTestFunction;
3032
}
3133
function buildItems(tests?: Tests): TestItem[] {
3234
const items: TestItem[] = [];
33-
items.push({ description: 'Run All Tests', label: 'Run All Tests', type: Type.RunAll });
34-
items.push({ description: 'Rediscover unit tests', label: 'Rediscover Tests', type: Type.ReDiscover });
35+
items.push({ description: '', label: 'Run All Tests', type: Type.RunAll });
36+
items.push({ description: '', label: 'Rediscover Tests', type: Type.ReDiscover });
37+
items.push({ description: '', label: 'View Test Output', type: Type.ViewTestOutput });
3538

3639
if (!tests) {
3740
return items;
@@ -74,19 +77,23 @@ function onItemSelected(selection: TestItem) {
7477
let args = [];
7578
switch (selection.type) {
7679
case Type.RunAll: {
77-
cmd = 'python.runtests';
80+
cmd = constants.Command_Tests_Run;
7881
break;
7982
}
8083
case Type.ReDiscover: {
81-
cmd = 'python.discoverTests';
84+
cmd = constants.Command_Tests_Discover;
85+
break;
86+
}
87+
case Type.ViewTestOutput: {
88+
cmd = constants.Command_Tests_ViewOutput;
8289
break;
8390
}
8491
case Type.RunFailed: {
85-
cmd = 'python.runFailedTests';
92+
cmd = constants.Command_Tests_Run_Failed;
8693
break;
8794
}
8895
case Type.RunMethod: {
89-
cmd = 'python.runtests';
96+
cmd = constants.Command_Tests_Run;
9097
args.push(selection.fn);
9198
break;
9299
}

src/client/unittest/main.ts

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {TestFileCodeLensProvider} from './testFileCodeLensProvider';
1010
import {TestDisplay} from './display/picker';
1111
import * as fs from 'fs';
1212
import * as path from 'path';
13+
import * as constants from '../common/constants';
1314

1415
const settings = PythonSettings.getInstance();
1516
let testManager: BaseTestManager;
@@ -40,15 +41,17 @@ function dispose() {
4041
}
4142
function registerCommands(): vscode.Disposable[] {
4243
const disposables = [];
43-
disposables.push(vscode.commands.registerCommand('python.discoverTests', (quiteMode: boolean) => {
44+
disposables.push(vscode.commands.registerCommand(constants.Command_Tests_Discover, (quiteMode: boolean) => {
4445
// Ignore the exceptions returned
4546
// This command will be invoked else where in the extension
4647
discoverTests(true, quiteMode).catch(() => { return null; });
4748
}));
48-
disposables.push(vscode.commands.registerCommand('python.runFailedTests', () => runTestsImpl(true)));
49-
disposables.push(vscode.commands.registerCommand('python.runtests', (testId) => runTestsImpl(testId)));
50-
disposables.push(vscode.commands.registerCommand('python.viewTests', () => displayUI()));
51-
disposables.push(vscode.commands.registerCommand('python.stopUnitTests', () => stopTests()));
49+
disposables.push(vscode.commands.registerCommand(constants.Command_Tests_Run_Failed, () => runTestsImpl(true)));
50+
disposables.push(vscode.commands.registerCommand(constants.Command_Tests_Run, (testId) => runTestsImpl(testId)));
51+
disposables.push(vscode.commands.registerCommand(constants.Command_Tests_View_UI, () => displayUI()));
52+
disposables.push(vscode.commands.registerCommand(constants.Command_Tests_Stop, () => stopTests()));
53+
disposables.push(vscode.commands.registerCommand(constants.Command_Tests_ViewOutput, () => outChannel.show()));
54+
5255
return disposables;
5356
}
5457

@@ -107,7 +110,7 @@ function isUri(arg: any): arg is vscode.Uri {
107110
}
108111
function isFlattenedTestFunction(arg: any): arg is FlattenedTestFunction {
109112
return arg && arg.testFunction && arg.xmlClassName && arg.parentTestFile &&
110-
arg.parentTestSuite && typeof arg.testFunction.name === 'boolean';
113+
typeof arg.testFunction.name === 'string';
111114
}
112115
function identifyTestType(arg?: vscode.Uri | TestsToRun | boolean | FlattenedTestFunction): TestsToRun | Boolean {
113116
if (typeof arg === 'boolean') {
@@ -137,17 +140,10 @@ function runTestsImpl(arg?: vscode.Uri | TestsToRun | boolean | FlattenedTestFun
137140
testResultDisplay = testResultDisplay ? testResultDisplay : new TestResultDisplay(outChannel);
138141
outChannel.appendLine('\n');
139142

140-
let runPromise = testManager.runTest(runInfo).then(tests => {
141-
if (tests.testFunctions.some(current => current.testFunction.passed === false)) {
142-
// Redisplay this if it was hidden, as we have errors
143-
outChannel.show();
144-
}
145-
return tests;
146-
}).catch(reason => {
143+
let runPromise = testManager.runTest(runInfo).catch(reason => {
147144
if (reason !== CANCELLATION_REASON) {
148145
outChannel.appendLine('Error: ' + reason);
149146
}
150-
outChannel.show();
151147
return Promise.reject(reason);
152148
});
153149

src/client/unittest/nosetest/collector.ts

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ export function discoverTests(rootDirectory: string, args: string[], token: Canc
7979
// process the last entry
8080
parseNoseTestModuleCollectionResult(rootDirectory, logOutputLines, testFiles);
8181
// Exclude tests that don't have any functions or test suites
82-
let indices = testFiles.filter(testFile => testFile.suites.length === 0 && testFile.functions.length === 0).map((testFile, index) => index);
82+
let indices = testFiles.filter(testFile => {
83+
return testFile.suites.length === 0 && testFile.functions.length === 0;
84+
}).map((testFile, index) => index);
8385
indices.sort();
8486

8587
indices.forEach((indexToRemove, index) => {
@@ -107,31 +109,47 @@ function parseNoseTestModuleCollectionResult(rootDirectory: string, lines: strin
107109

108110
// We need to display the path relative to the current directory
109111
fileName = fileName.substring(rootDirectory.length + 1);
112+
// we don't care about the compiled file
113+
if (path.extname(fileName) === '.pyc') {
114+
fileName = fileName.substring(0, fileName.length - 1);
115+
}
110116
currentPackage = convertFileToPackage(fileName);
111-
testFile = { functions: [], suites: [], name: fileName, rawName: fileName, xmlName: currentPackage, time: 0, functionsFailed: 0, functionsPassed: 0 };
117+
testFile = {
118+
functions: [], suites: [], name: fileName, nameToRun: fileName,
119+
xmlName: currentPackage, time: 0, functionsFailed: 0, functionsPassed: 0
120+
};
112121
testFiles.push(testFile);
113122
return;
114123
}
115124

116125
if (line.startsWith('nose.selector: DEBUG: wantClass <class \'')) {
117126
let name = extractBetweenDelimiters(line, 'nose.selector: DEBUG: wantClass <class \'', '\'>? True');
118-
const rawName = fileName + `:${name}`;
119-
const testSuite: TestSuite = { name: path.extname(name).substring(1), rawName: rawName, functions: [], suites: [], xmlName: name, time: 0, isUnitTest: false, isInstance: false, functionsFailed: 0, functionsPassed: 0 };
127+
const testSuite: TestSuite = {
128+
name: path.extname(name).substring(1), nameToRun: fileName + `:${name}`,
129+
functions: [], suites: [], xmlName: name, time: 0, isUnitTest: false,
130+
isInstance: false, functionsFailed: 0, functionsPassed: 0
131+
};
120132
testFile.suites.push(testSuite);
121133
return;
122134
}
123135
if (line.startsWith('nose.selector: DEBUG: wantClass ')) {
124136
let name = extractBetweenDelimiters(line, 'nose.selector: DEBUG: wantClass ', '? True');
125-
const rawName = fileName + `:${name}`;
126-
const testSuite: TestSuite = { name: path.extname(name).substring(1), rawName: rawName, functions: [], suites: [], xmlName: name, time: 0, isUnitTest: false, isInstance: false, functionsFailed: 0, functionsPassed: 0 };
137+
const testSuite: TestSuite = {
138+
name: path.extname(name).substring(1), nameToRun: `${fileName}:.${name}`,
139+
functions: [], suites: [], xmlName: name, time: 0, isUnitTest: false,
140+
isInstance: false, functionsFailed: 0, functionsPassed: 0
141+
};
127142
testFile.suites.push(testSuite);
128143
return;
129144
}
130145
if (line.startsWith('nose.selector: DEBUG: wantMethod <unbound method ')) {
131146
const name = extractBetweenDelimiters(line, 'nose.selector: DEBUG: wantMethod <unbound method ', '>? True');
132147
const fnName = path.extname(name).substring(1);
133148
const clsName = path.basename(name, path.extname(name));
134-
const fn: TestFunction = { name: fnName, rawName: fnName, time: 0, functionsFailed: 0, functionsPassed: 0 };
149+
const fn: TestFunction = {
150+
name: fnName, nameToRun: `${fileName}:${clsName}.${name}`,
151+
time: 0, functionsFailed: 0, functionsPassed: 0
152+
};
135153

136154
let cls = testFile.suites.find(suite => suite.name === clsName);
137155
if (!cls) {
@@ -142,7 +160,10 @@ function parseNoseTestModuleCollectionResult(rootDirectory: string, lines: strin
142160
}
143161
if (line.startsWith('nose.selector: DEBUG: wantFunction <function ')) {
144162
const name = extractBetweenDelimiters(line, 'nose.selector: DEBUG: wantFunction <function ', ' at ');
145-
const fn: TestFunction = { name: name, rawName: name, time: 0, functionsFailed: 0, functionsPassed: 0 };
163+
const fn: TestFunction = {
164+
name: name, nameToRun: `${fileName}:${name}`,
165+
time: 0, functionsFailed: 0, functionsPassed: 0
166+
};
146167
if (!testFile) {
147168
debugger;
148169
}

src/client/unittest/nosetest/main.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ export class TestManager extends BaseTestManager {
1717
let args = settings.unitTest.pyTestArgs.splice(0);
1818
return discoverTests(this.rootDirectory, args, this.cancellationToken);
1919
}
20-
runTestImpl(testsToRun?: TestsToRun, runFailedTests?: boolean): Promise<any> {
20+
runTestImpl(tests: Tests, testsToRun?: TestsToRun, runFailedTests?: boolean): Promise<any> {
2121
let args = settings.unitTest.pyTestArgs.splice(0);
2222
if (runFailedTests === true && args.indexOf('--failed') === -1) {
2323
args.push('--failed');
2424
}
25-
return runTest(this.rootDirectory, this.tests, args, testsToRun, this.stdOut.bind(this), this.cancellationToken);
25+
return runTest(this.rootDirectory, tests, args, testsToRun, this.stdOut.bind(this), this.cancellationToken);
2626
}
2727
}

src/client/unittest/nosetest/runner.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@ import {updateResultsFromXmlLogFile, PassCalculationFormulae} from '../xUnitPars
1212
export function runTest(rootDirectory: string, tests: Tests, args: string[], testsToRun?: TestsToRun, stdOut?: (output: string) => void, token?: CancellationToken): Promise<any> {
1313
let testPaths = [];
1414
if (testsToRun && testsToRun.testFolder) {
15-
testPaths = testPaths.concat(testsToRun.testFolder.map(f => f.rawName));
15+
testPaths = testPaths.concat(testsToRun.testFolder.map(f => f.nameToRun));
1616
}
1717
if (testsToRun && testsToRun.testFile) {
18-
testPaths = testPaths.concat(testsToRun.testFile.map(f => f.rawName));
18+
testPaths = testPaths.concat(testsToRun.testFile.map(f => f.nameToRun));
1919
}
2020
if (testsToRun && testsToRun.testSuite) {
21-
testPaths = testPaths.concat(testsToRun.testSuite.map(f => f.rawName));
21+
testPaths = testPaths.concat(testsToRun.testSuite.map(f => f.nameToRun));
2222
}
2323
if (testsToRun && testsToRun.testFunction) {
24-
testPaths = testPaths.concat(testsToRun.testFunction.map(f => f.rawName));
24+
testPaths = testPaths.concat(testsToRun.testFunction.map(f => f.nameToRun));
2525
}
2626

2727
let xmlLogFile = '';

0 commit comments

Comments
 (0)