Skip to content

Commit bf47601

Browse files
committed
Unit tests for var and method extraction #220
1 parent a14db36 commit bf47601

3 files changed

Lines changed: 503 additions & 150 deletions

File tree

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
import * as assert from 'assert';
2+
3+
// You can import and use all API from the \'vscode\' module
4+
// as well as import your extension to test it
5+
import * as vscode from 'vscode';
6+
import {TextDocument, TextLine, Position, Range} from 'vscode';
7+
import * as path from 'path';
8+
import * as settings from '../client/common/configSettings';
9+
import * as fs from 'fs-extra';
10+
import {initialize, closeActiveWindows} from './initialize';
11+
import {execPythonFile} from '../client/common/utils';
12+
import {extractVariable, extractMethod} from '../client/providers/simpleRefactorProvider';
13+
import {RefactorProxy} from '../client/refactor/proxy';
14+
15+
let EXTENSION_DIR = path.join(__dirname, '..', '..');
16+
let pythonSettings = settings.PythonSettings.getInstance();
17+
18+
const refactorSourceFile = path.join(__dirname, '..', '..', 'src', 'test', 'pythonFiles', 'refactoring', 'standAlone', 'refactor.py');
19+
const refactorTargetFile = path.join(__dirname, '..', '..', 'out', 'test', 'pythonFiles', 'refactoring', 'standAlone', 'refactor.py');
20+
let isPython3 = true;
21+
let isTRAVIS = (process.env['TRAVIS'] + '') === 'true';
22+
23+
interface RenameResponse {
24+
results: [{ diff: string }];
25+
}
26+
27+
class MockOutputChannel implements vscode.OutputChannel {
28+
constructor(name: string) {
29+
this.name = name;
30+
this.output = '';
31+
}
32+
name: string;
33+
output: string;
34+
append(value: string) {
35+
this.output += value;
36+
}
37+
appendLine(value: string) { this.append(value); this.append('\n'); }
38+
clear() { }
39+
show(preservceFocus?: boolean): void;
40+
show(column?: vscode.ViewColumn, preserveFocus?: boolean): void;
41+
show(x?: any, y?: any): void { }
42+
hide() { }
43+
dispose() { }
44+
}
45+
class MockTextDocument implements vscode.TextDocument {
46+
uri: vscode.Uri;
47+
fileName: string;
48+
isUntitled: boolean;
49+
languageId: string;
50+
version: number;
51+
isDirty: boolean;
52+
constructor(private sourceFile: string, private offsets: [{ position: Position, offset: number }]) {
53+
}
54+
save(): Thenable<boolean> {
55+
return Promise.resolve(true);
56+
}
57+
lineCount: number;
58+
lineAt(position: Position | number): TextLine {
59+
return null;
60+
}
61+
offsetAt(position: Position): number {
62+
return this.offsets.filter(item => item.position.isEqual(position))[0].offset;
63+
}
64+
positionAt(offset: number): Position {
65+
return null;
66+
}
67+
getText(range?: Range): string {
68+
return fs.readFileSync(this.sourceFile, 'utf-8');
69+
}
70+
getWordRangeAtPosition(position: Position): Range {
71+
return null;
72+
}
73+
validateRange(range: Range): Range {
74+
return null;
75+
}
76+
validatePosition(position: Position): Position {
77+
return null;
78+
}
79+
}
80+
81+
suiteSetup(done => {
82+
fs.copySync(refactorSourceFile, refactorTargetFile, { clobber: true });
83+
initialize().then(() => {
84+
new Promise<string>(resolve => {
85+
// Support for travis
86+
let version = process.env['TRAVIS_PYTHON_VERSION'];
87+
if (typeof version === 'string') {
88+
return resolve(version);
89+
}
90+
// Support for local tests
91+
execPythonFile('python', ['--version'], __dirname, true).then(resolve);
92+
}).then(version => {
93+
isPython3 = version.indexOf('3.') >= 0;
94+
done();
95+
});
96+
});
97+
});
98+
99+
suiteTeardown(done => {
100+
// deleteFile(targetPythonFileToLint).then(done, done);
101+
done();
102+
});
103+
104+
suite('Method Extraction', () => {
105+
setup(() => {
106+
if (fs.existsSync(refactorTargetFile)) {
107+
fs.unlinkSync(refactorTargetFile);
108+
}
109+
fs.copySync(refactorSourceFile, refactorTargetFile, { clobber: true });
110+
});
111+
teardown(done => {
112+
closeActiveWindows().then(() => {
113+
setTimeout(function () {
114+
RefactorProxy.pythonPath = null;
115+
done();
116+
}, 1000);
117+
});
118+
});
119+
120+
function testingMethodExtraction(shouldError: boolean, pythonSettings: settings.IPythonSettings, startPos: Position, endPos: Position) {
121+
let ch = new MockOutputChannel('Python');
122+
let textDocument: vscode.TextDocument;
123+
let textEditor: vscode.TextEditor;
124+
let rangeOfTextToExtract = new vscode.Range(startPos, endPos);
125+
let proxy = new RefactorProxy(EXTENSION_DIR, pythonSettings, path.dirname(refactorTargetFile));
126+
let mockTextDoc = new MockTextDocument(refactorTargetFile, [
127+
{ position: startPos, offset: 8346 },
128+
{ position: endPos, offset: 8519 }
129+
]);
130+
131+
const DIFF = `--- a/refactor.py\n+++ b/refactor.py\n@@ -237,9 +237,12 @@\n try:\n self._process_request(self._input.readline())\n except Exception as ex:\n- message = ex.message + ' \\n' + traceback.format_exc()\n- sys.stderr.write(str(len(message)) + ':' + message)\n- sys.stderr.flush()\n+ self.myNewMethod(ex)\n+\n+ def myNewMethod(self, ex):\n+ message = ex.message + ' \\n' + traceback.format_exc()\n+ sys.stderr.write(str(len(message)) + ':' + message)\n+ sys.stderr.flush()\n \n if __name__ == '__main__':\n RopeRefactoring().watch()\n`;
132+
133+
return proxy.extractMethod<RenameResponse>(mockTextDoc, 'myNewMethod', refactorTargetFile, rangeOfTextToExtract)
134+
.then(response => {
135+
assert.equal(response.results.length, 1, 'Invalid number of items in response');
136+
assert.equal(response.results[0].diff, DIFF, 'Invalid DIFF');
137+
}).catch(error => {
138+
if (shouldError) {
139+
// Wait a minute this shouldn't work, what's going on
140+
assert.equal(true, true, 'Error raised as expected');
141+
return;
142+
}
143+
144+
return Promise.reject(error);
145+
});
146+
}
147+
148+
test('Extract Method', done => {
149+
let startPos = new vscode.Position(239, 0);
150+
let endPos = new vscode.Position(241, 35);
151+
testingMethodExtraction(false, pythonSettings, startPos, endPos).then(() => done(), done);
152+
});
153+
154+
test('Extract Method will fail if complete statements are not selected', done => {
155+
let startPos = new vscode.Position(239, 30);
156+
let endPos = new vscode.Position(241, 35);
157+
testingMethodExtraction(true, pythonSettings, startPos, endPos).then(() => done(), done);
158+
});
159+
160+
test('Extract Method will try to find Python 2.x', done => {
161+
let startPos = new vscode.Position(239, 0);
162+
let endPos = new vscode.Position(241, 35);
163+
let clonedSettings = JSON.parse(JSON.stringify(pythonSettings));
164+
clonedSettings.python2Path = 'python3';
165+
testingMethodExtraction(false, clonedSettings, startPos, endPos).then(() => done(), done);
166+
});
167+
168+
test('Extract Method will not work in Python 3.x', done => {
169+
let startPos = new vscode.Position(239, 0);
170+
let endPos = new vscode.Position(241, 35);
171+
let clonedSettings = JSON.parse(JSON.stringify(pythonSettings));
172+
clonedSettings.pythonPath = 'python3';
173+
clonedSettings.python2Path = 'python3';
174+
testingMethodExtraction(true, clonedSettings, startPos, endPos).then(() => done(), done);
175+
});
176+
177+
if (!isTRAVIS) {
178+
function testingMethodExtractionEndToEnd(shouldError: boolean, pythonSettings: settings.IPythonSettings, startPos: Position, endPos: Position) {
179+
let ch = new MockOutputChannel('Python');
180+
let textDocument: vscode.TextDocument;
181+
let textEditor: vscode.TextEditor;
182+
let rangeOfTextToExtract = new vscode.Range(startPos, endPos);
183+
184+
return vscode.workspace.openTextDocument(refactorTargetFile).then(document => {
185+
textDocument = document;
186+
return vscode.window.showTextDocument(textDocument);
187+
}).then(editor => {
188+
editor.selections = [new vscode.Selection(rangeOfTextToExtract.start, rangeOfTextToExtract.end)];
189+
editor.selection = new vscode.Selection(rangeOfTextToExtract.start, rangeOfTextToExtract.end);
190+
textEditor = editor;
191+
return;
192+
}).then(() => {
193+
return extractMethod(EXTENSION_DIR, textEditor, rangeOfTextToExtract, ch, path.dirname(refactorTargetFile), false, pythonSettings).then(() => {
194+
if (shouldError) {
195+
// Wait a minute this shouldn't work, what's going on
196+
throw new Error('This should fail, but seems to have worked');
197+
}
198+
assert.equal(ch.output.length, 0, 'Output channel is not empty');
199+
assert.equal(textDocument.lineAt(241).text.trim().indexOf('def newmethod'), 0, 'New Method not created');
200+
assert.equal(textDocument.lineAt(239).text.trim().startsWith('self.newmethod'), true, 'New Method not being used');
201+
}).catch(error => {
202+
if (shouldError) {
203+
// Wait a minute this shouldn't work, what's going on
204+
assert.equal(true, true, 'Error raised as expected');
205+
return;
206+
}
207+
208+
return Promise.reject(error);
209+
});
210+
}, error => {
211+
if (shouldError) {
212+
// Wait a minute this shouldn't work, what's going on
213+
assert.equal(true, true, 'Error raised as expected');
214+
}
215+
else {
216+
assert.fail(error + '', null, 'Method extraction failed\n' + ch.output);
217+
return Promise.reject(error);
218+
}
219+
});
220+
}
221+
222+
test('Extract Method (end to end)', done => {
223+
let startPos = new vscode.Position(239, 0);
224+
let endPos = new vscode.Position(241, 35);
225+
testingMethodExtractionEndToEnd(false, pythonSettings, startPos, endPos).then(() => done(), done);
226+
});
227+
228+
test('Extract Method will fail if complete statements are not selected', done => {
229+
let startPos = new vscode.Position(239, 30);
230+
let endPos = new vscode.Position(241, 35);
231+
testingMethodExtractionEndToEnd(true, pythonSettings, startPos, endPos).then(() => done(), done);
232+
});
233+
234+
test('Extract Method will try to find Python 2.x (end to end)', done => {
235+
let startPos = new vscode.Position(239, 0);
236+
let endPos = new vscode.Position(241, 35);
237+
let clonedSettings = JSON.parse(JSON.stringify(pythonSettings));
238+
clonedSettings.python2Path = 'python3';
239+
testingMethodExtractionEndToEnd(false, clonedSettings, startPos, endPos).then(() => done(), done);
240+
});
241+
242+
test('Extract Method will not work in Python 3.x (end to end)', done => {
243+
let startPos = new vscode.Position(239, 0);
244+
let endPos = new vscode.Position(241, 35);
245+
let clonedSettings = JSON.parse(JSON.stringify(pythonSettings));
246+
clonedSettings.pythonPath = 'python3';
247+
clonedSettings.python2Path = 'python3';
248+
testingMethodExtractionEndToEnd(true, clonedSettings, startPos, endPos).then(() => done(), done);
249+
});
250+
}
251+
});

0 commit comments

Comments
 (0)