diff --git a/news/2 Fixes/6990.md b/news/2 Fixes/6990.md new file mode 100644 index 000000000000..77cdab51a022 --- /dev/null +++ b/news/2 Fixes/6990.md @@ -0,0 +1 @@ +Add support for the new JUnit XML format used by pytest 5.1+. diff --git a/src/client/testing/common/xUnitParser.ts b/src/client/testing/common/xUnitParser.ts index 9467fb6674c8..6078be80f115 100644 --- a/src/client/testing/common/xUnitParser.ts +++ b/src/client/testing/common/xUnitParser.ts @@ -59,8 +59,11 @@ export class XUnitParser implements IXUnitParser { ) { const data = await this.fs.readFile(outputXmlFile); - const parserResult = await parseXML(data) as { testsuite: TestSuiteResult }; - updateTests(tests, parserResult.testsuite); + const parserResult = await parseXML(data); + const junitResults = getJunitResults(parserResult); + if (junitResults) { + updateTests(tests, junitResults); + } } } @@ -80,6 +83,28 @@ async function parseXML(data: string): Promise { }); } +// Return the actual test results from the given data. +// tslint:disable-next-line:no-any +function getJunitResults(parserResult: any): TestSuiteResult | undefined { + // This is the newer JUnit XML format (e.g. pytest 5.1 and later). + const fullResults = parserResult as { testsuites: { testsuite: TestSuiteResult[] }}; + if (!fullResults.testsuites) { + return (parserResult as { testsuite: TestSuiteResult }).testsuite; + } + + const junitSuites = fullResults.testsuites.testsuite; + if (!Array.isArray(junitSuites)) { + throw Error('bad JUnit XML data'); + } + if (junitSuites.length === 0) { + return; + } + if (junitSuites.length > 1) { + throw Error('got multiple XML results'); + } + return junitSuites[0]; +} + // Update "tests" with the given results. function updateTests( tests: Tests, diff --git a/src/test/testing/common/xUnitParser.unit.test.ts b/src/test/testing/common/xUnitParser.unit.test.ts index 9a9b5498b4ca..fc8bf2aa682c 100644 --- a/src/test/testing/common/xUnitParser.unit.test.ts +++ b/src/test/testing/common/xUnitParser.unit.test.ts @@ -38,7 +38,7 @@ suite('Testing - parse JUnit XML file', () => { node.line = line; } - test('success with single passing test', async () => { + test('legacy - success with single passing test', async () => { const tests = createDeclaratively(` ./ test_spam.py @@ -72,6 +72,42 @@ suite('Testing - parse JUnit XML file', () => { fs.verifyAll(); }); + test('success with single passing test', async () => { + const tests = createDeclaratively(` + ./ + test_spam.py + + test_spam + `); + const expected = createDeclaratively(` + ./ + test_spam.py + + test_spam P 0.001 + `); + fixResult( + expected.testFunctions[0].testFunction, + 'test_spam.py', + 3 + ); + const filename = 'x/y/z/results.xml'; + fs.setup(f => f.readFile(filename)) + .returns(() => Promise.resolve(` + + + + + + + + `)); + + await parser.updateResultsFromXmlLogFile(tests, filename); + + expect(tests).to.deep.equal(expected); + fs.verifyAll(); + }); + test('no discovered tests', async () => { const tests: Tests = createEmptyResults(); const expected: Tests = createEmptyResults();