Skip to content

Commit 9a94d0f

Browse files
committed
Update pytest parser to handle pytest 4.0 through pytest 4.1
For microsoft#3911 - Handles stdout produced by pytest in the 4.0 and 4.1 series
1 parent 1517b12 commit 9a94d0f

File tree

1 file changed

+57
-41
lines changed

1 file changed

+57
-41
lines changed

src/client/unittests/pytest/services/parserService.ts

Lines changed: 57 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,15 @@ export class TestsParser implements ITestsParser {
3838

3939
const trimmedLine: string = line.trim();
4040

41-
if (trimmedLine.startsWith('<Package \'')) {
41+
if (trimmedLine.startsWith('<Package ')) {
4242
// Process the previous lines.
4343
this.parsePyTestModuleCollectionResult(options.cwd, logOutputLines, testFiles, parentNodes, packagePrefix);
4444
logOutputLines = [''];
4545

4646
packagePrefix = this.extractPackageName(trimmedLine, options.cwd);
4747
}
4848

49-
if (trimmedLine.startsWith('<Module \'') || index === lines.length - 1) {
49+
if (trimmedLine.startsWith('<Module ') || index === lines.length - 1) {
5050
// Process the previous lines.
5151
this.parsePyTestModuleCollectionResult(options.cwd, logOutputLines, testFiles, parentNodes, packagePrefix);
5252
logOutputLines = [''];
@@ -120,7 +120,7 @@ export class TestsParser implements ITestsParser {
120120
* @param rootDir Value is pytest's `--rootdir=` parameter.
121121
*/
122122
private extractPackageName(packageLine: string, rootDir: string): string {
123-
const packagePath: string = extractBetweenDelimiters(packageLine, DELIMITER, DELIMITER);
123+
const packagePath: string = extractBetweenDelimiters(packageLine, '<Package ', '>').trimQuotes();
124124
let packageName: string = path.normalize(packagePath);
125125
const tmpRoot: string = path.normalize(rootDir);
126126

@@ -149,10 +149,11 @@ export class TestsParser implements ITestsParser {
149149

150150
lines.forEach(line => {
151151
const trimmedLine = line.trim();
152-
let name: string = extractBetweenDelimiters(trimmedLine, DELIMITER, DELIMITER);
152+
let name: string = '';
153153
const indent = line.indexOf('<');
154154

155-
if (trimmedLine.startsWith('<Module \'')) {
155+
if (trimmedLine.startsWith('<Module ')) {
156+
name = extractBetweenDelimiters(trimmedLine, '<Module ', '>').trimQuotes();
156157
if (packagePrefix && packagePrefix.length > 0) {
157158
name = packagePrefix.concat('/', name);
158159
}
@@ -169,24 +170,39 @@ export class TestsParser implements ITestsParser {
169170

170171
const parentNode = this.findParentOfCurrentItem(indent, parentNodes);
171172

172-
if (parentNode && trimmedLine.startsWith('<Class \'') || trimmedLine.startsWith('<UnitTestCase \'')) {
173-
const isUnitTest = trimmedLine.startsWith('<UnitTestCase \'');
173+
if (parentNode && trimmedLine.startsWith('<Class ') || trimmedLine.startsWith('<UnitTestCase ')) {
174+
const isUnitTest = trimmedLine.startsWith('<UnitTestCase ');
175+
if (isUnitTest) {
176+
name = extractBetweenDelimiters(trimmedLine, '<UnitTestCase ', '>');
177+
} else {
178+
name = extractBetweenDelimiters(trimmedLine, '<Class ', '>');
179+
}
180+
name = name.trimQuotes();
181+
174182
const rawName = `${parentNode!.item.nameToRun}::${name}`;
175183
const xmlName = `${parentNode!.item.xmlName}.${name}`;
176184
const testSuite: TestSuite = { name: name, nameToRun: rawName, functions: [], suites: [], isUnitTest: isUnitTest, isInstance: false, xmlName: xmlName, time: 0 };
177185
parentNode!.item.suites.push(testSuite);
178186
parentNodes.push({ indent: indent, item: testSuite });
179187
return;
180188
}
181-
if (parentNode && trimmedLine.startsWith('<Instance \'')) {
189+
if (parentNode && trimmedLine.startsWith('<Instance ')) {
190+
name = extractBetweenDelimiters(trimmedLine, '<Instance ', '>').trimQuotes();
182191
// tslint:disable-next-line:prefer-type-cast
183192
const suite = (parentNode!.item as TestSuite);
184193
// suite.rawName = suite.rawName + '::()';
185194
// suite.xmlName = suite.xmlName + '.()';
186195
suite.isInstance = true;
187196
return;
188197
}
189-
if (parentNode && trimmedLine.startsWith('<TestCaseFunction \'') || trimmedLine.startsWith('<Function \'')) {
198+
if (parentNode && trimmedLine.startsWith('<TestCaseFunction ') || trimmedLine.startsWith('<Function ')) {
199+
if (trimmedLine.startsWith('<Function ')) {
200+
name = extractBetweenDelimiters(trimmedLine, '<Function ', '>');
201+
} else {
202+
name = extractBetweenDelimiters(trimmedLine, '<TestCaseFunction ', '>');
203+
}
204+
name = name.trimQuotes();
205+
190206
const rawName = `${parentNode!.item.nameToRun}::${name}`;
191207
const fn: TestFunction = { name: name, nameToRun: rawName, time: 0 };
192208
parentNode!.item.functions.push(fn);
@@ -209,37 +225,37 @@ export class TestsParser implements ITestsParser {
209225
}
210226
}
211227

212-
/* Sample output from pytest --collect-only
213-
<Module 'test_another.py'>
214-
<Class 'Test_CheckMyApp'>
215-
<Instance '()'>
216-
<Function 'test_simple_check'>
217-
<Function 'test_complex_check'>
218-
<Module 'test_one.py'>
219-
<UnitTestCase 'Test_test1'>
220-
<TestCaseFunction 'test_A'>
221-
<TestCaseFunction 'test_B'>
222-
<Module 'test_two.py'>
223-
<UnitTestCase 'Test_test1'>
224-
<TestCaseFunction 'test_A2'>
225-
<TestCaseFunction 'test_B2'>
226-
<Module 'testPasswords/test_Pwd.py'>
227-
<UnitTestCase 'Test_Pwd'>
228-
<TestCaseFunction 'test_APwd'>
229-
<TestCaseFunction 'test_BPwd'>
230-
<Module 'testPasswords/test_multi.py'>
231-
<Class 'Test_CheckMyApp'>
228+
/* Sample output from pytest --collect-only
229+
<Module 'test_another.py'>
230+
<Class 'Test_CheckMyApp'>
231+
<Instance '()'>
232+
<Function 'test_simple_check'>
233+
<Function 'test_complex_check'>
234+
<Module 'test_one.py'>
235+
<UnitTestCase 'Test_test1'>
236+
<TestCaseFunction 'test_A'>
237+
<TestCaseFunction 'test_B'>
238+
<Module 'test_two.py'>
239+
<UnitTestCase 'Test_test1'>
240+
<TestCaseFunction 'test_A2'>
241+
<TestCaseFunction 'test_B2'>
242+
<Module 'testPasswords/test_Pwd.py'>
243+
<UnitTestCase 'Test_Pwd'>
244+
<TestCaseFunction 'test_APwd'>
245+
<TestCaseFunction 'test_BPwd'>
246+
<Module 'testPasswords/test_multi.py'>
247+
<Class 'Test_CheckMyApp'>
248+
<Instance '()'>
249+
<Function 'test_simple_check'>
250+
<Function 'test_complex_check'>
251+
<Class 'Test_NestedClassA'>
232252
<Instance '()'>
233-
<Function 'test_simple_check'>
234-
<Function 'test_complex_check'>
235-
<Class 'Test_NestedClassA'>
253+
<Function 'test_nested_class_methodB'>
254+
<Class 'Test_nested_classB_Of_A'>
236255
<Instance '()'>
237-
<Function 'test_nested_class_methodB'>
238-
<Class 'Test_nested_classB_Of_A'>
239-
<Instance '()'>
240-
<Function 'test_d'>
241-
<Function 'test_username'>
242-
<Function 'test_parametrized_username[one]'>
243-
<Function 'test_parametrized_username[two]'>
244-
<Function 'test_parametrized_username[three]'>
245-
*/
256+
<Function 'test_d'>
257+
<Function 'test_username'>
258+
<Function 'test_parametrized_username[one]'>
259+
<Function 'test_parametrized_username[two]'>
260+
<Function 'test_parametrized_username[three]'>
261+
*/

0 commit comments

Comments
 (0)