From 02eb4cb73b1a25e78ca814c5c2fdd45fe7410168 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 5 Mar 2019 13:48:19 -0700 Subject: [PATCH 01/12] Add a NEWS entry. --- news/1 Enhancements/4503.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 news/1 Enhancements/4503.md diff --git a/news/1 Enhancements/4503.md b/news/1 Enhancements/4503.md new file mode 100644 index 000000000000..2db7395236fa --- /dev/null +++ b/news/1 Enhancements/4503.md @@ -0,0 +1 @@ +Show sub-tests in a subtree in the test explorer. From 5721a12afe25ac89b43c8d68cadc521425918158 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 6 Mar 2019 16:04:03 -0700 Subject: [PATCH 02/12] Add TestFunction.funcName. --- src/client/unittests/common/types.ts | 1 + src/client/unittests/pytest/services/parserService.ts | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/client/unittests/common/types.ts b/src/client/unittests/common/types.ts index 886dbde6fffe..095607834e10 100644 --- a/src/client/unittests/common/types.ts +++ b/src/client/unittests/common/types.ts @@ -72,6 +72,7 @@ export type TestFunction = TestResult & { resource: Uri; name: string; nameToRun: string; + funcName?: string; // used if is subtest }; export type TestResult = Node & { diff --git a/src/client/unittests/pytest/services/parserService.ts b/src/client/unittests/pytest/services/parserService.ts index 5bbf8a01e899..a36e23d77e28 100644 --- a/src/client/unittests/pytest/services/parserService.ts +++ b/src/client/unittests/pytest/services/parserService.ts @@ -206,7 +206,16 @@ export class TestsParser implements ITestsParser { name = name.trimQuotes(); const rawName = `${parentNode!.item.nameToRun}::${name}`; - const fn: TestFunction = { resource, name: name, nameToRun: rawName, time: 0 }; + const fn: TestFunction = { + resource: resource, + name: name, + nameToRun: rawName, + time: 0 + }; + const pos = name.indexOf('['); + if (pos > 0 && name.endsWith(']')) { + fn.funcName = name.substring(0, pos); + } parentNode!.item.functions.push(fn); return; } From 913d3833b95891d377a242432cfdade9029189d3 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 6 Mar 2019 18:07:25 -0700 Subject: [PATCH 03/12] funcName -> subtestParent --- src/client/unittests/common/types.ts | 2 +- .../pytest/services/parserService.ts | 52 +++++++++++++++++-- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/client/unittests/common/types.ts b/src/client/unittests/common/types.ts index 095607834e10..357f453d964a 100644 --- a/src/client/unittests/common/types.ts +++ b/src/client/unittests/common/types.ts @@ -72,7 +72,7 @@ export type TestFunction = TestResult & { resource: Uri; name: string; nameToRun: string; - funcName?: string; // used if is subtest + subtestParent?: TestFunction; }; export type TestResult = Node & { diff --git a/src/client/unittests/pytest/services/parserService.ts b/src/client/unittests/pytest/services/parserService.ts index a36e23d77e28..e9848340256d 100644 --- a/src/client/unittests/pytest/services/parserService.ts +++ b/src/client/unittests/pytest/services/parserService.ts @@ -148,6 +148,8 @@ export class TestsParser implements ITestsParser { let currentPackage: string = ''; const resource = Uri.file(rootDirectory); + + // tslint:disable-next-line:cyclomatic-complexity lines.forEach(line => { const trimmedLine = line.trim(); let name: string = ''; @@ -161,9 +163,17 @@ export class TestsParser implements ITestsParser { currentPackage = convertFileToPackage(name); const fullyQualifiedName = path.isAbsolute(name) ? name : path.resolve(rootDirectory, name); const testFile = { - resource, - functions: [], suites: [], name: name, fullPath: fullyQualifiedName, - nameToRun: name, xmlName: currentPackage, time: 0, functionsPassed: 0, functionsFailed: 0, functionsDidNotRun: 0 + resource: resource, + functions: [], + suites: [], + name: name, + fullPath: fullyQualifiedName, + nameToRun: name, + xmlName: currentPackage, + time: 0, + functionsPassed: 0, + functionsFailed: 0, + functionsDidNotRun: 0 }; testFiles.push(testFile); parentNodes.push({ indent: indent, item: testFile }); @@ -183,7 +193,20 @@ export class TestsParser implements ITestsParser { const rawName = `${parentNode!.item.nameToRun}::${name}`; const xmlName = `${parentNode!.item.xmlName}.${name}`; - const testSuite: TestSuite = { resource, name: name, nameToRun: rawName, functions: [], suites: [], isUnitTest: isUnitTest, isInstance: false, xmlName: xmlName, time: 0, functionsPassed: 0, functionsFailed: 0, functionsDidNotRun: 0 }; + const testSuite: TestSuite = { + resource: resource, + name: name, + nameToRun: rawName, + functions: [], + suites: [], + isUnitTest: isUnitTest, + isInstance: false, + xmlName: xmlName, + time: 0, + functionsPassed: 0, + functionsFailed: 0, + functionsDidNotRun: 0 + }; parentNode!.item.suites.push(testSuite); parentNodes.push({ indent: indent, item: testSuite }); return; @@ -214,7 +237,26 @@ export class TestsParser implements ITestsParser { }; const pos = name.indexOf('['); if (pos > 0 && name.endsWith(']')) { - fn.funcName = name.substring(0, pos); + const funcName = name.substring(0, pos); + const subtest = name.substring(pos); + + let subtestParent: TestFunction | undefined; + const last = parentNode!.item.functions.pop(); + if (last) { + parentNode!.item.functions.push(last); + if (last.subtestParent && last.subtestParent.name === funcName) { + subtestParent = last.subtestParent; + } + } + if (!subtestParent) { + subtestParent = { + resource: resource, + name: funcName, + nameToRun: rawName.substring(0, rawName.length - subtest.length), + time: 0 + }; + } + fn.subtestParent = subtestParent!; } parentNode!.item.functions.push(fn); return; From 74ef617440219d993ae310455c9b0851a003229c Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 7 Mar 2019 10:25:14 -0700 Subject: [PATCH 04/12] Add SubtestParent type. --- src/client/unittests/common/types.ts | 8 ++++++- .../pytest/services/parserService.ts | 22 ++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/client/unittests/common/types.ts b/src/client/unittests/common/types.ts index 357f453d964a..0542131ab681 100644 --- a/src/client/unittests/common/types.ts +++ b/src/client/unittests/common/types.ts @@ -72,7 +72,13 @@ export type TestFunction = TestResult & { resource: Uri; name: string; nameToRun: string; - subtestParent?: TestFunction; + subtestParent?: SubtestParent; +}; + +export type SubtestParent = TestResult & { + name: string; + nameToRun: string; + asSuite: TestSuite; }; export type TestResult = Node & { diff --git a/src/client/unittests/pytest/services/parserService.ts b/src/client/unittests/pytest/services/parserService.ts index e9848340256d..249dd34c6538 100644 --- a/src/client/unittests/pytest/services/parserService.ts +++ b/src/client/unittests/pytest/services/parserService.ts @@ -7,7 +7,10 @@ import * as path from 'path'; import { Uri } from 'vscode'; import '../../../common/extensions'; import { convertFileToPackage, extractBetweenDelimiters } from '../../common/testUtils'; -import { ITestsHelper, ITestsParser, ParserOptions, TestFile, TestFunction, Tests, TestSuite } from '../../common/types'; +import { + ITestsHelper, ITestsParser, ParserOptions, SubtestParent, + TestFile, TestFunction, Tests, TestSuite +} from '../../common/types'; @injectable() export class TestsParser implements ITestsParser { @@ -138,6 +141,7 @@ export class TestsParser implements ITestsParser { return packageName; } + // tslint:disable-next-line:max-func-body-length private parsePyTestModuleCollectionResult( rootDirectory: string, lines: string[], @@ -240,7 +244,7 @@ export class TestsParser implements ITestsParser { const funcName = name.substring(0, pos); const subtest = name.substring(pos); - let subtestParent: TestFunction | undefined; + let subtestParent: SubtestParent | undefined; const last = parentNode!.item.functions.pop(); if (last) { parentNode!.item.functions.push(last); @@ -249,14 +253,26 @@ export class TestsParser implements ITestsParser { } } if (!subtestParent) { - subtestParent = { + const subtestsSuite: TestSuite = { resource: resource, name: funcName, nameToRun: rawName.substring(0, rawName.length - subtest.length), + functions: [], + suites: [], + isUnitTest: false, + isInstance: false, + xmlName: '', + time: 0 + }; + subtestParent = { + name: subtestsSuite.name, + nameToRun: subtestsSuite.nameToRun, + asSuite: subtestsSuite, time: 0 }; } fn.subtestParent = subtestParent!; + subtestParent.asSuite.functions.push(fn); } parentNode!.item.functions.push(fn); return; From da9e27bc9b5fa1b60ab5a92796fed171d165dfad Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 7 Mar 2019 10:27:54 -0700 Subject: [PATCH 05/12] Nest subtests under their function in the test explorer. --- src/client/unittests/common/testUtils.ts | 65 ++++++++++++++++--- .../unittests/explorer/testTreeViewItem.ts | 8 ++- 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/src/client/unittests/common/testUtils.ts b/src/client/unittests/common/testUtils.ts index b7c299b01858..d21292c00b53 100644 --- a/src/client/unittests/common/testUtils.ts +++ b/src/client/unittests/common/testUtils.ts @@ -317,6 +317,14 @@ export function getParent(tests: Tests, data: TestDataItem): TestDataItem | unde // const parentSuite = tests.testSuites.find(item => item.testSuite.suites.some(child => child === data)); // const parentFile = tests.testFiles.find(item=> item.suites.find(data) // return item && (item.parentTestSuite || item.parentTestFile); + if (isSubtestsParent(suite)) { + const fn = suite.functions[0]; + const parent = tests.testSuites.find(item => item.testSuite.functions.indexOf(fn) >= 0); + if (parent) { + return parent.testSuite; + } + return tests.testFiles.find(item => item.functions.indexOf(fn) >= 0); + } const parentSuite = tests.testSuites.find(item => item.testSuite.suites.indexOf(suite) >= 0); if (parentSuite) { return parentSuite.testSuite; @@ -325,6 +333,9 @@ export function getParent(tests: Tests, data: TestDataItem): TestDataItem | unde } case TestType.testFunction: { const fn = data as TestFunction; + if (fn.subtestParent) { + return fn.subtestParent.asSuite; + } const parentSuite = tests.testSuites.find(item => item.testSuite.functions.indexOf(fn) >= 0); if (parentSuite) { return parentSuite.testSuite; @@ -427,22 +438,30 @@ export function findFlattendTestSuite(tests: Tests, suite: TestSuite): Flattened */ export function getChildren(item: TestDataItem): TestDataItem[] { switch (getTestType(item)) { - case TestType.testFile: { - return [ - ...(item as TestFile).functions, - ...(item as TestFile).suites - ]; - } case TestType.testFolder: { return [ ...(item as TestFolder).folders, ...(item as TestFolder).testFiles ]; } + case TestType.testFile: { + const [subSuites, functions] = divideSubtests((item as TestFile).functions); + return [ + ...functions, + ...(item as TestFile).suites, + ...subSuites + ]; + } case TestType.testSuite: { + let subSuites: TestSuite[] = []; + let functions = (item as TestSuite).functions; + if (!isSubtestsParent((item as TestSuite))) { + [subSuites, functions] = divideSubtests((item as TestSuite).functions); + } return [ - ...(item as TestSuite).functions, - ...(item as TestSuite).suites + ...functions, + ...(item as TestSuite).suites, + ...subSuites ]; } case TestType.testFunction: { @@ -454,6 +473,34 @@ export function getChildren(item: TestDataItem): TestDataItem[] { } } +function divideSubtests(mixed: TestFunction[]): [TestSuite[], TestFunction[]] { + const suites: TestSuite[] = []; + const functions: TestFunction[] = []; + mixed.forEach(func => { + if (!func.subtestParent) { + functions.push(func); + return; + } + const parent = func.subtestParent.asSuite; + if (suites.indexOf(parent) < 0) { + suites.push(parent); + } + }); + return [suites, functions]; +} + +export function isSubtestsParent(suite: TestSuite): boolean { + const functions = suite.functions; + if (functions.length === 0) { + return false; + } + const subtestParent = functions[0].subtestParent; + if (subtestParent === undefined) { + return false; + } + return subtestParent.asSuite === suite; +} + export function copyTestResults(source: Tests, target: Tests): void { copyResultsForFolders(source.testFolders, target.testFolders); } @@ -511,4 +558,4 @@ function copyValueTypes(source: T, target: T): void { target[key] = value; } }); -} + } diff --git a/src/client/unittests/explorer/testTreeViewItem.ts b/src/client/unittests/explorer/testTreeViewItem.ts index 5b7d2aefaff8..223576ff21f7 100644 --- a/src/client/unittests/explorer/testTreeViewItem.ts +++ b/src/client/unittests/explorer/testTreeViewItem.ts @@ -10,8 +10,8 @@ import { Commands } from '../../common/constants'; import { getIcon } from '../../common/utils/icons'; import { noop } from '../../common/utils/misc'; import { Icons } from '../common/constants'; -import { getTestType } from '../common/testUtils'; -import { TestResult, TestStatus, TestType } from '../common/types'; +import { getTestType, isSubtestsParent } from '../common/testUtils'; +import { TestResult, TestStatus, TestSuite, TestType } from '../common/types'; import { TestDataItem } from '../types'; /** @@ -114,6 +114,10 @@ export class TestTreeItem extends TreeItem { break; } case TestType.testSuite: { + if (isSubtestsParent(this.data as TestSuite)) { + this.command = { command: Commands.navigateToTestFunction, title: 'Open', arguments: [this.resource, this.data, false] }; + break; + } this.command = { command: Commands.navigateToTestSuite, title: 'Open', arguments: [this.resource, this.data, false] }; break; } From 9ac4e44fbde5c097190dd281961e9ab29d381856 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 7 Mar 2019 10:35:59 -0700 Subject: [PATCH 06/12] Minor drive-by clean-ups. --- src/client/unittests/common/testUtils.ts | 19 ------------------- .../pytest/services/parserService.ts | 2 -- 2 files changed, 21 deletions(-) diff --git a/src/client/unittests/common/testUtils.ts b/src/client/unittests/common/testUtils.ts index d21292c00b53..343b15fe0fd5 100644 --- a/src/client/unittests/common/testUtils.ts +++ b/src/client/unittests/common/testUtils.ts @@ -314,9 +314,6 @@ export function getParent(tests: Tests, data: TestDataItem): TestDataItem | unde } case TestType.testSuite: { const suite = data as TestSuite; - // const parentSuite = tests.testSuites.find(item => item.testSuite.suites.some(child => child === data)); - // const parentFile = tests.testFiles.find(item=> item.suites.find(data) - // return item && (item.parentTestSuite || item.parentTestFile); if (isSubtestsParent(suite)) { const fn = suite.functions[0]; const parent = tests.testSuites.find(item => item.testSuite.functions.indexOf(fn) >= 0); @@ -341,8 +338,6 @@ export function getParent(tests: Tests, data: TestDataItem): TestDataItem | unde return parentSuite.testSuite; } return tests.testFiles.find(item => item.functions.indexOf(fn) >= 0); - // const item = findFlattendTestFunction(tests, data as TestFunction); - // return item && (item.parentTestSuite || item.parentTestFile); } default: { throw new Error('Unknown test type'); @@ -388,20 +383,6 @@ function getParentTestFolderForFolder(tests: Tests, folder: TestFolder): TestFol return; } return tests.testFolders.find(item => item.folders.some(child => child === folder)); - // function getParentFolder(folders: TestFolder[], item: TestFolder): TestFolder { - // const index = folders.indexOf(item); - // if (index) { - // return folders[index]; - // } - // for (const f of folders) { - // const found = getParentFolder(f.folders, item); - // if (found) { - // return found; - // } - // } - // } - - // return getParentFolder(tests.testFolders, folder); } /** diff --git a/src/client/unittests/pytest/services/parserService.ts b/src/client/unittests/pytest/services/parserService.ts index 249dd34c6538..51a8d884830b 100644 --- a/src/client/unittests/pytest/services/parserService.ts +++ b/src/client/unittests/pytest/services/parserService.ts @@ -219,8 +219,6 @@ export class TestsParser implements ITestsParser { name = extractBetweenDelimiters(trimmedLine, '').trimQuotes(); // tslint:disable-next-line:prefer-type-cast const suite = (parentNode!.item as TestSuite); - // suite.rawName = suite.rawName + '::()'; - // suite.xmlName = suite.xmlName + '.()'; suite.isInstance = true; return; } From 07030b7792c8191cc4de7ada118f492758ae792b Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 11 Mar 2019 10:05:37 -0600 Subject: [PATCH 07/12] Add tests for the pytest parser. --- .../pytest/pytest.testparser.unit.test.ts | 130 +++++++++++++----- .../pytest/pytest_unittest_parser_data.ts | 23 ++++ 2 files changed, 117 insertions(+), 36 deletions(-) diff --git a/src/test/unittests/pytest/pytest.testparser.unit.test.ts b/src/test/unittests/pytest/pytest.testparser.unit.test.ts index dfef2b0a059c..3850997a50d9 100644 --- a/src/test/unittests/pytest/pytest.testparser.unit.test.ts +++ b/src/test/unittests/pytest/pytest.testparser.unit.test.ts @@ -12,13 +12,17 @@ import { OSType } from '../../../client/common/utils/platform'; import { IServiceContainer } from '../../../client/ioc/types'; import { TestsHelper } from '../../../client/unittests/common/testUtils'; import { TestFlatteningVisitor } from '../../../client/unittests/common/testVisitors/flatteningVisitor'; -import { FlattenedTestFunction, TestDiscoveryOptions, Tests } from '../../../client/unittests/common/types'; +import { + FlattenedTestFunction, SubtestParent, TestDiscoveryOptions, Tests +} from '../../../client/unittests/common/types'; import { TestsParser as PyTestsParser } from '../../../client/unittests/pytest/services/parserService'; import { getOSType } from '../../common'; import { PytestDataPlatformType, pytestScenarioData } from './pytest_unittest_parser_data'; use(chaipromise); +// tslint:disable:max-func-body-length + // The PyTest test parsing is done via the stdout result of the // `pytest --collect-only` command. // @@ -30,6 +34,44 @@ use(chaipromise); // created a JSON structure that defines all the tests - see file // `pytest_unittest_parser_data.ts` in this folder. suite('Unit Tests - PyTest - Test Parser used in discovery', () => { + let serviceContainer: typeMoq.IMock; + let appShell: typeMoq.IMock; + let cmdMgr: typeMoq.IMock; + + setup(() => { + serviceContainer = typeMoq.Mock.ofType(undefined, typeMoq.MockBehavior.Strict); + + appShell = typeMoq.Mock.ofType(undefined, typeMoq.MockBehavior.Strict); + serviceContainer.setup(s => s.get(typeMoq.It.isValue(IApplicationShell), typeMoq.It.isAny())) + .returns(() => appShell.object); + + cmdMgr = typeMoq.Mock.ofType(undefined, typeMoq.MockBehavior.Strict); + serviceContainer.setup(s => s.get(typeMoq.It.isValue(ICommandManager), typeMoq.It.isAny())) + .returns(() => cmdMgr.object); + }); + + function makeOptions(rootdir: string): TestDiscoveryOptions { + const outChannel = typeMoq.Mock.ofType(undefined, typeMoq.MockBehavior.Strict); + const cancelToken = typeMoq.Mock.ofType(undefined, typeMoq.MockBehavior.Strict); + cancelToken.setup(c => c.isCancellationRequested) + .returns(() => false); + const wsFolder = typeMoq.Mock.ofType(undefined, typeMoq.MockBehavior.Strict); + + return { + args: [], + cwd: rootdir, + ignoreCache: true, + outChannel: outChannel.object, + token: cancelToken.object, + workspaceFolder: wsFolder.object + }; + } + + function makeParser(): PyTestsParser { + const testFlattener: TestFlatteningVisitor = new TestFlatteningVisitor(); + const testHlp: TestsHelper = new TestsHelper(testFlattener, serviceContainer.object); + return new PyTestsParser(testHlp); + } // Build tests for the test data that is relevant for this platform. const testPlatformType: PytestDataPlatformType = @@ -43,45 +85,12 @@ suite('Unit Tests - PyTest - Test Parser used in discovery', () => { `PyTest${testScenario.pytest_version_spec}: ${testScenario.description}`; test(testDescription, async () => { - // Setup the service container for use by the parser. - const serviceContainer = typeMoq.Mock.ofType(); - const appShell = typeMoq.Mock.ofType(); - const cmdMgr = typeMoq.Mock.ofType(); - serviceContainer.setup(s => s.get(typeMoq.It.isValue(IApplicationShell), typeMoq.It.isAny())) - .returns(() => { - return appShell.object; - }); - serviceContainer.setup(s => s.get(typeMoq.It.isValue(ICommandManager), typeMoq.It.isAny())) - .returns(() => { - return cmdMgr.object; - }); - - // Create mocks used in the test discovery setup. - const outChannel = typeMoq.Mock.ofType(); - const cancelToken = typeMoq.Mock.ofType(); - cancelToken.setup(c => c.isCancellationRequested).returns(() => false); - const wsFolder = typeMoq.Mock.ofType(); - - // Create the test options for the mocked-up test. All data is either - // mocked or is taken from the JSON test data itself. - const options: TestDiscoveryOptions = { - args: [], - cwd: testScenario.rootdir, - ignoreCache: true, - outChannel: outChannel.object, - token: cancelToken.object, - workspaceFolder: wsFolder.object - }; - - // Setup the parser. - const testFlattener: TestFlatteningVisitor = new TestFlatteningVisitor(); - const testHlp: TestsHelper = new TestsHelper(testFlattener, serviceContainer.object); - const parser = new PyTestsParser(testHlp); - // Each test scenario has a 'stdout' member that is an array of // stdout lines. Join them here such that the parser can operate // on stdout-like data. const stdout: string = testScenario.stdout.join('\n'); + const options = makeOptions(testScenario.rootdir); + const parser = makeParser(); const parsedTests: Tests = parser.parse(stdout, options); @@ -107,4 +116,53 @@ suite('Unit Tests - PyTest - Test Parser used in discovery', () => { }); } }); + + test('Handle parameterized tests', () => { + // tslint:disable-next-line:no-multiline-string + const stdout = ` +============================= test session starts ============================= +platform linux -- Python 3.7.1, pytest-4.2.1, py-1.7.0, pluggy-0.8.1 +rootdir: /home/user/test/pytest_scenario, inifile: +collected 2 items + + + + + +======================== no tests ran in 0.36 seconds =========================`; + const options = makeOptions('/home/user/test/pytest_scenario'); + const parser = makeParser(); + + const tests: Tests = parser.parse(stdout, options); + + expect(tests.testFunctions.length).is.equal(2); + expect(tests.testFunctions[0].testFunction.name) + .is.equal('test_with_subtests[1-2]'); + expect(tests.testFunctions[0].testFunction.nameToRun) + .is.equal('tests/test_spam.py::test_with_subtests[1-2]'); + expect(tests.testFunctions[1].testFunction.name) + .is.equal('test_with_subtests[3-4]'); + expect(tests.testFunctions[1].testFunction.nameToRun) + .is.equal('tests/test_spam.py::test_with_subtests[3-4]'); + const parent: SubtestParent = { + name: 'test_with_subtests', + nameToRun: 'tests/test_spam.py::test_with_subtests', + asSuite: { + name: 'test_with_subtests', + nameToRun: 'tests/test_spam.py::test_with_subtests', + functions: [ + tests.testFunctions[0].testFunction, + tests.testFunctions[1].testFunction + ], + suites: [], + isUnitTest: false, + isInstance: false, + xmlName: '', + time: 0 + }, + time: 0 + }; + expect(tests.testFunctions[0].testFunction.subtestParent).is.deep.equal(parent); + expect(tests.testFunctions[1].testFunction.subtestParent).is.deep.equal(parent); + }); }); diff --git a/src/test/unittests/pytest/pytest_unittest_parser_data.ts b/src/test/unittests/pytest/pytest_unittest_parser_data.ts index 7b58ed407690..a01c7d7eb557 100644 --- a/src/test/unittests/pytest/pytest_unittest_parser_data.ts +++ b/src/test/unittests/pytest/pytest_unittest_parser_data.ts @@ -2078,5 +2078,28 @@ export const pytestScenarioData: PytestDiscoveryScenario[] = "", "======================== no tests ran in 0.36 seconds =========================" ] + }, + { + pytest_version_spec: ">= 4.1", + platform: PytestDataPlatformType.NonWindows, + description: "Parameterized tests", + rootdir: "/home/user/test/pytest_scenario", + test_functions: [ + "tests/test_spam.py::test_with_subtests[1-2]", + "tests/test_spam.py::test_with_subtests[3-4]" + ], + functionCount: 2, + stdout: [ + "============================= test session starts ==============================", + "platform linux -- Python 3.7.1, pytest-4.2.1, py-1.7.0, pluggy-0.8.1", + "rootdir: /home/user/test/pytest_scenario, inifile:", + "collected 2 items", + "", + " ", + " ", + " ", + "", + "========================= no tests ran in 0.02 seconds =========================" + ] } ]; From 9a359648c5d4d9365d38ea3627f5f30480122036 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 11 Mar 2019 12:38:04 -0600 Subject: [PATCH 08/12] Add tests for getParent() and getChildren() with subtests. --- .../unittests/common/testUtils.unit.test.ts | 182 ++++++++++++++++-- .../pytest/pytest.testparser.unit.test.ts | 1 + 2 files changed, 169 insertions(+), 14 deletions(-) diff --git a/src/test/unittests/common/testUtils.unit.test.ts b/src/test/unittests/common/testUtils.unit.test.ts index 12e9092cb14b..acb6566fcdab 100644 --- a/src/test/unittests/common/testUtils.unit.test.ts +++ b/src/test/unittests/common/testUtils.unit.test.ts @@ -4,48 +4,76 @@ 'use strict'; import * as assert from 'assert'; +import * as path from 'path'; import { Uri } from 'vscode'; import { getNamesAndValues } from '../../../client/common/utils/enum'; -import { getParent, getTestFile, getTestFolder, getTestFunction, getTestSuite, getTestType } from '../../../client/unittests/common/testUtils'; -import { FlattenedTestFunction, FlattenedTestSuite, TestFile, TestFolder, TestFunction, Tests, TestSuite, TestType } from '../../../client/unittests/common/types'; -import { TestDataItem, TestWorkspaceFolder } from '../../../client/unittests/types'; +import { + getChildren, getParent, getTestFile, getTestFolder, getTestFunction, + getTestSuite, getTestType +} from '../../../client/unittests/common/testUtils'; +import { + FlattenedTestFunction, FlattenedTestSuite, SubtestParent, TestFile, + TestFolder, TestFunction, Tests, TestSuite, TestType +} from '../../../client/unittests/common/types'; +import { + TestDataItem, TestWorkspaceFolder +} from '../../../client/unittests/types'; // tslint:disable:prefer-template -export function createMockTestDataItem(type: TestType, nameSuffix: string = '') { +function longestCommonSubstring(strings: string[]): string { + strings = strings.concat().sort(); + let substr = strings.shift() || ''; + strings.forEach(str => { + for (const [idx, ch] of [...substr].entries()) { + if (str[idx] !== ch) { + substr = substr.substring(0, idx); + break; + } + } + }); + return substr; +} + +export function createMockTestDataItem( + type: TestType, + nameSuffix: string = '', + name?: string, + nameToRun?: string +) { const folder: TestFolder = { resource: Uri.file(__filename), folders: [], - name: 'Some Folder' + nameSuffix, - nameToRun: ' Some Folder' + nameSuffix, + name: name || 'Some Folder' + nameSuffix, + nameToRun: nameToRun || name || ' Some Folder' + nameSuffix, testFiles: [], time: 0 }; const file: TestFile = { resource: Uri.file(__filename), - name: 'Some File' + nameSuffix, - nameToRun: ' Some File' + nameSuffix, + name: name || 'Some File' + nameSuffix, + nameToRun: nameToRun || name || ' Some File' + nameSuffix, fullPath: __filename, - xmlName: 'some xml name' + nameSuffix, + xmlName: name || 'some xml name' + nameSuffix, functions: [], suites: [], time: 0 }; const func: TestFunction = { resource: Uri.file(__filename), - name: 'Some Function' + nameSuffix, - nameToRun: ' Some Function' + nameSuffix, + name: name || 'Some Function' + nameSuffix, + nameToRun: nameToRun || name || ' Some Function' + nameSuffix, time: 0 }; const suite: TestSuite = { resource: Uri.file(__filename), - name: 'Some Suite' + nameSuffix, - nameToRun: ' Some Suite' + nameSuffix, + name: name || 'Some Suite' + nameSuffix, + nameToRun: nameToRun || name || ' Some Suite' + nameSuffix, functions: [], isInstance: true, isUnitTest: false, suites: [], - xmlName: 'some name' + nameSuffix, + xmlName: name || 'some name' + nameSuffix, time: 0 }; @@ -64,6 +92,59 @@ export function createMockTestDataItem(type: TestType, n throw new Error('Unknown type'); } } + +export function createSubtestParent(funcs: TestFunction[]): SubtestParent { + const name = longestCommonSubstring(funcs.map(func => func.name)); + const nameToRun = longestCommonSubstring(funcs.map(func => func.nameToRun)); + const subtestParent: SubtestParent = { + name: name, + nameToRun: nameToRun, + asSuite: { + resource: Uri.file(__filename), + name: name, + nameToRun: nameToRun, + functions: funcs, + suites: [], + isUnitTest: false, + isInstance: false, + xmlName: '', + time: 0 + }, + time: 0 + }; + funcs.forEach(func => { + func.subtestParent = subtestParent; + }); + return subtestParent; +} + +export function createTests( + folders: TestFolder[], + files: TestFile[], + suites: TestSuite[], + funcs: TestFunction[] +): Tests { + // tslint:disable:no-any + return { + summary: { errors: 0, skipped: 0, passed: 0, failures: 0 }, + rootTestFolders: folders.length > 0 ? [folders[0]] : [], + testFolders: folders, + testFiles: files, + testSuites: suites.map(suite => { + return { + testSuite: suite, + xmlClassName: suite.xmlName + } as any; + }), + testFunctions: funcs.map(func => { + return { + testFunction: func, + xmlClassName: func.name + } as any; + }) + }; +} + // tslint:disable:max-func-body-length no-any suite('Unit Tests - TestUtils', () => { test('Get TestType for Folders', () => { @@ -320,4 +401,77 @@ suite('Unit Tests - TestUtils', () => { assert.equal(getParent(tests, fn4), suite1); assert.equal(getParent(tests, fn5), suite3); }); + test('Get parent of parameterized function', () => { + const folder = createMockTestDataItem(TestType.testFolder); + const file = createMockTestDataItem(TestType.testFile); + const func1 = createMockTestDataItem(TestType.testFunction); + const func2 = createMockTestDataItem(TestType.testFunction); + const func3 = createMockTestDataItem(TestType.testFunction); + const subParent1 = createSubtestParent([func2, func3]); + const suite = createMockTestDataItem(TestType.testSuite); + const func4 = createMockTestDataItem(TestType.testFunction); + const func5 = createMockTestDataItem(TestType.testFunction); + const func6 = createMockTestDataItem(TestType.testFunction); + const subParent2 = createSubtestParent([func5, func6]); + folder.testFiles.push(file); + file.functions.push(func1); + file.functions.push(func2); + file.functions.push(func3); + file.suites.push(suite); + suite.functions.push(func4); + suite.functions.push(func5); + suite.functions.push(func6); + const tests = createTests( + [folder], + [file], + [suite], + [func1, func2, func3, func4, func5, func6] + ); + + assert.equal(getParent(tests, folder), undefined); + assert.equal(getParent(tests, file), folder); + assert.equal(getParent(tests, func1), file); + assert.equal(getParent(tests, subParent1.asSuite), file); + assert.equal(getParent(tests, func2), subParent1.asSuite); + assert.equal(getParent(tests, func3), subParent1.asSuite); + assert.equal(getParent(tests, suite), file); + assert.equal(getParent(tests, func4), suite); + assert.equal(getParent(tests, subParent2.asSuite), suite); + assert.equal(getParent(tests, func5), subParent2.asSuite); + assert.equal(getParent(tests, func6), subParent2.asSuite); + }); + test('Get children of parameterized function', () => { + const filename = path.join('tests', 'test_spam.py'); + const folder = createMockTestDataItem(TestType.testFolder, 'tests'); + const file = createMockTestDataItem(TestType.testFile, filename); + const func1 = createMockTestDataItem(TestType.testFunction, 'test_x'); + const func2 = createMockTestDataItem(TestType.testFunction, 'test_y'); + const func3 = createMockTestDataItem(TestType.testFunction, 'test_z'); + const subParent1 = createSubtestParent([func2, func3]); + const suite = createMockTestDataItem(TestType.testSuite); + const func4 = createMockTestDataItem(TestType.testFunction); + const func5 = createMockTestDataItem(TestType.testFunction); + const func6 = createMockTestDataItem(TestType.testFunction); + const subParent2 = createSubtestParent([func5, func6]); + folder.testFiles.push(file); + file.functions.push(func1); + file.functions.push(func2); + file.functions.push(func3); + file.suites.push(suite); + suite.functions.push(func4); + suite.functions.push(func5); + suite.functions.push(func6); + + assert.deepEqual(getChildren(folder), [file]); + assert.deepEqual(getChildren(file), [func1, suite, subParent1.asSuite]); + assert.deepEqual(getChildren(func1), []); + assert.deepEqual(getChildren(subParent1.asSuite), [func2, func3]); + assert.deepEqual(getChildren(func2), []); + assert.deepEqual(getChildren(func3), []); + assert.deepEqual(getChildren(suite), [func4, subParent2.asSuite]); + assert.deepEqual(getChildren(func4), []); + assert.deepEqual(getChildren(subParent2.asSuite), [func5, func6]); + assert.deepEqual(getChildren(func5), []); + assert.deepEqual(getChildren(func6), []); + }); }); diff --git a/src/test/unittests/pytest/pytest.testparser.unit.test.ts b/src/test/unittests/pytest/pytest.testparser.unit.test.ts index 3850997a50d9..4b44f1f0f86b 100644 --- a/src/test/unittests/pytest/pytest.testparser.unit.test.ts +++ b/src/test/unittests/pytest/pytest.testparser.unit.test.ts @@ -148,6 +148,7 @@ collected 2 items name: 'test_with_subtests', nameToRun: 'tests/test_spam.py::test_with_subtests', asSuite: { + resource: Uri.file(__filename), name: 'test_with_subtests', nameToRun: 'tests/test_spam.py::test_with_subtests', functions: [ From 5604bafd0217b66699d4db91551513f93d09ce75 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 11 Mar 2019 13:14:58 -0600 Subject: [PATCH 09/12] Add tests for TestTreeItem. --- .../explorer/testTreeViewItem.unit.test.ts | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/test/unittests/explorer/testTreeViewItem.unit.test.ts b/src/test/unittests/explorer/testTreeViewItem.unit.test.ts index e30b2681f01a..e6f536526296 100644 --- a/src/test/unittests/explorer/testTreeViewItem.unit.test.ts +++ b/src/test/unittests/explorer/testTreeViewItem.unit.test.ts @@ -5,13 +5,19 @@ import { expect } from 'chai'; import { Uri } from 'vscode'; +import { + Commands +} from '../../../client/common/constants'; import { TestFile, TestFolder, - TestFunction, TestSuite + TestFunction, TestSuite, TestType } from '../../../client/unittests/common/types'; import { TestTreeItem } from '../../../client/unittests/explorer/testTreeViewItem'; +import { + createMockTestDataItem, createSubtestParent +} from '../common/testUtils.unit.test'; import { getTestExplorerViewItemData } from './explorerTestData'; suite('Unit Tests Test Explorer View Items', () => { @@ -25,7 +31,7 @@ suite('Unit Tests Test Explorer View Items', () => { [testFolder, testFile, testFunction, testSuite, testSuiteFunction] = getTestExplorerViewItemData(); }); - test('Test folder created into test view item', () => { + test('Test root folder created into test view item', () => { const viewItem = new TestTreeItem(resource, testFolder); expect(viewItem.contextValue).is.equal('testFolder'); }); @@ -35,18 +41,38 @@ suite('Unit Tests Test Explorer View Items', () => { expect(viewItem.contextValue).is.equal('testFile'); }); - test('Test folder created into test view item', () => { + test('Test suite created into test view item', () => { const viewItem = new TestTreeItem(resource, testSuite); expect(viewItem.contextValue).is.equal('testSuite'); }); - test('Test folder created into test view item', () => { + test('Test function created into test view item', () => { const viewItem = new TestTreeItem(resource, testFunction); expect(viewItem.contextValue).is.equal('testFunction'); }); - test('Test folder created into test view item', () => { + test('Test suite function created into test view item', () => { const viewItem = new TestTreeItem(resource, testSuiteFunction); expect(viewItem.contextValue).is.equal('testFunction'); }); + + test('Test subtest parent created into test view item', () => { + const subtestParent = createSubtestParent([ + createMockTestDataItem(TestType.testFunction, 'test_x'), + createMockTestDataItem(TestType.testFunction, 'test_y') + ]); + + const viewItem = new TestTreeItem(resource, subtestParent.asSuite); + + expect(viewItem.contextValue).is.equal('testSuite'); + expect(viewItem.command!.command).is.equal(Commands.navigateToTestFunction); + }); + + test('Test subtest created into test view item', () => { + createSubtestParent([testFunction]); // sets testFunction.subtestParent + + const viewItem = new TestTreeItem(resource, testFunction); + + expect(viewItem.contextValue).is.equal('testFunction'); + }); }); From 547a05e3e52de192b3329639e393492621b3ff25 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 12 Mar 2019 21:10:13 -0600 Subject: [PATCH 10/12] Add tests for TestTreeViewProvider. --- .../testTreeViewProvider.unit.test.ts | 260 +++++++++++++++++- 1 file changed, 258 insertions(+), 2 deletions(-) diff --git a/src/test/unittests/explorer/testTreeViewProvider.unit.test.ts b/src/test/unittests/explorer/testTreeViewProvider.unit.test.ts index 190db10e8845..40230e6c6ecf 100644 --- a/src/test/unittests/explorer/testTreeViewProvider.unit.test.ts +++ b/src/test/unittests/explorer/testTreeViewProvider.unit.test.ts @@ -11,12 +11,17 @@ import { WorkspaceService } from '../../../client/common/application/workspace'; import { IDisposable } from '../../../client/common/types'; import { TestCollectionStorageService } from '../../../client/unittests/common/services/storageService'; import { getTestType } from '../../../client/unittests/common/testUtils'; -import { ITestCollectionStorageService, TestStatus, TestType } from '../../../client/unittests/common/types'; +import { + ITestCollectionStorageService, TestStatus, TestType +} from '../../../client/unittests/common/types'; import { TestTreeItem } from '../../../client/unittests/explorer/testTreeViewItem'; import { TestTreeViewProvider } from '../../../client/unittests/explorer/testTreeViewProvider'; import { TestDataItem } from '../../../client/unittests/types'; import { noop } from '../../core'; -import { createMockTestExplorer as createMockTestTreeProvider, createMockTestsData, getMockTestFile, getMockTestFunction, getMockTestSuite } from './explorerTestData'; +import { + createMockTestExplorer as createMockTestTreeProvider, createMockTestsData, + getMockTestFile, getMockTestFolder, getMockTestFunction, getMockTestSuite +} from './explorerTestData'; // tslint:disable:no-any @@ -335,4 +340,255 @@ suite('Unit Tests Test Explorer TestTreeViewProvider', () => { expect(child.name).oneOf(['test_suite', 'test_outer_fn']); }); }); + + test('Tree items for subtests are correct', async () => { + const resource = Uri.file(__filename); + // Set up the folder & file. + const folder = getMockTestFolder('tests'); + const file = getMockTestFile(`${folder.name}/test_file.py`); + folder.testFiles.push(file); + // Set up the file-level tests. + const func1 = getMockTestFunction(`${file.name}::test_spam`); + file.functions.push(func1); + const func2 = getMockTestFunction(`${file.name}::test_ham[1-2]`); + func2.subtestParent = { + name: 'test_ham', + nameToRun: `${file.name}::test_ham`, + asSuite: { + resource: resource, + name: 'test_ham', + nameToRun: `${file.name}::test_ham`, + functions: [func2], + suites: [], + isUnitTest: false, + isInstance: false, + xmlName: 'test_ham', + time: 0 + }, + time: 0 + }; + file.functions.push(func2); + const func3 = getMockTestFunction(`${file.name}::test_ham[3-4]`); + func3.subtestParent = func2.subtestParent; + func3.subtestParent.asSuite.functions.push(func3); + file.functions.push(func3); + // Set up the suite. + const suite = getMockTestSuite(`${file.name}::MyTests`); + file.suites.push(suite); + const func4 = getMockTestFunction('MyTests::test_foo'); + suite.functions.push(func4); + const func5 = getMockTestFunction('MyTests::test_bar[2-3]'); + func5.subtestParent = { + name: 'test_bar', + nameToRun: `${file.name}::MyTests::test_bar`, + asSuite: { + resource: resource, + name: 'test_bar', + nameToRun: `${file.name}::MyTests::test_bar`, + functions: [func5], + suites: [], + isUnitTest: false, + isInstance: false, + xmlName: 'test_bar', + time: 0 + }, + time: 0 + }; + suite.functions.push(func5); + // Set up the tests data. + const testData = createMockTestsData([file]); + + const testExplorer = createMockTestTreeProvider(undefined, testData); + const items = [ + await testExplorer.getTreeItem(func1), + await testExplorer.getTreeItem(func2), + await testExplorer.getTreeItem(func3), + await testExplorer.getTreeItem(func4), + await testExplorer.getTreeItem(func5), + await testExplorer.getTreeItem(file), + await testExplorer.getTreeItem(suite), + await testExplorer.getTreeItem(func2.subtestParent.asSuite), + await testExplorer.getTreeItem(func5.subtestParent.asSuite) + ]; + + expect(items).to.deep.equal([ + new TestTreeItem(func1.resource, func1), + new TestTreeItem(func2.resource, func2), + new TestTreeItem(func3.resource, func3), + new TestTreeItem(func4.resource, func4), + new TestTreeItem(func5.resource, func5), + new TestTreeItem(file.resource, file), + new TestTreeItem(suite.resource, suite), + new TestTreeItem(resource, func2.subtestParent.asSuite), + new TestTreeItem(resource, func5.subtestParent.asSuite) + ]); + }); + + test('Parents for subtests are correct', async () => { + const resource = Uri.file(__filename); + // Set up the folder & file. + const folder = getMockTestFolder('tests'); + const file = getMockTestFile(`${folder.name}/test_file.py`); + folder.testFiles.push(file); + // Set up the file-level tests. + const func1 = getMockTestFunction(`${file.name}::test_spam`); + file.functions.push(func1); + const func2 = getMockTestFunction(`${file.name}::test_ham[1-2]`); + func2.subtestParent = { + name: 'test_ham', + nameToRun: `${file.name}::test_ham`, + asSuite: { + resource: resource, + name: 'test_ham', + nameToRun: `${file.name}::test_ham`, + functions: [func2], + suites: [], + isUnitTest: false, + isInstance: false, + xmlName: 'test_ham', + time: 0 + }, + time: 0 + }; + file.functions.push(func2); + const func3 = getMockTestFunction(`${file.name}::test_ham[3-4]`); + func3.subtestParent = func2.subtestParent; + func3.subtestParent.asSuite.functions.push(func3); + file.functions.push(func3); + // Set up the suite. + const suite = getMockTestSuite(`${file.name}::MyTests`); + file.suites.push(suite); + const func4 = getMockTestFunction('MyTests::test_foo'); + suite.functions.push(func4); + const func5 = getMockTestFunction('MyTests::test_bar[2-3]'); + func5.subtestParent = { + name: 'test_bar', + nameToRun: `${file.name}::MyTests::test_bar`, + asSuite: { + resource: resource, + name: 'test_bar', + nameToRun: `${file.name}::MyTests::test_bar`, + functions: [func5], + suites: [], + isUnitTest: false, + isInstance: false, + xmlName: 'test_bar', + time: 0 + }, + time: 0 + }; + suite.functions.push(func5); + // Set up the tests data. + const testData = createMockTestsData([file]); + + const testExplorer = createMockTestTreeProvider(undefined, testData); + const parents = [ + await testExplorer.getParent(func1), + await testExplorer.getParent(func2), + await testExplorer.getParent(func3), + await testExplorer.getParent(func4), + await testExplorer.getParent(func5), + await testExplorer.getParent(suite), + await testExplorer.getParent(func2.subtestParent.asSuite), + await testExplorer.getParent(func3.subtestParent.asSuite), + await testExplorer.getParent(func5.subtestParent.asSuite) + ]; + + expect(parents).to.deep.equal([ + file, + func2.subtestParent.asSuite, + func3.subtestParent.asSuite, + suite, + func5.subtestParent.asSuite, + file, + file, + file, + suite + ]); + }); + + test('Children for subtests are correct', async () => { + const resource = Uri.file(__filename); + // Set up the folder & file. + const folder = getMockTestFolder('tests'); + const file = getMockTestFile(`${folder.name}/test_file.py`); + folder.testFiles.push(file); + // Set up the file-level tests. + const func1 = getMockTestFunction(`${file.name}::test_spam`); + file.functions.push(func1); + const func2 = getMockTestFunction(`${file.name}::test_ham[1-2]`); + func2.subtestParent = { + name: 'test_ham', + nameToRun: `${file.name}::test_ham`, + asSuite: { + resource: resource, + name: 'test_ham', + nameToRun: `${file.name}::test_ham`, + functions: [func2], + suites: [], + isUnitTest: false, + isInstance: false, + xmlName: 'test_ham', + time: 0 + }, + time: 0 + }; + file.functions.push(func2); + const func3 = getMockTestFunction(`${file.name}::test_ham[3-4]`); + func3.subtestParent = func2.subtestParent; + func3.subtestParent.asSuite.functions.push(func3); + file.functions.push(func3); + // Set up the suite. + const suite = getMockTestSuite(`${file.name}::MyTests`); + file.suites.push(suite); + const func4 = getMockTestFunction('MyTests::test_foo'); + suite.functions.push(func4); + const func5 = getMockTestFunction('MyTests::test_bar[2-3]'); + func5.subtestParent = { + name: 'test_bar', + nameToRun: `${file.name}::MyTests::test_bar`, + asSuite: { + resource: resource, + name: 'test_bar', + nameToRun: `${file.name}::MyTests::test_bar`, + functions: [func5], + suites: [], + isUnitTest: false, + isInstance: false, + xmlName: 'test_bar', + time: 0 + }, + time: 0 + }; + suite.functions.push(func5); + // Set up the tests data. + const testData = createMockTestsData([file]); + + const testExplorer = createMockTestTreeProvider(undefined, testData); + const childrens = [ + await testExplorer.getChildren(func1), + await testExplorer.getChildren(func2), + await testExplorer.getChildren(func3), + await testExplorer.getChildren(func4), + await testExplorer.getChildren(func5), + await testExplorer.getChildren(file), + await testExplorer.getChildren(suite), + await testExplorer.getChildren(func2.subtestParent.asSuite), + await testExplorer.getChildren(func3.subtestParent.asSuite), + await testExplorer.getChildren(func5.subtestParent.asSuite) + ]; + + expect(childrens).to.deep.equal([ + [], + [], + [], + [], + [], + [func1, suite, func2.subtestParent.asSuite], + [func4, func5.subtestParent.asSuite], + [func2, func3], + [func2, func3], + [func5] + ]); + }); }); From e74003dcca0885f29970b7d3913ab230f77a05e7 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 12 Mar 2019 21:20:27 -0600 Subject: [PATCH 11/12] lint --- src/client/unittests/pytest/services/parserService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/unittests/pytest/services/parserService.ts b/src/client/unittests/pytest/services/parserService.ts index 51a8d884830b..d03a44f30976 100644 --- a/src/client/unittests/pytest/services/parserService.ts +++ b/src/client/unittests/pytest/services/parserService.ts @@ -153,7 +153,7 @@ export class TestsParser implements ITestsParser { let currentPackage: string = ''; const resource = Uri.file(rootDirectory); - // tslint:disable-next-line:cyclomatic-complexity + // tslint:disable-next-line:cyclomatic-complexity max-func-body-length lines.forEach(line => { const trimmedLine = line.trim(); let name: string = ''; From 9af296c968b83f7d6e07a716df3407d336e0a9ee Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 12 Mar 2019 21:33:25 -0600 Subject: [PATCH 12/12] Fix the parser tests. --- src/test/unittests/pytest/pytest.testparser.unit.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/unittests/pytest/pytest.testparser.unit.test.ts b/src/test/unittests/pytest/pytest.testparser.unit.test.ts index 4b44f1f0f86b..40fc68eb304f 100644 --- a/src/test/unittests/pytest/pytest.testparser.unit.test.ts +++ b/src/test/unittests/pytest/pytest.testparser.unit.test.ts @@ -148,7 +148,7 @@ collected 2 items name: 'test_with_subtests', nameToRun: 'tests/test_spam.py::test_with_subtests', asSuite: { - resource: Uri.file(__filename), + resource: Uri.file('/home/user/test/pytest_scenario'), name: 'test_with_subtests', nameToRun: 'tests/test_spam.py::test_with_subtests', functions: [