From 9c5e49a61a30020bfc36b9513cc0f49ca78fb1ee Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 15 May 2019 13:48:19 -0600 Subject: [PATCH 01/13] Factor out _normalize_node_id(). --- .../adapter/pytest/_pytest_item.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py index 313b40372052..9eb99379e787 100644 --- a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py +++ b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py @@ -226,10 +226,7 @@ def _find_location(srcfile, lineno, relfile, func, _pathsep): def _parse_node_id(nodeid, kind, _pathsep, _normcase): """Return the components of the given node ID, in heirarchical order.""" - if not nodeid.startswith('.' + _pathsep): - nodeid = '.' + _pathsep + nodeid - while '::()::' in nodeid: - nodeid = nodeid.replace('::()::', '::') + nodeid = _normalize_node_id(nodeid, _pathsep) fileid, _, remainder = nodeid.partition('::') if not fileid or not remainder: @@ -279,6 +276,20 @@ def _parse_node_id(nodeid, kind, _pathsep, _normcase): return nodeid, fileid, suiteids, suites, funcid, name, parameterized +def _normalize_node_id(nodeid, _pathsep): + """Return the canonical form for the given node ID.""" + while '::()::' in nodeid: + nodeid = nodeid.replace('::()::', '::') + if _pathsep not in nodeid: + _pathsep = '/' + if nodeid.startswith(_pathsep): + print(nodeid) + raise NotImplementedError + if not nodeid.startswith('.' + _pathsep): + nodeid = '.' + _pathsep + nodeid + return nodeid + + def _get_item_kind(item): """Return (kind, isunittest) for the given item.""" try: From afb18e3116eb33d9a624d51c1a09a4f478ae9cc4 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 15 May 2019 15:42:23 -0600 Subject: [PATCH 02/13] Find and handle item kind properly. --- .../adapter/pytest/_pytest_item.py | 40 +++++------ .../adapter/pytest/test_discovery.py | 70 ++++++++++++------- 2 files changed, 65 insertions(+), 45 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py index 9eb99379e787..82cdcbf4d8f3 100644 --- a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py +++ b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py @@ -95,6 +95,10 @@ import sys +import pytest +import _pytest.doctest +import _pytest.unittest + from ..info import TestInfo, TestPath @@ -110,6 +114,7 @@ def parse_item(item, _normcase, _pathsep): #_debug_item(item, showsummary=True) kind, _ = _get_item_kind(item) # Figure out the func, suites, and subs. + print(vars(item)) (nodeid, fileid, suiteids, suites, funcid, basename, parameterized ) = _parse_node_id(item.nodeid, kind, _pathsep, _normcase) if kind == 'function': @@ -123,6 +128,9 @@ def parse_item(item, _normcase, _pathsep): elif kind == 'doctest': testfunc = None funcname = None + else: + testfunc = None + funcname = None # Figure out the file. relfile = _normcase(fileid) @@ -230,23 +238,16 @@ def _parse_node_id(nodeid, kind, _pathsep, _normcase): fileid, _, remainder = nodeid.partition('::') if not fileid or not remainder: + # TODO: Is fileid really required? print(nodeid) # TODO: Unexpected! What to do? raise NotImplementedError fileid = _normcase(fileid) nodeid = fileid + '::' + remainder - if kind == 'doctest': - try: - parentid, name = nodeid.split('::') - except ValueError: - print(nodeid) - # TODO: Unexpected! What to do? - raise NotImplementedError - funcid = None - parameterized = '' - else: - parameterized = '' + parameterized = '' + funcid = None + if kind == 'function': if nodeid.endswith(']'): funcid, sep, parameterized = nodeid.partition('[') if not sep: @@ -261,6 +262,9 @@ def _parse_node_id(nodeid, kind, _pathsep, _normcase): print(parentid, name) # TODO: What to do? We expect at least a filename and a function raise NotImplementedError + else: + # We require a fileid, so this won't fail. + parentid, name = nodeid.split('::') suites = [] suiteids = [] @@ -292,18 +296,12 @@ def _normalize_node_id(nodeid, _pathsep): def _get_item_kind(item): """Return (kind, isunittest) for the given item.""" - try: - itemtype = item.kind - except AttributeError: - itemtype = item.__class__.__name__ - - if itemtype == 'DoctestItem': + if isinstance(item, _pytest.doctest.DoctestItem): return 'doctest', False - elif itemtype == 'Function': - return 'function', False - elif itemtype == 'TestCaseFunction': + elif isinstance(item, _pytest.unittest.TestCaseFunction): return 'function', True - elif item.hasattr('function'): + elif isinstance(item, pytest.Function): + # TODO: maybe return "method", "subtest", etc.? return 'function', False else: return None, False diff --git a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py index 262d9514b095..8beba9c743b2 100644 --- a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py +++ b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py @@ -12,12 +12,14 @@ import sys import unittest +import pytest +import _pytest.doctest + from ....util import Stub, StubProxy from testing_tools.adapter.info import TestInfo, TestPath, ParentInfo from testing_tools.adapter.pytest._discovery import ( discover, TestCollector, DiscoveredTests ) -import pytest class StubPyTest(StubProxy): @@ -92,8 +94,6 @@ def __init__(self, name): class StubPytestItem(StubProxy): - kind = 'Function' - _debugging = False _hasfunc = True @@ -110,6 +110,9 @@ def __init__(self, stub=None, **attrs): if 'own_markers' not in attrs: self.own_markers = () + def __repr__(self): + return object.__repr__(self) + def __getattr__(self, name): if not self._debugging: self.add_call(name + ' (attr)', None, None) @@ -121,6 +124,29 @@ def func(*args, **kwargs): return func +class StubSubtypedItem(StubPytestItem): + + def __init__(self, *args, **kwargs): + super(StubSubtypedItem, self).__init__(*args, **kwargs) + if 'nodeid' in self.__dict__: + self._nodeid = self.__dict__.pop('nodeid') + + @property + def location(self): + return self.__dict__.get('location') + + +class StubFunctionItem(StubSubtypedItem, pytest.Function): + + @property + def function(self): + return self.__dict__.get('function') + + +class StubDoctestItem(StubSubtypedItem, _pytest.doctest.DoctestItem): + pass + + class StubPytestSession(StubProxy): def __init__(self, stub=None): @@ -281,7 +307,7 @@ def test_modifyitems(self): relfileid2 = os.path.join('.', relfile2) collector.pytest_collection_modifyitems(session, config, [ - StubPytestItem( + StubFunctionItem( stub, nodeid='test_spam.py::SpamTests::test_one', name='test_one', @@ -289,7 +315,7 @@ def test_modifyitems(self): fspath=os.path.join(testroot, 'test_spam.py'), function=FakeFunc('test_one'), ), - StubPytestItem( + StubFunctionItem( stub, nodeid='test_spam.py::SpamTests::test_other', name='test_other', @@ -297,7 +323,7 @@ def test_modifyitems(self): fspath=os.path.join(testroot, 'test_spam.py'), function=FakeFunc('test_other'), ), - StubPytestItem( + StubFunctionItem( stub, nodeid='test_spam.py::test_all', name='test_all', @@ -305,7 +331,7 @@ def test_modifyitems(self): fspath=os.path.join(testroot, 'test_spam.py'), function=FakeFunc('test_all'), ), - StubPytestItem( + StubFunctionItem( stub, nodeid='test_spam.py::test_each[10-10]', name='test_each[10-10]', @@ -313,7 +339,7 @@ def test_modifyitems(self): fspath=os.path.join(testroot, 'test_spam.py'), function=FakeFunc('test_each'), ), - StubPytestItem( + StubFunctionItem( stub, nodeid=relfile2 + '::All::BasicTests::test_first', name='test_first', @@ -321,7 +347,7 @@ def test_modifyitems(self): fspath=os.path.join(testroot, relfile2), function=FakeFunc('test_first'), ), - StubPytestItem( + StubFunctionItem( stub, nodeid=relfile2 + '::All::BasicTests::test_each[1+2-3]', name='test_each[1+2-3]', @@ -452,7 +478,7 @@ def test_finish(self): relfile = 'x/y/z/test_eggs.py'.replace('/', os.path.sep) relfileid = os.path.join('.', relfile) session.items = [ - StubPytestItem( + StubFunctionItem( stub, nodeid=relfile + '::SpamTests::test_spam', name='test_spam', @@ -496,38 +522,34 @@ def test_doctest(self): relfile = 'x/y/z/test_eggs.py'.replace('/', os.path.sep) relfileid = os.path.join('.', relfile) session.items = [ - StubPytestItem( + StubDoctestItem( stub, nodeid=doctestfile + '::test_doctest.txt', name='test_doctest.txt', location=(doctestfile, 0, '[doctest] test_doctest.txt'), fspath=os.path.join(testroot, doctestfile), - kind='DoctestItem', ), # With --doctest-modules - StubPytestItem( + StubDoctestItem( stub, nodeid=relfile + '::test_eggs', name='test_eggs', location=(relfile, 0, '[doctest] test_eggs'), fspath=os.path.join(testroot, relfile), - kind='DoctestItem', ), - StubPytestItem( + StubDoctestItem( stub, nodeid=relfile + '::test_eggs.TestSpam', name='test_eggs.TestSpam', location=(relfile, 12, '[doctest] test_eggs.TestSpam'), fspath=os.path.join(testroot, relfile), - kind='DoctestItem', ), - StubPytestItem( + StubDoctestItem( stub, nodeid=relfile + '::test_eggs.TestSpam.TestEggs', name='test_eggs.TestSpam.TestEggs', location=(relfile, 27, '[doctest] test_eggs.TestSpam.TestEggs'), fspath=os.path.join(testroot, relfile), - kind='DoctestItem', ), ] collector = TestCollector(tests=discovered) @@ -607,7 +629,7 @@ def test_nested_brackets(self): relfile = 'x/y/z/test_eggs.py'.replace('/', os.path.sep) relfileid = os.path.join('.', relfile) session.items = [ - StubPytestItem( + StubFunctionItem( stub, nodeid=relfile + '::SpamTests::test_spam[a-[b]-c]', name='test_spam[a-[b]-c]', @@ -649,7 +671,7 @@ def test_nested_suite(self): relfile = 'x/y/z/test_eggs.py'.replace('/', os.path.sep) relfileid = os.path.join('.', relfile) session.items = [ - StubPytestItem( + StubFunctionItem( stub, nodeid=relfile + '::SpamTests::Ham::Eggs::test_spam', name='test_spam', @@ -694,7 +716,7 @@ def test_windows(self): testroot = r'c:\a\b\c' relfile = r'X\Y\Z\test_eggs.py' session.items = [ - StubPytestItem( + StubFunctionItem( stub, nodeid='X/Y/Z/test_eggs.py::SpamTests::test_spam', name='test_spam', @@ -742,7 +764,7 @@ def test_mysterious_parens(self): relfile = 'x/y/z/test_eggs.py'.replace('/', os.path.sep) relfileid = os.path.join('.', relfile) session.items = [ - StubPytestItem( + StubFunctionItem( stub, nodeid=relfile + '::SpamTests::()::()::test_spam', name='test_spam', @@ -787,7 +809,7 @@ def test_imported_test(self): relfileid = os.path.join('.', relfile) srcfile = 'x/y/z/_extern.py'.replace('/', os.path.sep) session.items = [ - StubPytestItem( + StubFunctionItem( stub, nodeid=relfile + '::SpamTests::test_spam', name='test_spam', @@ -795,7 +817,7 @@ def test_imported_test(self): fspath=os.path.join(testroot, relfile), function=FakeFunc('test_spam'), ), - StubPytestItem( + StubFunctionItem( stub, nodeid=relfile + '::test_ham', name='test_ham', From e53462e55974d49826f44e6ac79f87548348b568 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 15 May 2019 16:28:34 -0600 Subject: [PATCH 03/13] Factor out _iter_nodes(). --- .../adapter/pytest/_pytest_item.py | 110 +++++++++++------- 1 file changed, 66 insertions(+), 44 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py index 82cdcbf4d8f3..257eb4b19959 100644 --- a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py +++ b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py @@ -114,7 +114,6 @@ def parse_item(item, _normcase, _pathsep): #_debug_item(item, showsummary=True) kind, _ = _get_item_kind(item) # Figure out the func, suites, and subs. - print(vars(item)) (nodeid, fileid, suiteids, suites, funcid, basename, parameterized ) = _parse_node_id(item.nodeid, kind, _pathsep, _normcase) if kind == 'function': @@ -133,7 +132,7 @@ def parse_item(item, _normcase, _pathsep): funcname = None # Figure out the file. - relfile = _normcase(fileid) + relfile = fileid fspath = str(item.fspath) if not _normcase(fspath).endswith(relfile[1:]): print(fspath) @@ -144,7 +143,7 @@ def parse_item(item, _normcase, _pathsep): if kind == 'function': if testfunc and fullname != testfunc + parameterized: print(item.nodeid) - print(fullname, suites, testfunc) + print(fullname, suites, testfunc, parameterized) # TODO: What to do? raise NotImplementedError elif kind == 'doctest': @@ -234,58 +233,81 @@ def _find_location(srcfile, lineno, relfile, func, _pathsep): def _parse_node_id(nodeid, kind, _pathsep, _normcase): """Return the components of the given node ID, in heirarchical order.""" - nodeid = _normalize_node_id(nodeid, _pathsep) - - fileid, _, remainder = nodeid.partition('::') - if not fileid or not remainder: - # TODO: Is fileid really required? - print(nodeid) - # TODO: Unexpected! What to do? - raise NotImplementedError - fileid = _normcase(fileid) - nodeid = fileid + '::' + remainder - - parameterized = '' - funcid = None - if kind == 'function': - if nodeid.endswith(']'): - funcid, sep, parameterized = nodeid.partition('[') - if not sep: - print(nodeid) - # TODO: Unexpected! What to do? - raise NotImplementedError - parameterized = sep + parameterized - else: - funcid = nodeid - parentid, _, name = funcid.rpartition('::') - if not parentid or not name: - print(parentid, name) - # TODO: What to do? We expect at least a filename and a function - raise NotImplementedError + nodes = iter(_iter_nodes(nodeid, kind, _pathsep, _normcase)) + nodeid, name, kind = next(nodes) + if kind == 'subtest': + funcid, name, _ = next(nodes) + parameterized = nodeid[len(funcid):] else: - # We require a fileid, so this won't fail. - parentid, name = nodeid.split('::') + funcid = nodeid + parameterized = '' suites = [] suiteids = [] - while '::' in parentid: - fullid = parentid - parentid, _, suitename = fullid.rpartition('::') - suiteids.insert(0, fullid) - suites.insert(0, suitename) - if parentid != fileid: - print(nodeid) - print(parentid, fileid) + for parentid, pname, kind in nodes: + if kind == 'file': + fileid = parentid + break + suites.insert(0, pname) + suiteids.insert(0, parentid) return nodeid, fileid, suiteids, suites, funcid, name, parameterized -def _normalize_node_id(nodeid, _pathsep): +def _iter_nodes(nodeid, kind, _pathsep, _normcase): + """Yield (nodeid, name, kind) for the given node ID and its parents.""" + nodeid = _normalize_node_id(nodeid, kind, _pathsep, _normcase) + + if kind == 'function' and nodeid.endswith(']'): + funcid, sep, parameterized = nodeid.partition('[') + if not sep: + print(nodeid) + raise NotImplementedError + yield (nodeid, sep + parameterized, 'subtest') + nodeid = funcid + + parentid, _, name = nodeid.rpartition('::') + if not parentid: + if kind is None: + # TODO: Is this really possible with plugins? + yield (nodeid, name, kind) + return + # TODO: What to do? We expect at least a filename and a name. + print(nodeid) + raise NotImplementedError + yield (nodeid, name, kind) + + # Extract the suites. + while '::' in parentid: + suiteid = parentid + parentid, _, name = parentid.rpartition('::') + yield (suiteid, name, 'suite') + + # Extract the file and folders. + fileid = parentid + parentid, _, filename = fileid.rpartition(_pathsep) + yield (fileid, filename, 'file') + # We're guaranteed at least one (the test root). + while _pathsep in parentid: + folderid = parentid + parentid, _, foldername = folderid.rpartition(_pathsep) + yield (folderid, foldername, 'folder') + testroot = None # TODO: For now we fill it in later, if needed. + yield (parentid, testroot, 'folder') + + +def _normalize_node_id(nodeid, kind, _pathsep, _normcase): """Return the canonical form for the given node ID.""" while '::()::' in nodeid: nodeid = nodeid.replace('::()::', '::') - if _pathsep not in nodeid: - _pathsep = '/' + if kind is None: + return nodeid + + fileid, sep, remainder = nodeid.partition('::') + if sep: + # pytest works fine even if we normalize the filename. + nodeid = _normcase(fileid) + sep + remainder + if nodeid.startswith(_pathsep): print(nodeid) raise NotImplementedError From d78ff8553a97745c65f1b9df272478bae151cecb Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 15 May 2019 17:23:22 -0600 Subject: [PATCH 04/13] Return the parents from _parse_node_id(). --- .../adapter/pytest/_pytest_item.py | 96 +++++++++++-------- 1 file changed, 54 insertions(+), 42 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py index 257eb4b19959..a5a7a320ef69 100644 --- a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py +++ b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py @@ -113,53 +113,40 @@ def parse_item(item, _normcase, _pathsep): """ #_debug_item(item, showsummary=True) kind, _ = _get_item_kind(item) - # Figure out the func, suites, and subs. - (nodeid, fileid, suiteids, suites, funcid, basename, parameterized + (nodeid, parents, fileid, testfunc, parameterized ) = _parse_node_id(item.nodeid, kind, _pathsep, _normcase) - if kind == 'function': - funcname = basename - # Note: funcname does not necessarily match item.function.__name__. - # This can result from importing a test function from another module. - if suites: - testfunc = '.'.join(suites) + '.' + funcname - else: - testfunc = funcname - elif kind == 'doctest': - testfunc = None - funcname = None - else: - testfunc = None - funcname = None + # Note: testfunc does not necessarily match item.function.__name__. + # This can result from importing a test function from another module. # Figure out the file. relfile = fileid - fspath = str(item.fspath) - if not _normcase(fspath).endswith(relfile[1:]): - print(fspath) + fspath = _normcase(str(item.fspath)) + if not fspath.endswith(relfile[1:]): + print(fspath, fileid) print(relfile) raise NotImplementedError - testroot = str(item.fspath)[:-len(relfile) + 1] + testroot = fspath[:-len(relfile) + 1] location, fullname = _get_location(item, relfile, _normcase, _pathsep) if kind == 'function': if testfunc and fullname != testfunc + parameterized: print(item.nodeid) - print(fullname, suites, testfunc, parameterized) + print(fullname, testfunc, parameterized) # TODO: What to do? raise NotImplementedError elif kind == 'doctest': - if testfunc and fullname != testfunc + parameterized: + if (testfunc and fullname != testfunc and + fullname != '[doctest] ' + testfunc): print(item.nodeid) print(fullname, testfunc) # TODO: What to do? raise NotImplementedError + testfunc = None # Sort out the parent. - if parameterized: - parentid = funcid - elif suites: - parentid = suiteids[-1] + if parents: + parentid, _, _ = parents[0] else: - parentid = fileid + parentid = None # Sort out markers. # See: https://docs.pytest.org/en/latest/reference.html#marks @@ -189,7 +176,8 @@ def parse_item(item, _normcase, _pathsep): markers=sorted(markers) if markers else None, parentid=parentid, ) - return test, suiteids + suiteids = reversed([nid for nid, _, kind in parents if kind == 'suite']) + return test, list(suiteids) def _get_location(item, relfile, _normcase, _pathsep): @@ -231,27 +219,51 @@ def _find_location(srcfile, lineno, relfile, func, _pathsep): return srcfile, lineno -def _parse_node_id(nodeid, kind, _pathsep, _normcase): +def _parse_node_id(testid, kind, _pathsep, _normcase): """Return the components of the given node ID, in heirarchical order.""" - nodes = iter(_iter_nodes(nodeid, kind, _pathsep, _normcase)) - nodeid, name, kind = next(nodes) - if kind == 'subtest': - funcid, name, _ = next(nodes) - parameterized = nodeid[len(funcid):] + nodes = iter(_iter_nodes(testid, kind, _pathsep, _normcase)) + + testid, name, kind = next(nodes) + parents = [] + parameterized = None + if kind == 'doctest': + parents = list(nodes) + fileid, _, _ = parents[0] + return testid, parents, fileid, name, parameterized + elif kind is None: + fullname = None else: - funcid = nodeid - parameterized = '' + if kind == 'subtest': + node = next(nodes) + parents.append(node) + funcid, funcname, _ = node + parameterized = testid[len(funcid):] + elif kind == 'function': + funcname = name + else: + print(testid, kind) + raise NotImplementedError + fullname = funcname - suites = [] - suiteids = [] - for parentid, pname, kind in nodes: + for node in nodes: + parents.append(node) + parentid, name, kind = node if kind == 'file': fileid = parentid break - suites.insert(0, pname) - suiteids.insert(0, parentid) + elif fullname is None: + # We don't guess how to interpret the node ID for these tests. + continue + elif kind == 'suite': + fullname = name + '.' + fullname + else: + print(testid, node) + raise NotImplementedError + else: + fileid = None + parents.extend(nodes) # Add the rest in as-is. - return nodeid, fileid, suiteids, suites, funcid, name, parameterized + return testid, parents, fileid, fullname, parameterized or '' def _iter_nodes(nodeid, kind, _pathsep, _normcase): From 80f790b908d039e01307a5fa1e2298ac45a52897 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 15 May 2019 17:26:19 -0600 Subject: [PATCH 05/13] Return the parents from parse_item(). --- pythonFiles/testing_tools/adapter/pytest/_discovery.py | 10 ++++++---- .../testing_tools/adapter/pytest/_pytest_item.py | 3 +-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/pytest/_discovery.py b/pythonFiles/testing_tools/adapter/pytest/_discovery.py index 1370298ffd6b..52b829909bc8 100644 --- a/pythonFiles/testing_tools/adapter/pytest/_discovery.py +++ b/pythonFiles/testing_tools/adapter/pytest/_discovery.py @@ -74,8 +74,9 @@ def pytest_collection_modifyitems(self, session, config, items): self._started = True self._tests.reset() for item in items: - test, suiteids = parse_item(item, self.NORMCASE, self.PATHSEP) - self._tests.add_test(test, suiteids) + test, parents = parse_item(item, self.NORMCASE, self.PATHSEP) + suiteids = reversed([nid for nid, _, kind in parents if kind == 'suite']) + self._tests.add_test(test, list(suiteids)) # This hook is not specified in the docs, so we also provide # the "modifyitems" hook just in case. @@ -88,8 +89,9 @@ def pytest_collection_finish(self, session): return self._tests.reset() for item in items: - test, suiteids = parse_item(item, self.NORMCASE, self.PATHSEP) - self._tests.add_test(test, suiteids) + test, parents = parse_item(item, self.NORMCASE, self.PATHSEP) + suiteids = reversed([nid for nid, _, kind in parents if kind == 'suite']) + self._tests.add_test(test, list(suiteids)) class DiscoveredTests(object): diff --git a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py index a5a7a320ef69..b05a84a1880e 100644 --- a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py +++ b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py @@ -176,8 +176,7 @@ def parse_item(item, _normcase, _pathsep): markers=sorted(markers) if markers else None, parentid=parentid, ) - suiteids = reversed([nid for nid, _, kind in parents if kind == 'suite']) - return test, list(suiteids) + return test, parents def _get_location(item, relfile, _normcase, _pathsep): From 93d459f3a2149971a29b4883831f73a98405f9cb Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 16 May 2019 09:09:48 -0600 Subject: [PATCH 06/13] Add a fix_path() testing helper. --- .../adapter/pytest/test_discovery.py | 84 ++++++++++--------- 1 file changed, 44 insertions(+), 40 deletions(-) diff --git a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py index 8beba9c743b2..1d6143c6c54a 100644 --- a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py +++ b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py @@ -22,6 +22,10 @@ ) +def fix_path(nodeid): + return nodeid.replace('/', os.path.sep) + + class StubPyTest(StubProxy): def __init__(self, stub=None): @@ -301,9 +305,9 @@ def test_modifyitems(self): config = StubPytestConfig(stub) collector = TestCollector(tests=discovered) - testroot = '/a/b/c'.replace('/', os.path.sep) - relfile1 = './test_spam.py'.replace('/', os.path.sep) - relfile2 = 'x/y/z/test_eggs.py'.replace('/', os.path.sep) + testroot = fix_path('/a/b/c') + relfile1 = fix_path('./test_spam.py') + relfile2 = fix_path('x/y/z/test_eggs.py') relfileid2 = os.path.join('.', relfile2) collector.pytest_collection_modifyitems(session, config, [ @@ -474,8 +478,8 @@ def test_finish(self): stub = Stub() discovered = StubDiscoveredTests(stub) session = StubPytestSession(stub) - testroot = '/a/b/c'.replace('/', os.path.sep) - relfile = 'x/y/z/test_eggs.py'.replace('/', os.path.sep) + testroot = fix_path('/a/b/c') + relfile = fix_path('x/y/z/test_eggs.py') relfileid = os.path.join('.', relfile) session.items = [ StubFunctionItem( @@ -516,10 +520,10 @@ def test_doctest(self): stub = Stub() discovered = StubDiscoveredTests(stub) session = StubPytestSession(stub) - testroot = '/a/b/c'.replace('/', os.path.sep) - doctestfile = 'x/test_doctest.txt'.replace('/', os.path.sep) + testroot = fix_path('/a/b/c') + doctestfile = fix_path('x/test_doctest.txt') doctestfileid = os.path.join('.', doctestfile) - relfile = 'x/y/z/test_eggs.py'.replace('/', os.path.sep) + relfile = fix_path('x/y/z/test_eggs.py') relfileid = os.path.join('.', relfile) session.items = [ StubDoctestItem( @@ -625,8 +629,8 @@ def test_nested_brackets(self): stub = Stub() discovered = StubDiscoveredTests(stub) session = StubPytestSession(stub) - testroot = '/a/b/c'.replace('/', os.path.sep) - relfile = 'x/y/z/test_eggs.py'.replace('/', os.path.sep) + testroot = fix_path('/a/b/c') + relfile = fix_path('x/y/z/test_eggs.py') relfileid = os.path.join('.', relfile) session.items = [ StubFunctionItem( @@ -667,8 +671,8 @@ def test_nested_suite(self): stub = Stub() discovered = StubDiscoveredTests(stub) session = StubPytestSession(stub) - testroot = '/a/b/c'.replace('/', os.path.sep) - relfile = 'x/y/z/test_eggs.py'.replace('/', os.path.sep) + testroot = fix_path('/a/b/c') + relfile = fix_path('x/y/z/test_eggs.py') relfileid = os.path.join('.', relfile) session.items = [ StubFunctionItem( @@ -760,8 +764,8 @@ def test_mysterious_parens(self): stub = Stub() discovered = StubDiscoveredTests(stub) session = StubPytestSession(stub) - testroot = '/a/b/c'.replace('/', os.path.sep) - relfile = 'x/y/z/test_eggs.py'.replace('/', os.path.sep) + testroot = fix_path('/a/b/c') + relfile = fix_path('x/y/z/test_eggs.py') relfileid = os.path.join('.', relfile) session.items = [ StubFunctionItem( @@ -804,10 +808,10 @@ def test_imported_test(self): stub = Stub() discovered = StubDiscoveredTests(stub) session = StubPytestSession(stub) - testroot = '/a/b/c'.replace('/', os.path.sep) - relfile = 'x/y/z/test_eggs.py'.replace('/', os.path.sep) + testroot = fix_path('/a/b/c') + relfile = fix_path('x/y/z/test_eggs.py') relfileid = os.path.join('.', relfile) - srcfile = 'x/y/z/_extern.py'.replace('/', os.path.sep) + srcfile = fix_path('x/y/z/_extern.py') session.items = [ StubFunctionItem( stub, @@ -871,7 +875,7 @@ def test_imported_test(self): class DiscoveredTestsTests(unittest.TestCase): def test_list(self): - testroot = '/a/b/c'.replace('/', os.path.sep) + testroot = fix_path('/a/b/c') relfile = 'test_spam.py' relfileid = os.path.join('.', relfile) tests = [ @@ -924,7 +928,7 @@ def test_list(self): self.assertEqual(snapshot, expected) def test_reset(self): - testroot = '/a/b/c'.replace('/', os.path.sep) + testroot = fix_path('/a/b/c') discovered = DiscoveredTests() discovered.add_test( TestInfo( @@ -949,8 +953,8 @@ def test_reset(self): self.assertEqual(after, (0, 0)) def test_parents(self): - testroot = '/a/b/c'.replace('/', os.path.sep) - relfile = 'x/y/z/test_spam.py'.replace('/', os.path.sep) + testroot = fix_path('/a/b/c') + relfile = fix_path('x/y/z/test_spam.py') relfileid = os.path.join('.', relfile) tests = [ TestInfo( @@ -1000,25 +1004,25 @@ def test_parents(self): name=testroot, ), ParentInfo( - id='./x'.replace('/', os.path.sep), + id=fix_path('./x'), kind='folder', name='x', root=testroot, parentid='.', ), ParentInfo( - id='./x/y'.replace('/', os.path.sep), + id=fix_path('./x/y'), kind='folder', name='y', root=testroot, - parentid='./x'.replace('/', os.path.sep), + parentid=fix_path('./x'), ), ParentInfo( - id='./x/y/z'.replace('/', os.path.sep), + id=fix_path('./x/y/z'), kind='folder', name='z', root=testroot, - parentid='./x/y'.replace('/', os.path.sep), + parentid=fix_path('./x/y'), ), ParentInfo( id=relfileid, @@ -1051,7 +1055,7 @@ def test_parents(self): ]) def test_add_test_simple(self): - testroot = '/a/b/c'.replace('/', os.path.sep) + testroot = fix_path('/a/b/c') test = TestInfo( id='test_spam.py::test_spam', name='test_spam', @@ -1091,7 +1095,7 @@ def test_add_test_simple(self): def test_multiroot(self): # the first root - testroot1 = '/a/b/c'.replace('/', os.path.sep) + testroot1 = fix_path('/a/b/c') relfile1 = 'test_spam.py' relfileid1 = os.path.join('.', relfile1) alltests = [ @@ -1112,7 +1116,7 @@ def test_multiroot(self): [], ] # the second root - testroot2 = '/x/y/z'.replace('/', os.path.sep) + testroot2 = fix_path('/x/y/z') relfile2 = 'w/test_eggs.py' relfileid2 = os.path.join('.', relfile2) alltests.extend([ @@ -1190,7 +1194,7 @@ def test_multiroot(self): name=testroot2, ), ParentInfo( - id='./w'.replace('/', os.path.sep), + id=fix_path('./w'), kind='folder', name='w', root=testroot2, @@ -1214,9 +1218,9 @@ def test_multiroot(self): def test_doctest(self): stub = Stub() - testroot = '/a/b/c'.replace('/', os.path.sep) - doctestfile = './x/test_doctest.txt'.replace('/', os.path.sep) - relfile = './x/y/z/test_eggs.py'.replace('/', os.path.sep) + testroot = fix_path('/a/b/c') + doctestfile = fix_path('./x/test_doctest.txt') + relfile = fix_path('./x/y/z/test_eggs.py') alltests = [ TestInfo( id=doctestfile + '::test_doctest.txt', @@ -1284,7 +1288,7 @@ def test_doctest(self): name=testroot, ), ParentInfo( - id='./x'.replace('/', os.path.sep), + id=fix_path('./x'), kind='folder', name='x', root=testroot, @@ -1298,18 +1302,18 @@ def test_doctest(self): parentid=os.path.dirname(doctestfile), ), ParentInfo( - id='./x/y'.replace('/', os.path.sep), + id=fix_path('./x/y'), kind='folder', name='y', root=testroot, - parentid='./x'.replace('/', os.path.sep), + parentid=fix_path('./x'), ), ParentInfo( - id='./x/y/z'.replace('/', os.path.sep), + id=fix_path('./x/y/z'), kind='folder', name='z', root=testroot, - parentid='./x/y'.replace('/', os.path.sep), + parentid=fix_path('./x/y'), ), ParentInfo( id=relfile, @@ -1324,8 +1328,8 @@ def test_nested_suite_simple(self): stub = Stub() discovered = StubDiscoveredTests(stub) session = StubPytestSession(stub) - testroot = '/a/b/c'.replace('/', os.path.sep) - relfile = './test_eggs.py'.replace('/', os.path.sep) + testroot = fix_path('/a/b/c') + relfile = fix_path('./test_eggs.py') alltests = [ TestInfo( id=relfile + '::TestOuter::TestInner::test_spam', From 0fe9c424237381590b961a11d17fe72f79891ea7 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 16 May 2019 09:50:24 -0600 Subject: [PATCH 07/13] Pass the parents to add_test(). --- .../adapter/pytest/_discovery.py | 12 +- .../adapter/pytest/_pytest_item.py | 2 + .../adapter/pytest/test_discovery.py | 225 ++++++++++++++---- 3 files changed, 181 insertions(+), 58 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/pytest/_discovery.py b/pythonFiles/testing_tools/adapter/pytest/_discovery.py index 52b829909bc8..69408d95bc08 100644 --- a/pythonFiles/testing_tools/adapter/pytest/_discovery.py +++ b/pythonFiles/testing_tools/adapter/pytest/_discovery.py @@ -75,8 +75,7 @@ def pytest_collection_modifyitems(self, session, config, items): self._tests.reset() for item in items: test, parents = parse_item(item, self.NORMCASE, self.PATHSEP) - suiteids = reversed([nid for nid, _, kind in parents if kind == 'suite']) - self._tests.add_test(test, list(suiteids)) + self._tests.add_test(test, parents) # This hook is not specified in the docs, so we also provide # the "modifyitems" hook just in case. @@ -90,8 +89,7 @@ def pytest_collection_finish(self, session): self._tests.reset() for item in items: test, parents = parse_item(item, self.NORMCASE, self.PATHSEP) - suiteids = reversed([nid for nid, _, kind in parents if kind == 'suite']) - self._tests.add_test(test, list(suiteids)) + self._tests.add_test(test, parents) class DiscoveredTests(object): @@ -115,8 +113,10 @@ def reset(self): self._parents = {} self._tests = [] - def add_test(self, test, suiteids): + def add_test(self, test, parents): """Add the given test and its parents.""" + parents = list(parents) + suiteids = list(reversed([nid for nid, _, kind in parents if kind == 'suite'])) parentid = self._ensure_parent(test.path, test.parentid, suiteids) test = test._replace(parentid=parentid) if not test.id.startswith('.' + os.path.sep): @@ -175,7 +175,7 @@ def _ensure_suites(self, fullsuite, rootdir, fileid, suiteids): raise NotImplementedError return None if len(suiteids) != fullsuite.count('.') + 1: - print(suiteids) + print(suiteids, fullsuite) # TODO: What to do? raise NotImplementedError diff --git a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py index b05a84a1880e..c33a82631b44 100644 --- a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py +++ b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py @@ -176,6 +176,8 @@ def parse_item(item, _normcase, _pathsep): markers=sorted(markers) if markers else None, parentid=parentid, ) + if parents and parents[-1] == ('.', None, 'folder'): # This should always be true? + parents[-1] = ('.', testroot, 'folder') return test, parents diff --git a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py index 1d6143c6c54a..9430a5abd4ff 100644 --- a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py +++ b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py @@ -80,8 +80,8 @@ def parents(self): def reset(self): self.add_call('reset', None, None) - def add_test(self, test, suiteids): - self.add_call('add_test', None, {'test': test, 'suiteids': suiteids}) + def add_test(self, test, parents): + self.add_call('add_test', None, {'test': test, 'parents': parents}) class FakeFunc(object): @@ -375,7 +375,11 @@ def test_modifyitems(self): self.assertEqual(stub.calls, [ ('discovered.reset', None, None), ('discovered.add_test', None, dict( - suiteids=[relfile1 + '::SpamTests'], + parents=[ + (relfile1 + '::SpamTests', 'SpamTests', 'suite'), + (relfile1, 'test_spam.py', 'file'), + ('.', testroot, 'folder'), + ], test=TestInfo( id=relfile1 + '::SpamTests::test_one', name='test_one', @@ -391,7 +395,11 @@ def test_modifyitems(self): ), )), ('discovered.add_test', None, dict( - suiteids=[relfile1 + '::SpamTests'], + parents=[ + (relfile1 + '::SpamTests', 'SpamTests', 'suite'), + (relfile1, 'test_spam.py', 'file'), + ('.', testroot, 'folder'), + ], test=TestInfo( id=relfile1 + '::SpamTests::test_other', name='test_other', @@ -407,7 +415,10 @@ def test_modifyitems(self): ), )), ('discovered.add_test', None, dict( - suiteids=[], + parents=[ + (relfile1, 'test_spam.py', 'file'), + ('.', testroot, 'folder'), + ], test=TestInfo( id=relfile1 + '::test_all', name='test_all', @@ -423,7 +434,11 @@ def test_modifyitems(self): ), )), ('discovered.add_test', None, dict( - suiteids=[], + parents=[ + (relfile1 + '::test_each', 'test_each', 'function'), + (relfile1, 'test_spam.py', 'file'), + ('.', testroot, 'folder'), + ], test=TestInfo( id=relfile1 + '::test_each[10-10]', name='test_each[10-10]', @@ -439,8 +454,15 @@ def test_modifyitems(self): ), )), ('discovered.add_test', None, dict( - suiteids=[relfileid2 + '::All', - relfileid2 + '::All::BasicTests'], + parents=[ + (relfileid2 + '::All::BasicTests', 'BasicTests', 'suite'), + (relfileid2 + '::All', 'All', 'suite'), + (relfileid2, 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), + ('.', testroot, 'folder'), + ], test=TestInfo( id=relfileid2 + '::All::BasicTests::test_first', name='test_first', @@ -456,8 +478,16 @@ def test_modifyitems(self): ), )), ('discovered.add_test', None, dict( - suiteids=[relfileid2 + '::All', - relfileid2 + '::All::BasicTests'], + parents=[ + (relfileid2 + '::All::BasicTests::test_each', 'test_each', 'function'), + (relfileid2 + '::All::BasicTests', 'BasicTests', 'suite'), + (relfileid2 + '::All', 'All', 'suite'), + (relfileid2, 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), + ('.', testroot, 'folder'), + ], test=TestInfo( id=relfileid2 + '::All::BasicTests::test_each[1+2-3]', name='test_each[1+2-3]', @@ -499,7 +529,14 @@ def test_finish(self): self.assertEqual(stub.calls, [ ('discovered.reset', None, None), ('discovered.add_test', None, dict( - suiteids=[relfileid + '::SpamTests'], + parents=[ + (relfileid + '::SpamTests', 'SpamTests', 'suite'), + (relfileid, 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), + ('.', testroot, 'folder'), + ], test=TestInfo( id=relfileid + '::SpamTests::test_spam', name='test_spam', @@ -564,7 +601,11 @@ def test_doctest(self): self.assertEqual(stub.calls, [ ('discovered.reset', None, None), ('discovered.add_test', None, dict( - suiteids=[], + parents=[ + (doctestfileid, 'test_doctest.txt', 'file'), + (fix_path('./x'), 'x', 'folder'), + ('.', testroot, 'folder'), + ], test=TestInfo( id=doctestfileid + '::test_doctest.txt', name='test_doctest.txt', @@ -579,7 +620,13 @@ def test_doctest(self): ), )), ('discovered.add_test', None, dict( - suiteids=[], + parents=[ + (relfileid, 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), + ('.', testroot, 'folder'), + ], test=TestInfo( id=relfileid + '::test_eggs', name='test_eggs', @@ -594,7 +641,13 @@ def test_doctest(self): ), )), ('discovered.add_test', None, dict( - suiteids=[], + parents=[ + (relfileid, 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), + ('.', testroot, 'folder'), + ], test=TestInfo( id=relfileid + '::test_eggs.TestSpam', name='test_eggs.TestSpam', @@ -609,7 +662,13 @@ def test_doctest(self): ), )), ('discovered.add_test', None, dict( - suiteids=[], + parents=[ + (relfileid, 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), + ('.', testroot, 'folder'), + ], test=TestInfo( id=relfileid + '::test_eggs.TestSpam.TestEggs', name='test_eggs.TestSpam.TestEggs', @@ -650,7 +709,15 @@ def test_nested_brackets(self): self.assertEqual(stub.calls, [ ('discovered.reset', None, None), ('discovered.add_test', None, dict( - suiteids=[relfileid + '::SpamTests'], + parents=[ + (relfileid + '::SpamTests::test_spam', 'test_spam', 'function'), + (relfileid + '::SpamTests', 'SpamTests', 'suite'), + (relfileid, 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), + ('.', testroot, 'folder'), + ], test=TestInfo( id=relfileid + '::SpamTests::test_spam[a-[b]-c]', name='test_spam[a-[b]-c]', @@ -692,10 +759,15 @@ def test_nested_suite(self): self.assertEqual(stub.calls, [ ('discovered.reset', None, None), ('discovered.add_test', None, dict( - suiteids=[ - relfileid + '::SpamTests', - relfileid + '::SpamTests::Ham', - relfileid + '::SpamTests::Ham::Eggs', + parents=[ + (relfileid + '::SpamTests::Ham::Eggs', 'Eggs', 'suite'), + (relfileid + '::SpamTests::Ham', 'Ham', 'suite'), + (relfileid + '::SpamTests', 'SpamTests', 'suite'), + (relfileid, 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), + ('.', testroot, 'folder'), ], test=TestInfo( id=relfileid + '::SpamTests::Ham::Eggs::test_spam', @@ -743,7 +815,14 @@ def normcase(path): self.assertEqual(stub.calls, [ ('discovered.reset', None, None), ('discovered.add_test', None, dict( - suiteids=[r'.\x\y\z\test_eggs.py::SpamTests'], + parents=[ + (r'.\x\y\z\test_eggs.py::SpamTests', 'SpamTests', 'suite'), + (r'.\x\y\z\test_eggs.py', 'test_eggs.py', 'file'), + (r'.\x\y\z', 'z', 'folder'), + (r'.\x\y', 'y', 'folder'), + (r'.\x', 'x', 'folder'), + ('.', testroot, 'folder'), + ], test=TestInfo( id=r'.\x\y\z\test_eggs.py::SpamTests::test_spam', name='test_spam', @@ -785,7 +864,14 @@ def test_mysterious_parens(self): self.assertEqual(stub.calls, [ ('discovered.reset', None, None), ('discovered.add_test', None, dict( - suiteids=[relfileid + '::SpamTests'], + parents=[ + (relfileid + '::SpamTests', 'SpamTests', 'suite'), + (relfileid, 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), + ('.', testroot, 'folder'), + ], test=TestInfo( id=relfileid + '::SpamTests::test_spam', name='test_spam', @@ -838,7 +924,14 @@ def test_imported_test(self): self.assertEqual(stub.calls, [ ('discovered.reset', None, None), ('discovered.add_test', None, dict( - suiteids=[relfileid + '::SpamTests'], + parents=[ + (relfileid + '::SpamTests', 'SpamTests', 'suite'), + (relfileid, 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), + ('.', testroot, 'folder'), + ], test=TestInfo( id=relfileid + '::SpamTests::test_spam', name='test_spam', @@ -854,7 +947,13 @@ def test_imported_test(self): ), )), ('discovered.add_test', None, dict( - suiteids=[], + parents=[ + (relfileid, 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), + ('.', testroot, 'folder'), + ], test=TestInfo( id=relfileid + '::test_ham', name='test_ham', @@ -906,18 +1005,22 @@ def test_list(self): parentid=relfile + '::All::BasicTests', ), ] - allsuiteids = [ - [], - [relfile + '::All', - relfile + '::All::BasicTests', + allparents= [ + [(relfileid, relfile, 'file'), + ('.', testroot, 'folder'), + ], + [(relfileid + '::All::BasicTests', 'BasicTests', 'suite'), + (relfileid + '::All', 'All', 'suite'), + (relfileid, relfile, 'file'), + ('.', testroot, 'folder'), ], ] expected = [test._replace(id=os.path.join('.', test.id), parentid=os.path.join('.', test.parentid)) for test in tests] discovered = DiscoveredTests() - for test, suiteids in zip(tests, allsuiteids): - discovered.add_test(test, suiteids) + for test, parents in zip(tests, allparents): + discovered.add_test(test, parents) size = len(discovered) items = [discovered[0], discovered[1]] snapshot = list(discovered) @@ -932,7 +1035,7 @@ def test_reset(self): discovered = DiscoveredTests() discovered.add_test( TestInfo( - id='test_spam.py::test_each', + id='./test_spam.py::test_each', name='test_each', path=TestPath( root=testroot, @@ -941,9 +1044,11 @@ def test_reset(self): ), source='{}:{}'.format('test_spam.py', 11), markers=[], - parentid='test_spam.py', + parentid='./test_spam.py', ), - []) + [('./test_spam.py', 'test_spam.py', 'file'), + ('.', testroot, 'folder'), + ]) before = len(discovered), len(discovered.parents) discovered.reset() @@ -984,15 +1089,22 @@ def test_parents(self): parentid=relfile + '::All::BasicTests', ), ] - allsuiteids = [ - [], - [relfile + '::All', - relfile + '::All::BasicTests', + allparents= [ + [(relfileid, relfile, 'file'), + ('.', testroot, 'folder'), + ], + [(relfileid + '::All::BasicTests', 'BasicTests', 'suite'), + (relfileid + '::All', 'All', 'suite'), + (relfileid, 'test_spam.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), + ('.', testroot, 'folder'), ], ] discovered = DiscoveredTests() - for test, suiteids in zip(tests, allsuiteids): - discovered.add_test(test, suiteids) + for test, parents in zip(tests, allparents): + discovered.add_test(test, parents) parents = discovered.parents @@ -1112,8 +1224,10 @@ def test_multiroot(self): parentid=relfile1, ), ] - allsuiteids = [ - [], + allparents = [ + [(relfileid1, 'test_spam.py', 'file'), + ('.', testroot1, 'folder'), + ], ] # the second root testroot2 = fix_path('/x/y/z') @@ -1133,14 +1247,17 @@ def test_multiroot(self): parentid=relfile2 + '::BasicTests', ), ]) - allsuiteids.extend([ - [relfile2 + '::BasicTests', + allparents.extend([ + [(relfileid2 + '::BasicTests', 'BasicTests', 'suite'), + (relfileid2, 'test_eggs.py', 'file'), + (fix_path('./w'), 'w', 'folder'), + ('.', testroot2, 'folder'), ], ]) discovered = DiscoveredTests() - for test, suiteids in zip(alltests, allsuiteids): - discovered.add_test(test, suiteids) + for test, parents in zip(alltests, allparents): + discovered.add_test(test, parents) tests = list(discovered) parents = discovered.parents @@ -1356,18 +1473,22 @@ def test_nested_suite_simple(self): parentid=relfile + '::TestOuter::TestInner', ), ] - allsuiteids = [ - [relfile + '::TestOuter', - relfile + '::TestOuter::TestInner', + allparents= [ + [(relfile + '::TestOuter::TestInner', 'TestInner', 'suite'), + (relfile + '::TestOuter', 'TestOuter', 'suite'), + (relfile, 'test_eggs.py', 'file'), + ('.', testroot, 'folder'), ], - [relfile + '::TestOuter', - relfile + '::TestOuter::TestInner', + [(relfile + '::TestOuter::TestInner', 'TestInner', 'suite'), + (relfile + '::TestOuter', 'TestOuter', 'suite'), + (relfile, 'test_eggs.py', 'file'), + ('.', testroot, 'folder'), ], ] discovered = DiscoveredTests() - for test, suiteids in zip(alltests, allsuiteids): - discovered.add_test(test, suiteids) + for test, parents in zip(alltests, allparents): + discovered.add_test(test, parents) tests = list(discovered) parents = discovered.parents From 0c4ff21e5cc611b7994256706bafc612e1eacfd1 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 16 May 2019 10:35:28 -0600 Subject: [PATCH 08/13] Pass the parents to _ensure_parents(). --- .../adapter/pytest/_discovery.py | 97 +++++-------------- .../adapter/pytest/test_discovery.py | 56 ++++++++--- 2 files changed, 66 insertions(+), 87 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/pytest/_discovery.py b/pythonFiles/testing_tools/adapter/pytest/_discovery.py index 69408d95bc08..d6ac7f670805 100644 --- a/pythonFiles/testing_tools/adapter/pytest/_discovery.py +++ b/pythonFiles/testing_tools/adapter/pytest/_discovery.py @@ -115,86 +115,33 @@ def reset(self): def add_test(self, test, parents): """Add the given test and its parents.""" - parents = list(parents) - suiteids = list(reversed([nid for nid, _, kind in parents if kind == 'suite'])) - parentid = self._ensure_parent(test.path, test.parentid, suiteids) + parentid = self._ensure_parent(test.path, parents) + # Updating the parent ID and the test ID aren't necessary if the + # provided test and parents (from the test collector) are + # properly generated. However, we play it safe here. test = test._replace(parentid=parentid) if not test.id.startswith('.' + os.path.sep): test = test._replace(id=os.path.join('.', test.id)) self._tests.append(test) - def _ensure_parent(self, path, parentid, suiteids): - if not parentid.startswith('.' + os.path.sep): - parentid = os.path.join('.', parentid) - fileid = self._ensure_file(path.root, path.relfile) + def _ensure_parent(self, path, parents): rootdir = path.root - if not path.func: - return parentid - - fullsuite, _, funcname = path.func.rpartition('.') - suiteid = self._ensure_suites(fullsuite, rootdir, fileid, suiteids) - parent = suiteid if suiteid else fileid - - if path.sub: - if (rootdir, parentid) not in self._parents: - funcinfo = ParentInfo(parentid, 'function', funcname, - rootdir, parent) - self._parents[(rootdir, parentid)] = funcinfo - elif parent != parentid: - print(parent, parentid) - # TODO: What to do? - raise NotImplementedError - return parentid - - def _ensure_file(self, rootdir, relfile): - if (rootdir, '.') not in self._parents: - self._parents[(rootdir, '.')] = ParentInfo('.', 'folder', rootdir) - if relfile.startswith('.' + os.path.sep): - fileid = relfile - else: - fileid = relfile = os.path.join('.', relfile) - - if (rootdir, fileid) not in self._parents: - folderid, filebase = os.path.split(fileid) - fileinfo = ParentInfo(fileid, 'file', filebase, rootdir, folderid) - self._parents[(rootdir, fileid)] = fileinfo - - while folderid != '.' and (rootdir, folderid) not in self._parents: - parentid, name = os.path.split(folderid) - folderinfo = ParentInfo(folderid, 'folder', name, rootdir, parentid) - self._parents[(rootdir, folderid)] = folderinfo - folderid = parentid - return relfile - - def _ensure_suites(self, fullsuite, rootdir, fileid, suiteids): - if not fullsuite: - if suiteids: - print(suiteids) - # TODO: What to do? - raise NotImplementedError - return None - if len(suiteids) != fullsuite.count('.') + 1: - print(suiteids, fullsuite) - # TODO: What to do? - raise NotImplementedError - - suiteid = suiteids.pop() - if not suiteid.startswith('.' + os.path.sep): - suiteid = os.path.join('.', suiteid) - final = suiteid - while '.' in fullsuite and (rootdir, suiteid) not in self._parents: - parentid = suiteids.pop() - if not parentid.startswith('.' + os.path.sep): + _parents = iter(parents) + nodeid, name, kind = next(_parents) + # As in add_test(), the node ID *should* already be correct. + if nodeid != '.' and not nodeid.startswith('.' + os.path.sep): + nodeid = os.path.join('.', nodeid) + _parentid = nodeid + for parentid, parentname, parentkind in _parents: + # As in add_test(), the parent ID *should* already be correct. + if parentid != '.' and not parentid.startswith('.' + os.path.sep): parentid = os.path.join('.', parentid) - fullsuite, _, name = fullsuite.rpartition('.') - suiteinfo = ParentInfo(suiteid, 'suite', name, rootdir, parentid) - self._parents[(rootdir, suiteid)] = suiteinfo - - suiteid = parentid - else: - name = fullsuite - suiteinfo = ParentInfo(suiteid, 'suite', name, rootdir, fileid) - if (rootdir, suiteid) not in self._parents: - self._parents[(rootdir, suiteid)] = suiteinfo - return final + info = ParentInfo(nodeid, kind, name, rootdir, parentid) + self._parents[(rootdir, nodeid)] = info + nodeid, name, kind = parentid, parentname, parentkind + assert nodeid == '.' + info = ParentInfo(nodeid, kind, name=rootdir) + self._parents[(rootdir, nodeid)] = info + + return _parentid diff --git a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py index 9430a5abd4ff..2cbf163c6ed4 100644 --- a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py +++ b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py @@ -1006,7 +1006,8 @@ def test_list(self): ), ] allparents= [ - [(relfileid, relfile, 'file'), + [(relfileid + '::test_each', 'test_each', 'function'), + (relfileid, relfile, 'file'), ('.', testroot, 'folder'), ], [(relfileid + '::All::BasicTests', 'BasicTests', 'suite'), @@ -1090,7 +1091,8 @@ def test_parents(self): ), ] allparents= [ - [(relfileid, relfile, 'file'), + [(relfileid + '::test_each', 'test_each', 'function'), + (relfileid, relfile, 'file'), ('.', testroot, 'folder'), ], [(relfileid + '::All::BasicTests', 'BasicTests', 'suite'), @@ -1168,24 +1170,29 @@ def test_parents(self): def test_add_test_simple(self): testroot = fix_path('/a/b/c') + relfile = 'test_spam.py' + relfileid = os.path.join('.', relfile) test = TestInfo( - id='test_spam.py::test_spam', + id=relfile + '::test_spam', name='test_spam', path=TestPath( root=testroot, - relfile='test_spam.py', + relfile=relfile, func='test_spam', ), - source='{}:{}'.format('test_spam.py', 11), + source='{}:{}'.format(relfile, 11), markers=[], - parentid='test_spam.py', + parentid=relfile, ) expected = test._replace(id=os.path.join('.', test.id), - parentid=os.path.join('.', test.parentid)) + parentid=relfileid) discovered = DiscoveredTests() before = list(discovered), discovered.parents - discovered.add_test(test, []) + discovered.add_test(test, [ + (relfile, relfile, 'file'), + ('.', testroot, 'folder'), + ]) after = list(discovered), discovered.parents self.maxDiff = None @@ -1197,9 +1204,9 @@ def test_add_test_simple(self): name=testroot, ), ParentInfo( - id=os.path.join('.', 'test_spam.py'), + id=relfileid, kind='file', - name='test_spam.py', + name=relfile, root=testroot, parentid='.', ), @@ -1389,10 +1396,35 @@ def test_doctest(self): parentid=relfile, ), ] + allparents = [ + [(doctestfile, 'test_doctest.txt', 'file'), + (fix_path('./x'), 'x', 'folder'), + ('.', testroot, 'folder'), + ], + [(relfile, 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), + ('.', testroot, 'folder'), + ], + [(relfile, 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), + ('.', testroot, 'folder'), + ], + [(relfile, 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), + ('.', testroot, 'folder'), + ], + ] + discovered = DiscoveredTests() - for test in alltests: - discovered.add_test(test, []) + for test, parents in zip(alltests, allparents): + discovered.add_test(test, parents) tests = list(discovered) parents = discovered.parents From 076228430dedb01d28223f2871a5ddcd3d8d9cfe Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 16 May 2019 10:45:36 -0600 Subject: [PATCH 09/13] Move DiscoveredTests out of the pytest code. --- .../testing_tools/adapter/discovery.py | 64 ++ .../adapter/pytest/_discovery.py | 60 +- .../adapter/pytest/test_discovery.py | 589 +---------------- .../testing_tools/adapter/test_discovery.py | 595 ++++++++++++++++++ 4 files changed, 662 insertions(+), 646 deletions(-) create mode 100644 pythonFiles/testing_tools/adapter/discovery.py create mode 100644 pythonFiles/tests/testing_tools/adapter/test_discovery.py diff --git a/pythonFiles/testing_tools/adapter/discovery.py b/pythonFiles/testing_tools/adapter/discovery.py new file mode 100644 index 000000000000..15196a6b0beb --- /dev/null +++ b/pythonFiles/testing_tools/adapter/discovery.py @@ -0,0 +1,64 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +from __future__ import absolute_import, print_function + +import os.path + +from .info import ParentInfo + + + +class DiscoveredTests(object): + """A container for the discovered tests and their parents.""" + + def __init__(self): + self.reset() + + def __len__(self): + return len(self._tests) + + def __getitem__(self, index): + return self._tests[index] + + @property + def parents(self): + return sorted(self._parents.values(), key=lambda v: (v.root or v.name, v.id)) + + def reset(self): + """Clear out any previously discovered tests.""" + self._parents = {} + self._tests = [] + + def add_test(self, test, parents): + """Add the given test and its parents.""" + parentid = self._ensure_parent(test.path, parents) + # Updating the parent ID and the test ID aren't necessary if the + # provided test and parents (from the test collector) are + # properly generated. However, we play it safe here. + test = test._replace(parentid=parentid) + if not test.id.startswith('.' + os.path.sep): + test = test._replace(id=os.path.join('.', test.id)) + self._tests.append(test) + + def _ensure_parent(self, path, parents): + rootdir = path.root + + _parents = iter(parents) + nodeid, name, kind = next(_parents) + # As in add_test(), the node ID *should* already be correct. + if nodeid != '.' and not nodeid.startswith('.' + os.path.sep): + nodeid = os.path.join('.', nodeid) + _parentid = nodeid + for parentid, parentname, parentkind in _parents: + # As in add_test(), the parent ID *should* already be correct. + if parentid != '.' and not parentid.startswith('.' + os.path.sep): + parentid = os.path.join('.', parentid) + info = ParentInfo(nodeid, kind, name, rootdir, parentid) + self._parents[(rootdir, nodeid)] = info + nodeid, name, kind = parentid, parentname, parentkind + assert nodeid == '.' + info = ParentInfo(nodeid, kind, name=rootdir) + self._parents[(rootdir, nodeid)] = info + + return _parentid diff --git a/pythonFiles/testing_tools/adapter/pytest/_discovery.py b/pythonFiles/testing_tools/adapter/pytest/_discovery.py index d6ac7f670805..5efac3a388ec 100644 --- a/pythonFiles/testing_tools/adapter/pytest/_discovery.py +++ b/pythonFiles/testing_tools/adapter/pytest/_discovery.py @@ -8,8 +8,7 @@ import pytest -from .. import util -from ..info import ParentInfo +from .. import util, discovery from ._pytest_item import parse_item @@ -63,7 +62,7 @@ class TestCollector(object): def __init__(self, tests=None): if tests is None: - tests = DiscoveredTests() + tests = discovery.DiscoveredTests() self._tests = tests self._started = False @@ -90,58 +89,3 @@ def pytest_collection_finish(self, session): for item in items: test, parents = parse_item(item, self.NORMCASE, self.PATHSEP) self._tests.add_test(test, parents) - - -class DiscoveredTests(object): - """A container for the discovered tests and their parents.""" - - def __init__(self): - self.reset() - - def __len__(self): - return len(self._tests) - - def __getitem__(self, index): - return self._tests[index] - - @property - def parents(self): - return sorted(self._parents.values(), key=lambda v: (v.root or v.name, v.id)) - - def reset(self): - """Clear out any previously discovered tests.""" - self._parents = {} - self._tests = [] - - def add_test(self, test, parents): - """Add the given test and its parents.""" - parentid = self._ensure_parent(test.path, parents) - # Updating the parent ID and the test ID aren't necessary if the - # provided test and parents (from the test collector) are - # properly generated. However, we play it safe here. - test = test._replace(parentid=parentid) - if not test.id.startswith('.' + os.path.sep): - test = test._replace(id=os.path.join('.', test.id)) - self._tests.append(test) - - def _ensure_parent(self, path, parents): - rootdir = path.root - - _parents = iter(parents) - nodeid, name, kind = next(_parents) - # As in add_test(), the node ID *should* already be correct. - if nodeid != '.' and not nodeid.startswith('.' + os.path.sep): - nodeid = os.path.join('.', nodeid) - _parentid = nodeid - for parentid, parentname, parentkind in _parents: - # As in add_test(), the parent ID *should* already be correct. - if parentid != '.' and not parentid.startswith('.' + os.path.sep): - parentid = os.path.join('.', parentid) - info = ParentInfo(nodeid, kind, name, rootdir, parentid) - self._parents[(rootdir, nodeid)] = info - nodeid, name, kind = parentid, parentname, parentkind - assert nodeid == '.' - info = ParentInfo(nodeid, kind, name=rootdir) - self._parents[(rootdir, nodeid)] = info - - return _parentid diff --git a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py index 2cbf163c6ed4..979ba50d70c2 100644 --- a/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py +++ b/pythonFiles/tests/testing_tools/adapter/pytest/test_discovery.py @@ -17,9 +17,7 @@ from ....util import Stub, StubProxy from testing_tools.adapter.info import TestInfo, TestPath, ParentInfo -from testing_tools.adapter.pytest._discovery import ( - discover, TestCollector, DiscoveredTests - ) +from testing_tools.adapter.pytest._discovery import discover, TestCollector def fix_path(nodeid): @@ -969,588 +967,3 @@ def test_imported_test(self): ), )), ]) - - -class DiscoveredTestsTests(unittest.TestCase): - - def test_list(self): - testroot = fix_path('/a/b/c') - relfile = 'test_spam.py' - relfileid = os.path.join('.', relfile) - tests = [ - TestInfo( - id=relfile + '::test_each[10-10]', - name='test_each[10-10]', - path=TestPath( - root=testroot, - relfile=relfile, - func='test_each', - sub=['[10-10]'], - ), - source='{}:{}'.format(relfile, 10), - markers=None, - parentid=relfile + '::test_each', - ), - TestInfo( - id=relfile + '::All::BasicTests::test_first', - name='test_first', - path=TestPath( - root=testroot, - relfile=relfile, - func='All.BasicTests.test_first', - sub=None, - ), - source='{}:{}'.format(relfile, 62), - markers=None, - parentid=relfile + '::All::BasicTests', - ), - ] - allparents= [ - [(relfileid + '::test_each', 'test_each', 'function'), - (relfileid, relfile, 'file'), - ('.', testroot, 'folder'), - ], - [(relfileid + '::All::BasicTests', 'BasicTests', 'suite'), - (relfileid + '::All', 'All', 'suite'), - (relfileid, relfile, 'file'), - ('.', testroot, 'folder'), - ], - ] - expected = [test._replace(id=os.path.join('.', test.id), - parentid=os.path.join('.', test.parentid)) - for test in tests] - discovered = DiscoveredTests() - for test, parents in zip(tests, allparents): - discovered.add_test(test, parents) - size = len(discovered) - items = [discovered[0], discovered[1]] - snapshot = list(discovered) - - self.maxDiff = None - self.assertEqual(size, 2) - self.assertEqual(items, expected) - self.assertEqual(snapshot, expected) - - def test_reset(self): - testroot = fix_path('/a/b/c') - discovered = DiscoveredTests() - discovered.add_test( - TestInfo( - id='./test_spam.py::test_each', - name='test_each', - path=TestPath( - root=testroot, - relfile='test_spam.py', - func='test_each', - ), - source='{}:{}'.format('test_spam.py', 11), - markers=[], - parentid='./test_spam.py', - ), - [('./test_spam.py', 'test_spam.py', 'file'), - ('.', testroot, 'folder'), - ]) - - before = len(discovered), len(discovered.parents) - discovered.reset() - after = len(discovered), len(discovered.parents) - - self.assertEqual(before, (1, 2)) - self.assertEqual(after, (0, 0)) - - def test_parents(self): - testroot = fix_path('/a/b/c') - relfile = fix_path('x/y/z/test_spam.py') - relfileid = os.path.join('.', relfile) - tests = [ - TestInfo( - id=relfile + '::test_each[10-10]', - name='test_each[10-10]', - path=TestPath( - root=testroot, - relfile=relfile, - func='test_each', - sub=['[10-10]'], - ), - source='{}:{}'.format(relfile, 10), - markers=None, - parentid=relfile + '::test_each', - ), - TestInfo( - id=relfile + '::All::BasicTests::test_first', - name='test_first', - path=TestPath( - root=testroot, - relfile=relfile, - func='All.BasicTests.test_first', - sub=None, - ), - source='{}:{}'.format(relfile, 61), - markers=None, - parentid=relfile + '::All::BasicTests', - ), - ] - allparents= [ - [(relfileid + '::test_each', 'test_each', 'function'), - (relfileid, relfile, 'file'), - ('.', testroot, 'folder'), - ], - [(relfileid + '::All::BasicTests', 'BasicTests', 'suite'), - (relfileid + '::All', 'All', 'suite'), - (relfileid, 'test_spam.py', 'file'), - (fix_path('./x/y/z'), 'z', 'folder'), - (fix_path('./x/y'), 'y', 'folder'), - (fix_path('./x'), 'x', 'folder'), - ('.', testroot, 'folder'), - ], - ] - discovered = DiscoveredTests() - for test, parents in zip(tests, allparents): - discovered.add_test(test, parents) - - parents = discovered.parents - - self.maxDiff = None - self.assertEqual(parents, [ - ParentInfo( - id='.', - kind='folder', - name=testroot, - ), - ParentInfo( - id=fix_path('./x'), - kind='folder', - name='x', - root=testroot, - parentid='.', - ), - ParentInfo( - id=fix_path('./x/y'), - kind='folder', - name='y', - root=testroot, - parentid=fix_path('./x'), - ), - ParentInfo( - id=fix_path('./x/y/z'), - kind='folder', - name='z', - root=testroot, - parentid=fix_path('./x/y'), - ), - ParentInfo( - id=relfileid, - kind='file', - name=os.path.basename(relfile), - root=testroot, - parentid=os.path.dirname(relfileid), - ), - ParentInfo( - id=relfileid + '::All', - kind='suite', - name='All', - root=testroot, - parentid=relfileid, - ), - ParentInfo( - id=relfileid + '::All::BasicTests', - kind='suite', - name='BasicTests', - root=testroot, - parentid=relfileid + '::All', - ), - ParentInfo( - id=relfileid + '::test_each', - kind='function', - name='test_each', - root=testroot, - parentid=relfileid, - ), - ]) - - def test_add_test_simple(self): - testroot = fix_path('/a/b/c') - relfile = 'test_spam.py' - relfileid = os.path.join('.', relfile) - test = TestInfo( - id=relfile + '::test_spam', - name='test_spam', - path=TestPath( - root=testroot, - relfile=relfile, - func='test_spam', - ), - source='{}:{}'.format(relfile, 11), - markers=[], - parentid=relfile, - ) - expected = test._replace(id=os.path.join('.', test.id), - parentid=relfileid) - discovered = DiscoveredTests() - - before = list(discovered), discovered.parents - discovered.add_test(test, [ - (relfile, relfile, 'file'), - ('.', testroot, 'folder'), - ]) - after = list(discovered), discovered.parents - - self.maxDiff = None - self.assertEqual(before, ([], [])) - self.assertEqual(after, ([expected], [ - ParentInfo( - id='.', - kind='folder', - name=testroot, - ), - ParentInfo( - id=relfileid, - kind='file', - name=relfile, - root=testroot, - parentid='.', - ), - ])) - - def test_multiroot(self): - # the first root - testroot1 = fix_path('/a/b/c') - relfile1 = 'test_spam.py' - relfileid1 = os.path.join('.', relfile1) - alltests = [ - TestInfo( - id=relfile1 + '::test_spam', - name='test_spam', - path=TestPath( - root=testroot1, - relfile=relfile1, - func='test_spam', - ), - source='{}:{}'.format(relfile1, 10), - markers=[], - parentid=relfile1, - ), - ] - allparents = [ - [(relfileid1, 'test_spam.py', 'file'), - ('.', testroot1, 'folder'), - ], - ] - # the second root - testroot2 = fix_path('/x/y/z') - relfile2 = 'w/test_eggs.py' - relfileid2 = os.path.join('.', relfile2) - alltests.extend([ - TestInfo( - id=relfile2 + 'BasicTests::test_first', - name='test_first', - path=TestPath( - root=testroot2, - relfile=relfile2, - func='BasicTests.test_first', - ), - source='{}:{}'.format(relfile2, 61), - markers=[], - parentid=relfile2 + '::BasicTests', - ), - ]) - allparents.extend([ - [(relfileid2 + '::BasicTests', 'BasicTests', 'suite'), - (relfileid2, 'test_eggs.py', 'file'), - (fix_path('./w'), 'w', 'folder'), - ('.', testroot2, 'folder'), - ], - ]) - - discovered = DiscoveredTests() - for test, parents in zip(alltests, allparents): - discovered.add_test(test, parents) - tests = list(discovered) - parents = discovered.parents - - self.maxDiff = None - self.assertEqual(tests, [ - # the first root - TestInfo( - id=relfileid1 + '::test_spam', - name='test_spam', - path=TestPath( - root=testroot1, - relfile=relfile1, - func='test_spam', - ), - source='{}:{}'.format(relfile1, 10), - markers=[], - parentid=relfileid1, - ), - # the secondroot - TestInfo( - id=relfileid2 + 'BasicTests::test_first', - name='test_first', - path=TestPath( - root=testroot2, - relfile=relfile2, - func='BasicTests.test_first', - ), - source='{}:{}'.format(relfile2, 61), - markers=[], - parentid=relfileid2 + '::BasicTests', - ), - ]) - self.assertEqual(parents, [ - # the first root - ParentInfo( - id='.', - kind='folder', - name=testroot1, - ), - ParentInfo( - id=relfileid1, - kind='file', - name=os.path.basename(relfile1), - root=testroot1, - parentid=os.path.dirname(relfileid1), - ), - # the secondroot - ParentInfo( - id='.', - kind='folder', - name=testroot2, - ), - ParentInfo( - id=fix_path('./w'), - kind='folder', - name='w', - root=testroot2, - parentid='.', - ), - ParentInfo( - id=relfileid2, - kind='file', - name=os.path.basename(relfile2), - root=testroot2, - parentid=os.path.dirname(relfileid2), - ), - ParentInfo( - id=relfileid2 + '::BasicTests', - kind='suite', - name='BasicTests', - root=testroot2, - parentid=relfileid2, - ), - ]) - - def test_doctest(self): - stub = Stub() - testroot = fix_path('/a/b/c') - doctestfile = fix_path('./x/test_doctest.txt') - relfile = fix_path('./x/y/z/test_eggs.py') - alltests = [ - TestInfo( - id=doctestfile + '::test_doctest.txt', - name='test_doctest.txt', - path=TestPath( - root=testroot, - relfile=doctestfile, - func=None, - ), - source='{}:{}'.format(doctestfile, 0), - markers=[], - parentid=doctestfile, - ), - # With --doctest-modules - TestInfo( - id=relfile + '::test_eggs', - name='test_eggs', - path=TestPath( - root=testroot, - relfile=relfile, - func=None, - ), - source='{}:{}'.format(relfile, 0), - markers=[], - parentid=relfile, - ), - TestInfo( - id=relfile + '::test_eggs.TestSpam', - name='test_eggs.TestSpam', - path=TestPath( - root=testroot, - relfile=relfile, - func=None, - ), - source='{}:{}'.format(relfile, 12), - markers=[], - parentid=relfile, - ), - TestInfo( - id=relfile + '::test_eggs.TestSpam.TestEggs', - name='test_eggs.TestSpam.TestEggs', - path=TestPath( - root=testroot, - relfile=relfile, - func=None, - ), - source='{}:{}'.format(relfile, 27), - markers=[], - parentid=relfile, - ), - ] - allparents = [ - [(doctestfile, 'test_doctest.txt', 'file'), - (fix_path('./x'), 'x', 'folder'), - ('.', testroot, 'folder'), - ], - [(relfile, 'test_eggs.py', 'file'), - (fix_path('./x/y/z'), 'z', 'folder'), - (fix_path('./x/y'), 'y', 'folder'), - (fix_path('./x'), 'x', 'folder'), - ('.', testroot, 'folder'), - ], - [(relfile, 'test_eggs.py', 'file'), - (fix_path('./x/y/z'), 'z', 'folder'), - (fix_path('./x/y'), 'y', 'folder'), - (fix_path('./x'), 'x', 'folder'), - ('.', testroot, 'folder'), - ], - [(relfile, 'test_eggs.py', 'file'), - (fix_path('./x/y/z'), 'z', 'folder'), - (fix_path('./x/y'), 'y', 'folder'), - (fix_path('./x'), 'x', 'folder'), - ('.', testroot, 'folder'), - ], - ] - - discovered = DiscoveredTests() - - for test, parents in zip(alltests, allparents): - discovered.add_test(test, parents) - tests = list(discovered) - parents = discovered.parents - - self.maxDiff = None - self.assertEqual(tests, alltests) - self.assertEqual(parents, [ - ParentInfo( - id='.', - kind='folder', - name=testroot, - ), - ParentInfo( - id=fix_path('./x'), - kind='folder', - name='x', - root=testroot, - parentid='.', - ), - ParentInfo( - id=doctestfile, - kind='file', - name=os.path.basename(doctestfile), - root=testroot, - parentid=os.path.dirname(doctestfile), - ), - ParentInfo( - id=fix_path('./x/y'), - kind='folder', - name='y', - root=testroot, - parentid=fix_path('./x'), - ), - ParentInfo( - id=fix_path('./x/y/z'), - kind='folder', - name='z', - root=testroot, - parentid=fix_path('./x/y'), - ), - ParentInfo( - id=relfile, - kind='file', - name=os.path.basename(relfile), - root=testroot, - parentid=os.path.dirname(relfile), - ), - ]) - - def test_nested_suite_simple(self): - stub = Stub() - discovered = StubDiscoveredTests(stub) - session = StubPytestSession(stub) - testroot = fix_path('/a/b/c') - relfile = fix_path('./test_eggs.py') - alltests = [ - TestInfo( - id=relfile + '::TestOuter::TestInner::test_spam', - name='test_spam', - path=TestPath( - root=testroot, - relfile=relfile, - func='TestOuter.TestInner.test_spam', - ), - source='{}:{}'.format(relfile, 10), - markers=None, - parentid=relfile + '::TestOuter::TestInner', - ), - TestInfo( - id=relfile + '::TestOuter::TestInner::test_eggs', - name='test_eggs', - path=TestPath( - root=testroot, - relfile=relfile, - func='TestOuter.TestInner.test_eggs', - ), - source='{}:{}'.format(relfile, 21), - markers=None, - parentid=relfile + '::TestOuter::TestInner', - ), - ] - allparents= [ - [(relfile + '::TestOuter::TestInner', 'TestInner', 'suite'), - (relfile + '::TestOuter', 'TestOuter', 'suite'), - (relfile, 'test_eggs.py', 'file'), - ('.', testroot, 'folder'), - ], - [(relfile + '::TestOuter::TestInner', 'TestInner', 'suite'), - (relfile + '::TestOuter', 'TestOuter', 'suite'), - (relfile, 'test_eggs.py', 'file'), - ('.', testroot, 'folder'), - ], - ] - - discovered = DiscoveredTests() - for test, parents in zip(alltests, allparents): - discovered.add_test(test, parents) - tests = list(discovered) - parents = discovered.parents - - self.maxDiff = None - self.assertEqual(tests, alltests) - self.assertEqual(parents, [ - ParentInfo( - id='.', - kind='folder', - name=testroot, - ), - ParentInfo( - id=relfile, - kind='file', - name=os.path.basename(relfile), - root=testroot, - parentid=os.path.dirname(relfile), - ), - ParentInfo( - id=relfile + '::TestOuter', - kind='suite', - name='TestOuter', - root=testroot, - parentid=relfile, - ), - ParentInfo( - id=relfile + '::TestOuter::TestInner', - kind='suite', - name='TestInner', - root=testroot, - parentid=relfile + '::TestOuter', - ), - ]) diff --git a/pythonFiles/tests/testing_tools/adapter/test_discovery.py b/pythonFiles/tests/testing_tools/adapter/test_discovery.py new file mode 100644 index 000000000000..01475afd4b00 --- /dev/null +++ b/pythonFiles/tests/testing_tools/adapter/test_discovery.py @@ -0,0 +1,595 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +from __future__ import absolute_import, print_function + +import os.path +import unittest + +from testing_tools.adapter.info import TestInfo, TestPath, ParentInfo +from testing_tools.adapter.discovery import DiscoveredTests + + +def fix_path(nodeid): + return nodeid.replace('/', os.path.sep) + + +class DiscoveredTestsTests(unittest.TestCase): + + def test_list(self): + testroot = fix_path('/a/b/c') + relfile = 'test_spam.py' + relfileid = os.path.join('.', relfile) + tests = [ + TestInfo( + id=relfile + '::test_each[10-10]', + name='test_each[10-10]', + path=TestPath( + root=testroot, + relfile=relfile, + func='test_each', + sub=['[10-10]'], + ), + source='{}:{}'.format(relfile, 10), + markers=None, + parentid=relfile + '::test_each', + ), + TestInfo( + id=relfile + '::All::BasicTests::test_first', + name='test_first', + path=TestPath( + root=testroot, + relfile=relfile, + func='All.BasicTests.test_first', + sub=None, + ), + source='{}:{}'.format(relfile, 62), + markers=None, + parentid=relfile + '::All::BasicTests', + ), + ] + allparents= [ + [(relfileid + '::test_each', 'test_each', 'function'), + (relfileid, relfile, 'file'), + ('.', testroot, 'folder'), + ], + [(relfileid + '::All::BasicTests', 'BasicTests', 'suite'), + (relfileid + '::All', 'All', 'suite'), + (relfileid, relfile, 'file'), + ('.', testroot, 'folder'), + ], + ] + expected = [test._replace(id=os.path.join('.', test.id), + parentid=os.path.join('.', test.parentid)) + for test in tests] + discovered = DiscoveredTests() + for test, parents in zip(tests, allparents): + discovered.add_test(test, parents) + size = len(discovered) + items = [discovered[0], discovered[1]] + snapshot = list(discovered) + + self.maxDiff = None + self.assertEqual(size, 2) + self.assertEqual(items, expected) + self.assertEqual(snapshot, expected) + + def test_reset(self): + testroot = fix_path('/a/b/c') + discovered = DiscoveredTests() + discovered.add_test( + TestInfo( + id='./test_spam.py::test_each', + name='test_each', + path=TestPath( + root=testroot, + relfile='test_spam.py', + func='test_each', + ), + source='{}:{}'.format('test_spam.py', 11), + markers=[], + parentid='./test_spam.py', + ), + [('./test_spam.py', 'test_spam.py', 'file'), + ('.', testroot, 'folder'), + ]) + + before = len(discovered), len(discovered.parents) + discovered.reset() + after = len(discovered), len(discovered.parents) + + self.assertEqual(before, (1, 2)) + self.assertEqual(after, (0, 0)) + + def test_parents(self): + testroot = fix_path('/a/b/c') + relfile = fix_path('x/y/z/test_spam.py') + relfileid = os.path.join('.', relfile) + tests = [ + TestInfo( + id=relfile + '::test_each[10-10]', + name='test_each[10-10]', + path=TestPath( + root=testroot, + relfile=relfile, + func='test_each', + sub=['[10-10]'], + ), + source='{}:{}'.format(relfile, 10), + markers=None, + parentid=relfile + '::test_each', + ), + TestInfo( + id=relfile + '::All::BasicTests::test_first', + name='test_first', + path=TestPath( + root=testroot, + relfile=relfile, + func='All.BasicTests.test_first', + sub=None, + ), + source='{}:{}'.format(relfile, 61), + markers=None, + parentid=relfile + '::All::BasicTests', + ), + ] + allparents= [ + [(relfileid + '::test_each', 'test_each', 'function'), + (relfileid, relfile, 'file'), + ('.', testroot, 'folder'), + ], + [(relfileid + '::All::BasicTests', 'BasicTests', 'suite'), + (relfileid + '::All', 'All', 'suite'), + (relfileid, 'test_spam.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), + ('.', testroot, 'folder'), + ], + ] + discovered = DiscoveredTests() + for test, parents in zip(tests, allparents): + discovered.add_test(test, parents) + + parents = discovered.parents + + self.maxDiff = None + self.assertEqual(parents, [ + ParentInfo( + id='.', + kind='folder', + name=testroot, + ), + ParentInfo( + id=fix_path('./x'), + kind='folder', + name='x', + root=testroot, + parentid='.', + ), + ParentInfo( + id=fix_path('./x/y'), + kind='folder', + name='y', + root=testroot, + parentid=fix_path('./x'), + ), + ParentInfo( + id=fix_path('./x/y/z'), + kind='folder', + name='z', + root=testroot, + parentid=fix_path('./x/y'), + ), + ParentInfo( + id=relfileid, + kind='file', + name=os.path.basename(relfile), + root=testroot, + parentid=os.path.dirname(relfileid), + ), + ParentInfo( + id=relfileid + '::All', + kind='suite', + name='All', + root=testroot, + parentid=relfileid, + ), + ParentInfo( + id=relfileid + '::All::BasicTests', + kind='suite', + name='BasicTests', + root=testroot, + parentid=relfileid + '::All', + ), + ParentInfo( + id=relfileid + '::test_each', + kind='function', + name='test_each', + root=testroot, + parentid=relfileid, + ), + ]) + + def test_add_test_simple(self): + testroot = fix_path('/a/b/c') + relfile = 'test_spam.py' + relfileid = os.path.join('.', relfile) + test = TestInfo( + id=relfile + '::test_spam', + name='test_spam', + path=TestPath( + root=testroot, + relfile=relfile, + func='test_spam', + ), + source='{}:{}'.format(relfile, 11), + markers=[], + parentid=relfile, + ) + expected = test._replace(id=os.path.join('.', test.id), + parentid=relfileid) + discovered = DiscoveredTests() + + before = list(discovered), discovered.parents + discovered.add_test(test, [ + (relfile, relfile, 'file'), + ('.', testroot, 'folder'), + ]) + after = list(discovered), discovered.parents + + self.maxDiff = None + self.assertEqual(before, ([], [])) + self.assertEqual(after, ([expected], [ + ParentInfo( + id='.', + kind='folder', + name=testroot, + ), + ParentInfo( + id=relfileid, + kind='file', + name=relfile, + root=testroot, + parentid='.', + ), + ])) + + def test_multiroot(self): + # the first root + testroot1 = fix_path('/a/b/c') + relfile1 = 'test_spam.py' + relfileid1 = os.path.join('.', relfile1) + alltests = [ + TestInfo( + id=relfile1 + '::test_spam', + name='test_spam', + path=TestPath( + root=testroot1, + relfile=relfile1, + func='test_spam', + ), + source='{}:{}'.format(relfile1, 10), + markers=[], + parentid=relfile1, + ), + ] + allparents = [ + [(relfileid1, 'test_spam.py', 'file'), + ('.', testroot1, 'folder'), + ], + ] + # the second root + testroot2 = fix_path('/x/y/z') + relfile2 = 'w/test_eggs.py' + relfileid2 = os.path.join('.', relfile2) + alltests.extend([ + TestInfo( + id=relfile2 + 'BasicTests::test_first', + name='test_first', + path=TestPath( + root=testroot2, + relfile=relfile2, + func='BasicTests.test_first', + ), + source='{}:{}'.format(relfile2, 61), + markers=[], + parentid=relfile2 + '::BasicTests', + ), + ]) + allparents.extend([ + [(relfileid2 + '::BasicTests', 'BasicTests', 'suite'), + (relfileid2, 'test_eggs.py', 'file'), + (fix_path('./w'), 'w', 'folder'), + ('.', testroot2, 'folder'), + ], + ]) + + discovered = DiscoveredTests() + for test, parents in zip(alltests, allparents): + discovered.add_test(test, parents) + tests = list(discovered) + parents = discovered.parents + + self.maxDiff = None + self.assertEqual(tests, [ + # the first root + TestInfo( + id=relfileid1 + '::test_spam', + name='test_spam', + path=TestPath( + root=testroot1, + relfile=relfile1, + func='test_spam', + ), + source='{}:{}'.format(relfile1, 10), + markers=[], + parentid=relfileid1, + ), + # the secondroot + TestInfo( + id=relfileid2 + 'BasicTests::test_first', + name='test_first', + path=TestPath( + root=testroot2, + relfile=relfile2, + func='BasicTests.test_first', + ), + source='{}:{}'.format(relfile2, 61), + markers=[], + parentid=relfileid2 + '::BasicTests', + ), + ]) + self.assertEqual(parents, [ + # the first root + ParentInfo( + id='.', + kind='folder', + name=testroot1, + ), + ParentInfo( + id=relfileid1, + kind='file', + name=os.path.basename(relfile1), + root=testroot1, + parentid=os.path.dirname(relfileid1), + ), + # the secondroot + ParentInfo( + id='.', + kind='folder', + name=testroot2, + ), + ParentInfo( + id=fix_path('./w'), + kind='folder', + name='w', + root=testroot2, + parentid='.', + ), + ParentInfo( + id=relfileid2, + kind='file', + name=os.path.basename(relfile2), + root=testroot2, + parentid=os.path.dirname(relfileid2), + ), + ParentInfo( + id=relfileid2 + '::BasicTests', + kind='suite', + name='BasicTests', + root=testroot2, + parentid=relfileid2, + ), + ]) + + def test_doctest(self): + testroot = fix_path('/a/b/c') + doctestfile = fix_path('./x/test_doctest.txt') + relfile = fix_path('./x/y/z/test_eggs.py') + alltests = [ + TestInfo( + id=doctestfile + '::test_doctest.txt', + name='test_doctest.txt', + path=TestPath( + root=testroot, + relfile=doctestfile, + func=None, + ), + source='{}:{}'.format(doctestfile, 0), + markers=[], + parentid=doctestfile, + ), + # With --doctest-modules + TestInfo( + id=relfile + '::test_eggs', + name='test_eggs', + path=TestPath( + root=testroot, + relfile=relfile, + func=None, + ), + source='{}:{}'.format(relfile, 0), + markers=[], + parentid=relfile, + ), + TestInfo( + id=relfile + '::test_eggs.TestSpam', + name='test_eggs.TestSpam', + path=TestPath( + root=testroot, + relfile=relfile, + func=None, + ), + source='{}:{}'.format(relfile, 12), + markers=[], + parentid=relfile, + ), + TestInfo( + id=relfile + '::test_eggs.TestSpam.TestEggs', + name='test_eggs.TestSpam.TestEggs', + path=TestPath( + root=testroot, + relfile=relfile, + func=None, + ), + source='{}:{}'.format(relfile, 27), + markers=[], + parentid=relfile, + ), + ] + allparents = [ + [(doctestfile, 'test_doctest.txt', 'file'), + (fix_path('./x'), 'x', 'folder'), + ('.', testroot, 'folder'), + ], + [(relfile, 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), + ('.', testroot, 'folder'), + ], + [(relfile, 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), + ('.', testroot, 'folder'), + ], + [(relfile, 'test_eggs.py', 'file'), + (fix_path('./x/y/z'), 'z', 'folder'), + (fix_path('./x/y'), 'y', 'folder'), + (fix_path('./x'), 'x', 'folder'), + ('.', testroot, 'folder'), + ], + ] + + discovered = DiscoveredTests() + + for test, parents in zip(alltests, allparents): + discovered.add_test(test, parents) + tests = list(discovered) + parents = discovered.parents + + self.maxDiff = None + self.assertEqual(tests, alltests) + self.assertEqual(parents, [ + ParentInfo( + id='.', + kind='folder', + name=testroot, + ), + ParentInfo( + id=fix_path('./x'), + kind='folder', + name='x', + root=testroot, + parentid='.', + ), + ParentInfo( + id=doctestfile, + kind='file', + name=os.path.basename(doctestfile), + root=testroot, + parentid=os.path.dirname(doctestfile), + ), + ParentInfo( + id=fix_path('./x/y'), + kind='folder', + name='y', + root=testroot, + parentid=fix_path('./x'), + ), + ParentInfo( + id=fix_path('./x/y/z'), + kind='folder', + name='z', + root=testroot, + parentid=fix_path('./x/y'), + ), + ParentInfo( + id=relfile, + kind='file', + name=os.path.basename(relfile), + root=testroot, + parentid=os.path.dirname(relfile), + ), + ]) + + def test_nested_suite_simple(self): + testroot = fix_path('/a/b/c') + relfile = fix_path('./test_eggs.py') + alltests = [ + TestInfo( + id=relfile + '::TestOuter::TestInner::test_spam', + name='test_spam', + path=TestPath( + root=testroot, + relfile=relfile, + func='TestOuter.TestInner.test_spam', + ), + source='{}:{}'.format(relfile, 10), + markers=None, + parentid=relfile + '::TestOuter::TestInner', + ), + TestInfo( + id=relfile + '::TestOuter::TestInner::test_eggs', + name='test_eggs', + path=TestPath( + root=testroot, + relfile=relfile, + func='TestOuter.TestInner.test_eggs', + ), + source='{}:{}'.format(relfile, 21), + markers=None, + parentid=relfile + '::TestOuter::TestInner', + ), + ] + allparents= [ + [(relfile + '::TestOuter::TestInner', 'TestInner', 'suite'), + (relfile + '::TestOuter', 'TestOuter', 'suite'), + (relfile, 'test_eggs.py', 'file'), + ('.', testroot, 'folder'), + ], + [(relfile + '::TestOuter::TestInner', 'TestInner', 'suite'), + (relfile + '::TestOuter', 'TestOuter', 'suite'), + (relfile, 'test_eggs.py', 'file'), + ('.', testroot, 'folder'), + ], + ] + + discovered = DiscoveredTests() + for test, parents in zip(alltests, allparents): + discovered.add_test(test, parents) + tests = list(discovered) + parents = discovered.parents + + self.maxDiff = None + self.assertEqual(tests, alltests) + self.assertEqual(parents, [ + ParentInfo( + id='.', + kind='folder', + name=testroot, + ), + ParentInfo( + id=relfile, + kind='file', + name=os.path.basename(relfile), + root=testroot, + parentid=os.path.dirname(relfile), + ), + ParentInfo( + id=relfile + '::TestOuter', + kind='suite', + name='TestOuter', + root=testroot, + parentid=relfile, + ), + ParentInfo( + id=relfile + '::TestOuter::TestInner', + kind='suite', + name='TestInner', + root=testroot, + parentid=relfile + '::TestOuter', + ), + ]) From 84255e9cae18546948438b1049bb926f3877b4ab Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 6 Jun 2019 14:45:30 -0600 Subject: [PATCH 10/13] Fix case on Windows. --- pythonFiles/tests/testing_tools/adapter/test_functional.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pythonFiles/tests/testing_tools/adapter/test_functional.py b/pythonFiles/tests/testing_tools/adapter/test_functional.py index c1a8a4597643..c5be00c6bc4a 100644 --- a/pythonFiles/tests/testing_tools/adapter/test_functional.py +++ b/pythonFiles/tests/testing_tools/adapter/test_functional.py @@ -21,7 +21,8 @@ def resolve_testroot(name): - projroot = os.path.join(DATA_DIR, name) + projroot = os.path.normcase( + os.path.join(DATA_DIR, name)) return projroot, os.path.join(projroot, 'tests') From f6fb826fea72f3add0a2968ae87af16ab0625a48 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 11 Jun 2019 13:29:25 -0600 Subject: [PATCH 11/13] Add ShouldNeverReachHere exception type. --- .../adapter/pytest/_pytest_item.py | 60 +++++++++++++------ 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py index c33a82631b44..133d08b54d5a 100644 --- a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py +++ b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py @@ -102,6 +102,20 @@ from ..info import TestInfo, TestPath +class ShouldNeverReachHere(NotImplementedError): + + def __init__(self, *info): + for line in info: + if isinstance(line, str): + print(str) + else: + try: + print(*line) + except TypeError: + print(line) + NotImplementedError.__init__(self, info) + + def parse_item(item, _normcase, _pathsep): """Return (TestInfo, [suite ID]) for the given item. @@ -122,24 +136,27 @@ def parse_item(item, _normcase, _pathsep): relfile = fileid fspath = _normcase(str(item.fspath)) if not fspath.endswith(relfile[1:]): - print(fspath, fileid) - print(relfile) - raise NotImplementedError + raise ShouldNeverReachHere( + (fspath, fileid), + relfile, + ) testroot = fspath[:-len(relfile) + 1] location, fullname = _get_location(item, relfile, _normcase, _pathsep) if kind == 'function': if testfunc and fullname != testfunc + parameterized: - print(item.nodeid) - print(fullname, testfunc, parameterized) # TODO: What to do? - raise NotImplementedError + raise ShouldNeverReachHere( + item.nodeid, + (fullname, testfunc, parameterized), + ) elif kind == 'doctest': if (testfunc and fullname != testfunc and fullname != '[doctest] ' + testfunc): - print(item.nodeid) - print(fullname, testfunc) # TODO: What to do? - raise NotImplementedError + raise ShouldNeverReachHere( + item.nodeid, + (fullname, testfunc), + ) testfunc = None # Sort out the parent. @@ -242,8 +259,9 @@ def _parse_node_id(testid, kind, _pathsep, _normcase): elif kind == 'function': funcname = name else: - print(testid, kind) - raise NotImplementedError + raise ShouldNeverReachHere( + (testid, kind), + ) fullname = funcname for node in nodes: @@ -258,8 +276,9 @@ def _parse_node_id(testid, kind, _pathsep, _normcase): elif kind == 'suite': fullname = name + '.' + fullname else: - print(testid, node) - raise NotImplementedError + raise ShouldNeverReachHere( + (testid, node), + ) else: fileid = None parents.extend(nodes) # Add the rest in as-is. @@ -274,8 +293,9 @@ def _iter_nodes(nodeid, kind, _pathsep, _normcase): if kind == 'function' and nodeid.endswith(']'): funcid, sep, parameterized = nodeid.partition('[') if not sep: - print(nodeid) - raise NotImplementedError + raise ShouldNeverReachHere( + nodeid, + ) yield (nodeid, sep + parameterized, 'subtest') nodeid = funcid @@ -286,8 +306,9 @@ def _iter_nodes(nodeid, kind, _pathsep, _normcase): yield (nodeid, name, kind) return # TODO: What to do? We expect at least a filename and a name. - print(nodeid) - raise NotImplementedError + raise ShouldNeverReachHere( + nodeid, + ) yield (nodeid, name, kind) # Extract the suites. @@ -322,8 +343,9 @@ def _normalize_node_id(nodeid, kind, _pathsep, _normcase): nodeid = _normcase(fileid) + sep + remainder if nodeid.startswith(_pathsep): - print(nodeid) - raise NotImplementedError + raise ShouldNeverReachHere( + nodeid, + ) if not nodeid.startswith('.' + _pathsep): nodeid = '.' + _pathsep + nodeid return nodeid From 01bf922b3ac566b241c55676942f95ceca5b448f Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 11 Jun 2019 13:41:56 -0600 Subject: [PATCH 12/13] Resolve all the TODO comments. --- .../testing_tools/adapter/pytest/_pytest_item.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py index 133d08b54d5a..c3b46bda649f 100644 --- a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py +++ b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py @@ -144,7 +144,6 @@ def parse_item(item, _normcase, _pathsep): location, fullname = _get_location(item, relfile, _normcase, _pathsep) if kind == 'function': if testfunc and fullname != testfunc + parameterized: - # TODO: What to do? raise ShouldNeverReachHere( item.nodeid, (fullname, testfunc, parameterized), @@ -152,7 +151,6 @@ def parse_item(item, _normcase, _pathsep): elif kind == 'doctest': if (testfunc and fullname != testfunc and fullname != '[doctest] ' + testfunc): - # TODO: What to do? raise ShouldNeverReachHere( item.nodeid, (fullname, testfunc), @@ -178,7 +176,7 @@ def parse_item(item, _normcase, _pathsep): markers.add('skip-if') elif marker.name == 'xfail': markers.add('expected-failure') - # TODO: Support other markers? + # We can add support for other markers as we need them? test = TestInfo( id=nodeid, @@ -302,10 +300,11 @@ def _iter_nodes(nodeid, kind, _pathsep, _normcase): parentid, _, name = nodeid.rpartition('::') if not parentid: if kind is None: - # TODO: Is this really possible with plugins? + # This assumes that plugins can generate nodes that do not + # have a parent. All the builtin nodes have one. yield (nodeid, name, kind) return - # TODO: What to do? We expect at least a filename and a name. + # We expect at least a filename and a name. raise ShouldNeverReachHere( nodeid, ) @@ -326,7 +325,8 @@ def _iter_nodes(nodeid, kind, _pathsep, _normcase): folderid = parentid parentid, _, foldername = folderid.rpartition(_pathsep) yield (folderid, foldername, 'folder') - testroot = None # TODO: For now we fill it in later, if needed. + # We set the actual test root later at the bottom of parse_item(). + testroot = None yield (parentid, testroot, 'folder') @@ -358,7 +358,7 @@ def _get_item_kind(item): elif isinstance(item, _pytest.unittest.TestCaseFunction): return 'function', True elif isinstance(item, pytest.Function): - # TODO: maybe return "method", "subtest", etc.? + # We *could* be more specific, e.g. "method", "subtest". return 'function', False else: return None, False @@ -370,7 +370,6 @@ def _get_item_kind(item): def _debug_item(item, showsummary=False): item._debugging = True try: - # TODO: Make a PytestTest class to wrap the item? summary = { 'id': item.nodeid, 'kind': _get_item_kind(item), From a13dffb84e0d51a07d0f7f915468541c4ab6138a Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Tue, 11 Jun 2019 14:33:42 -0600 Subject: [PATCH 13/13] Make ShouldNeverReachHere an informative function instead. --- .../adapter/pytest/_pytest_item.py | 114 +++++++++++++----- 1 file changed, 81 insertions(+), 33 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py index c3b46bda649f..92b48e820d4e 100644 --- a/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py +++ b/pythonFiles/testing_tools/adapter/pytest/_pytest_item.py @@ -102,10 +102,20 @@ from ..info import TestInfo, TestPath -class ShouldNeverReachHere(NotImplementedError): - - def __init__(self, *info): - for line in info: +def should_never_reach_here(node, *extra): + """Indicates a code path we should never reach.""" + print('The Python extension has run into an unexpected situation') + print('while processing a pytest node during test discovery. Please') + print('Please open an issue at:') + print(' https://github.com/microsoft/vscode-python/issues') + print('and paste the following output there.') + print() + for field, info in _summarize_item(node): + print('{}: {}'.format(field, info)) + if extra: + print() + print('extra info:') + for info in extra: if isinstance(line, str): print(str) else: @@ -113,7 +123,15 @@ def __init__(self, *info): print(*line) except TypeError: print(line) - NotImplementedError.__init__(self, info) + print() + print('traceback:') + import traceback + traceback.print_stack() + + msg = 'Unexpected pytest node (see printed output).' + exc = NotImplementedError(msg) + exc.node = node + return exc def parse_item(item, _normcase, _pathsep): @@ -136,24 +154,28 @@ def parse_item(item, _normcase, _pathsep): relfile = fileid fspath = _normcase(str(item.fspath)) if not fspath.endswith(relfile[1:]): - raise ShouldNeverReachHere( - (fspath, fileid), + raise should_never_reach_here( + item, + fspath, relfile, ) testroot = fspath[:-len(relfile) + 1] location, fullname = _get_location(item, relfile, _normcase, _pathsep) if kind == 'function': if testfunc and fullname != testfunc + parameterized: - raise ShouldNeverReachHere( - item.nodeid, - (fullname, testfunc, parameterized), + raise should_never_reach_here( + item, + fullname, + testfunc, + parameterized, ) elif kind == 'doctest': if (testfunc and fullname != testfunc and fullname != '[doctest] ' + testfunc): - raise ShouldNeverReachHere( - item.nodeid, - (fullname, testfunc), + raise should_never_reach_here( + item, + fullname, + testfunc, ) testfunc = None @@ -257,8 +279,9 @@ def _parse_node_id(testid, kind, _pathsep, _normcase): elif kind == 'function': funcname = name else: - raise ShouldNeverReachHere( - (testid, kind), + raise should_never_reach_here( + testid, + kind, ) fullname = funcname @@ -274,8 +297,9 @@ def _parse_node_id(testid, kind, _pathsep, _normcase): elif kind == 'suite': fullname = name + '.' + fullname else: - raise ShouldNeverReachHere( - (testid, node), + raise should_never_reach_here( + testid, + node, ) else: fileid = None @@ -291,7 +315,7 @@ def _iter_nodes(nodeid, kind, _pathsep, _normcase): if kind == 'function' and nodeid.endswith(']'): funcid, sep, parameterized = nodeid.partition('[') if not sep: - raise ShouldNeverReachHere( + raise should_never_reach_here( nodeid, ) yield (nodeid, sep + parameterized, 'subtest') @@ -305,7 +329,7 @@ def _iter_nodes(nodeid, kind, _pathsep, _normcase): yield (nodeid, name, kind) return # We expect at least a filename and a name. - raise ShouldNeverReachHere( + raise should_never_reach_here( nodeid, ) yield (nodeid, name, kind) @@ -343,7 +367,7 @@ def _normalize_node_id(nodeid, kind, _pathsep, _normcase): nodeid = _normcase(fileid) + sep + remainder if nodeid.startswith(_pathsep): - raise ShouldNeverReachHere( + raise should_never_reach_here( nodeid, ) if not nodeid.startswith('.' + _pathsep): @@ -367,22 +391,46 @@ def _get_item_kind(item): ############################# # useful for debugging +_FIELDS = [ + 'nodeid', + 'kind', + 'class', + 'name', + 'fspath', + 'location', + 'function', + 'markers', + 'user_properties', + 'attrnames', + ] + + +def _summarize_item(item): + if not hasattr(item, 'nodeid'): + yield 'nodeid', item + return + + for field in _FIELDS: + try: + if field == 'kind': + yield field,_get_item_kind(item) + elif field == 'class': + yield field, item.__class__.__name__ + elif field == 'markers': + yield field, item.own_markers + #yield field, list(item.iter_markers()) + elif field == 'attrnames': + yield field, dir(item) + else: + yield field, getattr(item, field, '') + except Exception as exc: + yield field, '' + + def _debug_item(item, showsummary=False): item._debugging = True try: - summary = { - 'id': item.nodeid, - 'kind': _get_item_kind(item), - 'class': item.__class__.__name__, - 'name': item.name, - 'fspath': item.fspath, - 'location': item.location, - 'func': getattr(item, 'function', None), - 'markers': item.own_markers, - #'markers': list(item.iter_markers()), - 'props': item.user_properties, - 'attrnames': dir(item), - } + summary = dict(_summarize_item(item)) finally: item._debugging = False